首页 > 解决方案 > 难以弄清楚对汇编函数的调用返回什么

问题描述

我是汇编语言的新手。我有以下汇编代码:

.intel_syntax noprefix
.bits 32

.global asm0

asm0:
    push    ebp
    mov ebp,esp
    mov eax,DWORD PTR [ebp+0x8]
    mov ebx,DWORD PTR [ebp+0xc]
    mov eax,ebx
    mov esp,ebp
    pop ebp 
    ret

我想弄清楚以下命令返回什么:asm0(0x2a,0x4f)。

我正在运行 Ubuntu,并且我已经下载了 NASM。我一直在这里阅读有关汇编代码的语法:https ://www.tutorialspoint.com/assembly_programming/assembly_basic_syntax.htm ,但我仍然无法弄清楚。我用 C++ 编码已经三年了,但这仍然让我感到困惑。有更多经验的人可以帮我解决这个问题吗?

编辑:上面的代码说 push 和 move,所以它看起来像一个堆栈数据结构?然后它在最后返回。但是,这个函数没有参数。这将如何工作?

运行 gcc -m32 -g main.c assembly.s 时出现错误消息

myName@myName:~/Downloads$ gcc -m32 -g main.c file.S

main.c: In function ‘main’:

main.c:5:17: warning: implicit declaration of function ‘asm0’ [-Wimplicit-
function-declaration]

    printf("%d", asm0(123,456));
                 ^~~~

file.S: Assembler messages:

file.S:2: Error: unknown pseudo-op: `.bits'

标签: assemblyx86

解决方案


您的函数在返回地址上方的堆栈中查找其 2 个参数。请注意在设置传统堆栈帧之后对[ebp+8]和的访问。[ebp+12]

您没有在 asm 中声明 args;这是一个高级概念,您可以在 asm 的代码中实现自己。

push操作调用堆栈,而不是堆栈数据结构。 https://felixcloutier.com/x86/PUSH.html。它只是减少 ESP 并存储到[esp]. 这样做是为了保存/恢复调用者的 EBP,作为制作传统堆栈框架的一部分。这是超级基础的东西,如果你还没有看过,请阅读更多教程或指南。


您可以轻松地尝试代码并通过从 C 程序调用它来找到它的作用。

除非你不能,因为你的 asm 不会组装。 .bits 32不是有效的 GAS 指令,但这很明显是 GNU 汇编器 (GAS) 语法。我不知道有任何汇编器具有.intel_syntax noprefix除 GAS 本身和 clang 的内置汇编器之外的指令,并且它们都不支持.bits 32. 所以我猜这个函数来自教程或其他东西,并且是从 NASM 语法 ( bits 32) 或其他东西手动移植到 GAS 的,而没有实际测试。但是您不需要也不应该使用该指令或 GAS 等效项.code32。32 位代码在组装成 32 位目标文件时已经是默认设置,因此.code32如果您只是在gcc foo.s没有使用的情况下将 32 位代码组装成 64 位目标文件-m32,然后你会遇到难以调试的问题是运行时而不是汇编时的明显错误。

所以删除.bits 32,这解决了问题中的错误消息。

要摆脱警告消息,请通过提供适当的标题和原型来扩展我在评论中写的单行。

#include <stdio.h>
int asm0(int,int);

int main(){
    printf("%d\n", asm0(123,456));
}

该函数也有问题:它破坏ebx了 i386 System V 调用约定中的调用保留寄存器。(就像所有正常的 x86 调用约定一样。)

启用默认 PIE 的 Linux 发行版上的 gcc 生成的代码依赖于 EBX 被保留调用,并且实际上崩溃了。(不过,您仍然可以使用调试器单步执行它,因为崩溃发生在asm0返回之后。)

但是您可以制作一个恰好可以工作的可执行文件,然后让您运行它以查看它返回的 arg:

peter@volta:/tmp$ gcc -g -Og -no-pie -fno-pie -m32 main.c asm0.s 
peter@volta:/tmp$ ./a.out 
456

asm0返回其第二个参数(通过将其复制到返回值寄存器 EAX)。它将它加载到 EBX,然后将 EBX 复制到 EAX,覆盖加载第一个 arg 的结果。

你可以看到这种情况发生gdb ./a.out

(gdb)  layout reg
       b main
       r
       si
       (press return to single-step one instruction at a time through the code, including into your function)

GDB 突出显示在每个步骤中更改了哪些寄存器。

有关更多调试提示,另请参阅https://stackoverflow.com/tags/x86/info的底部。


进一步阅读:


推荐阅读