x86_64 Linux系统的GCC调用大会

发布于 2025-01-29 08:41:56 字数 1752 浏览 1 评论 0原文

我写了一个最小功能来测试我是否可以致电/链接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编译器的呼叫约定应是通过堆栈通过参数。在这里,我认为参数是使用eaxebx传递的。我认为这是不对的。
  • 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 and ebx. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尘曦 2025-02-05 08:41:56

我不保存或还原基本指针(设置堆栈框架)。我实际上不明白为什么需要这一点,但是我看过的每个示例都这样做。

不需要。尝试使用-O3编译一些C代码,然后您会发现它不会发生。

Linux系统上GCC编译器的呼叫约定应是通过堆栈通过参数。在这里,我认为参数是使用EAX和EBX传递的。我认为这是不对的。

该部分仅由于flu虫而起作用。汇编恰好将10放入eax中,但也不能保证这将永远发生。同样,使用-O3编译,并且不会再编译了。

RET可能希望从某个地方获得返回地址。我相当确定我还没有提供。

这部分很好。返回地址由呼叫者提供。输入功能时,它总是位于堆栈的顶部。

甚至可能还有其他我不知道的原因。

是的,还有一个:ebx是呼叫的,但您正在抓住它。如果调用函数(或堆栈中的任何内容)使用它,则将破坏它。

我编写的内容是完整的fluke吗?

是的,因为上面的第二和第四点。

作为参考,以下是gcc-O0(默认优化级别)和-O3的组件进行比较。 : https://godbolt.org/z/7p13fbb1a

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.

That's not needed. Try compiling some C code with -O3 and you'll see it doesn't happen then.

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 and ebx. I don't think that is right.

That part is only working because of a fluke. The assembly happens to put 10 in eax too the way you compiled, but there's no guarantee this will always happen. Again, compile with -O3 and it won't anymore.

ret probably expects to pick up a return address from somewhere. I am fairly sure I haven't supplied this.

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.

There may even be other reasons which I don't know about.

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.

Is it a complete fluke that what I have written produces the correct output?

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

假装爱人 2025-02-05 08:41:56

未签名的长睾丸(无符号长c){return ++ c; }

gcc -12带有-o3:

0000000000400540 <teste>:
  400540:       48 8d 47 01             lea    rax,[rdi+0x1]
  400544:       c3                      ret

如果没有任何自动变量,则不需要弄乱bp。
使用-O3如果从未调用函数,则可以从代码中完全将其删除。

unsigned long teste(unsigned long c) { return ++c; }

gcc-12 with -O3:

0000000000400540 <teste>:
  400540:       48 8d 47 01             lea    rax,[rdi+0x1]
  400544:       c3                      ret

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文