Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
x86_64:强制gcc在堆栈上传递参数_C_Gcc_Parameter Passing_X86 64_Calling Convention - Fatal编程技术网

x86_64:强制gcc在堆栈上传递参数

x86_64:强制gcc在堆栈上传递参数,c,gcc,parameter-passing,x86-64,calling-convention,C,Gcc,Parameter Passing,X86 64,Calling Convention,我正在为x86-64系统开发一个setjmp/longjmp自定义实现,它保存了CPU的整个上下文(即,所有xmm、fpu堆栈等;而不仅仅是被调用方保存寄存器)。这是直接在汇编中编写的 #include <stdio.h> static void foo(void) { int i; asm volatile ("mov 16(%%rbp), %0" : "=g" (i)); printf("%d\n", i); } #define

我正在为x86-64系统开发一个
setjmp
/
longjmp
自定义实现,它保存了CPU的整个上下文(即,所有xmm、fpu堆栈等;而不仅仅是被调用方保存寄存器)。这是直接在汇编中编写的

#include <stdio.h>

static void foo(void)
{
        int i;
        asm volatile ("mov 16(%%rbp), %0" : "=g" (i));
        printf("%d\n", i);
}

#define foo(x) ({ int _i = (x); \
        asm ("push %0\ncall %P1\nadd $8, %%rsp\n" : : "g"(_i), "i"(foo)); })

int main(int argc, char *argv[])
{
        foo(argc-1);
        return 0;
}
该代码在极少的示例中运行良好(直接从程序集源调用时)。将其与C代码一起使用时会出现问题,这是由于参数传递给自制
setjmp
/
longjmp
函数的方式造成的。事实上,x64_64系统的SysV ABI规定参数应该通过寄存器传递(如果它们最多为6)。我的职能签字如下:

long-long-set_jmp(exec_-context_-t*env)
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

当然,这不能像现在这样起作用。事实上,当我输入
set_jmp
时,
rdi
rsi
已经被删除,以保持指向
env
val
的指针。对于
rdi
,这同样适用于
long_jmp


是否有任何方法可以强制GCC(例如,通过依赖某个属性)强制通过堆栈传递参数?这比使用一些define来包装
set_jmp
long_jmp
要优雅得多,define可以手动将被破坏的寄存器推送到堆栈上,以便以后检索它们。

您可以通过使用内联汇编调用函数来避免覆盖寄存器

#include <stdio.h>

static void foo(void)
{
        int i;
        asm volatile ("mov 16(%%rbp), %0" : "=g" (i));
        printf("%d\n", i);
}

#define foo(x) ({ int _i = (x); \
        asm ("push %0\ncall %P1\nadd $8, %%rsp\n" : : "g"(_i), "i"(foo)); })

int main(int argc, char *argv[])
{
        foo(argc-1);
        return 0;
}
#包括
静态void foo(void)
{
int i;
asm挥发性(“mov 16(%%rbp),%0”:“=g”(i));
printf(“%d\n”,i);
}
#定义foo(x)({int_i=(x))\
asm(“推送%0\n调用%P1\n添加$8,%%rsp\n):“g”(_i),“i”(foo));})
int main(int argc,char*argv[])
{
foo(argc-1);
返回0;
}

这里,在堆栈上推送一个整数,并调用函数foo。foo使该值在其局部变量i中可用。返回后,堆栈指针将调整回其原始值。

这将中断PCS/ABI。你从错误的方向靠近。汇编代码必须遵循ABI。最好是直接将C函数与内联汇编程序结合使用,或者将其作为实际代码的包装。这样,您就可以指定哪些寄存器/内存是您的代码阻塞器,并将保存/恢复留给gcc。
setjmp
不需要保存
rdi
rsi
——正如您所说,它们在
setjmp
中被阻塞,那么为什么要保存它们呢?事实上,只有标记为“被调用方已保存”的寄存器(即rbp、ebx、r12、r13、r14、r15,当然还有rsp)必须保留。我同意我不尊重ABI,但这是有原因的。使用
setjmp
longjmp
一切正常,因为
setjmp
未保存的内容实际上由调用方保存,以备需要,因为它们是调用方保存寄存器。在我的应用程序中,我与Linux内核进行了交互,在给定的中断时,Linux内核将控制返回到用户级代码的不同部分,而用户级代码又调用
setjmp
。通过这种构造,最初执行的代码不知道正在调用
setjmp
,因此也应该保存调用方保存寄存器。C标准将
setjmp
定义为宏,因此如果使用宏,则不受任何调用约定的约束。因为您似乎无论如何都在使用gcc,所以可以通过使用类似于
({exec\u context\u t*env=(env);…})
的东西来检查参数。您可以使用GNU C函数属性来指定函数使用的ABI。不过,我认为x86-64没有堆栈调用ABI,因此可能没有任何属性可以满足您的需要。我使用C++从Windows或Linux调用一个手写ASM函数(通过声明函数使用Sysv调用约定,即使不是默认调用约定)。因此,如果没有最新的gcc,您将得到不正确的代码:如果调用函数
main()
没有将
%rbp
用作帧指针,而是将其用作额外的整数寄存器,或者根本不使用它以避免推送/弹出开销,那么这当然会非常糟糕。如果您使用
-O2
@NateEldredge编译此代码,则实际会发生后者:只有
foo
的设计很糟糕,但它只是他的示例的一个占位符。生成自定义ABI
调用的宏看起来不错。OP将使用用ASM手工编写的自定义
setjmp
实现,它不会做出任何这些假设。它首先按下一些寄存器来获取一些临时寄存器,然后保存所有调用方的状态。IDK if
xsave
在用户空间中运行良好。该asm语句不安全:。您需要在推送之前添加$128,%rsp
,然后在推送之后添加$128+8。对于asm函数具有的任何自定义调用约定,您通常还需要在所有未保留调用的寄存器上声明clobber。e、 g.RAX、RCX、RDX、RDI、RSI、R8-R11、xmm0-15(或带有AVX-512的0-31)和x87 st0..7。