带有反汇编分析的Linux最小可运行示例
由于这是标准未指定的实现细节,因此让我们看一下编译器在特定实现上的工作。
在此答案中,我将链接到进行分析的特定答案,或者在此处直接提供分析,并在此处总结所有结果。
所有这些版本都在Ubuntu / GCC的各种版本中,结果在各个版本中可能都相当稳定,但是如果发现任何变化,我们可以指定更精确的版本。
函数内部的局部变量
是它main
还是任何其他功能:
void f(void) {
int my_local_var;
}
如以下所示:<值优化输出>在gdb中是什么意思?
-O0
:堆栈
-O3
:如果它们不溢出,则注册,否则堆叠
有关为什么存在堆栈的动机,请参见:x86汇编器中的寄存器上使用的push / pop指令的功能是什么?
全局变量和static
函数变量
/* 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'};
}
TODO还会将非常大的字符串文字放入堆栈吗?还是.data
?还是编译失败?
函数参数
void f(int i, int j);
必须遵循相关的调用约定,例如:用于X86的https://en.wikipedia.org/wiki/X86_calling_conventions,该规范为每个变量指定特定的寄存器或堆栈位置。
然后,如<值优化输出>在gdb中意味着什么所示?,-O0
然后将所有内容都放入堆栈中,同时-O3
尝试尽可能多地使用寄存器。
但是,如果函数内联,则将它们像普通本地人一样对待。
const
我相信这没有什么不同,因为您可以将其打字掉。
相反,如果编译器能够确定某些数据从未写入过,则理论上.rodata
即使没有const 也可以将其放入。
TODO分析。
指针
它们是变量(包含地址,即数字),与其余所有变量相同:-)
分配
该问题对于而言并没有多大意义malloc
,因为它malloc
是一个函数,并且在:
int *i = malloc(sizeof(int));
*i
是包含地址的变量,因此属于上述情况。
至于malloc在内部的工作方式,当您调用它时,Linux内核将某些地址标记为可写在其内部数据结构上,并且当程序最初对其进行触摸时,就会发生错误,内核会启用页表,从而允许访问页发生而没有segfaul:x86分页如何工作?
但是请注意,这基本上就是exec
您尝试运行可执行文件时syscall 在后台执行的操作:它标记要加载的页面,并在其中写入程序,另请参见:内核如何使可执行二进制文件在以下环境下运行Linux的?除此之外,exec
对加载到的位置有一些额外的限制(例如,代码不可重定位)。
确切的syscall malloc
是mmap
在2020年的现代实现中使用的,并且在过去brk
使用过:malloc()使用brk()还是mmap()吗?
动态库
基本上可以mmap
进入内存:https : //unix.stackexchange.com/questions/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
envinroment变量main
的argv
在初始堆栈上方:https: //unix.stackexchange.com/questions/75939/where-is-the-environment-string-actual-stored TODO为什么不在.data中?