簡體   English   中英

分段錯誤使用C和程序集創建用戶級線程

[英]Segmentation fault creating a user-level thread with C and assembly

我試圖通過一些任務來理解一些操作系統基礎知識。 我已經發布了一個類似的問題,得到了令人滿意的答案。 但這個略有不同,但我無法調試它。 所以這就是我的工作:

我想要做的是啟動一個主程序,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”。但是當單獨運行時,這會產生分段錯誤。我很困惑通過這個結果。

任何幫助,將不勝感激。 提前致謝。

您的代碼有兩個問題:

  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)。 這將使事情變得更加清晰,特別是如果您想要使程序多線程並且還將任何參數傳遞給啟動例程。

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的挑剔建議,但它確實有助於在可移植代碼中編寫盡可能多的軟件,而不是依賴於未定義的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM