检查NASM中的寄存器是否为负4字节值

时间:2019-02-05 23:14:56

标签: assembly x86 bit-manipulation nasm

我想检查放入--- title: "RMD_Example" output: html_document --- ```{r setup, include=FALSE} # Load ggplot2 library(ggplot2) # Define helper function adjust_title_position <- function(p) { # Shift the horizontal position of the plot title p_built <- invisible(ggplot2::ggplot_build(p)) gt <- invisible(ggplot2::ggplot_gtable(p_built)) gt$layout[which(gt$layout$name == "title"), c("l", "r")] <- c(2, max(gt$layout$r)) # Prints the plot to the current graphical device gridExtra::grid.arrange(gt, newpage = TRUE) # Invisibly return the gtable object invisible(gt) } ``` ```{r plot_1} ggplot(mtcars) + geom_point(aes(x = wt, y = mpg)) + labs(title = "Weights and miles-per-gallon") ``` ```{r plot_2} ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + labs(title = "Sepal length and width") ``` 寄存器中的值I是负的还是空的8字节值(在C语言中为负的rax)。

它导致我检查long int寄存器中的64位是否与带符号的位值相对应。

经过研究,我发现如果我们将base10中的-86之类的值的每个位取反并加1,我们将获得取反值86。

基于此,在位方面,负的反转值将小于负值。

我正在 x86_64 Linux 上的 NASM 中构建和运行代码。

我正在应用以下代码,当rax为负数时显示一条消息:

I

这是我编译 NASM 代码的方式:

section .data
msg db "I is negative", 0

section .text
global main
extern printf, exit

%define I 9

main:
mov rax, I
; Invert the bits into rax 
xor rax, 0xFFFFFF
inc rax
mov rbx, I
cmp rax, rbx
jl  EXIT
; Display message when I is negative
lea rdi, [msg]
xor rax, rax
call printf

EXIT:
call exit
ret

但是该程序是错误的,因为它无法正常工作。

似乎我误解了检查寄存器是否包含负整数的方式。有帮助吗?

2 个答案:

答案 0 :(得分:3)

比这容易得多。对64位执行test rax, rax,对于32位执行test eax, eax
然后使用条件指令检查标志标志(SF)。如果已设置,则该数字为负。

答案 1 :(得分:3)

EAX是RAX的低4字节。

test eax,eax               ; sets flags the same as cmp eax,0, like from eax-0
jnl  I_was_non_negative    ; jumps if EAX was *not* less-than 0.  Synonym for jge

Test whether a register is zero with CMP reg,0 vs OR reg,reg?解释了为什么test的效率要比cmp略高一些,以与零比较。 (通常是代码大小的1个字节)。

l小于条件测试SF和OF,但是与零进行比较不会溢出,因此,这等效于仅测试SF(从结果的MSB设置的符号标志)。在这种情况下,您可以选择jnljns(不少于或未签名)或jge。选择任何一个您最喜欢的语义。


C中的

int是4个字节,而不是8个。 (在所有标准的32位和64位x86 ABI中,包括在Linux上可以找到的x86-64 System V)。


您尝试使用2的补码身份实现-I < I(我认为)时遇到的问题
-x = ~x + 1

xor rax, 0xFFFFFF仅翻转低24位(即6 F位,而不是8位)。

但是xor rax, 0xFFFFFFFF不可编码,因为它不适合32位符号扩展的立即数。即使对于64位操作数大小,x86-64仍然使用8或32位立即数,而不是8或64,因为这将是非常膨胀的代码大小。有关可编码的内容,请参见https://felixcloutier.com/x86/XOR.html。 (有一个mov r64, imm64,但这是唯一需要64位立即数的指令。)

因此,如果您使用xor eax, -1(或not eax)而不是坚持使用64位操作数大小,则可能您的怪异代码会比较-I < I。但是翻转64位寄存器的低32位或24位,然后进行64位比较是没有帮助的。高位将始终为零。

如果您使用的是32位比较,那么对于负数最大的数字可能会遇到问题。 0x80000000是它自己的2的补码逆。即neg eax会保持不变(并设置OF,因为0 - 0x80000000导致有符号的溢出恢复为负)。

如果您在否定之前将EAX中的4字节输入符号扩展为RAX中的8字节,那么它可以正常工作。

    mov     eax, I
    movsxd  rax, eax     ; or cdqe
   ; not     rax
   ; inc     rax
    neg     rax
    cmp     rax, I        ; use I as a sign-extended 32-bit immediate
    jl     I_is_positive  ; won't be taken for 0 either.

请注意正负之间的区别。 -I < I在I = 0时为假,但您询问是否要检查是否为负(非负的相反,而不是正的相反)。


I是一个汇编时间常量

您可以使用the NASM preprocessor对其进行测试。

default rel        ; always a good idea to use RIP-relative for static data

; %define I 9

extern puts
global main
main:
%if  I < 0
    lea rdi, [rel msg]
    xor eax, eax
    call puts              ; puts appends a newline
%endif

   xor  eax,eax           ; return 0. Otherwise we might as well jmp puts to tailcall it
   ret

section .rodata    ; read-only data can go here

msg:  db "I is negative", 0     ; colon after labels is always good style in NASM

我注释掉了%define,因此可以在命令行中通过它:

$ nasm -felf64 -DI=0 nasm.asm  && gcc -no-pie nasm.o && ./a.out   # no output for I=0
$ nasm -felf64 -DI=9 nasm.asm  && gcc -no-pie nasm.o && ./a.out   # or I=9
$ nasm -felf64 -DI=-9 nasm.asm  && gcc -no-pie nasm.o && ./a.out 
I is negative

$ nasm -felf64 -DI=0x80000000 nasm.asm  && gcc -no-pie nasm.o && ./a.out  # NASM doesn't truncate to 32-bit 2's complement though.
$ nasm -felf64 -DI=0x8000000000000000 nasm.asm  && gcc -no-pie nasm.o && ./a.out # apparently it's 64-bit.

我必须使用-no-pie,因为我使用了call puts而不是call puts@plt或任何与PIE兼容的东西。由于某些原因,链接器在编写PIE而不是位置相关的可执行文件时,不会重写直接调用以使用PLT。