背景:
我是组装新手。当我学习编程时,我编写了一个程序,它实现了高达 1000 * 1000 的乘法表。这些表被格式化,以便每个答案都在线factor1 << 10 | factor2
(我知道,我知道,它不漂亮)。然后将这些表加载到数组中:int* tables
. 空行用 0 填充。这是表格文件的链接 (7.3 MB)。我知道使用汇编不会大大加快速度,但我只是为了好玩(和一些练习)。
问题:
我正在尝试将此代码转换为内联汇编(tables
是全局的):
int answer;
// ...
answer = tables [factor1 << 10 | factor2];
这就是我想出的:
asm volatile ( "shll $10, %1;"
"orl %1, %2;"
"movl _tables(,%2,4), %0;" : "=r" (answer) : "r" (factor1), "r" (factor2) );
我的 C++ 代码工作正常,但我的程序集失败了。与我的 C++ 相比,我的程序集(尤其是零件)有什么问题movl _tables(,%2,4), %0;
我为解决它所做的工作:
我使用了一些随机数: 89 796factor1
和factor2
. 我知道89 << 10 | 786
(即)处有一个元素91922
——用 C++ 验证了这一点。当我运行它时gdb
,我得到一个 SIGSEGV:
程序收到信号 SIGSEGV,分段错误。
在这一行:
"movl _tables(,%2,4), %0;" : "=r" (answer) : "r" (factor1), "r" (factor2) );
我在我的周围添加了两种方法asm
,这就是我知道asm
块在反汇编中的位置。
asm
我的块的拆卸:
拆卸objdump -M att -d
看起来不错(虽然我不确定,我是组装新手,正如我所说):
402096: 8b 45 08 mov 0x8(%ebp),%eax
402099: 8b 55 0c mov 0xc(%ebp),%edx
40209c: c1 e0 0a shl $0xa,%eax
40209f: 09 c2 or %eax,%edx
4020a1: 8b 04 95 18 e0 47 00 mov 0x47e018(,%edx,4),%eax
4020a8: 89 45 ec mov %eax,-0x14(%ebp)
反汇编来自objdump -M intel -d
:
402096: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]
402099: 8b 55 0c mov edx,DWORD PTR [ebp+0xc]
40209c: c1 e0 0a shl eax,0xa
40209f: 09 c2 or edx,eax
4020a1: 8b 04 95 18 e0 47 00 mov eax,DWORD PTR [edx*4+0x47e018]
4020a8: 89 45 ec mov DWORD PTR [ebp-0x14],eax
据我了解,它将我void calc ( int factor1, int factor2 )
函数的第一个参数移动到eax
. 然后它将第二个参数移动到edx
. 然后它向左移动eax
10 并or
用 s 它edx
。一个 32 位整数是 4 个字节,所以[edx*4+base_address]
. 将结果移动到eax
然后放入eax
(int answer
我猜它在-0x14
堆栈上)。我真的看不出有什么问题。
编译器的反汇编.exe
:
当我asm
用普通的 C++ ( answer = tables [factor1 << 10 | factor2];
) 替换块并反汇编它时,这就是我在 Intel 语法中得到的:
402096: a1 18 e0 47 00 mov eax,ds:0x47e018
40209b: 8b 55 08 mov edx,DWORD PTR [ebp+0x8]
40209e: c1 e2 0a shl edx,0xa
4020a1: 0b 55 0c or edx,DWORD PTR [ebp+0xc]
4020a4: c1 e2 02 shl edx,0x2
4020a7: 01 d0 add eax,edx
4020a9: 8b 00 mov eax,DWORD PTR [eax]
4020ab: 89 45 ec mov DWORD PTR [ebp-0x14],eax
AT&T 语法:
402096: a1 18 e0 47 00 mov 0x47e018,%eax
40209b: 8b 55 08 mov 0x8(%ebp),%edx
40209e: c1 e2 0a shl $0xa,%edx
4020a1: 0b 55 0c or 0xc(%ebp),%edx
4020a4: c1 e2 02 shl $0x2,%edx
4020a7: 01 d0 add %edx,%eax
4020a9: 8b 00 mov (%eax),%eax
4020ab: 89 45 ec mov %eax,-0x14(%ebp)
我对 Intel 语法不是很熟悉,所以我将尝试理解 AT&T 语法:
它首先将tables
数组的基地址移动到%eax
. 然后,将第一个参数移入%edx
. 它向左移动%edx
10,然后or
使用第二个参数。然后,通过%edx
向左移动 2,它实际上乘以%edx
4。然后,它将它添加到%eax
(数组的基地址)。所以,基本上它只是这样做了:([edx*4+0x47e018]
英特尔语法)或0x47e018(,%edx,4)
AT&T。它移动它进入的元素的值%eax
并将其放入int answer
. 这种方法更“扩展”,但它和我的手写组装一样!那么为什么我SIGSEGV
的编译器工作正常呢?