楔子
今天是南方小年,祝南方的朋友节日快乐。
上一篇讲了Glibc是如何操控CLR的:点击这里
本篇看下更底层的elf是如何调用_start函数操控Glibc(它则操控的是CLR)。
例子
为了观察以上步骤,需要搞个简单例子试下。这点非大名鼎鼎GCC莫属。
一:建立source code
用命令:vim test.c建一个test.c源文件,里面写入如下:
#include<stdio.h>
void main()
{
printf("hello tang!\r\n");
}
二:编译源文件
通过命令:gcc test.c -o test编译一个目标文件:test.o
三:链接成elf文件
通过命令:gcc test.o -o test
这样的话,就得到了一个elf格式的test文件,运行如下:
查看
运行命令readelf -s test查看elf文件段
可以看到_start函数的段起始地址data_start:0000000000004000
_start函数的偏移:0000000000001060
main入口函数(也就是上面例子的main)的偏移:0000000000001149
调看
在lldb里面看下test这个elf文件的运行状况
首先在main入口下个断点
看它这个断点的address就是上面elf文件main入口的地址。
r命令让它运行到main函数入口处
可以看到它的第一条指令的地址末尾两字节是5419,那么说明这个elf文件在被linux加载器(也就是系统函数)加载的时候它的基地址应该是:0x555555554000。
有个有趣的现象就是断点下在哪里,这个基地址的偏移就和谁有关。比如断点下在_start这个函数上。那么基地址一定是末尾两个字节一定是_start的偏移1060加上data_start的基址4000等于5060这点下面验证下。
在_start处下断点
然后run
可以看到基址是:0x555555555060,它的末尾两字节也确实是:5060.
总结
总体来说Ubuntu22.0上面,CLR被调用的顺序是:
linux elf加载器(这里有系统级调用比如fork)
-》_start
-》Glibc
-》CLR
-》托管main函数
所以这里来说,任何号称main函数为起始函数的都是不正确的描述。因为它们前面有N多的运行步骤。
结尾
作者:江湖评谈