了解 C 程序中的汇编语言 _start 标签
Understanding assembly language _start label in a C program
我写了一个简单的 c 程序,并试图使用 GDB 来调试程序。我了解在主要功能中使用以下内容:
进入时
push %ebp
mov %esp,%ebp
退出时
leave
ret
然后我在 _start 上尝试了 gdb,我得到了以下
xor %ebp,%ebp
pop %esi
mov %esp,%ecx
and [=12=]xfffffff0,%esp
push %eax
push %esp
push %edx
push [=12=]x80484d0
push [=12=]x8048470
push %ecx
push %esi
push [=12=]x8048414
call 0x8048328 <__libc_start_main@plt>
hlt
nop
nop
nop
nop
我无法理解这些台词,以及这背后的逻辑。
有人可以提供任何指导来帮助解释 _start
的代码吗?
这是您发布的代码的评论 assembly source。
总结起来,它做了以下事情:
- 建立一个 ebp = 0 的哨兵堆栈帧,以便遍历堆栈的代码可以轻松找到它的终点
- 将命令行参数的数量弹出到
esi
,这样我们就可以将它们传递给 __libc_start_main
- 将堆栈指针对齐到 16 位的倍数以符合 ABI。在某些版本的 Linux 中不能保证会出现这种情况,因此必须手动完成以防万一。
__libc_csu_fini
、__libc_csu_init
的地址、参数向量、参数个数和main
的地址作为参数推送给__libc_start_main
__libc_start_main
被调用。此函数(源代码 here)设置一些 glibc 内部变量并最终调用 main
。它永远不会 returns.
- 如果出于任何原因
__libc_start_main
应该 return,则在之后放置 hlt
指令。该指令在用户代码中是不允许的,应该会导致程序崩溃(希望如此)。
- 最后一系列
nop
指令是由汇编程序插入的填充,因此下一个函数从 16 字节的倍数开始以获得更好的性能。在正常执行中永远不会达到。
对于 gnu 工具,_start 标签是程序的入口点。为了使 C 语言工作,您需要有一个堆栈,您需要将一些 memory/variables 归零并将一些设置为您选择的值:
int x = 5;
int y;
int fun ( void )
{
static int z;
}
这三个变量 x,y,z 本质上都是全局的,一个是局部全局变量。因为我们是这样写的,所以我们假设当我们的程序开始时 x 包含值 5,并且假设 y 为零。为了使这些事情发生,需要一些 bootstrap 代码,这就是 _start 和 main() 之间发生的(以及更多)代码。
其他工具链可能会选择使用不同的标签来定义 entry/start 点,但 gnu 工具使用 _start。在调用 main() 之前,您的工具可能还需要其他东西 C++ 例如,需要的不仅仅是 C。
我写了一个简单的 c 程序,并试图使用 GDB 来调试程序。我了解在主要功能中使用以下内容:
进入时
push %ebp
mov %esp,%ebp
退出时
leave
ret
然后我在 _start 上尝试了 gdb,我得到了以下
xor %ebp,%ebp
pop %esi
mov %esp,%ecx
and [=12=]xfffffff0,%esp
push %eax
push %esp
push %edx
push [=12=]x80484d0
push [=12=]x8048470
push %ecx
push %esi
push [=12=]x8048414
call 0x8048328 <__libc_start_main@plt>
hlt
nop
nop
nop
nop
我无法理解这些台词,以及这背后的逻辑。
有人可以提供任何指导来帮助解释 _start
的代码吗?
这是您发布的代码的评论 assembly source。
总结起来,它做了以下事情:
- 建立一个 ebp = 0 的哨兵堆栈帧,以便遍历堆栈的代码可以轻松找到它的终点
- 将命令行参数的数量弹出到
esi
,这样我们就可以将它们传递给__libc_start_main
- 将堆栈指针对齐到 16 位的倍数以符合 ABI。在某些版本的 Linux 中不能保证会出现这种情况,因此必须手动完成以防万一。
__libc_csu_fini
、__libc_csu_init
的地址、参数向量、参数个数和main
的地址作为参数推送给__libc_start_main
__libc_start_main
被调用。此函数(源代码 here)设置一些 glibc 内部变量并最终调用main
。它永远不会 returns.- 如果出于任何原因
__libc_start_main
应该 return,则在之后放置hlt
指令。该指令在用户代码中是不允许的,应该会导致程序崩溃(希望如此)。 - 最后一系列
nop
指令是由汇编程序插入的填充,因此下一个函数从 16 字节的倍数开始以获得更好的性能。在正常执行中永远不会达到。
对于 gnu 工具,_start 标签是程序的入口点。为了使 C 语言工作,您需要有一个堆栈,您需要将一些 memory/variables 归零并将一些设置为您选择的值:
int x = 5;
int y;
int fun ( void )
{
static int z;
}
这三个变量 x,y,z 本质上都是全局的,一个是局部全局变量。因为我们是这样写的,所以我们假设当我们的程序开始时 x 包含值 5,并且假设 y 为零。为了使这些事情发生,需要一些 bootstrap 代码,这就是 _start 和 main() 之间发生的(以及更多)代码。
其他工具链可能会选择使用不同的标签来定义 entry/start 点,但 gnu 工具使用 _start。在调用 main() 之前,您的工具可能还需要其他东西 C++ 例如,需要的不仅仅是 C。