x86_64 Linux系统的GCC调用大会
我写了一个最小功能来测试我是否可以致电/链接C和x86_64汇编代码。
这是我的main.c
#include <stdio.h>
extern int test(int);
int main(int argc, char* argv[])
{
int a = 10;
int b = test(a);
printf("b=%d\n", b);
return 0;
}
这是我的test.asm
section .text
global test
test:
mov ebx,2
add eax,ebx
ret
我使用此脚本构建了一个可执行文件
#!/usr/bin/env bash
nasm -f elf64 test.asm -o test.o
gcc -c main.c -o main.o
gcc main.o test.o -o a.out
,我写了test.asm
我在做什么。然后,我走开了一些阅读,现在我不明白我的代码看起来如何工作,因为我说服自己不应该这样做。
这是我认为这不起作用的原因列表:
- 我不保存或还原基本指针(设置堆栈框架)。我实际上不明白为什么需要这一点,但是我研究的每个示例都这样做。
- Linux系统上GCC编译器的呼叫约定应是通过堆栈通过参数。在这里,我认为参数是使用
eax
和ebx
传递的。我认为这是不对的。 ret
可能希望从某个地方拿起返回地址。我相当确定我还没有提供。- 甚至可能还有其他我不知道的原因。
我所写的内容会产生正确的输出,这是完整的Fluke吗?
我完全是新手。虽然我听说过一些X86概念,这是我第一次真正尝试写一些概念。要开始某个地方吗?
编辑:以后的参考是一个更正的代码
test:
; save old base pointer
push rbp ; sub rsp, 8; mov [rsp] rbp
mov rbp, rsp ; mov rbp, rsp ;; rbp = rsp
; initializes new stack frame
add rdi, 2 ; add 2 to the first argument passed to this function
mov rax, rdi ; return value passed via rax
; did not allocate any local variables, nothing to add to
; stack pointer
; the stack pointer is unchanged
pop rbp ; restore old base pointer
ret ; pop the return address off the stack and jump
; call and ret modify or save the rip instruction pointer
I have written a minimal function to test whether I can call/link C and x86_64 assembly code.
Here is my main.c
#include <stdio.h>
extern int test(int);
int main(int argc, char* argv[])
{
int a = 10;
int b = test(a);
printf("b=%d\n", b);
return 0;
}
Here is my test.asm
section .text
global test
test:
mov ebx,2
add eax,ebx
ret
I built an executable using this script
#!/usr/bin/env bash
nasm -f elf64 test.asm -o test.o
gcc -c main.c -o main.o
gcc main.o test.o -o a.out
I wrote test.asm
without having any real clue what I was doing. I then went away and did some reading, and now I don't understand how my code appears to be working, as I have convinced myself that it shouldn't be.
Here's a list of reasons why I think this shouldn't work:
- I don't save or restore the base pointer (setup the stack frame). I actually don't understand why this is needed, but every example I have looked at does this.
- The calling convention for the gcc compiler on Linux systems should be to pass arguments via the stack. Here I assume the arguments are passed using
eax
andebx
. I don't think that is right. ret
probably expects to pick up a return address from somewhere. I am fairly sure I haven't supplied this.- There may even be other reasons which I don't know about.
Is it a complete fluke that what I have written produces the correct output?
I am completely new to this. While I have heard of some x86 concepts in passing this is the first time I have actually attempted to write some. Got to start somewhere?
Edit: For future reference here is a corrected code
test:
; save old base pointer
push rbp ; sub rsp, 8; mov [rsp] rbp
mov rbp, rsp ; mov rbp, rsp ;; rbp = rsp
; initializes new stack frame
add rdi, 2 ; add 2 to the first argument passed to this function
mov rax, rdi ; return value passed via rax
; did not allocate any local variables, nothing to add to
; stack pointer
; the stack pointer is unchanged
pop rbp ; restore old base pointer
ret ; pop the return address off the stack and jump
; call and ret modify or save the rip instruction pointer
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不需要。尝试使用
-O3
编译一些C代码,然后您会发现它不会发生。该部分仅由于flu虫而起作用。汇编恰好将
10
放入eax
中,但也不能保证这将永远发生。同样,使用-O3
编译,并且不会再编译了。这部分很好。返回地址由呼叫者提供。输入功能时,它总是位于堆栈的顶部。
是的,还有一个:
ebx
是呼叫的,但您正在抓住它。如果调用函数(或堆栈中的任何内容)使用它,则将破坏它。是的,因为上面的第二和第四点。
作为参考,以下是
gcc
从-O0
(默认优化级别)和-O3
的组件进行比较。 : https://godbolt.org/z/7p13fbb1aThat's not needed. Try compiling some C code with
-O3
and you'll see it doesn't happen then.That part is only working because of a fluke. The assembly happens to put
10
ineax
too the way you compiled, but there's no guarantee this will always happen. Again, compile with-O3
and it won't anymore.This part is fine. The return address gets supplied by the caller. It'll always be at the top of the stack when your function gets entered.
Yes, there's one more:
ebx
is call-saved but you're clobbering it. If the calling function (or anything above it in the stack) used it, then this would break it.Yes, because of the second and fourth points above.
For reference, here's a comparison of the assembly that
gcc
produces from your C code at the-O0
(the default optimization level) and-O3
: https://godbolt.org/z/7P13fbb1a未签名的长睾丸(无符号长c){return ++ c; }
gcc -12带有-o3:
如果没有任何自动变量,则不需要弄乱bp。
使用-O3如果从未调用函数,则可以从代码中完全将其删除。
unsigned long teste(unsigned long c) { return ++c; }
gcc-12 with -O3:
You don't need to mess with bp if you don't have any auto variables.
With -O3 if the function is never called, it can be fully removed from the code.