GUN/Linux的源头Elf文件是如何操控Glibc的

楔子

本篇看下更底层的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文件,运行如下:
GUN/Linux的源头Elf文件是如何操控Glibc的

查看

运行命令readelf -s test查看elf文件段
GUN/Linux的源头Elf文件是如何操控Glibc的
可以看到_start函数的段起始地址data_start:0000000000004000
_start函数的偏移:0000000000001060
main入口函数(也就是上面例子的main)的偏移:0000000000001149

调看

在lldb里面看下test这个elf文件的运行状况
首先在main入口下个断点
GUN/Linux的源头Elf文件是如何操控Glibc的
看它这个断点的address就是上面elf文件main入口的地址。

r命令让它运行到main函数入口处
GUN/Linux的源头Elf文件是如何操控Glibc的
可以看到它的第一条指令的地址末尾两字节是5419,那么说明这个elf文件在被linux加载器(也就是系统函数)加载的时候它的基地址应该是:0x555555554000。

有个有趣的现象就是断点下在哪里,这个基地址的偏移就和谁有关。比如断点下在_start这个函数上。那么基地址一定是末尾两个字节一定是_start的偏移1060加上data_start的基址4000等于5060这点下面验证下。
在_start处下断点
GUN/Linux的源头Elf文件是如何操控Glibc的
然后run
GUN/Linux的源头Elf文件是如何操控Glibc的
可以看到基址是:0x555555555060,它的末尾两字节也确实是:5060.

总结

总体来说Ubuntu22.0上面,CLR被调用的顺序是:
linux elf加载器(这里有系统级调用比如fork)
-》_start
-》Glibc
-》CLR
-》托管main函数

所以这里来说,任何号称main函数为起始函数的都是不正确的描述。因为它们前面有N多的运行步骤。