首页 > 解决方案 > 尝试学习 x86 汇编,在简单的测试程序中得到奇怪的结果

问题描述

我正在尝试从现代 x86 汇编语言编程的第 2 版中学习 x64 汇编语言的基础知识。

第 2 章中的第五个示例程序介绍了如何使用各种宽度参数执行简单的整数除法,并提供了一个简单的示例测试例程来展示结果。我遇到的问题是我似乎返回了一个错误的结果,并且我一生都无法理解为什么。

为例程提供的 C++ 代码如下:

void UnsignedIntegerDiv(void)
{
    uint8_t a = 12;
    uint16_t b = 17;
    uint32_t c = 71000000;
    uint64_t d = 90000000000;
    uint8_t e = 101;
    uint16_t f = 37;
    uint32_t g = 25;
    uint64_t h = 5;
    uint64_t quo1, rem1;
    uint64_t quo2, rem2;

    quo1 = (a + b + c + d) / (e + f + g + h);
    rem1 = (a + b + c + d) % (e + f + g + h);
    UnsignedIntegerDiv_(a, b, c, d, e, f, g, h, &quo2, &rem2);

    cout << "\nResults for UnsignedIntegerDiv\n";
    cout << "a = " << (unsigned)a << ", b = " << b << ", c = " << c << ' ';
    cout << "d = " << d << ", e = " << (unsigned)e << ", f = " << f << ' ';
    cout << "g = " << g << ", h = " << h << '\n';
    cout << "quo1 = " << quo1 << ", rem1 = " << rem1 << '\n';
    cout << "quo2 = " << quo2 << ", rem2 = " << rem2 << '\n';
}

对应的汇编程序是:

UnsignedIntegerDiv_ proc

; Calculate a + b + c + d
        movzx rax,cl                        ;rax = zero_extend(a)
        movzx rdx,dx                        ;rdx = zero_extend(b)
        add rax,rdx                         ;rax = a + b
        mov r8d,r8d                         ;r8 = zero_extend(c)
        add r8,r9                           ;r8 = c + d
        add rax,r8                          ;rax = a + b + c + d
        xor rdx,rdx                         ;rdx:rax = a + b + c + d

; Calculate e + f + g + h
        movzx r8,byte ptr [rsp+40]          ;r8 = zero_extend(e)
        movzx r9,word ptr [rsp+48]          ;r9 = zero_extend(f)
        add r8,r9                           ;r8 = e + f
        mov r10d,[rsp+56]                   ;r10 = zero_extend(g)
        add r10,[rsp+64]                    ;r10 = g + h;
        add r8,r10                          ;r8 = e + f + g + h
        jnz DivOK                           ;jump if divisor is not zero

        xor eax,eax                         ;set error return code
        jmp done

; Calculate (a + b + c + d) / (e + f + g + h)

DivOK:  div r8                              ;unsigned divide rdx:rax / r8
        mov rcx,[rsp+72]
        mov [rcx],rax                       ;save quotient
        mov rcx,[rsp+80]
        mov [rcx],rdx                       ;save remainder

        mov eax,1                           ;set success return code

Done:   ret

虽然代码似乎有意义,但与程序集实现相比,我从例程的 C++ 实现中得到了不同的输出(C++ 实现给出了 quo1、rem1 的预期结果——它是不正确的 quo2、rem2):

Results for UnsignedIntegerDiv
a = 12, b = 17, c = 71000000 d = 90000000000, e = 101, f = 37 g = 25, h = 5
quo1 = 536136904, rem1 = 157
quo2 = 0, rem2 = 90071000029

这让我摸不着头脑,为什么。我的猜测是我在某处遗漏了一个错字(尽管我从书籍 GitHub 页面的可下载代码中得到相同的结果)导致 ASM 部分中某处的溢出或截断。

社区的任何帮助将不胜感激,因为我知道我自己的知识不足以完全解决此问题。

标签: assemblyx86-64masm64

解决方案


看起来我一直在遭受代码盲症:)

UnsignedIntegerDiv_ 函数的原型的参数之一(下面定义中的 h)的数据类型不正确:

extern "C" int UnsignedIntegerDiv_(uint8_t a, uint16_t b, uint32_t c, uint64_t d, uint8_t e, uint16_t f, uint32_t g, uint32_t h, uint64_t* quo, uint64_t* rem);

代替

extern "C" int UnsignedIntegerDiv_(uint8_t a, uint16_t b, uint32_t c, uint64_t d, uint8_t e, uint16_t f, uint32_t g, uint64_t h, uint64_t* quo, uint64_t* rem);

感谢@Peter Cordes 为我指明了正确的方向。


推荐阅读