典型的ELF可重定位文件格式

示例代码
/* main.c */int sum(int *a, int n);int array[2] = {1, 2};int main(){int value = sum(array, 2);return value;}
/* sum.c */int sum(int *a,int n){int i, sum = 0;for (i = 0; i < n; ++i){sum += a[i];}return sum;}
符号表
编译成elf可重定位文件后,使用 readelf -all main.o 打印出如下信息
ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: REL (Relocatable file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x0Start of program headers: 0 (bytes into file)Start of section headers: 720 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 0 (bytes)Number of program headers: 0Size of section headers: 64 (bytes)Number of section headers: 12Section header string table index: 11Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .text PROGBITS 0000000000000000 000000400000000000000021 0000000000000000 AX 0 0 1[ 2] .rela.text RELA 0000000000000000 000002280000000000000030 0000000000000018 I 9 1 8[ 3] .data PROGBITS 0000000000000000 000000680000000000000008 0000000000000000 WA 0 0 8[ 4] .bss NOBITS 0000000000000000 000000700000000000000000 0000000000000000 WA 0 0 1[ 5] .comment PROGBITS 0000000000000000 00000070000000000000002a 0000000000000001 MS 0 0 1[ 6] .note.GNU-stack PROGBITS 0000000000000000 0000009a0000000000000000 0000000000000000 0 0 1[ 7] .eh_frame PROGBITS 0000000000000000 000000a00000000000000038 0000000000000000 A 0 0 8[ 8] .rela.eh_frame RELA 0000000000000000 000002580000000000000018 0000000000000018 I 9 7 8[ 9] .symtab SYMTAB 0000000000000000 000000d80000000000000120 0000000000000018 10 8 8[10] .strtab STRTAB 0000000000000000 000001f8000000000000002d 0000000000000000 0 0 1[11] .shstrtab STRTAB 0000000000000000 000002700000000000000059 0000000000000000 0 0 1Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)There are no section groups in this file.There are no program headers in this file.There is no dynamic section in this file.Relocation section '.rela.text' at offset 0x228 contains 2 entries:Offset Info Type Sym. Value Sym. Name + Addend000000000010 000800000002 R_X86_64_PC32 0000000000000000 array - 4000000000015 000b00000004 R_X86_64_PLT32 0000000000000000 sum - 4Relocation section '.rela.eh_frame' at offset 0x258 contains 1 entry:Offset Info Type Sym. Value Sym. Name + Addend000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.Symbol table '.symtab' contains 12 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c2: 0000000000000000 0 SECTION LOCAL DEFAULT 13: 0000000000000000 0 SECTION LOCAL DEFAULT 34: 0000000000000000 0 SECTION LOCAL DEFAULT 45: 0000000000000000 0 SECTION LOCAL DEFAULT 66: 0000000000000000 0 SECTION LOCAL DEFAULT 77: 0000000000000000 0 SECTION LOCAL DEFAULT 58: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 array9: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 main10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND sumNo version information found in this file.
重点关注符号表的最后4行,表示main是一个.text节(由Ndx=1代表text节)中偏移量为0的大小为33字节的函数,array为一个.data节(由Ndx=3代表data节)中偏移量为0的大小为8字节的对象,sum是一个对外部符号的引用。
符号解析
强符号与弱符号
强符号: 函数与已初始化的全局变量
弱符号: 未初始化的全局变量
Linux处理规则
objdump -dx main.o 得到以下输出
0000000000000000 <main>:0: 55 push %rbp1: 48 89 e5 mov %rsp,%rbp4: 48 83 ec 10 sub $0x10,%rsp8: be 02 00 00 00 mov $0x2,%esid: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 14 <main+0x14>10: R_X86_64_PC32 array-0x414: e8 00 00 00 00 callq 19 <main+0x19>15: R_X86_64_PLT32 sum-0x419: 89 45 fc mov %eax,-0x4(%rbp)1c: 8b 45 fc mov -0x4(%rbp),%eax1f: c9 leaveq20: c3 retq
其中第6、7行是对array的重定位条目,8、9行是对sum的重定位条目。
创建静态库
代码部分
/* multvec.c *//* $begin multvec */int multcnt = 0;void multvec(int *x, int *y,int *z, int n){int i;multcnt++;for (i = 0; i < n; i++)z[i] = x[i] * y[i];}/* $end multvec */
/* addvec.c *//* $begin addvec */int addcnt = 0;void addvec(int *x, int *y,int *z, int n){int i;addcnt++;for (i = 0; i < n; i++)z[i] = x[i] + y[i];}/* $end addvec */
/* main.c *//* $begin main */#include <stdio.h>#include "vector.h"int x[2] = {1, 2};int y[2] = {3, 4};int z[2];int main(){addvec(x, y, z, 2);printf("z = [%d %d]\n", z[0], z[1]);return 0;}/* $end main2 */
利用AR工具创建静态库gcc -c multvec.c addvec.c ar rcs libvector.a addvec.o multvec.o
使用: gcc -o prog1 main.c -L. -lvector
创建动态库
命令 gcc -shared -fpic -o libvector.so addvec.c multvec.c
其中-fpic用于生成与位置无关的代码,-shared指示链接器创造一个共享的目标文件。
使用: gcc -o prog2 main.c -L. -lvector 
可以看出动态链接比静态链接产生的代码体积小
Linux运行时加载动态库
主要包括函数 dlopen dlsym dlclose 与· dlerror
示例(来自CSAPP第七章7.11):
/* $begin dll */#include <stdio.h>#include <stdlib.h>#include <dlfcn.h>int x[2] = {1, 2};int y[2] = {3, 4};int z[2];int main(){void *handle;void (*addvec)(int *, int *, int *, int);char *error;/* Dynamically load the shared library that contains addvec() */handle = dlopen("./libvector.so", RTLD_LAZY);if (!handle) {fprintf(stderr, "%s\n", dlerror());exit(1);}/* Get a pointer to the addvec() function we just loaded */addvec = dlsym(handle, "addvec");if ((error = dlerror()) != NULL) {fprintf(stderr, "%s\n", error);exit(1);}/* Now we can call addvec() just like any other function */addvec(x, y, z, 2);printf("z = [%d %d]\n", z[0], z[1]);/* Unload the shared library */if (dlclose(handle) < 0) {fprintf(stderr, "%s\n", dlerror());exit(1);}return 0;}/* $end dll */
