x86-64分段故障保存堆栈指针

我目前正在关注本教程 ,但我不是该学校的学生。

GDB thread_start上的thread_start给出了一个分段错误:

 movq %rsp, (%rdi) # save sp in old thread's tcb 

这是我回溯时的附加信息:

 #0 thread_start () at thread_start.s:16 #1 0x0000000180219e83 in _cygtls::remove(unsigned int)::__PRETTY_FUNCTION__ () from /usr/bin/cygwin1.dll #2 0x00000000ffffcc6b in ?? () Backtrace stopped: previous frame inner to this frame (corrupt stack?) 

作为一个新手,我不能为我的生活找出原因。 这是我的主要文件:

 #define STACK_SIZE 1024*1024 //Thread TCB struct thread { unsigned char * stack_pointer; void(*initial_function)(void *); void * initial_argument; }; struct thread * current_thread; struct thread * inactive_thread; void thread_switch(struct thread * old_t, struct thread * new_t); void thread_start(struct thread * old_t, struct thread * new_t); void yield() { //swap threads struct thread * temp = current_thread; current_thread = inactive_thread; inactive_thread = temp; thread_switch(inactive_thread, current_thread); } void thread_wrap() { // call the thread's function current_thread->initial_function(current_thread->initial_argument); yield(); } int factorial(int n) { return n == 0 ? 1 : n * factorial(n - 1); } // calls and print the factorial void fun_with_threads(void * arg) { int n = *(int*)arg; printf("%d! = %d\n", n, factorial(n)); } int main() { //allocate memory for threads inactive_thread = (struct thread*) malloc(sizeof(struct thread)); current_thread = (struct thread*) malloc(sizeof(struct thread)); // argument for factorial int *p= (int *) malloc(sizeof(int)); *p = 5; // intialise thread current_thread->initial_argument = p; current_thread->initial_function = fun_with_threads; current_thread->stack_pointer = ((unsigned char*) malloc(STACK_SIZE)) + STACK_SIZE; thread_start(inactive_thread, current_thread); return 0; } 

这是我的thread_start的asm代码

 # Inline comment /* Block comment */ # void thread_switch(struct thread * old_t, struct thread * new_t); .globl thread_start thread_start: pushq %rbx # callee-save pushq %rbp # callee-save pushq %r12 # callee-save pushq %r13 # callee-save pushq %r14 # callee-save pushq %r15 # callee-save movq %rsp, (%rdi) # save sp in old thread's tcb movq (%rsi), %rsp # load sp from new thread jmp thread_wrap 

和thread_switch:

 # Inline comment /* Block comment */ # void thread_switch(struct thread * old_t, struct thread * new_t); .globl thread_switch thread_switch: pushq %rbx # callee-save pushq %rbp # callee-save pushq %r12 # callee-save pushq %r13 # callee-save pushq %r14 # callee-save pushq %r15 # callee-save movq %rsp, (%rdi) # save sp in old thread's tcb movq (%rsi), %rsp # load sp from new thread popq %r15 # callee-restore popq %r14 # callee-restore popq %r13 # callee-restore popq %r12 # callee-restore popq %rbp # callee-restore popq %rbx # callee-restore ret # return 

你在cygwin,对吧? 它默认使用Windows x64调用约定,而不是System V x86-64 psABI。 所以你的args不在%rdi%rsi

调用约定是Windows x64,但ABI略有不同: long是64位,所以它是LP64而不是LLP64。 请参阅cygwin文档 。

您可以在原型上使用__attribute__((sysv_abi))覆盖默认值,但这仅适用于了解GNU C的编译器。


Agner Fog的调用约定指南提供了一些关于如何编写源代码的建议,这些源代码可以组装到Windows与非Windows上的工作函数。 最直接的事情是使用#ifdef来选择不同的function序言。


此英特尔x64程序集介绍有点以Windows为中心,详细介绍了Windows x64 __fastcall调用约定。

(接下来是示例和内容。这是一个非常大而且很好的教程,从非常基本的东西开始,包括如何使用像汇编程序这样的工具。我建议它在Windows开发环境中学习x86-64 asm,也许一般来说。)

Windows x64 __fastcall (如x64 __vectorcall但不传递向量regs中的向量)

  • RCX,RDX,R8,R9从左到右依次用于整数和指针参数
  • XMM0,1,2和3用于浮点参数。
  • 其他参数从左到右推入堆栈。
  • 小于64位的参数不是零扩展; 高位包含垃圾。
  • 在调用函数之前,调用者有责任分配32个字节的“阴影空间”(如果需要,用于存储RCX,RDX,R8和R9)。
  • 呼叫后清理堆栈是调用者的责任。
  • 如果64位或更少,则在RAX中返回整数返回值(类似于x86)。
  • 浮点返回值在XMM0中返回。
  • 较大的返回值(结构)由调用者在堆栈上分配空间,然后RCX在调用被调用者时包含指向返回空间的指针。 然后,向右推一个整数参数的寄存器用法。 RAX将此地址返回给调用者。
  • 堆栈是16字节对齐的。 “call”指令推送一个8字节的返回值,因此在分配堆栈空间时,所有非叶函数必须通过16n + 8forms的值调整堆栈。
  • 寄存器RAX,RCX,RDX,R8,R9,R10和R11被认为是易失性的,必须在函数调用时被视为已销毁。 RBX,RBP,RDI,RSI,R12,R14,R14和R15必须保存在使用它们的任何function中。
  • 注意浮点(以及MMX)寄存器没有调用约定。
  • 更多细节(varargs,exception处理,堆栈展开)在微软的网站上。

链接到MS的x86标签wiki中的调用约定文档(以及System V ABI文档,以及大量其他好东西)。

另请参见为什么Windows64在x86-64上使用与所有其他操作系统不同的调用约定?