Executables and Linking CS449 Fall 2017
Remember External Linkage Scope? #include <stdio.h> int global = 0; void foo(); int main() { } foo(); printf( global=%d\n, global); return 0; <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc./main.c./foo.c >>./a.out global=10 How did global in foo.c find global in main.c? How did foo() in main.c find foo() in foo.c? Where is the code for printf() and how does the call to printf() find it? What does a.out look like stored as a file? How does it look like while executing in memory? 2
Compiler gcc C source Preprocessed source Object files Executable.c cpp cc1.o ld Preprocessor Compiler Linker
Preprocessor gcc C source Preprocessed source Object files Executable.c cpp cc1.o ld Preprocessor Compiler Linker
Preprocessor Input: A C source file with directives Output: The source after directives have been processed #include directive Copies the contents of the (header) file to (source) file Source file (files with.c extensions): Contains function/variable definitions Header file (files with.h extensions): Contains function/variable declarations and type definitions Typically included at top of.c file hence the name #include <...> or #include... <...> searches for file under standard include path (e.g. /usr/include)... searches for file under local directory or paths given as I arguments (e.g. gcc I ~/local/include main.c)
Preprocessor #define directive Defines a macro (a symbol that is expanded to some other text before compilation) #define PI 3.14 All instances of PI gets replaced by 3.14 in source #define AVG(a, b) (((a) + (b)) / 2) All instances of AVG(2, 4) gets replaced by (((2) + (4)) / 2) Not a function call, just a text replacement No argument / return type declarations Will even work on AVG( Hello, 3.14), even though the result will not make sense at all -E option produces preprocessed source file gcc E main.c dumps preprocessed code to stdout
Compiler gcc C source Preprocessed source Object files Executable.c cpp cc1.o ld Preprocessor Compiler Linker
Compiler Input: A preprocessed C source file Output: A binary object file in machine language In the process it performs: Syntax and type checking Compiler optimizations to reduce execution time and memory footprint E.g. gcc O2 main.c (applies level 2 optimizations) Code generation for given machine One object file (.o) for one C source file (.c) The c option invokes the compiler only and produces object files instead of the final linked executable gcc main.c foo.c translates to: gcc c main.c foo.c (produces.o files; this is compilation) gcc main.o foo.o (produces a.out; this is linking)
Linker gcc C source Preprocessed source Object files Executable.c cpp cc1.o ld Preprocessor Compiler Linker
Linker Input: One or more object files Output: A linked executable image Why separate out compile and link stages? Leads to shorter executable generation time Compiling is slow, linking is fast On a source code update: Need only compile changed source file and then re-link (cheap) No need to recompile the entire project (expensive) Intermediate object files can be reused A library file is a package of one or more object files e.g. C standard library, Math library, Networking library etc. Libraries can be reused across multiple projects
Memory Space of Program 0xc0000000 Stack (Automatic variables) brk Dynamic Data 0 Heap (Malloc ed Memory) Data Segment (Static variables) Text Segment ( Function Code) Executable Image Linker: generates executable image from multiple object files Executable image: copied to memory when program launched
main.o foo.o The Linking Process [Data Segment] int global; [Text Segment] Int main() { foo(); 0 } [Data Segment] [Text Segment] void foo() { global += 10; 0 } Link [Data Segment] int global; [Text Segment] Int main() { foo(); } [Text Segment] void foo() { global += 10; } Relocation: Figuring out address of main, foo, global in a.out Symbol Resolution: Replacing uses of foo, global with addresses 0 [Data Segment] a.out
thoth $ objdump -S./main.o Disassembly of section.text: 0000000000000000 <main>: The Linking Process 0: 55 push %rbp... 9: e8 00 00 00 00 callq <foo> e: 8b 15 00 00 00 00 mov... thoth $ objdump -S./foo.o Disassembly of section.text: 0000000000000000 <foo>: 0: 55 push %rbp... 14: c3 retq Reloca'on Resolu'on thoth $ gcc o./a.out./main.o./foo.o thoth $ objdump S./a.out Disassembly of section.text: 00000000004004c4 <main>: 4004c4: 55 push %rbp... 4004cd: e8 22 00 00 00 4004d2: 8b 15 04 04 20 00 mov... Reloca'on 00000000004004f4 <foo>: callq 4004f4 <foo> 4004f4: 55 push %rbp... 400508: c3 retq objdump -S: dumps text segment for object 13
The Linking Process (in Detail) Previous compiler stage embeds two tables in each object Symbol table: Stores addresses of symbol locations Where each symbol is located at (in either data or code section) Entry fields: symbol name, section name, address Relocation table: Stores addresses of symbol uses Locations of all the uses of symbol (in code section) Entry fields: symbol name, address Step 1: Relocation of object files into executable image Merge individual code / data sections into single sections Merge individual symbol / relocation tables into single tables Translate address within each object to address in final image Step 2: Symbol resolution across object files Replaces all symbol uses with addresses using merged global symbol table and relocation table
Symbol Tables (Before Linking) #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc c./main.c./foo.c >> nm./main.o U foo 00000000 B global 00000004 B global2 00000000 T main >> nm./foo.o U printf 00000000 T foo U global nm: a tool that shows the symbol table offset + section name + symbol name Symbols foo, printf undefined in main.o Symbol global undefined in foo.o 15
Relocation Tables (Before Linking) #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc c./main.c./foo.c >> objdump r./main.o [sic] OFFSET TYPE 0000000a R_X86_64_PC32 VALUE foo 00000010 R_X86_64_32 global 00000015 R_X86_64_32.rodata 00000021 R_X86_64_PC32 printf objdump -r: dumps relocation table for object Offsets store code locations using symbol For example, in main.o at address 0xa: 9: e8 00 00 00 00 (callq 00 00 00 00 <foo>) Linker will rewrite call target with final location of foo. In a.out after rewriting: 4004cd: e8 22 00 00 00 (callq 4004f4 <foo>) 16
Relocation Tables (Before Linking) #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc c./main.c./foo.c >> objdump r./foo.o [sic] OFFSET TYPE VALUE 00000006 R_X86_64_PC32 global 0000000f R_X86_64_PC32 global In foo.o (read / write of global): 4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax a: 83 c0 0a add $0xa,%eax d: 89 05 00 00 00 00 mov %eax,0x0(%rip) Linker will rewrite memory accesses with final location of global. In a.out after rewriting: 4: 8b 05 e2 03 20 00 mov 0x2003e2(%rip),%eax a: 83 c0 0a add $0xa,%eax d: 89 05 d9 03 20 00 mov %eax, 0x2003d9(%rip) 17
Symbol Tables (After Linking) #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc c./main.c./foo.c >> gcc./main.o./foo.o >> nm./a.out 00000000004004f4 T foo 00000000006008e0 B global 00000000006008e4 B global2 00000000004004c4 T main U printf@@glibc_2.2.5 Symbols foo, global defined and now have relocated addresses in memory Main.o data section relocated to 6008e0 Foo.o code section relocated to 4004f4 Symbol printf still undefined Will be defined through dynamic linking 18
Relocation Tables (After Linking) #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc c./main.c./foo.c >> objdump R./a.out [sic] OFFSET TYPE [sic] VALUE 08049674 R_X86_64_JUMP_SLOT printf Relocation table of a.out contains printf since it has not yet been resolved This will be resolved later at load time during dynamic linking 19
Quick Quiz Q: Are automatic local variables relocated and resolved? A: No. Local variables are stored in the stack and is not part of the executable image generated by linker. Q: Are internal linkage symbols relocated and resolved? A: Yes. Internal linkage symbols are part of the data segment and need to be relocated. Also, uses of the symbols need to be resolved to the final address. Q: Are static local symbols relocated and resolved? A: Yes. Static local symbols are also part of the data segment since their lifetime is static. Hence, they need to be relocated and resolved for the same reasons.
3 Ways to Link in Libraries Static Linking Copy library code into executable image at compile time Done by linker (ld) at last stage of compilation Dynamic Linking Copy library code into memory at program load time Done by dynamic linker (ld-linux.so) when program first launched Dynamic Loading Copy library code into memory in the middle of execution Done through dynamic loader (libdl.so) library by program itself DL library provides APIs to load/unload libraries into memory, search in library symbol table to perform symbol resolution
Static Linking #include <stdio.h> #include <math.h> int main() { printf( The sqrt of 9 is %f\n, sqrt(9)); return 0; } Archives /usr/lib/libc.a Object files /usr/lib/libm.a Executable.o ld Linker
Static Linking #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc -m32 -static./main.c./foo.c -lc >> ls l./a.out -rwxr-xr-x 1 wahn UNKNOWN1 639385 Sep 30 15:20./a.out >> objdump R./a.out objdump:./a.out: not a dynamic object objdump:./a.out: Invalid operation -static tells gcc linker to do static linking -lc links in library libc.a (found in /usr/lib) Static linking copies over all libraries into binary (just like with object files) That s why a.out is so large! 23
Dynamic Linking Stack /usr/lib/libc.so Shared Objects /usr/lib/libm.so libc.so libm.so Executable ld-linux.so Dynamic Linker Data (Heap) Data (Globals) Text (Code)
Dynamic Linking #include <stdio.h> int global = 0; int global2 = 0; void foo(); int main() { foo(); printf( global=%d\n, global); return 0; } <main.c> extern int global; void foo() { global += 10; } <foo.c> >> gcc m32./main.c./foo.c -lc >> ls l./a.out -rwxr-xr-x 1 wahn UNKNOWN1 4769 Sep 30 15:26./a.out >> ldd./a.out linux-gate.so.1 => (0x00110000) libc.so.6 => /lib/libc.so.6 (0x00bbd000) /lib/ld-linux.so.2 (0x00b9b000) By default gcc does dynamic linking Now a.out is much smaller! -lc: marks dependency on libc.so ldd: prints out shared library dependencies Program will not start if not satisfied 25
Dynamic Loading Stack Stack libc.so libm.so Data (Heap) Data (Heap) Data (Globals) Text (Code) dlopen( libc.so ); dlopen( libm.so ); Data (Globals) Text (Code)
Function Pointers How are function names translated to function addresses on a library call? Static linking: linker resolves address in image Dynamic linking: dynamic linker resolves address when image is loaded into memory Dynamic loading: program itself does resolving Address of function is searched in object file symbol table, with the help of dynamic loader library The type of that address is a function pointer
Dynamic Loading #include <dlfcn.h> int main() { } void *handle; int (*pf)(const char *format,...); /* load C library into memory*/ handle = dlopen("libc.so.6", RTLD_LAZY); /* find printf in symbol table */ pf = dlsym(handle, "printf"); pf("hello world!\n"); /* unload C library from memory */ dlclose(handle); return 0; >> gcc -ldl./dlsym.c >>./a.out Hello world! For when program doesn t know what libraries will be used beforehand E.g. browser plugins that start working right away after download 28
Dynamic Linking (Loading) Pros/Cons Pro: Library code is not duplicated Efficient use of file storage library code need not be duplicated in every application using it Easier to update libraries don t have to relink every application on update Con: Program binary is not self-contained Less portable program relies on certain libraries being installed on the system to run correctly Maintain multiple versions of same library (DLL Hell) if programs rely on specific versions Security challenges security properties of program change depending on libraries installed on system
Executable File Includes everything needed to run on a system Executable image (that is loaded directly into memory) Code segment Data segment (with initial values of variables) Symbol / relocation tables (if applicable) Code / Data segment may be relocated (again) through dynamic linking Executable files and object files share same format Executables are generated by the linker Object files are generated by the compiler But they need mostly the same things
Older Executable Formats a.out (Assembler OUTput) Oldest UNIX format No longer commonly used COFF (Common Object File Format) Older UNIX Format No longer commonly used
Modern Executable Formats ELF (Executable and Linkable Format) Linux/UNIX format PE (Portable Executable) Windows format Based on COFF Mach-O file Mac format
a.out exec header (describes structure) text segment (code) data segment (static lifetime data) symbol table string table (symbol name strings) text relocation table data relocation table
Header Every a.out formatted binary file begins with an exec header structure: struct exec { unsigned long unsigned long unsigned long unsigned long unsigned long unsigned long unsigned long unsigned long }; a_midmag; // format identifier a_text; // size of text segment a_data; // size of initialized data a_bss; // size of uninitialized data a_syms; // size of symbol table a_entry; // entry point of program a_trsize; // size of text relocation table a_drsize; // size of data relocation table
Linux Address Space