2017-09-24 272 views
2

我正在嘗試在程序集中製作一個小程序(對於AT & T)。我試圖以整型的形式從用戶那裏得到一個輸入,然後遞增,然後輸出遞增的值。但是,該值不會增加。我花了最後幾個小時嘗試所有我能想到的東西,但它仍然不起作用,所以我有這樣的想法,即我可能理解集會中的一個概念不好,導致我沒有發現錯誤。這是我的代碼:我的(AT&T)程序集(x86-x64)代碼應該增加,但不會

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是必要的,但是,我想我不使用堆棧空間我ADRESS那裏,所以我懷疑這個錯誤有什麼。我不確定的課程。先謝謝你。

編輯,更新的代碼(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 

它運行和增量但是,現在,當增加值outputed,那裏顯示的數值後一些奇怪的跡象。

編輯:無所謂,上面只發生過一次,現在沒有增加值輸出,只有奇怪的跡象。

+0

您的上次編輯再次從'inout'的末尾刪除'ret'。 –

回答

1

這是關於如何正確調用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 

到現在爲止你的代碼是正確的(除非你沒有正確對齊堆棧,但不用擔心,現在,scanf將可能讓你逃脫它)。

22  incq %rsi 

這是你出錯的地方。在調用之前,您將RSI(scanf的第二個參數寄存器)設置爲指針到存儲位置。 scanf從stdin讀取一個數字,並將它寫入到那個存儲位置,而不是RSI。

從評論中的討論,你的意圖是增加一個scanf讀取的值,並立即打印出來。正如其他幾個人指出的那樣,在scanf返回後,您不能認爲您加載到RSI,RDI或RAX的值是完整的。 (x86-64 psABI指定通過函數調用保留哪些寄存器:整數寄存器中只有RBX,RBP和R12到R15被保留。如果您打算在x86上進行大量的彙編編程,您應該閱讀本文檔以瞭解覆蓋範圍。-64(注意:Windows使用不同的ABI這不,據我所知,任何相關文檔。)),所以你必須在呼叫從無到有設立printf

 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),whe當您撥打printf時,您會通過要打印的movq -8(%rbp), %rsi; incq %rsi)。

(其實你應該當你調用printf使用略有不同的格式字符串,因爲你需要的號碼後打印換行符,所以"%ld\n"效果會更好。)

您當前的代碼做幾乎這個,用不同的方式。我這樣做是因爲在函數的中間亂堆棧指針(popq %rax)是不好的做法。 (還記得我之前提到的不正確對齊堆棧嗎?如果你在進入時設置了一個完整的「調用幀」,然後直接退出堆棧指針直到退出,那麼保持堆棧對齊就容易多了。從技術上講,你只需要需要必須在每次調用指令的點對準堆棧指針,雖然)

您還沒有正確地結束這個函數:

27  addq $8, %rs 

我想你沒有複製和粘貼整個程序 - 這看起來像是在線路中間被切斷了。無論如何,如果你去打擾首先有一個幀指針(幀指針不需要對x86-64的),你應該再使用它退出:「AT & T」

 movq %rbp, %rsp 
     popq %rbp 
     ret 

順便說一句,彙編語法用於許多不同的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編譯器做了與你不同的事情時,這可能意味着它知道你不知道的東西,你應該知道這件事。

+0

感謝您的回答和整體反饋,但是,我認爲我仍然需要一些更多的指針。我實際上嘗試了您提供的代碼,但是,我仍然沒有得到正確的輸出結果。(當我在inout子程序中調用printf時,我仍然得到非遞增的輸入,當我在那裏刪除printf,並在我的主例程中調用它(將rax的內容移動到rsi後),我根本沒有輸出。所以,我仍然不明白我認爲的東西 –

+1

OP應該進一步注意,函數被允許(並且通常會)破壞他們在返回時收到的參數,rsi包含你最初放在那裏的值從'scanf'返回 – fuz

+0

@RomeeB請給我們顯示您的更新代碼,不要從問題中刪除原始代碼,只需將更新後的代碼粘貼到下面 – fuz

0

重:更新的代碼:

它運行和增量但是,現在,當增加值outputed,那裏顯示的值後一些奇怪的跡象。

通過轉換的寄存器被調用了。您在不將格式字符串放入%rdi的情況下調用printf,在scanf返回後,您必須假定其保留垃圾。

用調試器單步執行代碼。使用ni來跳過gdb中的call。 (有關GDB提示,請參閱標記wiki的底部)。

相關問題