c语言 - ASM x64 函数指针未返回正常值



我在汇编中的函数指针方面遇到了问题,即使我的函数返回负数,它也总是rax设置为正数,我用一个比较两个整数的函数做了一个最小的可重用示例,它做了同样的事情:

ASM 函数代码 [编辑]:

global foo
section .data
msg: db `superiorn`
msg_len: equ $-msg
section .text
foo:
push rbx
mov rbx, rdi
mov rdi, 2
mov rsi, 1
sub rsp, 8  ; align the stack frame
call rbx
add rsp, 8
test rax, rax   ;[EDIT] correct: test eax, eax
js  bar
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
bar:
mov rdi, 1
mov rsi, 2
sub rsp, 8  ; same here
call rbx
add rsp, 8
test rax, rax  ;[EDIT] correct: test eax, eax
js exit
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
exit:
pop rbx   ;restoring initial data of rbx
ret

主代码:

#include <stdio.h>
int foo(int (*f)());  //my asm function prototype
int cmp(int i, int j)
{
printf("%d - %dn", i, j);
return(i - j);
}
int main(void)
{
foo(&cmp);
return (0);
}

输出为:

2 - 1
superior
1 - 2
superior

但它应该只是:

2 - 1
superior

汇编:

nasm -f elf64 foo.s
gcc -c main.c -o main.o
gcc main.o foo.o

感谢您的帮助

[编辑] 它不起作用,因为我检查了 rax 而不是 eax,现在它可以工作了。感谢您的帮助

int是32位,但rax是64位寄存器。 一个返回int的函数会将其返回值放在eax中,这通常会将rax的高半部分归零。 因此,如果cmp返回-1,即 32 位数字0xffffffff,则rax将包含0x00000000ffffffff。 这不是负 64 位数字,因此test rax, rax不会设置符号标志。

请尝试改用test eax, eax作为测试。

你的代码似乎太复杂了。

首先,让我们用 C 这样的语言编写要做的事情:

int foo(int (*f)()) {
if (cmp(2, 1) > 0) {
PRINT;
}
if (cmp(1, 2) > 0) {
PRINT;
}
}

然后,让我们根据以下内容编写汇编代码:

global foo
section .data
msg: db `superiorn`
msg_len: equ $-msg
section .text
; int foo(int (*f)()) {
foo:
mov rbx, rdi   ; function pointer stored in rbx
;   if (cmp(2, 1) > 0) {
mov rdi, 2     ; first integer
mov rsi, 1     ; second integer
call rbx       ; call function pointer
cmp rax, 0
jle bar        ; jump if rdi <= rsi (signed)
;     PRINT;
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall        ; write "superiorn"
;   }
bar:
;   if (cmp(1, 2) > 0) {
mov rdi, 1
mov rsi, 2
call rbx
cmp rax, 0
jle bar2
;     PRINT;
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall        ; write "superiorn"
;   }
bar2:
; }
ret

若要保留代码,要修复的要点包括:

  • ja用于无符号比较。jg应该用于签名比较。
  • 有代码在bar2后打印,但也有代码在跳转ja bar2不被打印后打印。您应该在bar:之前添加ret以防止执行此操作。

最新更新