我目前正在关注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
答案 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,异常处理,堆栈展开)在微软的网站上。
在x86标记wiki中链接到MS的调用约定文档(以及System V ABI文档,以及大量其他好东西)。
另见Why does Windows64 use a different calling convention from all other OSes on x86-64?