Linux最小可运行示例与反汇编分析
因为这是标准中没有指定的实现细节,所以让我们看看编译器在特定实现上做了什么。
在这个答案中,我将链接到做分析的具体答案,或者直接在这里提供分析,并在这里总结所有结果。
所有这些都在不同的Ubuntu / GCC版本中,不同版本之间的结果可能相当稳定,但如果我们发现任何变化,让我们指定更精确的版本。
函数中的局部变量
无论是main函数还是其他函数:
void f(void) {
int my_local_var;
}
如:<value optimized out>在gdb中是什么意思?
o0:堆栈
-O3:如果不溢出,则寄存器,否则堆叠
关于为什么堆栈存在的动机,请参阅:在x86汇编中对寄存器使用的推/弹出指令的功能是什么?
全局变量和静态函数变量
/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;
/* DATA */
int my_global_implicit_explicit_1 = 1;
void f(void) {
/* BSS */
static int my_static_local_var_implicit;
static int my_static_local_var_explicit_0 = 0;
/* DATA */
static int my_static_local_var_explicit_1 = 1;
}
如果初始化为0或未初始化(因此隐式初始化为0):.bss段,请参见:为什么需要.bss段?
否则:.data节
Char *和Char c[]
如:静态变量存储在C和c++的哪里?
void f(void) {
/* RODATA / TEXT */
char *a = "abc";
/* Stack. */
char b[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
}
将非常大的字符串字面值也放在堆栈?或. data ?或者编译失败?
函数参数
void f(int i, int j);
必须经过相关的调用约定,例如:X86的https://en.wikipedia.org/wiki/X86_calling_conventions,它为每个变量指定了特定的寄存器或堆栈位置。
然后如所示<value optimized out>在gdb中是什么意思?, -O0然后将所有内容都放入堆栈,而-O3则尝试尽可能多地使用寄存器。
然而,如果函数内联,它们就像普通的局部变量一样被对待。
常量
我相信这没什么区别,因为你可以把它类型转换掉。
相反,如果编译器能够确定某些数据从未被写入,理论上它可以将其放置在.rodata中,即使不是const。
TODO微积分。
指针
它们是变量(包含地址,地址是数字),所以和其他所有变量一样:-)
malloc
这个问题对malloc没有太大意义,因为malloc是一个函数,并且在:
int *i = malloc(sizeof(int));
*i是一个包含地址的变量,所以它属于上述情况。
至于malloc在内部是如何工作的,当您调用它时,Linux内核将某些地址标记为其内部数据结构上的可写地址,当程序最初接触这些地址时,就会发生错误,内核启用页表,这样就可以在没有segaul的情况下进行访问:x86分页是如何工作的?
但是请注意,这基本上就是当你试图运行一个可执行文件时,exec系统调用在底层所做的事情:它标记它想要加载到的页面,并在那里编写程序,另参见:内核如何获得在linux下运行的可执行二进制文件?除了exec对加载到哪里有一些额外的限制(例如,代码是不可重定位的)。
在现代2020实现中,用于malloc的确切系统调用是mmap,在过去使用brk: malloc()使用brk()还是mmap()?
动态库
基本上得到映射到内存:https://unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
环境变量和main的argv
上面的初始堆栈:https://unix.stackexchange.com/questions/75939/where-is-the-environment-string-actual-stored TODO为什么不在.data?