尝试学习 x86 汇编,在简单的测试程序中得到奇怪的结果
Trying to learn x86 Assembly, getting odd results in simple test program
我正在尝试从现代 x86 汇编语言编程第 2 版中学习 x64 汇编语言的基础知识。
第二章的第五个示例程序介绍了如何使用各种宽度参数进行简单的整数除法,并提供了一个简单的示例测试例程来展示结果。我遇到的问题是我似乎返回了一个错误的结果,而且我无法理解为什么。
例程提供的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 部分某处溢出或截断。
我们将不胜感激来自社区的任何帮助,因为我知道我的知识不足以完全解决我自己的问题。
看来我已经患上了代码盲症:)
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 为我指明了正确的方向。
我正在尝试从现代 x86 汇编语言编程第 2 版中学习 x64 汇编语言的基础知识。
第二章的第五个示例程序介绍了如何使用各种宽度参数进行简单的整数除法,并提供了一个简单的示例测试例程来展示结果。我遇到的问题是我似乎返回了一个错误的结果,而且我无法理解为什么。
例程提供的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 部分某处溢出或截断。
我们将不胜感激来自社区的任何帮助,因为我知道我的知识不足以完全解决我自己的问题。
看来我已经患上了代码盲症:)
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 为我指明了正确的方向。