c - 我的 (AT&T) 程序集 (x86-x64) 代码应该增加但不增加

标签 c assembly x86-64 att

我正在尝试在汇编中制作一个小程序(用于 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

从我 friend 的教程中,我了解到第 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

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

到目前为止,您的代码是正确的(除了您没有正确对齐堆栈,但现在不要担心,scanf 可能会让您侥幸逃脱)。更新:glibc 的现代版本确实有 a scanf that faults on a misaligned 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 tag wiki 中的链接。))

所以你必须将参数设置为 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

密切关注scanf的区别和 printf此处:您可以对两者使用相同的格式字符串,但是当您调用 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 编译器做了一些与你不同的事情时,这可能意味着它知道一些你不知道的东西,你应该了解一些东西。

或者更简单地说,look at it on the Godbolt compiler explorer

关于c - 我的 (AT&T) 程序集 (x86-x64) 代码应该增加但不增加,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46389957/

相关文章:

c++ - 从地址位置加载 XMM 寄存器

linux-kernel - 为什么在使用ASENI指令访问之前要检查irq_fpu_usable

linux - 内核端页面缓存 virt <-> phys 映射如何与 TLB 交互?

c - 如何在C中传递一个字符串作为参数,然后返回一个字符串

c - TCP 连接中链接断开时的 send() 函数行为

linux - 拉出二进制文件时,Shellcode 不起作用

assembly - SIMD指令用于浮点相等比较(NaN == NaN)

c - 了解 C 内置库函数实现

c++ - 从程序的角度来看,内存段是如何组织的

将多个 C 代码编译为一个汇编文件