免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1740 | 回复: 7
打印 上一主题 下一主题

[内核入门] 链接器、加载器、页式、段式管理 Binding 关系 [复制链接]

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:49:03
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-07-11 00:47 |只看该作者 |倒序浏览
本帖最后由 shihyu 于 2015-07-11 00:59 编辑

何谓Binding
Def: 决定程序执行的起始地址。
即:程序要在内存的哪个地方开始执行。

可能的Binding时期有三个:
1. Compiling Time
2. Loading Time
3. Execution Time
3-1 : Dynamic Binding
3-2 : Dynamic Loading

链接器( Linker )是把不同部分的代码和数据,收集、组合成为一个可加载、可执行的文件。
加载器( Loader )把可执行文件从外存装入内存并进行执行

MMU : 段式 + 页式
段式 - 逻辑地址 -> 线性地址
页式 - 线性地址 -> 实地址

_________________________________________________________________________________

我被上面情况给搞的有点乱

1.
Binging 三个时期程序地址都算是虚地址?
Compiling Time 地址是由编译器计算出来?
Loading Time   是由 加载器 计算出地址?
Execution Time : 地址是 Local Address + Base Register ?

2.
目前Linux 是用MMU 段式 + 页式 ?   
Linux 跟 Binding三个时期有关系嘛?
Binding三个时期技术是早期的技术嘛? 目前有机会使用到嘛?

3.
链接器( Linker )是把不同部分的代码和数据,收集、组合成为一个可加载、可执行的文件。


我认知编译出执行文件使用 objdump -d 就可以看到虚地址 , 就地址是ld   Linker  计算出来的嘛? 如果是它是属于哪个Binding?
gcc -g test.c
使用 objdump -d ./a.out

08048414 <main>:
8048414:        55                           push   %ebp
8048415:        89 e5                        mov    %esp,%ebp
8048417:        6a 03                        push   $0x3
8048419:        6a 02                        push   $0x2
804841b:        e8 e1 ff ff ff               call   8048401 <foo>
8048420:        83 c4 08                     add    $0x8,%esp
8048423:        b8 00 00 00 00               mov    $0x0,%eax
8048428:        c9                           leave  
8048429:        c3                           ret   
804842a:        66 90                        xchg   %ax,%ax
804842c:        66 90                        xchg   %ax,%ax
804842e:        66 90                        xchg   %ax,%ax


加载器( Loader )把可执行文件从外存装入内存并进行执行 <-- 这过程有经过虚地址映射物理地址转换嘛?

Linux 系统的 加载器( Loader ) 这是位于 linux kernel 里面?

谢谢

论坛徽章:
0
2 [报告]
发表于 2015-07-11 20:42 |只看该作者
有一本叫做《程序员的自我修养--链接、装载与库》的书,可以详细回答你的所有问题(MMU那个除外)。。。如果楼主有需要我可以把书翻出来给你详细解答,有点长。。。

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:49:03
3 [报告]
发表于 2015-07-11 22:59 |只看该作者
本帖最后由 shihyu 于 2015-07-11 22:59 编辑


努力工作的搬砖工 可以麻烦帮我看一下书解释嘛?

感谢

论坛徽章:
0
4 [报告]
发表于 2015-07-12 09:47 |只看该作者
其实我觉得你自己看书收获会比较大的。。。关于第一个问题,绑定地址都是虚地址(至少对于应用层来说是的)。
这里先来看下编译过程中符号的地址变化,暂时不考虑运行的时候的地址。
首先,对于不同的符号(是否全局变量,是函数还是变量,是临时的还是静态的,是本地变量还是引用外部库的),编译的过程中编译器写进二进制文件的地址是有所不同的。我看了下你上面的汇编,大致认为你的test.c内容是这样的(可能有一些不同,但是应该影响不大):

  1. void foo(int a,int b)
  2. {
  3. ....
  4. }

  5. int main()
  6. {
  7.     foo(2,3);
  8.     return 0;
  9. }
复制代码


我就简单按照这个代码分析你的问题。
首先不要直接将他编译成可执行文件,先编译成目标文件。
gcc -o test.o -c test.c
然后读取这个文件的符号列表(表示不会上传图片,就不截图了,拷贝出来)
  1. [johnzeng@localhost linkage]$ objdump -t test.o

  2. test.o:     文件格式 elf64-x86-64

  3. SYMBOL TABLE:
  4. 0000000000000000 l    df *ABS*        0000000000000000 test.c
  5. 0000000000000000 l    d  .text        0000000000000000 .text
  6. 0000000000000000 l    d  .data        0000000000000000 .data
  7. 0000000000000000 l    d  .bss        0000000000000000 .bss
  8. 0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
  9. 0000000000000000 l    d  .eh_frame        0000000000000000 .eh_frame
  10. 0000000000000000 l    d  .comment        0000000000000000 .comment
  11. 0000000000000000 g     F .text        0000000000000017 foo
  12. 0000000000000017 g     F .text        000000000000001a main
复制代码
这是目标文件的符号表,第一列为符号所在地址,第二列表示是本地符号还是全局符号,第三列表示符号的性质,是函数还是定义之类的,第四列是符号所在的段。第五列是符号占用位置的大小,第六列是符号名(编译之后的符号名)。

我们先来分析这个目标文件。前面几行都可以忽略,我们只看最后两行。

倒数第二行,符号是foo,在.text段,从地址0开始,大小是0x17。紧接着马上就是main,地址是0x17,大小是0x1a。两个符号在文件中紧紧相邻。

这里可以看到,符号的地址还是简单的线性排列。这里已经是虚地址了,但是肯定不是执行的时候的地址,而仅仅是一个偏移量。此时的符号仍旧是需要重定位的,仍旧不是正式的地址。之所以会有一个这样的中间的目标文件,是因为并非所有代码都是直接编译成可执行的文件,他还可以编译成静态库,动态库等,之所以此时没有计算实际的地址,是因为在之后的链接过程中还要进行段的合并,合并之后才能看出具体哪个符号安排在哪个位置。

现在我们将这个目标文件编译成可执行的文件。

gcc test.o -o test
还是查看符号表:
  1. [johnzeng@localhost linkage]$ objdump -t test

  2. test:     文件格式 elf64-x86-64

  3. SYMBOL TABLE:
  4. 0000000000400238 l    d  .interp        0000000000000000              .interp
  5. 0000000000400254 l    d  .note.ABI-tag        0000000000000000              .note.ABI-tag
  6. 0000000000400274 l    d  .note.gnu.build-id        0000000000000000              .note.gnu.build-id
  7. 0000000000400298 l    d  .gnu.hash        0000000000000000              .gnu.hash
  8. 00000000004002b8 l    d  .dynsym        0000000000000000              .dynsym
  9. 0000000000400300 l    d  .dynstr        0000000000000000              .dynstr
  10. 0000000000400338 l    d  .gnu.version        0000000000000000              .gnu.version
  11. 0000000000400340 l    d  .gnu.version_r        0000000000000000              .gnu.version_r
  12. 0000000000400360 l    d  .rela.dyn        0000000000000000              .rela.dyn
  13. 0000000000400378 l    d  .rela.plt        0000000000000000              .rela.plt
  14. 00000000004003a8 l    d  .init        0000000000000000              .init
  15. 00000000004003d0 l    d  .plt        0000000000000000              .plt
  16. 0000000000400400 l    d  .text        0000000000000000              .text
  17. 00000000004005a4 l    d  .fini        0000000000000000              .fini
  18. 00000000004005b0 l    d  .rodata        0000000000000000              .rodata
  19. 00000000004005c0 l    d  .eh_frame_hdr        0000000000000000              .eh_frame_hdr
  20. 0000000000400600 l    d  .eh_frame        0000000000000000              .eh_frame
  21. 0000000000600e10 l    d  .init_array        0000000000000000              .init_array
  22. 0000000000600e18 l    d  .fini_array        0000000000000000              .fini_array
  23. 0000000000600e20 l    d  .jcr        0000000000000000              .jcr
  24. 0000000000600e28 l    d  .dynamic        0000000000000000              .dynamic
  25. 0000000000600ff8 l    d  .got        0000000000000000              .got
  26. 0000000000601000 l    d  .got.plt        0000000000000000              .got.plt
  27. 0000000000601028 l    d  .data        0000000000000000              .data
  28. 000000000060102c l    d  .bss        0000000000000000              .bss
  29. 0000000000000000 l    d  .comment        0000000000000000              .comment
  30. 0000000000000000 l    df *ABS*        0000000000000000              crtstuff.c
  31. 0000000000600e20 l     O .jcr        0000000000000000              __JCR_LIST__
  32. 0000000000400430 l     F .text        0000000000000000              deregister_tm_clones
  33. 0000000000400460 l     F .text        0000000000000000              register_tm_clones
  34. 00000000004004a0 l     F .text        0000000000000000              __do_global_dtors_aux
  35. 000000000060102c l     O .bss        0000000000000001              completed.6337
  36. 0000000000600e18 l     O .fini_array        0000000000000000              __do_global_dtors_aux_fini_array_entry
  37. 00000000004004c0 l     F .text        0000000000000000              frame_dummy
  38. 0000000000600e10 l     O .init_array        0000000000000000              __frame_dummy_init_array_entry
  39. 0000000000000000 l    df *ABS*        0000000000000000              test.c
  40. 0000000000000000 l    df *ABS*        0000000000000000              crtstuff.c
  41. 0000000000400710 l     O .eh_frame        0000000000000000              __FRAME_END__
  42. 0000000000600e20 l     O .jcr        0000000000000000              __JCR_END__
  43. 0000000000000000 l    df *ABS*        0000000000000000              
  44. 0000000000600e18 l       .init_array        0000000000000000              __init_array_end
  45. 0000000000600e28 l     O .dynamic        0000000000000000              _DYNAMIC
  46. 0000000000600e10 l       .init_array        0000000000000000              __init_array_start
  47. 0000000000601000 l     O .got.plt        0000000000000000              _GLOBAL_OFFSET_TABLE_
  48. 00000000004005a0 g     F .text        0000000000000002              __libc_csu_fini
  49. 0000000000000000  w      *UND*        0000000000000000              _ITM_deregisterTMCloneTable
  50. 0000000000601028  w      .data        0000000000000000              data_start
  51. 000000000060102c g       .data        0000000000000000              _edata
  52. 00000000004005a4 g     F .fini        0000000000000000              _fini
  53. 0000000000000000       F *UND*        0000000000000000              __libc_start_main@@GLIBC_2.2.5
  54. 0000000000601028 g       .data        0000000000000000              __data_start
  55. 0000000000000000  w      *UND*        0000000000000000              __gmon_start__
  56. 00000000004005b8 g     O .rodata        0000000000000000              .hidden __dso_handle
  57. 00000000004005b0 g     O .rodata        0000000000000004              _IO_stdin_used
  58. 0000000000400530 g     F .text        0000000000000065              __libc_csu_init
  59. 00000000004004f0 g     F .text        0000000000000017              foo
  60. 0000000000601030 g       .bss        0000000000000000              _end
  61. 0000000000400400 g     F .text        0000000000000000              _start
  62. 000000000060102c g       .bss        0000000000000000              __bss_start
  63. 0000000000400507 g     F .text        000000000000001a              main
  64. 0000000000000000  w      *UND*        0000000000000000              _Jv_RegisterClasses
  65. 0000000000601030 g     O .data        0000000000000000              .hidden __TMC_END__
  66. 0000000000000000  w      *UND*        0000000000000000              _ITM_registerTMCloneTable
  67. 00000000004003a8 g     F .init        0000000000000000              _init
复制代码
这次可以明显看到,符号变多了。这是因为链接成可执行文件的过程会链入很多glibc的符号。这里面非常有代表性的两个符号是_init符号和_fini符号。_init为默认初始化函数,我们常说main是函数入口,但是并不见得main就是第一个被执行到函数。因为实际上系统是先执行.init段的函数(这个函数只有_init函数),然后才是main函数。同理,并不是main函数结束,程序运行就结束的,后面还会执行.fini段的内容,这里就是_fini函数。但是可以看到这两个符号占用的空间是0,这是因为这两个函数的代码并没有编译进来,而是被动态链接。我们可以看看test链接了哪些动态库:
  1. [johnzeng@localhost linkage]$ ldd test
  2.         linux-vdso.so.1 =>  (0x00007fff13bfe000)
  3.         libc.so.6 => /lib64/libc.so.6 (0x00007ff652b97000)
  4.         /lib64/ld-linux-x86-64.so.2 (0x00007ff652f6b000)
复制代码

可以看到,链接器默认给我们讲libc和ld链接进来了。使用readelf工具可以查看到其中的_init函数

  1. [johnzeng@localhost linkage]$ readelf -s /lib64/libc.so.6 |grep -e '\b_init'
  2.   3841: 00000000000218c0   300 FUNC    LOCAL  DEFAULT   12 _init
复制代码
第3841个符号,文件偏移地址为0x218c0,大小为0x300,名称为_init。


回归我们的test文件,其中的foo符号和main符号的地址已经发生了变化,foo的地址变成了0x4004f0,main的地址变成了0x400507。由于我的是64位系统,楼主是32位系统,所以起始地址有点不同,导致最后的函数地址也不同,32位系统默认起始地址为0x8048000,所以楼主的main地址为0x08048414。
至此,基本上一个简单编译的二进制文件内的符号地址就出来了。已经明确定义的几个符号,foo和main在装载运行的时候的虚地址也就是这个,不会再变化。这些被编译进二进制文件的符号的地址全部由编译器安排好地址,并写入文件中。不需要重新计算了。




论坛徽章:
0
5 [报告]
发表于 2015-07-12 10:53 |只看该作者
现在开始回答第二个问题,加载中的地址是不是由加载器决定的。重新计算地址的工作由ld.so,动态链接器完成。
首先要明白哪些符号是需要加载的时候计算地址的。上面的回复已经讲到,foo和main函数已经写死了地址,后面加载的时候已经不需要重新计算了,这种情况叫做链接时重定位。但是很明显,还存在需要加载的时候定位的符号,这种情况叫做装载时重定位。这种情况最简单的例子,就是动态链接的符号。

为了方便解释,这里还是用上面那个程序,进行简单的试验。
先将foo独立出来,写到lib.c文件中(为了好玩我加了一个printf函数)。
  1. #include <stdio.h>

  2. //file :lib.c
  3. void foo(int a,int b)
  4. {
  5.         int c = a + b;
  6.         printf("c is : %d\n", c);
  7. }
复制代码
test.c 文件砍掉foo相关的内容:
  1. //file: test.c
  2. void foo(int a,int b);
  3. int main()
  4. {
  5.         foo(2,3);
  6.         return 0;
  7. }
复制代码
然后进行编译:
  1. gcc -c test.c -o test.o
  2. gcc -fPIC -shared lib.c -o liba.so
  3. gcc -L./ -la test.o -o test
复制代码
由于我的系统默认不会在当前目录查找动态库,所以需要将当前路径加入动态库搜索路径中:
export LD_LIBRARY_PATH=/work/libLD_LIBRARY_PATH

好了,我们现在来看下test这个链接了一个动态库的二进制文件是什么情况。
  1. [johnzeng@localhost linkage]$ ldd test
  2.         linux-vdso.so.1 =>  (0x00007ffe54dfe000)
  3.         liba.so (0x00007f359b865000)
  4.         libc.so.6 => /lib64/libc.so.6 (0x00007f359b491000)
  5.         /lib64/ld-linux-x86-64.so.2 (0x00007f359ba68000)
复制代码
嗯,liba被链接进去了。再看看符号表。
  1. [johnzeng@localhost linkage]$ readelf -s test

  2. Symbol table '.dynsym' contains 12 entries:
  3.    Num:    Value          Size Type    Bind   Vis      Ndx Name
  4.      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
  5.      1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
  6.      2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
  7.      3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
  8.      4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND foo
  9.      5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
  10.      6: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
  11.      7: 0000000000601034     0 NOTYPE  GLOBAL DEFAULT   24 _edata
  12.      8: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   25 _end
  13.      9: 0000000000601034     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
  14.     10: 0000000000400540     0 FUNC    GLOBAL DEFAULT   11 _init
  15.     11: 0000000000400724     0 FUNC    GLOBAL DEFAULT   14 _fini

  16. Symbol table '.symtab' contains 65 entries:
  17.    Num:    Value          Size Type    Bind   Vis      Ndx Name
  18.      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
  19.      1: 0000000000400238     0 SECTION LOCAL  DEFAULT    1
  20.      2: 0000000000400254     0 SECTION LOCAL  DEFAULT    2
  21.      3: 0000000000400274     0 SECTION LOCAL  DEFAULT    3
  22.      4: 0000000000400298     0 SECTION LOCAL  DEFAULT    4
  23.      5: 00000000004002d0     0 SECTION LOCAL  DEFAULT    5
  24.      6: 00000000004003f0     0 SECTION LOCAL  DEFAULT    6
  25.      7: 00000000004004a2     0 SECTION LOCAL  DEFAULT    7
  26.      8: 00000000004004c0     0 SECTION LOCAL  DEFAULT    8
  27.      9: 00000000004004e0     0 SECTION LOCAL  DEFAULT    9
  28.     10: 00000000004004f8     0 SECTION LOCAL  DEFAULT   10
  29.     11: 0000000000400540     0 SECTION LOCAL  DEFAULT   11
  30.     12: 0000000000400560     0 SECTION LOCAL  DEFAULT   12
  31.     13: 00000000004005a0     0 SECTION LOCAL  DEFAULT   13
  32.     14: 0000000000400724     0 SECTION LOCAL  DEFAULT   14
  33.     15: 0000000000400730     0 SECTION LOCAL  DEFAULT   15
  34.     16: 0000000000400740     0 SECTION LOCAL  DEFAULT   16
  35.     17: 0000000000400778     0 SECTION LOCAL  DEFAULT   17
  36.     18: 0000000000600e00     0 SECTION LOCAL  DEFAULT   18
  37.     19: 0000000000600e08     0 SECTION LOCAL  DEFAULT   19
  38.     20: 0000000000600e10     0 SECTION LOCAL  DEFAULT   20
  39.     21: 0000000000600e18     0 SECTION LOCAL  DEFAULT   21
  40.     22: 0000000000600ff8     0 SECTION LOCAL  DEFAULT   22
  41.     23: 0000000000601000     0 SECTION LOCAL  DEFAULT   23
  42.     24: 0000000000601030     0 SECTION LOCAL  DEFAULT   24
  43.     25: 0000000000601034     0 SECTION LOCAL  DEFAULT   25
  44.     26: 0000000000000000     0 SECTION LOCAL  DEFAULT   26
  45.     27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
  46.     28: 0000000000600e10     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
  47.     29: 00000000004005d0     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
  48.     30: 0000000000400600     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
  49.     31: 0000000000400640     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
  50.     32: 0000000000601034     1 OBJECT  LOCAL  DEFAULT   25 completed.6337
  51.     33: 0000000000600e08     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
  52.     34: 0000000000400660     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
  53.     35: 0000000000600e00     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
  54.     36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test.c
  55.     37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
  56.     38: 0000000000400868     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
  57.     39: 0000000000600e10     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
  58.     40: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS
  59.     41: 0000000000600e08     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
  60.     42: 0000000000600e18     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
  61.     43: 0000000000600e00     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
  62.     44: 0000000000601000     0 OBJECT  LOCAL  DEFAULT   23 _GLOBAL_OFFSET_TABLE_
  63.     45: 0000000000400720     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
  64.     46: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
  65.     47: 0000000000601030     0 NOTYPE  WEAK   DEFAULT   24 data_start
  66.     48: 0000000000601034     0 NOTYPE  GLOBAL DEFAULT   24 _edata
  67.     49: 0000000000400724     0 FUNC    GLOBAL DEFAULT   14 _fini
  68.     50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
  69.     51: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
  70.     52: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
  71.     53: 0000000000400738     0 OBJECT  GLOBAL HIDDEN    15 __dso_handle
  72.     54: 0000000000400730     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
  73.     55: 00000000004006b0   101 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
  74.     56: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND foo
  75.     57: 0000000000601038     0 NOTYPE  GLOBAL DEFAULT   25 _end
  76.     58: 00000000004005a0     0 FUNC    GLOBAL DEFAULT   13 _start
  77.     59: 0000000000601034     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
  78.     60: 0000000000400690    26 FUNC    GLOBAL DEFAULT   13 main
  79.     61: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
  80.     62: 0000000000601038     0 OBJECT  GLOBAL HIDDEN    24 __TMC_END__
  81.     63: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
  82.     64: 0000000000400540     0 FUNC    GLOBAL DEFAULT   11 _init
复制代码
可以看到,此时foo符号,其size是0,且被标示为UND了。显然,编译后的test文件没有foo的定义(只有声明),所以编译器并不知道应该给他分配到哪个地址。你可能觉得链接的时候将liba链进来了,但是注意,由于liba是共享动态库,其代码只在内存保有一个拷贝,所以没到加载的时候都不知道具体会在哪个地址,他的地址是随情况变化的,在运行之前,都不知道这个符号会出现在哪里,所以编译器只能告诉二进制文件,这个符号在liba这个库,但是具体什么地址,我也不知道。

那么接下来,我们将test这个二进制丢到gdb里面调试下。由于我编译的时候忘记加-g参数,只能无符号调试了。。。。但是这并不影响啥。
gdb test
接下来的命令为gdb命令。
  1. (gdb) b _dl_start
  2. Function "_dl_start" not defined.
  3. Make breakpoint pending on future shared library load? (y or [n]) y

  4. Breakpoint 1 (_dl_start) pending.
  5. (gdb) b main
  6. Breakpoint 2 at 0x400694
复制代码
首先我们在_dl_start函数上断点,为啥是这个函数?因为这个是ld.so的自举函数入口,如果装载器完成了地址重定位,那么此时foo函数的地址应该已经计算好了。但是如果需要ld.so进行重定位,那么这个时候foo的地址就还是没有计算好的。ld.so的自举过程也是非常有(jian)趣(xin)的。有兴趣可以看下Glibc的源码,位于elf/rtld.c下。
然后我们在main函数下一个断点。此时地址重定位应该已经完成了。也就是说foo函数已经是正确的地址了。那么我们来看下是不是这样。
进入gdb的汇编视图(不用这一步也是ok的),然后run
(gdb)layout asm
然后打印此时的foo函数的地址。
  1. (gdb) r

  2. Breakpoint 1, 0x00007ffff7ddf934 in _dl_start ()
  3.    from /lib64/ld-linux-x86-64.so.2
  4. Missing separate debuginfos, use: debuginfo-install glibc-2.17-78.el7.x86_64
  5. (gdb) p foo
  6. p        foo
  7. $1 = {<text variable, no debug info>} 0x400590 <foo@plt>
复制代码
可以看到这个时候foo地址是0x400590接着我们continue到main,然后看下foo的地址
  1. (gdb)c
  2. Breakpoint 2, 0x0000000000400694 in main ()
  3. (gdb) p foo
  4. p        foo
  5. $2 = {<text variable, no debug info>} 0x7ffff7bd96c8 <foo>
复制代码
好了,此时foo的地址变成了 0x7ffff7bd96c8。
那么问题就很明朗了。加载的时候,foo的地址被写成是0x400590,经过ld.so的重定位,才得到正确的地址。ld.so计算了正确的地址。

论坛徽章:
0
6 [报告]
发表于 2015-07-12 11:18 |只看该作者
第二个问题解决完了第三个问题其实差不多也就那样了,程序执行的时候计算地址的情况,也就是上面那种动态库加载的情况了。比较特别的是dlopen等运行中加载动态库的情况,也是运行中去确定的。我并不明白为啥你会在这个问题特地问地址是 Local Address + Base Register这种问题。。。因为感觉这里没有太大关系,地址具体是什么构成在现代操作系统下并没有那么简单,特别是页式寻址的情况。。。这个问题我并不清楚,坐等大神回复了。

接下来几个问题都是零碎的问题了,我一次性回答完他 。

linux的mmu。。没有研究过,应该是页式的。。。
linux跟binding的关系,怎么说呢,不好说一个操作系统跟应用层的东西没有关系。程序的运行地址啥的,在哪个操作系统都是那么几个确认地址的步骤,只是确认的方法稍微不同。linux本身影响地址的方式大致也就是文件系统要求的加载方式,地址安排方式之类的。linux要求的地址肯定跟windows要求的地址不同,最后运行的地址也肯定不同啊。。。
binding是不是早期技术。。。。应该蛮早的,针孔机那会就要用链接器了。。。至于现在有没有机会用到,你如果想写操作系统啥的还是有的。

加载器( Loader )把可执行文件从外存装入内存并进行执行 <-- 这过程有经过虚地址映射物理地址转换嘛?
虚地址映射为物理地址貌似是内核的工作吧。。。可执行文件装载之后的地址还是虚地址,内核根据虚地址给你转换成物理地址呀。

Linux 系统的 加载器( Loader ) 这是位于 linux kernel 里面?
有一个叫做execve的调用,追进去是do_execve,文件在fs/exec.c下。应用层执行一个文件的时候,内核调用这个方法加载文件。然后根据文件最开头的几个字节的魔数,决定这个执行到底要用什么文件系统去解析,装载,执行。这部分解析起来又是一个长篇大论了。。。

另外,关于运行时重定位函数入口,有一部分知识是关于PLT(延迟绑定)的。这部分的内容在上个回答有一定涉及,但是没有展开详细讲。百度一下就有很多答案了。

回答有点长,多少有疏漏的地方,见谅。。。

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:49:03
7 [报告]
发表于 2015-07-12 19:52 |只看该作者
努力工作的搬砖工

非常感謝

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
8 [报告]
发表于 2015-07-13 11:05 |只看该作者
学习了,谢谢推荐 《程序员的自我修养--链接、装载与库》
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP