我把很短的C代码正确地翻译成汇编程序了吗?



我目前正在学习汇编x86,我为自己做了一个小任务。

C代码:

if (a == 4711) { a = a + 2 } else
               { a = a - 2 }

汇编代码(eax为寄存器,cmp为比较,jne为不相等时跳转,jmp为相等时跳转):

         mov eax, a
         cmp eax, 4711
         jmp equal
equal:   add eax, 2
         jne unequal
unequal: sub eax, 2

我认为更有效的方法是:

         mov eax, a
         cmp eax, 4711
         jne unequal
         add eax, 2
unequal: sub eax, 2
编辑:

         mov eax, a
         cmp eax, 4711
         jne unequal
equal:   add eax, 2
         jmp continue
unequal: sub eax, 2
continue: ...

我翻译对了吗?

在第一种情况下,你的jne unequal什么也不做,因为控制无论如何都会去那里。你需要在之后跳转到

在第二个例子中,如果比较为真,则同时加减2,不做任何操作。

您也不会将结果存储回原始值所在的位置,您只需将其保留在eax中。

你的编辑是正确的,除了一件事。

mov    eax, a

将"a"的地址移动到eax中,而不是内容/值


这个简短的代码片段是在Ubuntu 16.04 elf64上使用NASM完成的

            section .text
    global _start
_start          
        mov     eax, a
        cmp     eax, 4711
        jnz     unequal
        add     eax, 2
        jmp     Done
    unequal:
        sub     eax, 2
Done:   mov [a], eax
        section .rodata         
a   dd  180308

它反汇编为;

00400080  B89C004000        mov eax,0x40009c
00400085  3D67120000        cmp eax,0x1267
0040008A  7505              jnz 0x400091
0040008C  83C002            add eax,byte +0x2
0040008F  EB03              jmp short 0x400094
00400091  83E802            sub eax,byte +0x2
00400094  8904259C004000    mov [0x40009c],eax

变量"a"在这里

0040009C  54C00200

注意@ 4000080 a的地址被移动到EAX中,但是@ Done (400091), EAX中的任何内容都被移动到该地址中。还要注意,值@"a"是以反向顺序存储的(小端序)。通常在代码中你会看到它是0x2c054

让我们回到你的第一个代码:

         mov eax, a
         cmp eax, 4711
         jmp equal
equal:   add eax, 2
         jne unequal
unequal: sub eax, 2

让我们假设第一条指令用"a"加载eax(实际上在TASM/MASM中是这样的,但更坚持明确和准确的[a],这更容易阅读源代码,并且在NASM中也可以工作)。

第二条指令是cmp,它从eax中减去4711,把结果扔掉(不存储在任何地方),只有标志寄存器受到影响。如果"a"为4711,则减法的结果为零,因此ZF=1。否则ZF = 0。(有关受CMP影响的其他标志,请参阅一些文档)。

所以在第3行eax仍然包含来自"a"的值,并且标志寄存器包含cmp eax,4711的结果。然后做jmp。这是无条件跳转,无论发生什么,所以你直接继续指令在"相等"的地址,这是add eax,2。=>每次都要给"a"加2

add本身也影响标志,所以对于"a" == -2 ZF=1,否则ZF=0!

然后是第一个条件跳转,根据当前标志寄存器的内容分支代码。jne是"jump not equal"的缩写,这里的"equal"表示设置零标志(ZF=1)。

因此,当"a"为-2时,ZF在jne之前为1("等于"),因此jne不会跳转到"不相等"的地址,而是继续到下一条指令(实际上是在"不相等"的地址,所以jne是没有意义的)。

对于与-2不同的"a", ZF将为0 ("is not equal"),因此jne将在提供的标签上执行跳转,继续执行地址为"不相等"的指令。


所以你必须引导CPU远离你不想执行的指令。

    xor eax,eax   ; sets eax to 0, and ZF=1
    jz  label_1   ; ZF is 1, so jump is executed, CPU goes to "label_1"
    inc eax       ; this instruction is then skipped and not executed
label_1:
    ; eax being still 0, and ZF being still set ON
    ; whatever instruction is here, CPU will execute it after the "jz"

稍微修改的示例,以显示condition为false的情况

    xor eax,eax   ; sets eax to 0, and CF=0, ZF=1, ...
    jc  label_1   ; CF is 0, so "jump carry" is NOT executed
    inc eax       ; this instruction is executed after "jc"
label_1:
    ; here eax is 1
    ; CF is still 0 (not affected by INC)
    ; but ZF is 0 (affected by INC)

总结:你应该非常清楚哪些指令影响哪些标志,以及以何种方式影响。当不确定时,将CMP + Jcc对保持在一起(以免意外影响cmp的标志结果)。Jcc表示任何"条件跳转"指令。当满足条件时,执行跳转到提供的标签。否则Jcc指令将被忽略,并继续执行它后面的指令。


顺便说一句,我个人会写C代码:

if (a == 4711) { a = a + 2 } else
               { a = a - 2 }

:

    cmp [a],DWORD 4711
    mov eax,2
    je a_is_4711
    neg eax  ; -2 for non 4711 value
a_is_4711:
    add [a],eax

最新更新