簡體   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