ASM x64 函数指针未返回有效值

ASM x64 function pointer not returning the good value

我在汇编中遇到函数指针问题,即使我的函数 returns 是负数,它总是将 rax 设置为正数,我做了一个最小的可重现示例,其中包含一个函数比较两个整数,它做同样的事情:

ASM 函数代码[编辑]:

global foo

section .data
msg: db `superior\n`
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

main.c代码:

#include <stdio.h>

int foo(int (*f)());  //my asm function prototype

int cmp(int i, int j)
{
  printf("%d - %d\n", 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,现在它起作用了。感谢您的帮助

您的代码似乎太复杂了。

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

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

那我们就照着写汇编代码吧:

global foo

section .data
msg: db `superior\n`
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 "superior\n"
  ;   }
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 "superior\n"
  ;   }
bar2:
  ; }
  ret

要保留您的代码,需要修复的要点是:

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

一个int是32位的,但是rax是一个64位的寄存器。 returns int 的函数会将其 return 值放入 eax,这通常会将 rax 的高半部分置零。所以如果cmpreturns-1,也就是32位的数0xffffffff,那么rax就会包含0x00000000ffffffff。这不是负 64 位数字,因此 test rax, rax 不会设置符号标志。

尝试使用 test eax, eax 作为测试。