我目前正在跟随本教程,但是我不是那所学校的学生。
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的呼叫约定?