C ASM x64函数指针未返回正确值
我对汇编中的函数指针有问题,即使我的函数返回负数,它也总是将rax设置为正数,我用一个比较两个整数的函数做了一个最小的可复制示例,它做了相同的事情: ASM功能代码[编辑]:C ASM x64函数指针未返回正确值,c,assembly,x86-64,nasm,function-pointers,C,Assembly,X86 64,Nasm,Function Pointers,我对汇编中的函数指针有问题,即使我的函数返回负数,它也总是将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
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
汇编:
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后有代码要打印,但也有代码要在未使用bar2后打印。您应该在bar:之前添加ret,以防止执行此操作。
你的代码似乎太复杂了 首先,让我们用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后有代码要打印,但也有代码要在未使用bar2后打印。您应该在bar:之前添加ret,以防止执行此操作。
int是32位,但rax是64位寄存器。返回int的函数将其返回值放在eax中,这通常会将rax的高半部分归零。因此,如果cmp返回-1,即32位数字0xFFFFFF,则rax将包含0x00000000FFFFFF。这不是一个负的64位数字,因此测试rax时,rax不会设置符号标志
尝试使用test eax,eax作为您的测试。int是32位的,但rax是64位的寄存器。返回int的函数将其返回值放在eax中,这通常会将rax的高半部分归零。因此,如果cmp返回-1,即32位数字0xFFFFFF,则rax将包含0x00000000FFFFFF。这不是一个负的64位数字,因此测试rax时,rax不会设置符号标志
尝试使用test eax,eax作为您的测试。Edited,我在帖子中手工编写了代码,因为我在一个虚拟机上,我忘记了那一行,但并不是我想比较两次,这就是为什么jmp foo2不是因为我忘了cmp,它对cmp rax做了同样的事情,0@Fayeure您的cmp将打印x-x,所以你应该做一次比较,而不做那次比较,在只打印一次x-x的情况下进行两次比较。我打印x-x只是为了显示它调用了函数,但没有返回正确的值,没有这一点,输出就更好了,我也有同样的问题,我在帖子中手工编写了代码,因为我在一台虚拟机上,我忘记了那一行,但不是我想比较两次,这就是为什么jmp Foo2不是因为我忘记了cmp,它对cmp rax做了同样的事情,0@Fayeure您的cmp将打印x-x,所以你应该做一次比较,而不是两次比较,同时只打印一次x-x。我打印x-x只是为了显示它调用了函数,但没有返回好的值,否则输出就更好了,我也有同样的问题。你的跳转指令应该是标签foo中的JG吗?JA没有检查符号标志。@JA可能是外星人,但JA不是跳到上面的吗?@JA和jg也做了同样的事情。你的代码有多个ABI冲突。它正在重击属于调用者的rbx,而不恢复它,并使用rsp not aligned mod 16进行函数调用。我不确定这些是否是您所看到的内容的来源,但是如果没有它们的修复,它就不可能可靠地工作。@Fayeure:Read,x86-64部分,用于调用约定。这些都是必须了解和遵循的。rbx是被叫方保存的寄存器,您必须保留它;也就是说,您必须确保当函数返回时,rbx包含与输入函数时相同的值。您可以在函数开始时将其推到堆栈上,然后在结束时将其弹出,以代替堆栈调整。但更简单的方法是使用调用方保存的暂存寄存器;rax、rcx、rdx、r8、r9、r10、r11中的任何一个都可以。您的跳转指令不应该是标签foo中的JG吗?JA没有检查符号标志。@JA可能是外星人,但JA不是跳到上面的吗?@JA和jg也做了同样的事情。你的代码有多个ABI冲突。它正在重击属于调用者的rbx,而不恢复它,并使用rsp not aligned mod 16进行函数调用。我不确定这些是否是您所看到的内容的来源,但是如果没有它们的修复,它就不可能可靠地工作。@Fayeure:Read,x86-64部分,用于调用约定。这些都是必须了解和遵循的。rbx是被叫方保存的区域
你必须保存它;也就是说,您必须确保当函数返回时,rbx包含与输入函数时相同的值。您可以在函数开始时将其推到堆栈上,然后在结束时将其弹出,以代替堆栈调整。但更简单的方法是使用调用方保存的暂存寄存器;rax,rcx,rdx,r8,r9,r10,r11中的任何一个都可以。谢谢你是垃圾堆溢出之神,伙计,是这样的:谢谢你是垃圾堆溢出之神,伙计,是这样的:D