2013-04-22 16 views
5

いくつかの割り当てを使用して、いくつかのOSの基本を理解しようとしています。私はすでに同様の質問を投稿しており、満足のいく回答を得ています。しかし、これは少し異なりますが、私はそれをデバッグすることができませんでした。だからここに私は何をすべきかです:セグメンテーションフォールトでCおよびアセンブリを使用したユーザーレベルのスレッドを作成する

私が何をしたいのか、メインプログラムを起動した空間をを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は私のように書いたアセンブリコードである[Iは、最新の私の答えの現在の状態にそれを作るために自分のコードを編集しています]このコードは、GDBの下で本当によく実行され、期待される出力(で、simple_functionに制御を渡すと、印刷できます「!私は機能していますが、kは次のとおりです。0」

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

以下。しかし、個別に実行する場合、この私はこの結果に困惑しています。

ご協力いただければ幸いです。前もって感謝します。あなたのコードで

+0

関数呼び出しで他のレジスタを気にする必要はないと思いませんか? – Alex

+2

スタックを16で整列させることを忘れないでください。 –

+0

@AkiSuihkonenそれは私が明確にしたいひとつのポイントです。なぜそれが16で整列されるべきか説明していただけますか? – user2290802

答えて

7

2つの問題:

  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 

FYI:もしあなたが自分でスレッドを初期化する再、私はあなたが最初のスレッドのエントリポイントとして機能し、適切なトランポリン(例えばNTDLLのRtlUserThreadStart)を作成することを示唆しています。これにより、特にプログラムをマルチスレッド化し、スタートルーチンに任意のパラメータを渡したい場合には、よりクリーンなものになります。

+1

Cプログラムで 'malloc'の戻り値をキャストする必要はありません。 –

+0

あなたの説明はあまり明確ではありません。あなたはそれを言い換えることができますか?また、call%rsi命令の使用は何ですか? – user2290802

+1

@ user2290802%ebpは、特定のプロシージャのスタックフレームを実際に構築/破壊しているとき、つまり 'ENTER/LEAVE'命令(または同等のプロシージャプロローグ/エピローグ)を実行しているときにのみ参照されます。あなたのswitch_thread関数では、そのようなフレームはありません(とにかく必要はありません)。 %rsiレジスタの値は、 'simple_function()'へのポインタを保持します。 – JosephH

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のNIT-うるさい助言であることを理解し、それは本当に移植可能なコードではなく、未定義の動作に依存で可能な限りあなたのソフトウェア限りを書くのに役立ちありません。

関連する問題