免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 8026 | 回复: 19
打印 上一主题 下一主题

x86-64汇编问题请教 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-07-16 15:08 |只看该作者 |倒序浏览
这里有篇文档:

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来说就不行?

论坛徽章:
3
2015年迎新春徽章
日期:2015-03-04 09:56:11数据库技术版块每日发帖之星
日期:2016-08-03 06:20:00数据库技术版块每日发帖之星
日期:2016-08-04 06:20:00
2 [报告]
发表于 2008-07-16 15:16 |只看该作者
有这事?!

论坛徽章:
0
3 [报告]
发表于 2008-07-16 15:20 |只看该作者
原帖由 albcamus 于 2008-7-16 15:08 发表
这里有篇文档:

http://www.x86-64.org/documentation/assembly.html



文档中有一段:



红色的这句没看懂, 为什么立即数符号扩展到64位, 对32位的-1来说就不行?

奇怪哦,intel的手册上对这段的论述,没有提到有-1非法的情况
2.2.1.5   Immediates
In 64-bit mode, the typical size of immediate operands remains 32 bits. When the
operand size is 64 bits, the processor sign-extends all immediates to 64 bits prior to
their use.
Support for 64-bit immediate operands is accomplished by expanding the semantics
of the existing move (MOV reg, imm16/32) instructions. These instructions (opcodes
B8H ¨C BFH) move 16-bits or 32-bits of immediate data (depending on the effective
operand size) into a GPR. When the effective operand size is 64 bits, these instruc-
tions can be used to load an immediate into a GPR. A REX prefix is needed to override
the 32-bit default operand size to a 64-bit operand size.
For example:
48 B8 8877665544332211 MOV RAX,1122334455667788H

论坛徽章:
0
4 [报告]
发表于 2008-07-16 15:26 |只看该作者
原帖由 zx_wing 于 2008-7-16 15:20 发表

奇怪哦,intel的手册上对这段的论述,没有提到有-1非法的情况


谢谢两位关注。

感觉好像不是-1非法, 而是 > 0x7fffffff的立即数都非法!  没明白为什么会这样。。。

论坛徽章:
0
5 [报告]
发表于 2008-07-16 15:30 |只看该作者
[ ~]$ cat aa.c
/* 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'

论坛徽章:
0
6 [报告]
发表于 2008-07-16 15:36 |只看该作者
这好像是个基本功问题? 感觉很诡异, 一定是关于汇编, 我们有某个很初级的地方没弄明白。

论坛徽章:
0
7 [报告]
发表于 2008-07-16 16:45 |只看该作者
刚好是32位的界限。不懂

论坛徽章:
0
8 [报告]
发表于 2008-07-16 17:19 |只看该作者
这个问题应该和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

论坛徽章:
0
9 [报告]
发表于 2008-07-16 17:22 |只看该作者
mik版主写过a64汇编器, 是不是 对Unix来说, 只有as作者才需要关注REX?

论坛徽章:
0
10 [报告]
发表于 2008-07-16 23:10 |只看该作者
原帖由 albcamus 于 2008-7-16 17:22 发表
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位立即数的两条指令
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP