Relocatable Object Files
The running examples for this part will be the following two .c codes:
// main.c
extern swap();
int buf[2] = {1,2};
int main() {
swap();
return 0;
}// swap.c
extern int buf[];
int* bufp0 = &buf[0]
int* bufp1;
void swap() {
int temp;
bufp1 = &buf[1];
temp = *bufp0;
*bufp0 = *bufp1;
*bufp1 = temp;
}Then we compile the source files using gcc tool. In order to do the compilation only and no linking, we need to use the -c flag.
gcc -c main.c swap.cWe can inspect the symbol table section of each object file as follows:
# readelf -s main.o
Symbol table '.symtab' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
3: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 buf
4: 0000000000000000 25 FUNC GLOBAL DEFAULT 1 main
5: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND swap
and similarly for the swap.o
# readelf -s swap.o
Symbol table '.symtab' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS swap.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 .text
3: 0000000000000000 8 OBJECT GLOBAL DEFAULT 5 bufp0
4: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND buf
5: 0000000000000000 8 OBJECT GLOBAL DEFAULT 4 bufp1
6: 0000000000000000 67 FUNC GLOBAL DEFAULT 1 swapApart from the extra symbols related to the compilation chain, we have
| object file | symbol table |
|---|---|
main.o | main.c, .text, buf, main, swap |
swap.o | swap.c, .text, bufp0, buf, bufp, swap |
Note that since the variable int temp is a local variables, it is allocated on stack in the run time, so it does not appear in the symbols table. |
Also, we can inspect the relocation entries of these object files with the command readelf -r.
# readelf -r main.o
Relocation section '.rela.text' at offset 0x198 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
00000000000e 000500000004 R_X86_64_PLT32 0000000000000000 swap - 4And similarly for swap.o we will have
# readelf -r swap.o
Relocation section '.rela.text' at offset 0x1e0 contains 6 entries:
Offset Info Type Sym. Value Sym. Name + Addend
00000000000b 000400000002 R_X86_64_PC32 0000000000000000 buf + 0
000000000012 000500000002 R_X86_64_PC32 0000000000000000 bufp1 - 4
000000000019 000300000002 R_X86_64_PC32 0000000000000000 bufp0 - 4
000000000025 000500000002 R_X86_64_PC32 0000000000000000 bufp1 - 4
00000000002c 000300000002 R_X86_64_PC32 0000000000000000 bufp0 - 4
000000000037 000500000002 R_X86_64_PC32 0000000000000000 bufp1 - 4
Relocation section '.rela.data.rel' at offset 0x270 contains 1 entry:
Offset Info Type Sym. Value Sym. Name + Addend
000000000000 000400000001 R_X86_64_64 0000000000000000 buf + 0The information above signals that these object files are not linked yet, and are not ready for execution.
Un-linked Object Files
Un-linked object files are not ready for execution as their
.dataand.textregions are full of patches (placeholders) that needs to be completed by a linker.
Linking and Linker Script
Linker script has two main jobs
- To place sections like
.textand.datain appropriate memory layout, and finalize their addresses - To patch the place holders corresponding to the global variables and function calls according to their finalized address.
In our exposition here, we will use the GNU linker (ld command). The official document for this software is very well written and easy to follow (see References), and we will follow a very similar exploration path.
A linker scrip *.ld is written in the linker command syntax and specifies all required information by the linker to do the linking. The default linker scrip is compiled into the binary, and can be accessed in running the linker command with --verbose command. A custom linker script can be provided by -T flag.
A Simple Linker Script
I will not be discussing the anatomy and syntax for a linker scrip (see the GNU linker document for that). Instead I will walk through a simple example to highlight important aspects of the linking. Consider the following linker script:
/* linker.ld */
SECTIONS
{
. = 0xDEADBEEF;
.text : {
*(.text)
}
. = 0xBAADF00D;
.data : {
*(.data)
}
. = 0xFEE1DEAD;
.bss : {
*(.bss)
}
}linking the object files with this linker script with the command
ld -T linker.ld main.o swap.o -o main we will obtain the main executable.
Inspecting the contents of the symbols table we will bet
# readelf -s main
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
2: 0000000000000000 0 FILE LOCAL DEFAULT ABS swap.c
3: 00000000baadf018 8 OBJECT GLOBAL DEFAULT 5 bufp0
4: 00000000deadbf08 67 FUNC GLOBAL DEFAULT 1 swap
5: 00000000deadbeef 25 FUNC GLOBAL DEFAULT 1 main
6: 00000000baadf010 8 OBJECT GLOBAL DEFAULT 4 buf
7: 00000000fee1deb0 8 OBJECT GLOBAL DEFAULT 6 bufp1where we see that the symbol tables of the previously separate object files are now combined, and also they have their finalized values .
We can also inspect the .data and .text parts of the elf file to observe that they indeed sit in correct memory locations:
# readelf -x .data main
Hex dump of section '.data':
0xbaadf010 01000000 02000000
# readelf -x .text main
Hex dump of section '.text':
0xdeadbeef f30f1efa 554889e5 b8000000 00e80700 ....UH..........
0xdeadbeff 0000b800 0000005d c3f30f1e fa554889 .......].....UH.
0xdeadbf0f e5488d05 fd3000dc 48890592 1f342048 .H...0..H....4 H
0xdeadbf1f 8b05f330 00dc8b00 8945fc48 8b157f1f ...0.....E.H....
0xdeadbf2f 3420488b 05e03000 dc8b1289 10488b05 4 H...0......H..
0xdeadbf3f 6d1f3420 8b55fc89 10905dc3 m.4 .U....].
One interesting fact from above is that while in the linker script we have specified for the .data section to sit at 0xBAADF00D, the inspection above shows that it actually sits in the memory location 0xBAADF010. The reason is that the memory location 0xBAADFOOD is not 8 byte aligned memory address (it is not a multiple of 8). That is why the linker decides to put it in the nearest aligned memory address which is 0xBAADF010.
References
Most of the content here is a combination of stuff I learned from the following resources:
- STM32CubeIDE user guide: Section 2.2
- Computer Systems: A Programmer’s Perspective by Bryant: Chapter 7
- The GNU linker official document: Chapter 3,
Comments & reactions