了解 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

总结起来,它做了以下事情:

  1. 建立一个 ebp = 0 的哨兵堆栈帧,以便遍历堆栈的代码可以轻松找到它的终点
  2. 将命令行参数的数量弹出到 esi,这样我们就可以将它们传递给 __libc_start_main
  3. 将堆栈指针对齐到 16 位的倍数以符合 ABI。在某些版本的 Linux 中不能保证会出现这种情况,因此必须手动完成以防万一。
  4. __libc_csu_fini__libc_csu_init的地址、参数向量、参数个数和main的地址作为参数推送给__libc_start_main
  5. __libc_start_main 被调用。此函数(源代码 here)设置一些 glibc 内部变量并最终调用 main。它永远不会 returns.
  6. 如果出于任何原因 __libc_start_main 应该 return,则在之后放置 hlt 指令。该指令在用户代码中是不允许的,应该会导致程序崩溃(希望如此)。
  7. 最后一系列 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。