x86-64分段故障保存堆栈指针 [英] x86-64 segmentation fault saving stack pointer

查看:93
本文介绍了x86-64分段故障保存堆栈指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在与本教程一起关注, 但我不是那所学校的学生.

I am currently following along with this tutorial, but I'm not a student of that school.

GDB在行上的thread_start中给了我一个分段错误:

GDB gives me a segmentation fault in thread_start on the line:

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

回溯时,还有其他信息:

Here's additional info when I backtrace:

#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?)

作为一个新手,我一生都无法弄清楚为什么.这是我的主文件:

Being a newbie, I can't for my life figure out why. Here is my main file:

#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代码

Here's my asm code for thread_start

# 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:

and 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.因此,您的参数不在%rdi%rsi中.

You're on cygwin, right? It uses the Windows x64 calling convention by default, not the System V x86-64 psABI. So your args aren't in %rdi and %rsi.

调用约定是Windows x64,但是ABI略有不同:long是64位,因此它是LP64,而不是LLP64.请参见 cygwin文档.

The calling convention is Windows x64, but the ABI is slightly different: long is 64 bit, so it's LP64 not LLP64. See the cygwin docs.

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

You could override the default with __attribute__((sysv_abi)) on the prototype, but that only works for compilers that understand GNU C.

Agner Fog的调用约定指南对如何编写可汇编为工作功能的源代码提出了一些建议Windows与非Windows.最简单的方法是使用#ifdef选择不同的功能序言.

Agner Fog's calling convention guide has some suggestions on how to write source code that assembles to working functions on Windows vs. non-Windows. The most straightforward thing is to use an #ifdef to choose different function prologues.

Intel x64汇编简介是有点以Windows为中心,并详细介绍了Windows x64 __fastcall调用约定.

This Intel intro to x64 assembly is somewhat Windows-centric, and details the Windows x64 __fastcall calling convention.

(后面是示例和内容.这是一个非常大而不错的教程,从非常基础的内容开始,包括如何使用汇编程序之类的工具.我建议您在Windows开发环境中学习x86-64 asm ,也许也可以.)

(It's followed by examples and stuff. It's a pretty big and good tutorial that starts from very basic stuff, including how to use tools like an assembler. I'd recommend it for learning x86-64 asm in a Windows dev environment, and maybe in general.)

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字节对齐的. 呼叫"指示指令压入一个8字节的返回值,因此所有非叶子函数都必须调整 分配堆栈空间时,以16n + 8形式的值进行堆栈.
  • 寄存器RAX,RCX,RDX,R8,R9,R10和R11被认为是易失性的,必须在函数调用时被视为已销毁. RBX,RBP, 必须使用以下命令将RDI,RSI,R12,R14,R14和R15保存在任何函数中 他们.
  • 请注意,对于浮点寄存器(因此MMX)没有调用约定.
  • 更多详细信息(可变参数,异常处理,堆栈展开)在Microsoft网站上.

Windows x64 __fastcall (like x64 __vectorcall but doesn't pass vectors in vector regs)

  • RCX, RDX, R8, R9 are used for integer and pointer arguments in that order left to right
  • XMM0, 1, 2, and 3 are used for floating point arguments.
  • Additional arguments are pushed on the stack left to right.
  • Parameters less than 64 bits long are not zero extended; the high bits contain garbage.
  • It is the caller's responsibility to allocate 32 bytes of "shadow space" (for storing RCX, RDX, R8, and R9 if needed) before calling the function.
  • It is the caller's responsibility to clean the stack after the call.
  • Integer return values (similar to x86) are returned in RAX if 64 bits or less.
  • Floating point return values are returned in XMM0.
  • Larger return values (structs) have space allocated on the stack by the caller, and RCX then contains a pointer to the return space when the callee is called. Register usage for integer parameters is then pushed one to the right. RAX returns this address to the caller.
  • The stack is 16-byte aligned. The "call" instruction pushes an 8-byte return value, so the all non-leaf functions must adjust the stack by a value of the form 16n+8 when allocating stack space.
  • Registers RAX, RCX, RDX, R8, R9, R10, and R11 are considered volatile and must be considered destroyed on function calls. RBX, RBP, RDI, RSI, R12, R14, R14, and R15 must be saved in any function using them.
  • Note there is no calling convention for the floating point (and thus MMX) registers.
  • Further details (varargs, exception handling, stack unwinding) are at Microsoft's site.

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

这篇关于x86-64分段故障保存堆栈指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆