use*_*291 1 c x86 assembly nasm
我正在按照Nick Blundell的操作系统开发部分5.1.3进行操作.我正在研究如何将以下C代码编译成机器代码:
void caller_fun(){
callee_fun(0xdede);
}
int callee_fun(int arg){
return arg;
}
Run Code Online (Sandbox Code Playgroud)
我最后拆解的机器代码ndisasm
是这样的:
00000000 55 push ebp
00000001 89E5 mov ebp,esp
00000003 83EC08 sub esp,byte +0x8
00000006 83EC0C sub esp,byte +0xc
00000009 68DEDE0000 push dword 0xdede
0000000E E806000000 call dword 0x19
00000013 83C410 add esp,byte +0x10
00000016 90 nop
00000017 C9 leave
00000018 C3 ret
00000019 55 push ebp
0000001A 89E5 mov ebp,esp
0000001C 8B4508 mov eax,[ebp+0x8]
0000001F 5D pop ebp
00000020 C3 ret
Run Code Online (Sandbox Code Playgroud)
研究堆栈指针和基指针是如何工作的,我制作了下面的图表,显示了当0x1C
处理器运行偏移量的操作码时的堆栈情况:
Stack situation when processor is running `mov eax,[ebp+0x8]` at offset 0x1C +---------------------------------+ | 4 bytes for | | `push ebp` at offset 0x00 | +---------------------------------+ | 20 (8+12) bytes for | | `sub esp,byte +0x8` | | and `sub esp,byte +0xc` | | at offsets 0x03 and 0x06 | +---------------------------------+ | 4 bytes for `push dword 0xdede` | | at offset 0x09 | +---------------------------------+ | 4 bytes for instruction pointer | | by `call dword 0x19` | | at offset 0x0E | +---------------------------------+ | 4 bytes for `push ebp` | | at offset 0x19 | +---------------------------------+ --> ebp & esp are both here by `mov ebp,esp` at offset 0x1A
现在,我有一些我无法通过研究和研究得出的问题:
我的堆栈情况图是否正确?
为什么20个字节压入堆栈通过sub esp,byte +0x8
与sub esp,byte +0xc
在偏移0x03
和0x06
?
即使需要20个字节的堆栈存储器,为什么它不是由单个指令分配的sub esp,byte +0x14
,即0x14=0x8+0xc
我正在用这个make文件编译C代码:
all: call_fun.o call_fun.bin call_fun.dis
call_fun.o: call_fun.c
gcc -ffreestanding -c call_fun.c -o call_fun.o
call_fun.bin: call_fun.o
ld -o call_fun.bin -Ttext 0x0 --oformat binary call_fun.o
call_fun.dis: call_fun.bin
ndisasm -b 32 call_fun.bin > call_fun.dis
Run Code Online (Sandbox Code Playgroud)
如果没有优化,堆栈将用于保存和恢复基本指针.在x86_64调用约定(https://en.wikipedia.org/wiki/X86_calling_conventions)中,在调用函数时,堆栈必须以16字节边界对齐,因此很可能在您的情况下会发生这种情况.至少,当我在我的系统上编译代码时,这就是我的情况.这是ASM:
callee_fun(int): # @callee_fun(int)
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
retq
caller_fun(): # @caller_fun()
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $57054, %edi # imm = 0xDEDE
callq callee_fun(int)
movl %eax, -4(%rbp) # 4-byte Spill
addq $16, %rsp
popq %rbp
retq
Run Code Online (Sandbox Code Playgroud)
值得注意的是,当启用优化时,根本没有堆栈使用或修改:
callee_fun(int): # @callee_fun(int)
movl %edi, %eax
retq
caller_fun(): # @caller_fun()
retq
Run Code Online (Sandbox Code Playgroud)
最后,但并非最不重要的是,在使用ASM列表时,请不要反汇编目标文件或可执行文件.而是指导您的编译器生成程序集列表.这将为您提供更多背景信息.
如果你正在使用gcc,那么这样做的一个很好的命令就是
gcc -fverbose-asm -S -O
Run Code Online (Sandbox Code Playgroud)