繁体   English   中英

我的(AT&T)汇编(x86-x64)代码应该增加但不增加

[英]My (AT&T) assembly (x86-x64) code should increment but doesn't

我正在尝试组装一个小程序(用于AT&T)。 我正在尝试以整数形式从用户那里获得输入,在此之后增加它,然后输出增加的值。 但是,该值不会增加。 我花了最后几个小时尝试所有可能的方法,但是仍然无法正常工作,所以我有一个想法,就是我可能对汇编中的一个概念不太了解,导致我没有发现错误。 这是我的代码:

1 hiString: .asciz "Hi\n"
  2 formatstr: .asciz "%ld"
  3 
  4 .global main
  5 
  6 main:
  7     movq $0, %rax           #no vector registers printf
  8     movq $hiString, %rdi    #load hiString
  9     call printf             #printf
 10     call inout              #inout
 11     movq $0, %rdi           #loading exit value into register rdi
 12     call exit               #exit
 13 
 14 inout:
 15     pushq %rbp              #Pushing bp
 16     movq %rsp, %rbp         #Moving sp to bp
 17     subq $8, %rsp           #Space on stack for variable
 18     leaq -8(%rbp), %rsi
 19     movq $formatstr, %rdi   #1st argument scanf
 20     movq $0, %rax           #no vector for scanf registers
 21     call scanf              #scanf
 22     incq %rsi
 23     call printf

从我的一个朋友那里获得的教程中,我知道第17至19行是必要的,但是,我认为我不使用我在那儿找到的堆栈空间,因此我怀疑该错误与之有关。 当然,我不确定。 先感谢您。

编辑,更新代码(现在仍在子例程中调用printf)

    1 hiString: .asciz "hi\n"
  2 formatstr: .asciz "%ld"
  3 
  4 .global main
  5 
  6 main:
  7     movq $0, %rax          
  8     movq $hiString, %di   
  9     call printf             
 10     call inout              
 11     movq $0, %rdi           
 12     call exit               
 13 
 14 inout:
 15     pushq %rbp             
 16     movq %rsp, %rbp         
 17     subq $8, %rsp         
 18     leaq -8(%rbp), %rsi
 19     movq $formatstr, %rdi   
 20     movq $0, %rax           
 21     call scanf              
 22     popq %rax
 23     incq %rax
 24     movq %rax, %rsi
 25     movq $0, %rax
 26     call printf
 27     addq $8, %rs  

它现在运行并递增,但是,当输出递增的值时,该值之后会出现一些奇怪的符号。

编辑:没关系,上面只发生过一次,现在没有输出增量值,只有怪异的迹象。

这是有关如何正确调用scanf的经典混淆的汇编级版本。

 14 inout:
 15     pushq %rbp              #Pushing bp
 16     movq %rsp, %rbp         #Moving sp to bp
 17     subq $8, %rsp           #Space on stack for variable
 18     leaq -8(%rbp), %rsi
 19     movq $formatstr, %rdi   #1st argument scanf
 20     movq $0, %rax           #no vector for scanf registers
 21     call scanf              #scanf

(编者注:最好是mov $formatstr, %edi Linux非PIE可执行文件中的mov $formatstr, %edi ,或者更可移植的与位置无关的lea formatstr(%rip), %rdi将静态存储中的字符串地址放入寄存器中)。

到目前为止,您的代码是正确的(除非您没有正确对齐堆栈,但是现在不必担心, scanf可能会让您摆脱它)。 更新:现代版本的glibc确实存在scanf,该错误会在未对齐的RSP上发生故障 ,例如从Ubuntu 18.04开始,可能更早。

 22     incq %rsi

这是您出问题的地方。 在调用之前,将RSI( scanf的第二个参数寄存器)设置为指向存储位置的指针 scanf从stdin读取一个数字并将其写入该存储位置 ,而不是RSI。

从评论的讨论中,您的意图是将一个值添加到scanf读取的值中,然后立即将其打印出来。 正如其他一些人指出的那样,在scanf返回之后,您不能假定加载到RSI,RDI或RAX中的值是完整的。 x86-64 psABI指定通过函数调用保留哪些寄存器:在整数寄存器中,仅保留RBX,RBP和R12至R15。如果您打算进行大量汇编,则应阅读本文档的封面,以了解更多信息。在x86-64上进行编程(警告:Windows使用不同的ABI,其调用约定已记录在MSDN上,请参阅x86标签wiki中的链接。)

因此,您必须将args从头设置为printf ,因为scanf破坏了这些寄存器:

       movq -8(%rbp), %rsi   # load variable as arg 2 of printf
       incq %rsi             # and add one
       movq $formatstr, %rdi # first argument to printf
       xorl %rax, %rax       # no vector args to printf
       call printf

请在此处密切注意scanfprintf之间的区别:您可以为两者使用相同的格式字符串,但是当调用scanf您传递存储位置地址leaq -8(%rbp), %rsi ),而当您调用printf传递要打印movq -8(%rbp), %rsi; incq %rsi )。

(实际上,在调用printf ,您应该使用略有不同的格式字符串,因为您需要在数字后打印换行符,因此"%ld\\n"会更好。)

您当前的代码几乎以不同的方式完成此任务。 我这样做是因为在函数中间弄乱堆栈指针( popq %rax )是一种不好的做法。 (还记得我说过的关于未正确对齐堆栈的内容吗?如果在进入时设置一个完整的“调用框架”,然后不理会堆栈指针直到退出,则使堆栈保持对齐会容易得多。从技术上讲,您只需要具有不过,堆栈指针会在每个调用指令的位置对齐。)

您也没有正确结束该功能:

 27     addq $8, %rs  

我认为您没有复制并粘贴整个程序-看起来好像已经在行中间被切断了。 无论如何,如果您首先要麻烦帧指针(x86-64上不需要帧指针),则应再次使用它退出:

        movq %rbp, %rsp
        popq %rbp
        ret

顺便说一句,“ AT&T”汇编语法用于许多不同的CPU体系结构。 在谈到汇编语言,我们总是首先要知道CPU的架构; 语法变体(如果有)是次要的。 您应该将问题命名为“我的汇编程序(x86-64,AT&T语法)...”。


作为最后的建议,我建议您编译此C程序

#include <stdio.h>

static void inout(void)
{
    long x;
    scanf("%ld", &x);
    printf("%ld\n", x+1);
}

int main(void)
{
    printf("hi\n");
    inout();
    return 0;
}

使用您选择的C编译器时,请使用与-S -O2 -fno-inline等效的选项(即:生成文本汇编语言,进行了优化,但不进行任何内联),然后逐行读取汇编输出。 每当C编译器执行与您不同的操作时,这可能意味着C编译器知道您不知道的内容,因此您应该了解该内容。

或更简单地说, 在Godbolt编译器资源管理器中查看它

回复:更新的代码:

它现在运行并递增,但是,当输出递增的值时,该值之后会出现一些奇怪的符号。

传递arg的寄存器被调用。 您可以在不将格式字符串放入%rdi情况下调用printf ,而在scanf返回后必须假定该字符串保留垃圾。

使用调试器单步执行代码。 使用ni结束gdb中的call (有关GDB技巧,请参见标签Wiki的底部)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM