x86绝对值混淆

时间:2018-09-29 19:32:39

标签: assembly x86 intel

我对x86 Intel大会还很陌生。目前,我正在尝试获取用户传递的数字的绝对值。目前,该代码只是将传回的数字吐出。如果它是-5,那么程序会指出它的绝对值仍然是-5。如果它是5,则程序声明其绝对值为5。我试图使用条件语句,并且仅跳转到一段代码,当我们的数字小于零时,该代码段将翻转我们的符号位。我不认为我正确地跳到了这段代码,不知道为什么。到目前为止,我的代码如下:

#compare with 0 and if less then flip sign

#Syntax: int abs_val(int num)
#Returns the absolute value of num
abs_val:
#num will be stored in %rdi
push %rbp
mov %rsp, %rbp
cmpq $0, %rdi
jl signFlip

leave
ret

signFlip:
    neg %rdi
    mov %rdi, %rax
    leave
    ret

请注意:我不是在找别人为我输入完整的解决方案。我希望能朝着正确的方向前进,例如:“您需要使用xyz函数调用而不是neg”或“您没有正确存储值,请尝试将其移入该变量中”

提前谢谢!

1 个答案:

答案 0 :(得分:2)

您也用C声明了这个int吗? int是您正在使用的x86-64 System V ABI中的32位类型。

但是在告诉编译器arg的高32位无关紧要之后,您正在对qword long操作数进行操作。因此,当您将-5作为int arg传递时,您会在RDI中得到0x00000000fffffffb,它是64位2的补码正整数4294967291。(这是假定高位是零,例如,如果调用方像C编译器那样使用mov $-5, %edi并使用一个常量arg,但是如果将long强制转换为int,则可能会得到任意垃圾:调用方将假定函数会忽略广告中的高位。)

如果arg为正,则您忘记了mov %rdi, %rax。 (然后执行条件neg %rax或实际上是mov %edi, %eax / neg %eax

因此,当RDI为正时,您的函数将RAX保持不变。 (但是由于调用者可能是gcc -O0中未优化的C语言,因此它恰好还保存了arg的副本。这解释了abs(-5) => -5的行为,而不是半-您希望从此类错误中得到的随机垃圾)。


x86-64 System V调用约定不是要求调用者对扩展的args进行符号扩展或将值返回为完整的寄存器宽度,因此您的cmp / jl取决于调用方留在RDI上半部分的任何垃圾。

Is a sign or zero extension required when adding a 32bit offset to a pointer for the x86-64 ABI?)。没有迹象表明符号或零的行为会将窄args扩展到32位,而不是64位。


大多数操作的自然/默认大小为32位操作数大小,64位地址大小,并且在写入32位寄存器时具有隐式零扩展为64位。除非您有使用64位的特定原因(例如,对于指针),否则请使用32位操作数大小。


看看编译器通常会做什么;他们通常将cmov用于无分支的abs()。请参见启用了优化功能的C ++编译器的long foo(long x) { return std::abs(x); }的编译器输出,例如https://godbolt.org/z/I3NSIZ适用于longint版本。

# clang7.0  -O3
foo(int):                                # @foo(int)
    movl    %edi, %eax
    negl    %eax                   # neg sets flags according to eax = 0-eax
    cmovll  %edi, %eax             # copy the original if that made it negative
    retq

 # gcc7.3 -O3
foo(int):
    movl    %edi, %edx
    movl    %edi, %eax
    sarl    $31, %edx
    xorl    %edx, %eax    # 2's complement identity hack.  Maybe nice with slow cmov
    subl    %edx, %eax
    ret

但是不幸的是,即使-march=skylake是1 uop,gcc也不会切换到cmov。