我试图通过一些任务来理解一些操作系统基础知识.我已经发布了一个类似的问题,得到了令人满意的答案.但这个略有不同,但我无法调试它.所以这就是我的工作:
我想要做的是启动一个主程序,malloc一个空间,用它作为一个堆栈来启动用户级线程.我的问题是返回地址.这是迄今为止的代码:
[我正在编辑我的代码,使其与我的答案的当前状态保持同步]
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define STACK_SIZE 512
void switch_thread(int*,int*);
int k = 0;
void simple_function()
{
printf("I am the function! k is: %d\n",k);
exit(0);
}
void create_thread(void (*function)())
{
int* stack = malloc(STACK_SIZE + 32);
stack = (int* )(((long)stack & (-1 << 4)) + 0x10);
stack = (int* ) ((long)stack + STACK_SIZE);
*stack = (long) function;
switch_thread(stack,stack);
}
int main()
{
create_thread(simple_function);
assert(0);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
switch_thread是我编写的汇编代码如下:
.text
.globl switch_thread
switch_thread:
movq %rdi, %rsp
movq %rsi, %rbp
ret
Run Code Online (Sandbox Code Playgroud)
这个代码在GDB下运行得非常好并且给出了预期的输出(即,将控制传递给simple_function并打印"我是函数!k是:0".但是当单独运行时,这会产生分段错误.我很困惑通过这个结果.
任何帮助,将不胜感激.提前致谢.
您的代码有两个问题:
除非你的线程实际上在正确的程序(或嵌套的程序)中,否则就没有"基指针"这样的东西.这使得%rbp的值无关紧要,因为线程在初始化时不在特定过程内.
与您的想法相反,当ret指令执行时,%rsp所指的值将成为程序计数器的新值.这意味着,当它被执行时*(base_pointer + 1),*(base_pointer)将被咨询而不是.同样,%rbp的值在这里无关紧要.
您的代码(进行最少的修改以使其运行)应如下所示:
void switch_thread(int* stack_pointer,int* entry_point);
void create_thread(void (*function)())
{
int* stack_pointer = malloc(STACK_SIZE + 8);
stack_pointer += STACK_SIZE; //you'd probably want to back up the original allocated address if you intend to free it later for any reason.
switch_thread(stack_pointer,function);
}
Run Code Online (Sandbox Code Playgroud)
你的switch_thread例程应如下所示:
.text
.globl switch_thread
switch_thread:
mov %rsp, %rax //move the original stack pointer to a scratch register
mov %rdi, %rsp //set stack pointer
push %rax //back-up the original stack pointer
call %rsi //call the function
pop %rsp //restore the original stack pointer
ret //return to create_thread
Run Code Online (Sandbox Code Playgroud)
仅供参考:如果您正在自己初始化一个线程,我建议您首先创建一个适当的蹦床作为线程入口点(例如ntdll的RtlUserThreadStart).这将使事情变得更加清晰,特别是如果您想要使程序多线程并且还将任何参数传递给启动例程.