Chinaunix
标题:
链接器、加载器、页式、段式管理 Binding 关系
[打印本页]
作者:
shihyu
时间:
2015-07-11 00:47
标题:
链接器、加载器、页式、段式管理 Binding 关系
本帖最后由 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 里面?
谢谢
作者:
努力工作的搬砖工
时间:
2015-07-11 20:42
有一本叫做《程序员的自我修养--链接、装载与库》的书,可以详细回答你的所有问题(MMU那个除外)。。。如果楼主有需要我可以把书翻出来给你详细解答,有点长。。。
作者:
shihyu
时间:
2015-07-11 22:59
本帖最后由 shihyu 于 2015-07-11 22:59 编辑
努力工作的搬砖工 可以麻烦帮我看一下书解释嘛?
感谢
作者:
努力工作的搬砖工
时间:
2015-07-12 09:47
其实我觉得你自己看书收获会比较大的。。。关于第一个问题,绑定地址都是虚地址(至少对于应用层来说是的)。
这里先来看下编译过程中符号的地址变化,暂时不考虑运行的时候的地址。
首先,对于不同的符号(是否全局变量,是函数还是变量,是临时的还是静态的,是本地变量还是引用外部库的),编译的过程中编译器写进二进制文件的地址是有所不同的。我看了下你上面的汇编,大致认为你的test.c内容是这样的(可能有一些不同,但是应该影响不大):
void foo(int a,int b)
{
....
}
int main()
{
foo(2,3);
return 0;
}
复制代码
我就简单按照这个代码分析你的问题。
首先不要直接将他编译成可执行文件,先编译成目标文件。
gcc -o test.o -c test.c
然后读取这个文件的符号列表(表示不会上传图片,就不截图了,拷贝出来)
[johnzeng@localhost linkage]$ objdump -t test.o
test.o: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 0000000000000017 foo
0000000000000017 g F .text 000000000000001a main
复制代码
这是目标文件的符号表,第一列为符号所在地址,第二列表示是本地符号还是全局符号,第三列表示符号的性质,是函数还是定义之类的,第四列是符号所在的段。第五列是符号占用位置的大小,第六列是符号名(编译之后的符号名)。
我们先来分析这个目标文件。前面几行都可以忽略,我们只看最后两行。
倒数第二行,符号是foo,在.text段,从地址0开始,大小是0x17。紧接着马上就是main,地址是0x17,大小是0x1a。两个符号在文件中紧紧相邻。
这里可以看到,符号的地址还是简单的线性排列。这里已经是虚地址了,但是肯定不是执行的时候的地址,而仅仅是一个偏移量。此时的符号仍旧是需要重定位的,仍旧不是正式的地址。之所以会有一个这样的中间的目标文件,是因为并非所有代码都是直接编译成可执行的文件,他还可以编译成静态库,动态库等,之所以此时没有计算实际的地址,是因为在之后的链接过程中还要进行段的合并,合并之后才能看出具体哪个符号安排在哪个位置。
现在我们将这个目标文件编译成可执行的文件。
gcc test.o -o test
还是查看符号表:
[johnzeng@localhost linkage]$ objdump -t test
test: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000400238 l d .interp 0000000000000000 .interp
0000000000400254 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
0000000000400274 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000400298 l d .gnu.hash 0000000000000000 .gnu.hash
00000000004002b8 l d .dynsym 0000000000000000 .dynsym
0000000000400300 l d .dynstr 0000000000000000 .dynstr
0000000000400338 l d .gnu.version 0000000000000000 .gnu.version
0000000000400340 l d .gnu.version_r 0000000000000000 .gnu.version_r
0000000000400360 l d .rela.dyn 0000000000000000 .rela.dyn
0000000000400378 l d .rela.plt 0000000000000000 .rela.plt
00000000004003a8 l d .init 0000000000000000 .init
00000000004003d0 l d .plt 0000000000000000 .plt
0000000000400400 l d .text 0000000000000000 .text
00000000004005a4 l d .fini 0000000000000000 .fini
00000000004005b0 l d .rodata 0000000000000000 .rodata
00000000004005c0 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000400600 l d .eh_frame 0000000000000000 .eh_frame
0000000000600e10 l d .init_array 0000000000000000 .init_array
0000000000600e18 l d .fini_array 0000000000000000 .fini_array
0000000000600e20 l d .jcr 0000000000000000 .jcr
0000000000600e28 l d .dynamic 0000000000000000 .dynamic
0000000000600ff8 l d .got 0000000000000000 .got
0000000000601000 l d .got.plt 0000000000000000 .got.plt
0000000000601028 l d .data 0000000000000000 .data
000000000060102c l d .bss 0000000000000000 .bss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000600e20 l O .jcr 0000000000000000 __JCR_LIST__
0000000000400430 l F .text 0000000000000000 deregister_tm_clones
0000000000400460 l F .text 0000000000000000 register_tm_clones
00000000004004a0 l F .text 0000000000000000 __do_global_dtors_aux
000000000060102c l O .bss 0000000000000001 completed.6337
0000000000600e18 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
00000000004004c0 l F .text 0000000000000000 frame_dummy
0000000000600e10 l O .init_array 0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000400710 l O .eh_frame 0000000000000000 __FRAME_END__
0000000000600e20 l O .jcr 0000000000000000 __JCR_END__
0000000000000000 l df *ABS* 0000000000000000
0000000000600e18 l .init_array 0000000000000000 __init_array_end
0000000000600e28 l O .dynamic 0000000000000000 _DYNAMIC
0000000000600e10 l .init_array 0000000000000000 __init_array_start
0000000000601000 l O .got.plt 0000000000000000 _GLOBAL_OFFSET_TABLE_
00000000004005a0 g F .text 0000000000000002 __libc_csu_fini
0000000000000000 w *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000601028 w .data 0000000000000000 data_start
000000000060102c g .data 0000000000000000 _edata
00000000004005a4 g F .fini 0000000000000000 _fini
0000000000000000 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.2.5
0000000000601028 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
00000000004005b8 g O .rodata 0000000000000000 .hidden __dso_handle
00000000004005b0 g O .rodata 0000000000000004 _IO_stdin_used
0000000000400530 g F .text 0000000000000065 __libc_csu_init
00000000004004f0 g F .text 0000000000000017 foo
0000000000601030 g .bss 0000000000000000 _end
0000000000400400 g F .text 0000000000000000 _start
000000000060102c g .bss 0000000000000000 __bss_start
0000000000400507 g F .text 000000000000001a main
0000000000000000 w *UND* 0000000000000000 _Jv_RegisterClasses
0000000000601030 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
00000000004003a8 g F .init 0000000000000000 _init
复制代码
这次可以明显看到,符号变多了。这是因为链接成可执行文件的过程会链入很多glibc的符号。这里面非常有代表性的两个符号是_init符号和_fini符号。_init为默认初始化函数,我们常说main是函数入口,但是并不见得main就是第一个被执行到函数。因为实际上系统是先执行.init段的函数(这个函数只有_init函数),然后才是main函数。同理,并不是main函数结束,程序运行就结束的,后面还会执行.fini段的内容,这里就是_fini函数。但是可以看到这两个符号占用的空间是0,这是因为这两个函数的代码并没有编译进来,而是被动态链接。我们可以看看test链接了哪些动态库:
[johnzeng@localhost linkage]$ ldd test
linux-vdso.so.1 => (0x00007fff13bfe000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff652b97000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff652f6b000)
复制代码
可以看到,链接器默认给我们讲libc和ld链接进来了。使用readelf工具可以查看到其中的_init函数
[johnzeng@localhost linkage]$ readelf -s /lib64/libc.so.6 |grep -e '\b_init'
3841: 00000000000218c0 300 FUNC LOCAL DEFAULT 12 _init
复制代码
第3841个符号,文件偏移地址为0x
218c0,大小为0x300,名称为_init。
回归我们的test文件,其中的foo符号和main符号的地址已经发生了变化,foo的地址变成了
0x4004f0,main的地址变成了0x
400507。由于我的是64位系统,楼主是32位系统,所以起始地址有点不同,导致最后的函数地址也不同,32位系统默认起始地址为0x8048000,所以楼主的main地址为0x
08048414。
至此,基本上一个简单编译的二进制文件内的符号地址就出来了。已经明确定义的几个符号,foo和main在装载运行的时候的虚地址也就是这个,不会再变化。这些被编译进二进制文件的符号的地址全部由编译器安排好地址,并写入文件中。不需要重新计算了。
作者:
努力工作的搬砖工
时间:
2015-07-12 10:53
现在开始回答第二个问题,加载中的地址是不是由加载器决定的。重新计算地址的工作由ld.so,动态链接器完成。
首先要明白哪些符号是需要加载的时候计算地址的。上面的回复已经讲到,foo和main函数已经写死了地址,后面加载的时候已经不需要重新计算了,这种情况叫做
链接时重定位
。但是很明显,还存在需要加载的时候定位的符号,这种情况叫做
装载时重定位
。这种情况最简单的例子,就是动态链接的符号。
为了方便解释,这里还是用上面那个程序,进行简单的试验。
先将foo独立出来,写到lib.c文件中(为了好玩我加了一个printf函数)。
#include <stdio.h>
//file :lib.c
void foo(int a,int b)
{
int c = a + b;
printf("c is : %d\n", c);
}
复制代码
test.c 文件砍掉foo相关的内容:
//file: test.c
void foo(int a,int b);
int main()
{
foo(2,3);
return 0;
}
复制代码
然后进行编译:
gcc -c test.c -o test.o
gcc -fPIC -shared lib.c -o liba.so
gcc -L./ -la test.o -o test
复制代码
由于我的系统默认不会在当前目录查找动态库,所以需要将当前路径加入动态库搜索路径中:
export LD_LIBRARY_PATH=/work/lib
LD_LIBRARY_PATH
好了,我们现在来看下test这个链接了一个动态库的二进制文件是什么情况。
[johnzeng@localhost linkage]$ ldd test
linux-vdso.so.1 => (0x00007ffe54dfe000)
liba.so (0x00007f359b865000)
libc.so.6 => /lib64/libc.so.6 (0x00007f359b491000)
/lib64/ld-linux-x86-64.so.2 (0x00007f359ba68000)
复制代码
嗯,liba被链接进去了。再看看符号表。
[johnzeng@localhost linkage]$ readelf -s test
Symbol table '.dynsym' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND foo
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
6: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
7: 0000000000601034 0 NOTYPE GLOBAL DEFAULT 24 _edata
8: 0000000000601038 0 NOTYPE GLOBAL DEFAULT 25 _end
9: 0000000000601034 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
10: 0000000000400540 0 FUNC GLOBAL DEFAULT 11 _init
11: 0000000000400724 0 FUNC GLOBAL DEFAULT 14 _fini
Symbol table '.symtab' contains 65 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400238 0 SECTION LOCAL DEFAULT 1
2: 0000000000400254 0 SECTION LOCAL DEFAULT 2
3: 0000000000400274 0 SECTION LOCAL DEFAULT 3
4: 0000000000400298 0 SECTION LOCAL DEFAULT 4
5: 00000000004002d0 0 SECTION LOCAL DEFAULT 5
6: 00000000004003f0 0 SECTION LOCAL DEFAULT 6
7: 00000000004004a2 0 SECTION LOCAL DEFAULT 7
8: 00000000004004c0 0 SECTION LOCAL DEFAULT 8
9: 00000000004004e0 0 SECTION LOCAL DEFAULT 9
10: 00000000004004f8 0 SECTION LOCAL DEFAULT 10
11: 0000000000400540 0 SECTION LOCAL DEFAULT 11
12: 0000000000400560 0 SECTION LOCAL DEFAULT 12
13: 00000000004005a0 0 SECTION LOCAL DEFAULT 13
14: 0000000000400724 0 SECTION LOCAL DEFAULT 14
15: 0000000000400730 0 SECTION LOCAL DEFAULT 15
16: 0000000000400740 0 SECTION LOCAL DEFAULT 16
17: 0000000000400778 0 SECTION LOCAL DEFAULT 17
18: 0000000000600e00 0 SECTION LOCAL DEFAULT 18
19: 0000000000600e08 0 SECTION LOCAL DEFAULT 19
20: 0000000000600e10 0 SECTION LOCAL DEFAULT 20
21: 0000000000600e18 0 SECTION LOCAL DEFAULT 21
22: 0000000000600ff8 0 SECTION LOCAL DEFAULT 22
23: 0000000000601000 0 SECTION LOCAL DEFAULT 23
24: 0000000000601030 0 SECTION LOCAL DEFAULT 24
25: 0000000000601034 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 SECTION LOCAL DEFAULT 26
27: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
28: 0000000000600e10 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
29: 00000000004005d0 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
30: 0000000000400600 0 FUNC LOCAL DEFAULT 13 register_tm_clones
31: 0000000000400640 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
32: 0000000000601034 1 OBJECT LOCAL DEFAULT 25 completed.6337
33: 0000000000600e08 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
34: 0000000000400660 0 FUNC LOCAL DEFAULT 13 frame_dummy
35: 0000000000600e00 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
36: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
38: 0000000000400868 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
39: 0000000000600e10 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
40: 0000000000000000 0 FILE LOCAL DEFAULT ABS
41: 0000000000600e08 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
42: 0000000000600e18 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC
43: 0000000000600e00 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
44: 0000000000601000 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_
45: 0000000000400720 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
46: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
47: 0000000000601030 0 NOTYPE WEAK DEFAULT 24 data_start
48: 0000000000601034 0 NOTYPE GLOBAL DEFAULT 24 _edata
49: 0000000000400724 0 FUNC GLOBAL DEFAULT 14 _fini
50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
51: 0000000000601030 0 NOTYPE GLOBAL DEFAULT 24 __data_start
52: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
53: 0000000000400738 0 OBJECT GLOBAL HIDDEN 15 __dso_handle
54: 0000000000400730 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
55: 00000000004006b0 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
56: 0000000000000000 0 FUNC GLOBAL DEFAULT UND foo
57: 0000000000601038 0 NOTYPE GLOBAL DEFAULT 25 _end
58: 00000000004005a0 0 FUNC GLOBAL DEFAULT 13 _start
59: 0000000000601034 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
60: 0000000000400690 26 FUNC GLOBAL DEFAULT 13 main
61: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
62: 0000000000601038 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__
63: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
64: 0000000000400540 0 FUNC GLOBAL DEFAULT 11 _init
复制代码
可以看到,此时foo符号,其size是0,且被标示为UND了。显然,编译后的test文件没有foo的定义(只有声明),所以编译器并不知道应该给他分配到哪个地址。你可能觉得链接的时候将liba链进来了,但是注意,由于liba是共享动态库,其代码只在内存保有一个拷贝,所以没到加载的时候都不知道具体会在哪个地址,他的地址是随情况变化的,在运行之前,都不知道这个符号会出现在哪里,所以编译器只能告诉二进制文件,这个符号在liba这个库,但是具体什么地址,我也不知道。
那么接下来,我们将test这个二进制丢到gdb里面调试下。由于我编译的时候忘记加-g参数,只能无符号调试了。。。。但是这并不影响啥。
gdb test
接下来的命令为gdb命令。
(gdb) b _dl_start
Function "_dl_start" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_dl_start) pending.
(gdb) b main
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函数的地址。
(gdb) r
Breakpoint 1, 0x00007ffff7ddf934 in _dl_start ()
from /lib64/ld-linux-x86-64.so.2
Missing separate debuginfos, use: debuginfo-install glibc-2.17-78.el7.x86_64
(gdb) p foo
p foo
$1 = {<text variable, no debug info>} 0x400590 <foo@plt>
复制代码
可以看到这个时候foo地址是0x400590接着我们continue到main,然后看下foo的地址
(gdb)c
Breakpoint 2, 0x0000000000400694 in main ()
(gdb) p foo
p foo
$2 = {<text variable, no debug info>} 0x7ffff7bd96c8 <foo>
复制代码
好了,此时foo的地址变成了 0x7ffff7bd96c8。
那么问题就很明朗了。加载的时候,foo的地址被写成是0x400590,经过ld.so的重定位,才得到正确的地址。ld.so计算了正确的地址。
作者:
努力工作的搬砖工
时间:
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(延迟绑定)的。这部分的内容在上个回答有一定涉及,但是没有展开详细讲。百度一下就有很多答案了。
回答有点长,多少有疏漏的地方,见谅。。。
作者:
shihyu
时间:
2015-07-12 19:52
努力工作的搬砖工
非常感謝
作者:
nswcfd
时间:
2015-07-13 11:05
学习了,谢谢推荐 《程序员的自我修养--链接、装载与库》
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2