x86-64汇编问题请教
这里有篇文档:
http://www.x86-64.org/documentation/assembly.html
文档中有一段:
Immediate values inside instructions remain 32 bits and their value is sign extended to 64 bits before calculation. This means that:
addq $1, %rax # Valid instruction
addq $0x7fffffff, %rax # As this
addq $0xffffffffffffffff, %rax # as this one
addq $0xffffffff, %rax # Invalid instruction
addl $0xffffffff, %eax # Valid instruction
红色的这句没看懂, 为什么立即数符号扩展到64位, 对32位的-1来说就不行?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
有这事?!
奇怪哦,intel的手册上对这段的论述,没有提到有-1非法的情况
谢谢两位关注。
感觉好像不是-1非法, 而是 > 0x7fffffff的立即数都非法! 没明白为什么会这样。。。
/* filename :aa.c */
#include <stdio.h>
int main(void)
{
unsigned long long tmp = 2;
/* 下面这行改成asm volatile("addq $0x7fffffff, %0" 就可以 */
asm volatile("addq $0x80000000, %0"
: "=a" (tmp)
: "0" (tmp)
);
printf("tmp is %llu\n", tmp);
return 0;
}
[~]$ gcc -m64 aa.c
/var/tmp//ccXZa4Ns.s: Assembler messages:
/var/tmp//ccXZa4Ns.s:19: Error: suffix or operands invalid for `add'
这好像是个基本功问题? 感觉很诡异, 一定是关于汇编, 我们有某个很初级的地方没弄明白。
刚好是32位的界限。不懂
这个问题应该和REX前缀没关系。
只有在64bit模式下,REX才是有意义的。 AMD x86-64的文档是这么说REX的:
-> 即使是在64bit模式下,operand size仍然默认为32位;
-> 指令想访问64位的GPR,或者被x86-64扩展了的XMM寄存器,就需要REX前缀;
-> 只有一部分指令是例外:对这些指令来说,默认情况下operand size就是64bit。
因此,这些指令不需要REX前缀
/*{{{*/默认的operand size == 64bit,从而不需要REX前缀的指令:
CALL (Near) POP reg/mem
ENTER POP reg
Jcc POP FS
JrCXZ POP GS
JMP (Near) POPFQ
LEAVE PUSH imm8
LGDT PUSH imm32
LIDT PUSH reg/mem
LLDT PUSH reg
LOOP PUSH FS
LOOPcc PUSH GS
LTR PUSHFQ
MOV CR(n) RET (Near)
MOV DR(n)
/*}}}*/
所谓REX prefix,其实是指令码(opcode)的最低一个字节(8bit),合法值是0x41 - 0x4f。
注意,把REX.W设置为1,就指定了64位的operand size。
Table: REX Prefix-Byte Fields
mik版主写过a64汇编器, 是不是 对Unix来说, 只有as作者才需要关注REX?
偶最善此道了。
你提到的问题并不复杂。 实际上只是汇编语言层面的语法而已。
1、从本质上讲与你所说的 -1 完全无关,也没有什么 0x7fffffff 的制约。
2、出现 addq $0xffffffff, %rax 错误的原因完全是语法问题,或者说是 gas 这个汇编器的语法限制,当然在 masm 的语法也有这方面的限制。
从 amd64 的指令本质来说:add rax, 0FFFFFFFF 是完全正确的。
反而 add rax, 0FFFFFFFFFFFFFFFF 这是错误的。因为,x86_64 指令要求的立即数除了mov,push 指令外的其它指令,是不允许有64位立即数的。
然而,从汇编语法上来讲:x86 & x64 的指令格式绝大部分要求:源操作数与目的操作数的大小要一致,汇编器在进行翻译为机器码时,就会遵从指令的本质。
3、所以,基于第2条的论述:
addq $0xffffffffffffffff, %rax 这条指令译为机器码是:
49 05 FF FF FF FF // 共 6 个字节,而不是 10 个字节
同样: 若是: 49 05 FF FF FF FF // 会被 objdump 之类的程序显示为: addq $0xFFFFFFFFFFFFFFFF, %rax
// 而不会显示为: addq $0xFFFFFFFF, %rax
总结: 这可以说是语法,或者是显示上的问题,而不论是 AMD 或 Intel 的官方文档上说的是:指令的本质问题。
所以,若遇到另一个汇编编译器,对 add rax, 0xFFFFFFFF 这条指令判断是正确的话,也不要觉得奇怪,这是它的语法上可以通过
我自己写的 a64 是按本质来定语法的
4、另外,扯一下, add 与 mov 这两类指令的 64 位立即数编码的区别:
addq $0xffffffffffffffff, %rax // 基上所述是: 6 个字节
movq $0xffffffffffffffff, %rax // 则是实打实的: 10 个字节。 mov 与 push 指令是唯一可接受64位立即数的两条指令