5

我正在尝试使用一些作业来理解一些操作系统基础知识。我已经发布了一个类似的问题并得到了令人满意的答案。但这一个略有不同,但我无法调试它。所以这就是我所做的:

我要做的是启动一个主程序,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;
}

switch_thread 是我编写的汇编代码,如下所示:

.text
    .globl  switch_thread
switch_thread:  
    movq    %rdi, %rsp
    movq    %rsi, %rbp
    ret

这段代码在 GDB 下运行得非常好,并给出了预期的输出(即,将控制权传递给 simple_function 并打印“我是函数!k 是:0”。但是当单独运行时,这会产生分段错误。我很困惑通过这个结果。

任何帮助,将不胜感激。提前致谢。

4

2 回答 2

7

您的代码有两个问题:

  1. 除非您的线程实际上位于正确的过程(或嵌套过程)中,否则不存在“基指针”之类的东西。这使得 %rbp 的值无关紧要,因为线程在初始化时不在特定过程中。

  2. 与您的想法相反,当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);      
}

您的 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

仅供参考:如果您自己初始化线程,我建议您首先创建一个适当的蹦床作为线程入口点(例如 ntdll 的 RtlUserThreadStart)。这将使事情变得更加清晰,特别是如果您想使您的程序多线程并且还将任何参数传递给启动例程。

于 2013-04-22T17:36:23.750 回答
0

base_pointer需要适当地对齐以存储void (*)()值,否则您正在处理未定义的行为。我想你的意思是这样的:

void create_thread(void (*function)())
{
    size_t offset = STACK_SIZE + sizeof function - STACK_SIZE % sizeof function;
    char *stack_pointer = malloc(offset + sizeof *base_pointer);
    void (**base_pointer)() = stack_pointer + offset;
    *base_pointer = function;
    switch_thread(stack_pointer,base_pointer);      
}

无需强制转换 malloc。将指针转换为整数类型或将函数指针转换为对象指针类型通常是一个坏主意。

我知道这都是可移植 C 挑剔的建议,但它确实有助于尽可能多地用可移植代码编写软件,而不是依赖于未定义的行为。

于 2013-04-22T18:37:47.413 回答