2013-04-21 11 views
9

Próbuję zakodować prostą bibliotekę wątków na poziomie użytkownika jako ćwiczenie dla mojego kursu OS. Jako pierwszy krok próbuję uruchomić program i przejść do funkcji opuszczającej pierwszy program. Kod tej pory to:Kodowanie prostego wątku z C i złożeniem

Wstępny program:

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <assert.h> 

#define STACK_SIZE (sizeof(void *) * 512) 


void proc2() //This is the function that should run as the thread. 
{ 
    int i; 
    for(i=0;i<30;i++) 
    { 
     printf("Here I am!\n"); 
     sleep(0.5); 
    } 
    exit(0); 
} 

void* malloc_stack() //used to malloc the stack for the new thread. 
{ 
    void *ptr = malloc(STACK_SIZE + 16); 
    if (!ptr) return NULL; 
     ptr = (void *)(((unsigned long)ptr & (-1 << 4)) + 0x10); //size align 
    return ptr; 
} 

int main() 
{ 
    int *bp, *sp; 
    sp = malloc_stack(); 
    bp = (int*) ((unsigned long)sp + STACK_SIZE); 
    proc1(&proc2,sp,bp); //the actual code that runs the thread. Written in assembly 
    assert(0); 
} 

A potem napisałem prosty kod montaż nazwie PROC1 który przyjmuje trzy argumenty, wskaźnik do funkcji (używany jako wskaźnik instrukcji), stos wskaźnik i wskaźnik bazowy oraz zastąpienie obecnych rejestrów tymi wartościami. Kod, który napisałem, to:

.globl proc1 
proc1: 
movq %rdx, %rbp  #store the new base pointer 
movq %rsi,%rsp   #store the new stack pointer 
jmp  %rdi    #jump to the new instruction pointer. 

Ale po uruchomieniu tego kodu otrzymuję błąd segmentacji. Proszę pomóż mi znaleźć błąd tutaj.

Dobrze to działa poprawnie, kiedy prowadził ją pod GDB stosując następujące polecenia:

gcc -g test.c switch.s 
gdb a.out 
run 

ale kiedy rus sam jak w ./a.out, to nie działa !!!! Proszę o pomoc.

Z góry dziękuję.

+1

na początek, to prawdopodobnie nie chcą "& PROC2" ... Q: Jaki jest Twój kompilator/debugger? GCC i GDB? Czy próbowałeś przejść przez twój kod pod debuggerem, patrząc na kluczowe zmienne po drodze? Jeśli nie, dlaczego nie? – paulsm4

+6

Po napotkaniu przy segfault, programista zwykle uruchamia debuggera, aby zobaczyć, gdzie dokładnie wystąpił błąd seg, zwłaszcza jeśli segfault jest w 100% powtarzalny. Powinieneś też, a następnie ewentualnie zaktualizować pytanie. – hyde

+1

@ paulsm4 Można używać '&' przy adresowaniu funkcji, a wiele osób ją preferuje, ponieważ (prawdopodobnie) czyni ją bardziej zrozumiałą co się dzieje. – hyde

Odpowiedz

2

Twój movq w górnej części zespołu są (dobrze „były” ty edytowane :-) wcześniej) napisany jako

movq dst,src 

ale swój movq przed jmp napisano movq %rax,%rsp i %rsp jest oczywiście pożądana dst. To oczywiście źle, nie jestem pewien niczego innego.

+0

no nie działa jeszcze. Wciąż – user2290802

+0

-1: To jest wyraźnie w składni AT & T, a kolejność operandów to src, dest w składni AT & T. Wydajesz się mylić składnię Intela ze składnią AT & T. W składni Intela kolejność operandów to dest, src. – nrz

+0

@nrz: OP zredagował swoje pytanie; wcześniej pobierał parametry ze stosu do rejestrów, używając kolejności (dst, src). Przypuszczalnie nie składał jednej linii z jedną składnią, a następną z drugą, co powodowało, że co najmniej jedna linia była niepoprawna. Moim zdaniem wtedy było to, że złapał kolejność (dst, src) z jakiegoś wyjścia kompilatora, ale możliwe, że dostał to z innego miejsca. – torek

4

Spróbuj zmienić swój kod zawierać instrukcję montażu bezpośrednio w źródle C następujące:

void proc1(void (*fun)(), int *sp, int *bp){ 
    register int *sptr asm ("%rsi") = sp; 
    register int *bptr asm ("%rdx") = bp; 
    register void (*fptr)() asm ("%rdi") = fun; 

    asm (
     "mov %rdx, %ebp\n" 
     "mov %rsi, %esp\n" 
     "jmp *%rdi\n" 
    ); 
} 

Powyższy kod gwarantuje, że parametry do proc1 są w odpowiednich rejestrach (chociaż kod wydaje się być poprawne wrt the abi). Zauważ * przed argumentem jmp, o której moja wersja gnu ostrzegała, gdy pierwszy raz wypróbowałem twój kod.

Z powyższej funkcji, a kod skompilowany z -g powinieneś móc go prawidłowo debugowania (użyj instrukcji breakpoint na proc1 i info registers aby sprawdzić zawartość CPU).


Kwestia jest rzeczywiście na wskaźniku %rsp, która zawsze musi być równa lub większa niż %rbp (stos rośnie w dół).Po prostu przechodząc bp zamiast sp do proc1 w głównym powinno rozwiązać problem:

proc1(&proc2, bp, bp); 

2 małe UWAGI:

  • nie zapomnij dać proc1 prototyp w kodzie C dla wersji asm:

    extern void proc1(void (*)(), int *, int *); 
    
  • tylko libc funkcja sleep wziąć unsigned long, nie float.

    sleep(1); 
    
+0

Dziękuję bardzo. To rozwiązało problem. Właściwie to pomyliłem sp z bp. Dziękuję za przypomnienie o prototypie funkcji i zastanawiam się, dlaczego GCC nie ostrzegł mnie o śnie? Może powinienem użyć opcji -Wall. Dzięki jeszcze raz. – user2290802

+0

Nie wiem. Nie użyłem też '-Wall', chociaż powinienem (to dobra praktyka). – didierc