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

image.png
示例代码

  1. /* main.c */
  2. int sum(int *a, int n);
  3. int array[2] = {1, 2};
  4. int main()
  5. {
  6. int value = sum(array, 2);
  7. return value;
  8. }
  1. /* sum.c */
  2. int sum(int *a,int n){
  3. int i, sum = 0;
  4. for (i = 0; i < n; ++i)
  5. {
  6. sum += a[i];
  7. }
  8. return sum;
  9. }

符号表

编译成elf可重定位文件后,使用 readelf -all main.o 打印出如下信息

  1. ELF Header:
  2. Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  3. Class: ELF64
  4. Data: 2's complement, little endian
  5. Version: 1 (current)
  6. OS/ABI: UNIX - System V
  7. ABI Version: 0
  8. Type: REL (Relocatable file)
  9. Machine: Advanced Micro Devices X86-64
  10. Version: 0x1
  11. Entry point address: 0x0
  12. Start of program headers: 0 (bytes into file)
  13. Start of section headers: 720 (bytes into file)
  14. Flags: 0x0
  15. Size of this header: 64 (bytes)
  16. Size of program headers: 0 (bytes)
  17. Number of program headers: 0
  18. Size of section headers: 64 (bytes)
  19. Number of section headers: 12
  20. Section header string table index: 11
  21. Section Headers:
  22. [Nr] Name Type Address Offset
  23. Size EntSize Flags Link Info Align
  24. [ 0] NULL 0000000000000000 00000000
  25. 0000000000000000 0000000000000000 0 0 0
  26. [ 1] .text PROGBITS 0000000000000000 00000040
  27. 0000000000000021 0000000000000000 AX 0 0 1
  28. [ 2] .rela.text RELA 0000000000000000 00000228
  29. 0000000000000030 0000000000000018 I 9 1 8
  30. [ 3] .data PROGBITS 0000000000000000 00000068
  31. 0000000000000008 0000000000000000 WA 0 0 8
  32. [ 4] .bss NOBITS 0000000000000000 00000070
  33. 0000000000000000 0000000000000000 WA 0 0 1
  34. [ 5] .comment PROGBITS 0000000000000000 00000070
  35. 000000000000002a 0000000000000001 MS 0 0 1
  36. [ 6] .note.GNU-stack PROGBITS 0000000000000000 0000009a
  37. 0000000000000000 0000000000000000 0 0 1
  38. [ 7] .eh_frame PROGBITS 0000000000000000 000000a0
  39. 0000000000000038 0000000000000000 A 0 0 8
  40. [ 8] .rela.eh_frame RELA 0000000000000000 00000258
  41. 0000000000000018 0000000000000018 I 9 7 8
  42. [ 9] .symtab SYMTAB 0000000000000000 000000d8
  43. 0000000000000120 0000000000000018 10 8 8
  44. [10] .strtab STRTAB 0000000000000000 000001f8
  45. 000000000000002d 0000000000000000 0 0 1
  46. [11] .shstrtab STRTAB 0000000000000000 00000270
  47. 0000000000000059 0000000000000000 0 0 1
  48. Key to Flags:
  49. W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  50. L (link order), O (extra OS processing required), G (group), T (TLS),
  51. C (compressed), x (unknown), o (OS specific), E (exclude),
  52. l (large), p (processor specific)
  53. There are no section groups in this file.
  54. There are no program headers in this file.
  55. There is no dynamic section in this file.
  56. Relocation section '.rela.text' at offset 0x228 contains 2 entries:
  57. Offset Info Type Sym. Value Sym. Name + Addend
  58. 000000000010 000800000002 R_X86_64_PC32 0000000000000000 array - 4
  59. 000000000015 000b00000004 R_X86_64_PLT32 0000000000000000 sum - 4
  60. Relocation section '.rela.eh_frame' at offset 0x258 contains 1 entry:
  61. Offset Info Type Sym. Value Sym. Name + Addend
  62. 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0
  63. The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
  64. Symbol table '.symtab' contains 12 entries:
  65. Num: Value Size Type Bind Vis Ndx Name
  66. 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
  67. 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
  68. 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
  69. 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
  70. 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
  71. 5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
  72. 6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
  73. 7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
  74. 8: 0000000000000000 8 OBJECT GLOBAL DEFAULT 3 array
  75. 9: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 main
  76. 10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
  77. 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND sum
  78. No version information found in this file.

重点关注符号表的最后4行,表示main是一个.text节(由Ndx=1代表text节)中偏移量为0的大小为33字节的函数,array为一个.data节(由Ndx=3代表data节)中偏移量为0的大小为8字节的对象,sum是一个对外部符号的引用。

符号解析

(c++中的符号重整)
image.png

强符号与弱符号

强符号: 函数与已初始化的全局变量
弱符号: 未初始化的全局变量
Linux处理规则
image.png
objdump -dx main.o 得到以下输出

  1. 0000000000000000 <main>:
  2. 0: 55 push %rbp
  3. 1: 48 89 e5 mov %rsp,%rbp
  4. 4: 48 83 ec 10 sub $0x10,%rsp
  5. 8: be 02 00 00 00 mov $0x2,%esi
  6. d: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 14 <main+0x14>
  7. 10: R_X86_64_PC32 array-0x4
  8. 14: e8 00 00 00 00 callq 19 <main+0x19>
  9. 15: R_X86_64_PLT32 sum-0x4
  10. 19: 89 45 fc mov %eax,-0x4(%rbp)
  11. 1c: 8b 45 fc mov -0x4(%rbp),%eax
  12. 1f: c9 leaveq
  13. 20: c3 retq

其中第6、7行是对array的重定位条目,8、9行是对sum的重定位条目。

创建静态库

代码部分

  1. /* multvec.c */
  2. /* $begin multvec */
  3. int multcnt = 0;
  4. void multvec(int *x, int *y,
  5. int *z, int n)
  6. {
  7. int i;
  8. multcnt++;
  9. for (i = 0; i < n; i++)
  10. z[i] = x[i] * y[i];
  11. }
  12. /* $end multvec */
  1. /* addvec.c */
  2. /* $begin addvec */
  3. int addcnt = 0;
  4. void addvec(int *x, int *y,
  5. int *z, int n)
  6. {
  7. int i;
  8. addcnt++;
  9. for (i = 0; i < n; i++)
  10. z[i] = x[i] + y[i];
  11. }
  12. /* $end addvec */
  1. /* main.c */
  2. /* $begin main */
  3. #include <stdio.h>
  4. #include "vector.h"
  5. int x[2] = {1, 2};
  6. int y[2] = {3, 4};
  7. int z[2];
  8. int main()
  9. {
  10. addvec(x, y, z, 2);
  11. printf("z = [%d %d]\n", z[0], z[1]);
  12. return 0;
  13. }
  14. /* $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
image.png
可以看出动态链接比静态链接产生的代码体积小

Linux运行时加载动态库

主要包括函数 dlopen dlsym dlclose 与· dlerror
示例(来自CSAPP第七章7.11):

  1. /* $begin dll */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <dlfcn.h>
  5. int x[2] = {1, 2};
  6. int y[2] = {3, 4};
  7. int z[2];
  8. int main()
  9. {
  10. void *handle;
  11. void (*addvec)(int *, int *, int *, int);
  12. char *error;
  13. /* Dynamically load the shared library that contains addvec() */
  14. handle = dlopen("./libvector.so", RTLD_LAZY);
  15. if (!handle) {
  16. fprintf(stderr, "%s\n", dlerror());
  17. exit(1);
  18. }
  19. /* Get a pointer to the addvec() function we just loaded */
  20. addvec = dlsym(handle, "addvec");
  21. if ((error = dlerror()) != NULL) {
  22. fprintf(stderr, "%s\n", error);
  23. exit(1);
  24. }
  25. /* Now we can call addvec() just like any other function */
  26. addvec(x, y, z, 2);
  27. printf("z = [%d %d]\n", z[0], z[1]);
  28. /* Unload the shared library */
  29. if (dlclose(handle) < 0) {
  30. fprintf(stderr, "%s\n", dlerror());
  31. exit(1);
  32. }
  33. return 0;
  34. }
  35. /* $end dll */