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

时间:2016-11-29 22:46:30

标签: c assembly x86 cygwin x86-64

我目前正在关注this tutorial, 但我不是那所学校的学生。

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! = %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

1 个答案:

答案 0 :(得分:4)

你在使用cygwin,对吗?它默认使用Windows x64调用约定,而不是System V x86-64 psABI。因此,您的参数不在%rdi%rsi

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

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

Agner Fog's calling convention guide有一些关于如何编写汇编到Windows与非Windows上的工作函数的源代码的建议。最直接的事情是使用#ifdef来选择不同的功能序言。

Intel intro to x64 assembly在某种程度上以Windows为中心,详细介绍了Windows x64 __fastcall调用约定。

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

  

Windows x64 __fastcall(如x64 __vectorcall但不传递矢量注册表中的向量)

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

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

另见Why does Windows64 use a different calling convention from all other OSes on x86-64?