x86-64分段故障保存堆栈指针 [英] x86-64 segmentation fault saving stack pointer
问题描述
我目前正在与本教程一起关注, 但我不是那所学校的学生.
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.
在标签Wiki(以及System V ABI文档以及大量其他好东西).
Links to MS's calling-convention docs in the x86 tag wiki (along with System V ABI docs, and tons of other good stuff).
另请参见为什么Windows64使用与x86-64上所有其他操作系统不同的调用约定?
这篇关于x86-64分段故障保存堆栈指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!