x86 汇编 LEA 指令的奇怪行为 - 对堆栈的影响

问题描述 投票:0回答:1

我用 x86 汇编语言编写了一个小程序,将 2 个分数相加。 这是结构:

struct FRACTION 
    numer   dd  ? 
    denom   dd  ? 
ends 

这是两个分数相加的函数:

    
;===================================
; add_fractions(frac_addr_1, frac_addr_2)
;
add_fractions:
    .frac_addr_1 = 8h
    .frac_addr_2 = 0ch 
    push    ebp 
    mov     ebp, esp
    sub     esp, sizeof.FRACTION
    
    .sum_numer_offset = -8h
    .sum_denom_offset = -4h
    
    push    esi
    push    edi
    push    edx 
    push    ecx 
    push    ebx 
    
    mov esi, dword [ebp + .frac_addr_1]
    mov edi, dword [ebp + .frac_addr_2]
    
    ;=================================
    ; Get product of both denominator
    ;================================= 
    mov eax, dword [esi + FRACTION.denom]
    mov ebx, dword [edi + FRACTION.denom]
    mul ebx 
    mov ecx, eax            ; ecx = product 
    
    ;===============================
    ; Get GCD of both denominator
    ;===============================
    mov eax, dword [esi + FRACTION.denom]
    mov ebx, dword [edi + FRACTION.denom]
    push    ebx 
    push    eax
    call    stein 
    add esp, 4*2            ; eax has GCD 
    
    ;===============================
    ; Get the LCM = product / GCD 
    ;===============================
    mov ebx, eax            ; swap eax and ecx
    mov eax, ecx            ; eax is not product        
    mov ecx, ebx            ; ecx is GCD to divide 
    div ecx                 ; eax is now LCM after division
    
    ;=========================================
    ; store LCM as the denominator of the sum
    ;=========================================
    mov dword [ebp + .sum_denom_offset], eax 
    
    ;=========================================
    ; compute for the numerator of the sum 
    ;=========================================
    mov eax, dword [ebp + .sum_denom_offset]
    mov ebx, dword [esi + FRACTION.denom]
    div ebx 
    mov ebx, dword [esi + FRACTION.numer]
    mul ebx
    mov dword [ebp + .sum_numer_offset], eax 
    
    mov eax, dword [ebp + .sum_denom_offset]
    mov ebx, dword [edi + FRACTION.denom]
    div ebx 
    mov ebx, dword [edi + FRACTION.numer]
    mul ebx 
    
    add eax, dword [ebp + .sum_numer_offset]
    mov dword [ebp + .sum_numer_offset], eax 
    
    lea eax, dword [ebp - sizeof.FRACTION]
    
.end_func:
    pop ebx 
    pop ecx 
    pop edx 
    pop edi 
    pop esi 
    
    add esp, sizeof.FRACTION

    pop ebp
    ret

add_fractions 子例程中的 LEA 指令工作正常,它将正确的地址返回到具有 sum_numer 和 sum_denom 的本地变量。

这是调用程序中调用 add_fractions 的代码片段:

mov esi, frac_1 
mov edi, frac_2
push    edi 
push    esi
call    add_fractions
add esp, 4*2    

lea ebx, dword [eax]
lea ecx, dword [eax + 4]
    
mov eax, [ebx] 
call    print_eax 
    
mov eax, [ecx] 
call    print_eax 

在上面的代码片段中,frac_1 是 1/2,frac_2 是 1/3。调用后,我将分子的地址存储在 ebx 中,将分母的地址存储在 ecx 中,然后打印这些地址中的值。它应该打印出来 (5/6) 但实际结果很奇怪:

5
dff5c

不是6,而是dff5c,这是我用来存储分母的堆栈地址。但是当我直接把lea改为mov时。效果很好。

mov esi, frac_1 
mov edi, frac_2
push    edi 
push    esi
call    add_fractions
add esp, 4*2    

mov ebx, dword [eax]
mov ecx, dword [eax + 4]
    
mov eax, ebx
call    print_eax 
        
mov eax, ecx
call    print_eax 

这次我将枚举数和分母的值存储到ebx、ecx中,而不是地址中。并且打印正确

5
6

问题是“lea ebx, dword [eax]”是否会影响堆栈并更改[eax + 4]中的值,因此它会打印出dff5c而不是6?

assembly x86 stack
1个回答
0
投票

我在这段代码中看到(至少)三个问题:

  • div r32
    之前您需要清除EDX寄存器!您有
    div ecx
    和两次
    div ebx

  • 您在 EAX 中返回的地址指向本地区域,在 add_fractions 返回到其调用者之后,该地址不再有效。

lea eax, dword [ebp - sizeof.FRACTION]
...
add esp, sizeof.FRACTION

add esp, sizeof.FRACTION
使信息无效。

一个简单的解决方案可能是覆盖堆叠的参数。因此,您不是存储在

[ebp - 8]
[ebp - 4]
,而是存储在
[ebp + 8]
[ebp + 12]
。到那时,原始参数已经转移到 ESI 和 EDI。

  • 您在没有地址的地方取消引用!
push    edi 
push    esi
call    add_fractions
add esp, 4*2    

lea ebx, dword [eax]
lea ecx, dword [eax + 4]
mov eax, [ebx] 
call print_eax
mov eax, [ecx] 
call print_eax

[eax]
[eax + 4]
,您通常会得到 .sum_numer.sum_denom。这些是实际数字,而不是指向这些值的指针。您不需要取消引用。
应用我的第二个要点中的“简单解决方案”,这可能会变成:

push    edi 
push    esi
call    add_fractions
pop     ebx              ; -> EBX is sum_nomer
pop     ecx              ; -> ECX is sum_denom
mov     eax, ebx 
call    print_eax
mov     eax, ecx 
call    print_eax
© www.soinside.com 2019 - 2024. All rights reserved.