2017-10-21 202 views
0

所以,我有一個彙編函數,它在C中調用。它編譯並且沒有給出警告,但是當我嘗試運行它時,它給了我一個分段錯誤。我認爲這是因爲我不能將一個常量移動到一個寄存器中,但是使用mul/div命令它需要一個值在EAX寄存器中。 我如何在Assembly中乘兩個常量?裝配 - 如何在裝配中用常數乘以一個常數?

下面的代碼到目前爲止...

.section .data 
.global n 
.equ A, 50 
.equ B, 5 

.section .text 
.global loop_function 

loop_function: 
    # prologue 
    pushl %ebp  # save previous stack frame pointer 
    movl %esp, %ebp # the stack frame pointer for sum function 
    # beginning 
    movl i, %ebx # place i (declared in c) in ebx 
    movl A, %eax # place A in eax 
    movl B, %ecx # place B in ecx 
    jmp loop 
loop: 
    movl $0, %edx # clean edx register 
    cdq 
    idivl %ecx # A/B, result in eax 
    imull %ebx # i * A/B, result in eax 

    incl %ebx 
    cmpl %ebx, n # if i <= n 
    jle loop # then jumps to loop 
    jmp end # else jumps to end 

end: 
    # epilogue 
    movl %ebp, %esp # restore the previous stack pointer ("clear" the stack) 
    popl %ebp  # restore the previous stack frame pointer 
    ret 
+1

真正的答案是在[Application_binary_interface](https://en.wikipedia.org/ wiki/Application_binary_interface)(ABI)。如果您沒有ABI,那麼我建議您將每個使用的寄存器推入堆棧,並在返回之前將其彈出。 – user3386109

+2

錯誤的可能原因是您沒有遵循標準調用約定。特別是,你銷燬了'ebx'這是一個被保存的被保存的寄存器,所以你的調用者可能會希望它沒有改變。 – Jester

+2

A和B似乎是常量,我猜測我是一個變量,但是對所有變量使用完全相同的語法。我認爲它試圖從內存地址50和地址5加載值,這不會起作用。我建議使用$ A和$ B。 – prl

回答

1

GAS支持常量the * operators for assemble-time multiplication。例如,mov $(5 * 50), %eax彙編到與mov $250, %eax完全相同的機器代碼。其他運算符(如+ - /%)和位運算符也可用。我只是使用*作爲示例,但只要它們計算爲單個數字(或鏈接器可以解析的符號的偏移量),就可以從編譯時常量中構造任意表達式。

這適用於彙編常量,如.equ A, 50A = 50

.equ A, 50 
.equ B, 5 

aa = 3 
bb = 7 

.globl _start 
_start:     # machine code   .intel_syntax disassembly 
    mov $(5 * 50), %eax # b8 fa 00 00 00 mov eax,0xfa # 250 

    mov $(aa * B), %ecx # b9 0f 00 00 00 mov ecx,0xf # 3*5 = 15 
    mov $A * B, %edx # ba fa 00 00 00 mov edx,0xfa # 250 

注意,整個立即數只使用一個$,而不是在每一個符號名稱$。例如,mov $(5 + $A), %eax會嘗試將名爲$A(加5)的符號地址放入%eax中,以便爲未定義的符號獲取鏈接時錯誤。

mov $($A * $B), %eax甚至沒有組裝:
Error: invalid operands (*UND* and *UND* sections) for '*'
這是因爲你想乘兩個未知符號($A$B)的地址,而不是你的彙編常數AB

在GAS中,all symbols have an associated section。當您使用.equ=定義符號時,它是一個「絕對」符號(而不是.data節或.text節符號,就像您從標籤A:中獲得的那樣)。

彙編程序常量與用標號定義的符號並無太大區別。但是,除+-之外,全部爲assemble-time math operators require both args to be absolute, and the result is absolute


您的代碼似乎試圖將常量放入寄存器中以在運行時將它們相乘。如果你堅持這樣做,作爲一個練習,

mov $A, %ecx   # put symbol's value in ECX 
imul $B, %ecx, %eax  # EAX = A * B 

mov A, %eax是從符號的值的負載。即來自絕對地址50的負載,這顯然是段錯誤。使用調試器進行單步調試,並查看反彙編以瞭解發生了什麼。

AT &對於即時常量,T語法使用$,所以使用它來獲取值。 (請記住,.equ符號的行爲與標籤相同,例如您將如何使用$my_string來獲取地址作爲即時貼。)

0

感謝您的幫助,夥計們,我能夠做到用下面的代碼練習:

.section .data 
    .global n 
    .global i 
    .equ A, 50 
    .equ B, 5 

.section .text 
    .global loop_function 

loop_function: 
    # prologue 
    pushl %ebp  # save previous stack frame pointer 
    movl %esp, %ebp # the stack frame pointer for sum function 
    # beginning 
    movl i, %ecx # place i (declared in c) in ecx 
    movl $A, %eax # place A in eax 
    movl $B, %ebx # place B in ebx 
    movl $0, %edx # clean edx register 
    cdq 
    idivl %ebx # (A/B), result goes to eax 
loop: 
    incl %ecx # increment i, which is in ecx 
    cmpl n, %ecx # if n > i 
    jg loop # then jumps to loop 
end: 
    incl %ecx 
    imull %ecx # multiply i by (A/B), result in eax 
    # epilogue 
    movl %ebp, %esp # restore the previous stack pointer ("clear" the stack) 
    popl %ebp  # restore the previous stack frame pointer 
    ret 
+0

您應該將'i'和'n'作爲函數參數而不是全局變量。另外,'cdq'將'edx'設置爲'eax',所以首先將'edx'歸零是沒有意義的。另外,我沒有看到循環的重點。你可以用'mov n,%ecx'替換它。 (或者我想用額外的'inc',你正在做'%ecx =(n)+ 1') –

+0

另外,你在不保存它的情況下'%ebx'。使用'%ecx'來保存idiv除數的'$ B'。 –

+0

所以你的整個功能可能是'mov $ A,%eax'; 'cdq'; 'mov $ B,%ecx'; 'idiv%ecx'; 'imul n,%eax'。 (或者,如果它實際上是你需要的n + 1,則將'n'加載到'ecx'或'edx'中,然後加載'inc%ecx')。除非您想在'edx:eax'中返回完整的64位產品,否則不需要使用單操作數格式。 2操作數格式更快,只寫入一個寄存器。) –