• ZR2021
    置顶
    2021-12-07
    于老师,为啥main函数里的asm("movl $0x1, %eax");是将1给eax的,src 跟dst反过来了好像

    作者回复: 这又是一个好问题!其实这里我们在代码中使用的是 AT&T 的写法,是一种默认被编译器广泛支持的内联汇编写法。当然,我们也可以做适当的调整来使用 Intel 写法,比如这样: #include <stdio.h> int main(void) { register long num asm("rax") = 0x100000000; asm( ".intel_syntax noprefix \n\t" "mov eax, 1\n\t" // "mov ax, 1\n\t" ".att_syntax" ); printf("%ld\n", num); return 0; } 但这种方式的问题在于,对于某些汇编器可能没有很好的兼容性。

    共 2 条评论
    21
  • 继业(Adrian)
    2021-12-12
    quizz: ``` mov eax, 0x1 # 0b1 inc eax # 0b10 sub eax, 10 # 0b10 - 0b1010 = 0xfffffff8 xor eax, eax # 0xfffffff8 xor 0xfffffff8 = 0x00000000 add eax, 1 # 0x00000001 mul eax # 0x00000001 mul 0x00000001 = 0x00000001 ``` answer: 0x00000001 = 1

    作者回复: 完全正确!

    
    18
  • 糊糊
    2021-12-07
    请教老师, 文中 mov ebx, 1 ,它所对应的机器指令代码为二进制值 bb 01 00 00 00 ,问题有两个: 1、汇编助记符 mov 是如何被翻译为二进制的 ? 是通过在哪里查的表吗 ,如果是查的表,那表又存在哪里呢 2、 那根据机器码二进制能否推导出汇编代码呢?bb 01 00 00 00 ==》mov ebx, 1

    作者回复: 很好的问题! 第一个问题:每一个汇编指令都有其对应的组成结构,汇编器会根据助记符的名称进行相应的转换。具体的转换细节可以参考官方手册。比如对于 x86-64:https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html 第二个问题:二进制状态下的机器指令识别可以通过每个指令对应的 OpCode 字节进行切分,然后再根据相应的结构解析出整体指令的结构。

    共 3 条评论
    6
  • 权
    2021-12-06
    验证重写64位寄存器低32位/低16位/低8位的代码中如果不是按照文中那样给寄存器赋值,而是任意赋值,建议大家将printf("%ld\n", num);改为printf("%lx\n", num);,输出16进制数,这样才能观察到正确的结果,否则输出10进制数,一开始没注意的话会发现结果很奇怪 思考题: xor eax, eax是异或,eax自己与自己异或应该为0 add eax, 1,相当于0x0+0x1=0x1 mul eax之后eax的结果应该还是0x1,不知道理论分析是否正确,汇编还不是很熟

    作者回复: 答案是正确的哈!

    
    5
  • cc
    2021-12-08
    在 x86-64 架构下,CPU 指令集架构(ISA)中一共定义了 16 个通用目的寄存器,这些寄存器最大可以存放 4 个字,即 64 位长的数据。在汇编代码中 ---- x86-64 下一个字长不就 64bit 了吗,为什么这里说 「4 个字,即 64 位长的数据」

    作者回复: 这是一个很好的发现!我们修改了文章,稍后会更新。 简单来讲就是:字这个概念会在多个地方的多种不同场景下使用。而文中我们介绍的字实际上是指可以体现 CPU 硬件特征的那个字,我一般会称它为硬件字。而在 CPU 指令集中,字也被用来作为衡量数据大小的单位。x86 架构由于历史原因,会将指令集中出现的 WORD 定义为固定 16 位的大小。所以实际文中提到的 4 个字你可以理解为指令字,它的单位是 WORD,4 个字即对应 64 位大小。

    
    3
  • dog_brother
    2021-12-07
    按老师的程序去执行,第一种(asm("movl $0x1, %eax");)结果是1,第二种(asm("movw $0x1, %ax");)结果是4294967297。 ============================== ```c #include <stdio.h> int main(void) { register long num asm("rax") = 0x100000000; asm("movl $0x1, %eax"); // 第一种 // asm("movw $0x1, %ax"); // 第二种 printf("%d\n", num); printf("%ld\n", num); printf("%X\n", num); return 0; } ``` 按我这段的代码去执行,第一种(movl)和第二种(movw)的执行结果都是 ```shell 1 2 2 ```

    作者回复: 关于这个问题我可以先给点提示,然后你再思考下看看。第一个提示就是把第一行 printf 中的 “%d” 改成 “%ld” 就可以输出正确的结果;第二个提示是 printf 的调用会影响寄存器 rax 的值。

    
    3
  • jack123
    2021-12-07
    xor eax,eax 相当于把eax置零了,最后+1,然后mul相乘 默认与eax相乘,最后还是为1

    作者回复: 没错!

    
    3
  • 琥珀·
    2021-12-13
    因为我的环境有 warning: overflow in conversion from 'long long int' to 'long int' 的告警,所以把 register long num 改成了 register long long num #include <stdio.h> int main(void) { register long long num asm("rax") = 0x100000000; asm("movl $0x1, %eax"); // asm("movw $0x1, %ax"); printf("%llx\n", num); return 0; } 不知道这样理解对不对: 当某个指令需要重写寄存器的低 16 位或低 8 位数据时,寄存器中其他位上的数据不会被修改。所以asm("movw $0x1, %ax") 不会修改rax的值,这时num输出的值为0x100000001 而当指令需要重写寄存器低 32 位的数据时,高 32 位的数据会被同时复位,即置零。所以 asm("movl $0x1, %eax") 会将 rax 的结果置零后再写入1,这时num输出的值为0x1。

    作者回复: 理解正确哈!

    
    2
  • 墨
    2021-12-12
    eax = 1 eax = eax + 1 eax = eax - 10 异或操作令 eax = 0 eax = eax + 1 eax = eax * eax 最后eax = 1

    作者回复: 正解!

    
    2
  • 傻猫周大福
    2021-12-11
    在clang下,rax寄存器虽然被置位,但在此之前会将rax寄存器的值拷贝到栈中,在输出时不会把被置位后的rax输出,而是从栈中重新取出num的值

    作者回复: 这是一个很好的发现!

    共 3 条评论
    2