use*_*588 1 x86 assembly bit-manipulation nasm
我想检查I放入rax寄存器的值是否为负数或空 8 字节值(long intC 中的负数)。
它让我检查rax寄存器中的 64 位是否对应于有符号位值。
经过研究,我发现如果我们将base10中的-86之类的值的每一位反转并加1,我们得到反转值86。
基于此,负反转值在位方面将小于负值。
我正在x86_64 Linux上的NASM 中构建和运行代码。
我正在应用以下代码,当I为负时显示一条消息:
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
Run Code Online (Sandbox Code Playgroud)
这是我编译NASM代码的方法:
nasm -f elf64 Program.s -o Program.o -Werror
gcc Program.o -o a.out
Run Code Online (Sandbox Code Playgroud)
但是这个程序是错误的,因为它不能正常工作。
似乎我误解了检查寄存器是否包含负整数的方式。有什么帮助吗?
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
Run Code Online (Sandbox Code Playgroud)
使用 CMP reg,0 与 OR reg,reg 测试寄存器是否为零?解释了为什么test比cmp与零进行比较更有效。(主要是代码大小的 1 个字节)。
该lESS-比条件测试SF和,但与零进行比较不会溢出,所以它相当于只是测试SF(符号标志,从结果的MSB集)。在这种情况下,您可以选择jnl或jns(不少于或未签名),或jge。选择具有您最喜欢的语义的任何一个。
int在 C 中是 4 个字节,而不是 8。(在所有标准的 32 位和 64 位 x86 ABI 中,包括您将在 Linux 上找到的 x86-64 System V)。
您尝试-I < I使用 2 的补码标识来实现(我认为)的问题
-x = ~x + 1
xor rax, 0xFFFFFF只翻转低 24 位(即 6F位,而不是 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.
Run Code Online (Sandbox Code Playgroud)
注意正数和非负数的区别。 -I < I对于 I=0 是错误的,但是您询问检查是否为负(非负的反面,而不是正的反面)。
I 是一个组装时间常数您可以使用NASM 预处理器来测试它。
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
Run Code Online (Sandbox Code Playgroud)
我注释掉了%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.
Run Code Online (Sandbox Code Playgroud)
我不得不使用,-no-pie因为我使用了call puts而不是call puts@plt或任何与 PIE 兼容的东西。出于某种原因,在制作 PIE 而不是位置相关的可执行文件时,链接器不会重写直接调用以使用 PLT。
| 归档时间: |
|
| 查看次数: |
1006 次 |
| 最近记录: |