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



我目前正在跟随本教程,但是我不是那所学校的学生。

GDB在行上的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! = %dn", 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调用约定,而不是系统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选择不同的功能序列。


x64汇编的此英特尔介绍在某种程度上是以Windows为中心的,详细介绍了Windows X64 __fastcall调用常规。

(接下来是示例和内容。这是一个非常大且不错的教程,从非常基本的内容开始,包括如何使用诸如汇编器之类的工具。我建议它在Windows Dev环境中学习X86-64 ASM,也许一般。)

Windows X64 __fastcall(例如X64 __vectorcall,但不通过向量登记中的向量)

  • rcx,rdx,r8,r9用于整数和指针参数以该顺序向右到右
  • xmm0、1、2和3用于浮点参数。
  • 将其他参数推到从左到右的堆栈。
  • 参数小于64位的长度不是零扩展;高点包含垃圾。
  • 分配"阴影空间"的32个字节是呼叫者的责任。(如果需要,用于存储RCX,RDX,R8和R9)功能。
  • 呼叫者在通话后清洁堆栈是责任。
  • 整数返回值(类似于x86),如果64位或更少。
  • 浮点返回值在XMM0中返回。
  • 较大的返回值(structs)具有呼叫者分配的堆栈上的空间,然后RCX随后包含一个指向返回空间的指针卡莉被称为。然后,注册用于整数参数的使用情况将一个推到右边。RAX将此地址返回给呼叫者。
  • 堆栈是16字节对齐的。"呼叫"指令推动了8字节的返回值,因此所有非叶函数必须调整分配堆栈空间时按16n 8的值堆叠。
  • 寄存器RAX,RCX,RDX,R8,R9,R10和R11被视为挥发性,必须在功能调用中被视为破坏。RBX,RBP,RDI,RSI,R12,R14,R14和R15必须保存在任何功能中他们。
  • 请注意,浮动点(以及MMX)寄存器没有呼叫约定。
  • 更多的详细信息(varargs,异常处理,放松堆栈)在Microsoft的网站上。

链接到X86 TAG WIKI中的MS的调用文档(以及System v Abi Docs,还有许多其他好东西)。

另请参见为什么Windows64在x86-64上使用所有其他OS的呼叫约定?

最新更新