我正在为x86-64系统开发setjmp/ longjmp自定义实现,它保存了CPU的整个上下文(即所有xmm,fpu堆栈等;不仅是callee-save寄存器).这是直接在汇编中编写的.
代码在最小的示例中工作正常(直接从汇编源调用它时).由于参数传递给自制程序setjmp/ longjmp函数的方式,在使用C代码时会出现问题.实际上,x64_64系统的SysV ABI规定参数应该通过寄存器传递(如果它们最多为6).我的职能签名是:
long long set_jmp(exec_context_t *env);
__attribute__ ((__noreturn__)) void long_jmp(exec_context_t *env, long long val);
当然,这不能正常工作.事实上,当我进入set_jmp,rdi并rsi已经被破坏,以保持指针env和val.这同样适用long_jmp于rdi.
有没有办法强制GCC,例如依靠某些属性,强制通过堆栈传递参数?这将比包装set_jmp和long_jmp使用某些定义更加优雅,这些定义手动将堆栈寄存器压入堆栈,以便稍后检索它们.
这是我通过反汇编看到的声明function(1,2,3);:
movl $0x3,0x8(%esp)
movl $0x2,0x4(%esp)
movl $0x1,(%esp)
call 0x4012d0 <_Z8functioniii>
Run Code Online (Sandbox Code Playgroud)
似乎ret地址根本没有被推入堆栈,那怎么ret工作呢?
从历史上看,为什么它似乎只是关于每个人和他们的孩子兄弟定义他们自己的呼叫惯例?你有C,C++,Windows,Pascal,Fortran,Fastcall以及其他可能没有提及的其他人.对于绝大多数用例,一项公约不应该是最有效的吗?是否有任何理由更喜欢一个而不是另一个?
我遇到了崩溃,在调查时我发现自己完全被以下代码阻止:
0000000000000a00 <_IO_vfprintf>:
a00: 55 push %rbp
a01: 48 89 e5 mov %rsp,%rbp
a04: 41 57 push %r15
a06: 41 56 push %r14
a08: 41 55 push %r13
a0a: 41 54 push %r12
a0c: 53 push %rbx
a0d: 48 81 ec 48 06 00 00 sub $0x648,%rsp
a14: 48 89 95 98 f9 ff ff mov %rdx,0xfffffffffffff998(%rbp)
Run Code Online (Sandbox Code Playgroud)
这是通过objdump --disassemble /usr/lib64/libc.a在64位Linux x86系统上运行,然后搜索输出生成的.这是AT&T语法,所以目的地在右边.
具体来说,我不明白最后一条指令.它似乎是rdx在函数触及该寄存器之前将寄存器的值写入堆栈中某处(远,远)的内存中.对我来说,这没有任何意义.
我试着阅读调用约定,现在我最好的理论rdx是用于参数,所以代码基本上是"返回"参数值.这不是功能的结束,当然,它并没有真正回归.
在C/C++中(具体来说,我使用的是MSVS),在什么情况下需要担心为函数定义指定调用约定?它们是否重要?是否有能力在必要时选择最佳约定(即fastcall等).
也许我的理解是缺乏的,但我只是不知道他们的情况何时会成为程序员需要关心的事情,比如参数放在堆栈上的顺序等等.我也不明白为什么编译器的优化不能选择任何最适合该特定函数的方案.任何人都可以提供给我的任何知识都会很棒.谢谢!
我试图从另一个wpf应用程序调用一个wpf应用程序.调用wpf应用程序进行调用
ProcessStartInfo BOM = new ProcessStartInfo();
BOM.FileName = @"D:\WPFAPPLICATION.exe";
BOM.Arguments = temp;
Process.Start(BOM);
Run Code Online (Sandbox Code Playgroud)
现在在调用的应用程序中,我尝试检索传递的参数
string arguments =Process.GetCurrentProcess().StartInfo.Arguments;
Run Code Online (Sandbox Code Playgroud)
但是参数没有通过.为什么是这样??
我还尝试了一种替代方法,其中:
public partial class App : Application
{
public static String[] mArgs;
private void Application_Startup(object sender, StartupEventArgs e)
{
if (e.Args.Length > 0)
{
mArgs = e.Args;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这也不起作用!请帮忙!!
我fastcall在许多函数之前看过附加的符号.为什么用它?
我正在研究C中未定义的行为,我发表了一个声明
函数参数的评估没有特定的顺序
但那么标准调用约定如何_cdecl和_stdcall,其定义(在一本书中)说参数是从右到左进行评估.
现在我对这两个定义感到困惑,根据UB,一个状态与另一个定义不同,这是根据调用约定的定义.请证明两者的合理性.
我一直在阅读Windows x64 ABI上很长篇的非常好的文章目录.这些文章的一个非常小的方面是帧指针的描述.一般要点是,因为Windows x64调用堆栈规则非常严格,所以通常不需要专用的帧指针,尽管它是可选的.
我一直注意到的一个例外是当alloca()用于在堆栈上动态分配内存时.这样做的功能显然需要一个帧指针.例如,引用微软关于"堆栈分配"的文档(斜体和粗体由我添加):
如果在函数中动态分配空间(alloca),则必须使用非易失性寄存器作为帧指针来标记堆栈固定部分的基础,并且必须在prolog中保存和初始化该寄存器.请注意,当使用alloca时,来自同一调用者的对同一被调用者的调用可能具有其寄存器参数的不同家庭地址.
对此,Microsoft的x64 ABI alloca()文档密码添加:
_alloca需要16字节对齐,并且还需要使用帧指针.
首先,为什么必须使用它?我假设在异常时调用堆栈展开,但我还没有找到令人满意的解释.
下一个问题:它必须指向哪里?在上述两个引文中的第一个中,它表示"必须"用于标记" 堆栈的固定部分 "的基础.什么是"堆栈的固定部分"?我得到的印象是,这个术语在给定的框架中表示包含的地址范围(较高地址到较低地址):
同样,我还没有找到这个"固定部分"的令人满意的定义.我上面链接的"堆栈分配"页面包含下面的图表以及"如果使用,则堆栈指针通常指向此处":

这个非常漂亮的博客文章同样含糊不清,包括一个图表,说明框架指针"点在这里的某处",其中"here"是保存的非易失性寄存器和本地的地址.
最后一点含糊不清,来自微软的MSDN文章"动态参数堆栈区域构建",其中仅包含以下内容:
如果使用帧指针,则存在动态创建参数堆栈区域的选项.目前在x64编译器中没有这样做.
"一般"是什么意思?"在这里的某个地方"在哪里?存在的选项是什么?有规则吗?谁在乎?
或者,tl;博士:标题要求什么.任何包含带注释的程序集的答案都会感激不尽.
除了其他方面,x86-64 SysV ABI指定了如何在寄存器中传递函数参数(第一个参数in rdi,then rsi等等),以及如何传回整数返回值(in rax和then rdx表示非常大的值).
然而,我找不到的是当传递小于64位的类型时,参数或返回值寄存器的高位应该是什么.
例如,对于以下功能:
void foo(unsigned x, unsigned y);
Run Code Online (Sandbox Code Playgroud)
... x将被传入rdi和y在rsi,但他们只是32位.不要的高32位rdi和rsi必须为零?直观地说,我会假设是,但是所有gcc,clang和icc 生成的代码mov在开始时都有特定的指令将高位清零,所以看起来编译器假定不然.
类似地,编译器似乎假设rax如果返回值小于64 位,则返回值的高位可能具有垃圾位.例如,以下代码中的循环:
unsigned gives32();
unsigned short gives16();
long sum32_64() {
long total = 0;
for (int i=1000; i--; ) {
total += gives32();
}
return total;
}
long sum16_64() {
long total = 0;
for (int i=1000; i--; ) {
total += …Run Code Online (Sandbox Code Playgroud)