帮助与理解GDB一个非常基本的main()拆卸 [英] Help with understanding a very basic main() disassembly in GDB

查看:122
本文介绍了帮助与理解GDB一个非常基本的main()拆卸的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Heyo,

我写了这个非常基本的主要功能进行实验,拆装也能看到,希望了解什么是下级事情:

I have written this very basic main function to experiment with disassembly and also to see and hopefully understand what is going on at the lower level:

int main() {
  return 6;
}

使用gdb进行disas主要产生这样的:

Using gdb to disas main produces this:

0x08048374 <main+0>:    lea    0x4(%esp),%ecx
0x08048378 <main+4>:    and    $0xfffffff0,%esp
0x0804837b <main+7>:    pushl  -0x4(%ecx)
0x0804837e <main+10>:   push   %ebp
0x0804837f <main+11>:   mov    %esp,%ebp
0x08048381 <main+13>:   push   %ecx
0x08048382 <main+14>:   mov    $0x6,%eax
0x08048387 <main+19>:   pop    %ecx
0x08048388 <main+20>:   pop    %ebp
0x08048389 <main+21>:   lea    -0x4(%ecx),%esp
0x0804838c <main+24>:   ret  

下面是我最好的猜测,我认为是怎么回事,什么我需要帮助行由行:

Here is my best guess as to what I think is going on and what I need help with line-by-line:

LEA为0x4(%ESP),ECX%

加载ESP + 4的地址到ECX。 为什么我们加4到ESP?

Load the address of esp + 4 into ecx. Why do we add 4 to esp?

我读的地方,这是命令行参数的地址。但是,当我做了 X / D $ ECX 我得到ARGC的值。 在哪里实际的命令行参数值存储在哪里?

I read somewhere that this is the address of the command line arguments. But when I did x/d $ecx I get the value of argc. Where are the actual command line argument values stored?

和$ 0xfffffff0,%ESP

对齐栈

pushl -0x4(ECX%)

推电除尘器的地方原是入堆栈的地址。 什么是这样做的目的是什么?

Push the address of where esp was originally onto the stack. What is the purpose of this?

推%EBP

推基指针到堆栈

MOV%ESP,EBP%

将当前堆栈指针到基本指针

Move the current stack pointer into the base pointer

推%ECX

推原始ESP + 4上堆叠的地址。 为什么?

Push the address of original esp + 4 on to stack. Why?

MOV $为0x6,%eax中

我想在这里回归6所以我猜的返回值保存在EAX?

弹出%ECX

恢复ECX价值是在堆栈中。 我们为什么要ECX将ESP + 4,当我们回来?

Restore ecx to value that is on the stack. Why would we want ecx to be esp + 4 when we return?

弹出的%ebp

恢复EBP价值是在栈上

Restore ebp to value that is on the stack

LEA -0x4(ECX%),ESP%

ESP恢复到原来值

RET

我是一个的n00b当谈到集会所以任何帮助将是巨大的!此外,如果你看到什么,我想是怎么回事,请大家指正任何虚假陈述。

I am a n00b when it comes to assembly so any help would be great! Also if you see any false statements about what I think is going on please correct me.

感谢一大堆! :]

推荐答案

在code函数体的开头:

Stack frames

The code at the beginning of the function body:

push  %ebp
mov   %esp, %ebp

是创建所谓的堆栈帧的,这是一个坚实的基础为参考参数和对象本地的程序。在的%ebp 寄存器时(正如其名称所示)作为的基指针的,它指向的的(或底部)的程序内本地堆栈的。

is to create the so-called stack frame, which is a "solid ground" for referencing parameters and objects local to the procedure. The %ebp register is used (as its name indicates) as a base pointer, which points to the base (or bottom) of the local stack inside the procedure.

进入程序后,堆栈指针寄存器(%ESP )指向的的调用指令保存在栈上的返回地址的(它是指令的地址刚刚呼叫后)。如果你只是调用 RET 现在,这个地址将被从堆栈到%EIP (指令指针弹出)和code中的调用后,会从该地址进一步执行(下一条指令的)。但是,我们不回呢,我们呢? ; - )

After entering the procedure, the stack pointer register (%esp) points to the return address stored on the stack by the call instruction (it is the address of the instruction just after the call). If you'd just invoke ret now, this address would be popped from the stack into the %eip (instruction pointer) and the code would execute further from that address (of the next instruction after the call). But we don't return yet, do we? ;-)

您再按下的%ebp 注册以保存其previous价值的地方,并没有失去它,因为你很快就会用它的东西。 (顺便说一句,它通常包含调用函数的基指针,当你偷看的价值,你会发现一个previously存储的%ebp ,这将是再次函数的基类指针一个级别更高的,所以你可以跟踪调用栈的方式。)当您保存的%ebp ,然后你可以存储当前%ESP (堆栈指针)的出现,让的%ebp 将指向同一地址:当前本地栈的基础。在%ESP 将来回移动时,你会推栈上弹出值或保留与放大器在过程中;释放局部变量。但的%ebp 将保持固定,仍然指向本地堆栈帧的基础。

You then push %ebp register to save its previous value somewhere and not lose it, because you'll use it for something shortly. (BTW, it usually contains the base pointer of the caller function, and when you peek that value, you'll find a previously stored %ebp, which would be again a base pointer of the function one level higher, so you can trace the call stack that way.) When you save the %ebp, you can then store the current %esp (stack pointer) there, so that %ebp will point to the same address: the base of the current local stack. The %esp will move back and forth inside the procedure when you'll be pushing and popping values on the stack or reserving & freeing local variables. But %ebp will stay fixed, still pointing to the base of the local stack frame.

由呼叫者传递给过程的参数埋只是uner地面(即,它们具有的阳性的偏移相对于基座,因为堆栈增长向下)。你必须在的%ebp 本地堆栈,其中位于的%ebp 。在它的下面(即在 4(%EBP)位于返回地址,所以第一个参数将在 8(%EBP),第二个在 12(%EBP)等。

Parameters passed to the procedure by the caller are "burried just uner the ground" (that is, they have positive offsets relative to the base, because stack grows down). You have in %ebp the address of the base of the local stack, where lies the previous value of the %ebp. Below it (that is, at 4(%ebp) lies the return address. So the first parameter will be at 8(%ebp), the second at 12(%ebp) and so on.

和局部变量可以在基部上方的堆栈上被分配(即,他们不得不的的偏移相对于底座)。只是减去N至的%ESP 和你刚刚分配 N 字节的堆栈中为局部变量上,通过移动上面的堆叠的顶部(或,precisely,下同)该区域:-)可以通过的的偏移相对于的%ebp ,即 -4(%EBP)是第一个字, -8(%EBP)是第二等请记住,(%EBP)指向本地栈的基础上,其中previous 的%ebp 值已保存。所以请记住堆栈恢复为previous位置,你试图通过弹出的%ebp 的%ebp 前>在该过程的末尾。你可以这样做两种方式:

1.您可以自由只能通过加回了 N %ESP (堆栈指针),本地变量是,移动堆栈的顶部,如果这些局部变量从未去过那儿。 (当然,它们的值将保持在栈中,但他们会被认为是释放,并可以通过后续的推被覆盖,因此它不再是安全的转介他们他们的尸体。-J)

2.您可以通过简单地恢复%ESP 的%ebp 已较早到堆栈的底部固定。这将堆栈指针恢复到状态,它刚刚进入程序,节省了%ESP 的%ebp 。这就像加载$ P $当你乱七八糟的东西pviously保存的游戏; - )

And local variables could be allocated on the stack above the base (that is, they'd have negative offsets relative to the base). Just subtract N to the %esp and you've just allocated N bytes on the stack for local variables, by moving the top of the stack above (or, precisely, below) this region :-) You can refer to this area by negative offsets relative to %ebp, i.e. -4(%ebp) is the first word, -8(%ebp) is second etc. Remember that (%ebp) points to the base of the local stack, where the previous %ebp value has been saved. So remember to restore the stack to the previous position before you try to restore the %ebp through pop %ebp at the end of the procedure. You can do it two ways:
1. You can free only the local variables by adding back the N to the %esp (stack pointer), that is, moving the top of the stack as if these local variables had never been there. (Well, their values will stay on the stack, but they'll be considered "freed" and could be overwritten by subsequent pushes, so it's no longer safe to refer them. They're dead bodies ;-J )
2. You can flush the stack down to the ground and free all local space by simply restoring the %esp from the %ebp which has been fixed earlier to the base of the stack. It'll restore the stack pointer to the state it has just after entering the procedure and saving the %esp into %ebp. It's like loading the previously saved game when you've messed something ;-)

有可能通过增加一个开关 -fomit-frame-pointer的 GCC -S 不太凌乱的组装>。它告诉GCC不要安装任何code设置/直到它真正需要的东西,重新设置堆栈帧。只要记住,它可以混淆调试器,因为它们通常依赖栈帧在那里上能够跟踪调用堆栈。但是,如果你不需要调试这个二进制它不会破坏任何东西。它是释放的目标完全没有问题,它可以节省一些时空。

It's possible to have a less messy assembly from gcc -S by adding a switch -fomit-frame-pointer. It tells GCC to not assemble any code for setting/resetting the stack frame until it's really needed for something. Just remember that it can confuse debuggers, because they usually depend on the stack frame being there to be able to track up the call stack. But it won't break anything if you don't need to debug this binary. It's perfectly fine for release targets and it saves some spacetime.

有时你可以满足 .cfi 起一些奇怪的汇编程序指令与函数头交错。这是一个所谓的呼叫帧信息的。它使用调试器来跟踪函数调用。但它也可用于高级语言的异常处理,需要基于调用堆栈栈展开和其他操作。您可以在装配关掉它也通过增加一个开关 -fno-DWARF2-CFI-ASM 。这告诉使用普通的旧标签,而不是那些奇怪的 .cfi 指令海湾合作委员会,并在程序集的末尾增加了一个特殊的数据结构,指的是那些标签。这不关CFI,只是改变格式,以更加透明之一:在CFI表然后对程序员可见

Sometimes you can meet some strange assembler directives starting from .cfi interleaved with the function header. This is a so-called Call Frame Information. It's used by debuggers to track the function calls. But it's also used for exception handling in high-level languages, which needs stack unwinding and other call-stack-based manipulations. You can turn it off too in your assembly, by adding a switch -fno-dwarf2-cfi-asm. This tells the GCC to use plain old labels instead of those strange .cfi directives, and it adds a special data structures at the end of your assembly, refering to those labels. This doesn't turn off the CFI, just changes the format to more "transparent" one: the CFI tables are then visible to the programmer.

这篇关于帮助与理解GDB一个非常基本的main()拆卸的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆