9

I'm trying to code a simple user-level thread library as an exercise for my OS course. As the first step, I am trying to run a program and jump to a function leaving the first program. The code so far is this:

The initial 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);
}

And then I wrote a simple assembly code called proc1 that takes three arguments, pointer to a function (used as the instruction pointer), stack pointer and base pointer and replace the current registers with these values. The code I wrote is:

.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.

But when I run this code, what I get is a segmentation fault. Please help me to find the error here.

Well it's working correctly when I ran it under GDB using the following commands:

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

but when it rus alone as in ./a.out, it doesn't work!!!! Please help.

Thanks in advance.

nrz
  • 10,435
  • 4
  • 39
  • 71
user2290802
  • 253
  • 2
  • 7
  • 1
    for starters, you probably don't want "&proc2"... Q: What's your compiler/debugger? GCC and GDB? Have you tried stepping through your code under the debugger, looking at key variables along the way? If not, why not? – paulsm4 Apr 21 '13 at 07:06
  • 6
    When encountered with a segfault, a programmer typically fires up a debugger to see where exactly the seg fault happened, especially if the segfault is 100% repeatable. You should too, and then possibly update the question. – hyde Apr 21 '13 at 07:07
  • 1
    @paulsm4 It's ok to use `&` when taking address of a function, and many people prefer it because (arguably) it makes it clearer what's going on. – hyde Apr 21 '13 at 07:09
  • 1
    To add on @hyde's comment: A ken of coredumps and gdb on Linux boxes is highly advisable. – Sebastian Mach Apr 21 '13 at 07:10
  • @paulsm4 my compiler is GCC and debugger is GDB I didn't try stepping through the code because I didn't know how to, I'm just a starter. And even when I did use GDB (looking at a tutorial, the output of it seems to make no sense to me) I edited the question according to the output I got. the assembly code is supposed to retrive three values from the memory the new stack pointer, the new base pointer and the new instruction pointer for my thread and store the stack pointer and base pointer in their registers and then jump to the location of the instruction pointer. – user2290802 Apr 21 '13 at 07:51
  • @hyde the additional unnecessary code was because I used a template I had once. As I told before, I'm a starter. Changed that now. But the result is the same. – user2290802 Apr 21 '13 at 08:00
  • 1
    related: http://stackoverflow.com/questions/13283294/how-to-make-thread-in-c-without-using-posix-library-pthread-h – didierc Apr 21 '13 at 08:18
  • do you still get the segmentation fault? Which OS are you using? – didierc Apr 21 '13 at 08:21
  • @didierc I don't get it when I run it in GDB. It runs fine in GDB and produces the desired output but get the segfault when run alone. I'm using Ubuntu 12.04 on my VMWare virtual machine (which runs upon Windows 7) – user2290802 Apr 21 '13 at 08:34
  • I believe the `sleep(0.5)` might not do what's expected. I believe `sleep()` takes an int and 0.5 will then be truncated down to zero. – epatel Apr 21 '13 at 12:26

2 Answers2

4

Try changing your code to include the assembly instructions directly in the C source as follow:

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"
    );
}

The above code ensures that the parameters to proc1 are in the right registers (although your code seems to be correct wrt the abi). Note the * in front of the jmp argument, which my version of gnu as warned about when I first tried your code.

With the above function, and code compiled with -g you should be able to debug it properly (use the breakpoint instruction on proc1 and info registers to check the content of the cpu).


The issue is actually on the %rsp pointer, which must always be equal to or greater than %rbp (the stack grows downward). Simply passing bp instead of sp to proc1 in main should fix the issue:

 proc1(&proc2, bp, bp);

2 small remarks:

  • don't forget to give proc1 prototype in the C code for the asm version:

    extern void proc1(void (*)(), int *, int *);
    
  • the sleep libc function only take unsigned long, not float.

    sleep(1);
    
didierc
  • 14,572
  • 3
  • 32
  • 52
  • Thank you very much. That solved the issue. Actually I have confused sp with bp. Thank you for the reminder on the function prototype and I wonder why GCC didn't warn me about sleep? Maybe I should have used -Wall option. Thanks again. – user2290802 Apr 21 '13 at 11:01
  • I don't know. I didn't use `-Wall` either, though I ought to (it's good practice). – didierc Apr 21 '13 at 12:21
2

Your movq at the top of your assembly are (well, "were" before you edited :-) ) written as

movq dst,src

but your movq before the jmp is written movq %rax,%rsp and %rsp is clearly the desired dst. That's obviously wrong, not sure about anything else.

torek
  • 448,244
  • 59
  • 642
  • 775
  • -1: This is clearly in AT&T syntax, and the order of the operands is src, dest in AT&T syntax. You seem to confuse Intel syntax with AT&T syntax. In Intel syntax the order of the operands is dest, src. – nrz Apr 21 '13 at 08:46
  • @nrz: the OP edited his question; earlier, he was pulling parameters from the stack into registers, using the (dst,src) order. Presumably he was not assembling one line with one syntax and the next with another, which made at least one line incorrect. My best guess at the time was that he grabbed the (dst,src) order from some compiler output, but it's possible he got it from elsewhere. – torek Apr 21 '13 at 08:52
  • @torek OK, the question has been edited after your answer. `movq %rsi,%rsp` (the current code) seems correct to me, being equivalent to to `mov rsp,rsi` in Intel syntax. – nrz Apr 21 '13 at 09:01
  • @nrz sorry for not informing my changes. Yes I did edit it. Now it works in GDB but not alone. Can you guess why?? – user2290802 Apr 21 '13 at 09:20