首先,我们所在的执行环境会透过 execve("./test", ["./test"], []) 的函式呼叫来启动 test 执行档。
呼叫 open("/etc/ld.so.cache", O_RDONLY),以唯读模式开启 ld.so.cache,这个档案的功能是作为动态函式库的快取,它会记录了目前系统中所存在的动态函式库的资讯以及这些函式库所存在的位置。所以说,如果我们在系统中安装了新的函式库时,我们便需要去更新这个档案的内容,以使新的函式库可以在我们的 Linux 环境中发生作用,我们可以透过 ldconfig 这个指令来更新 ld.so.cache 的内容。
呼叫 mmap(0, 9937, PROT_READ, MAP_PRIVATE, 3, 0),把 ld.so.cache 档案映射到记忆体中,mmap 函式的宣告为 mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset),在笔者的电脑上 ld.so.cache 的档案大小为 9937 bytes,PROT_READ代表这块记忆体位置是可读取的,MAP_PRIVATE 则表示产生一个行程私有的 copy-on-write 映射,因此这个呼叫会把整个 ld.so.cache 档案映射到记忆体中,在笔者电脑上所传回的映射记忆体起始位置为 0x40013000。
注: mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)代表我们要求在档案 fd中,起始位置为offset去映射 length 长度的资料,到记忆体位置 start ,而 prot 是用来描述该记忆体位置的保护权限(例如:读、写、执行),flags用来定义所映射物件的型态,例如这块记忆体是否允许多个 Process 同时映射到,也就是说一旦有一个 Process 更改了这个记忆体空间,那所有映射到这块记忆体的Process 都会受到影响,或是 flag 设定为 Process 私有的记忆体映射,这样就会透过 copy-on-write 的机制,当这块记忆体被别的 Process 修改後,会自动配置实体的记忆体位置,让其他的 Process 所映射到的记忆体内容与原本的相同。(有关mmap的其它应用,可参考本文最後的注一)
呼叫 open("/lib/libc.so.6", O_RDONLY),开启 libc.so.6。
呼叫 read(3, "177ELF111331250202"..., 4096) 读取libc.so.6的档头。
呼叫 mmap(0, 993500, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0),把 libc.so.6 映射到记忆体中,由档头开始映射 993500 bytes,若是使用 RedHat 6.1(或其它版本的 RedHat)的读者或许会好奇 libc.so.6 所 link 到的档案 libc-2.1.2.so 大小不是 4118715 bytes 吗? 其实原本 RedHat 所附的 libc.so.6 动态函式库是没有经过 strip 过的,如果经过 strip 後,大小会变为 1052428 bytes,而 libc.so.6 由档头开始在 993500 bytes 之後都是一些版本的资讯,笔者猜想应该是这样的原因,所以在映射档时,并没有把整个 libc.so.6 档案映射到记忆体中,只映射前面有意义的部分。与映射 ld.so.cache 不同的是,除了 PROT_READ 属性之外,libc.so.6 的属性还多了PROT_EXEC,这代表了所映射的这块记忆体是可读可执行的。在笔者的电脑中,libc.so.6 所映射到的记忆体起始位置为 0x40016000。
呼叫 mprotect(0x40101000, 30940, PROT_NONE),用来设定记忆体的使用权限,而 PROT_NONE 属性是代表这块记忆体区间(0x40101000—0x401088DC)是不能读取、写入与执行的。
呼叫 mmap(0x40101000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0xea000),映射 libc.so.6 由起始位置 0xea000 映射 16384bytes 到记忆体位置 0x40101000。
Linux操作系统的动态函式库详细解析(3)
内容版权声明:除非注明,否则皆为本站原创文章。
转载注明出处:https://www.heiqu.com/e080da99f38e0adcf8497e07717851af.html