用C和汇编编写一个简单的线程

时间:2021-11-03 03:15:03

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:

我正在尝试编写一个简单的用户级线程库作为我的OS课程的练习。作为第一步,我试图运行一个程序并跳转到一个函数离开第一个程序。到目前为止的代码是这样的:

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:

然后我编写了一个名为proc1的简单汇编代码,它接受三个参数,指向函数的指针(用作指令指针),堆栈指针和基指针,并用这些值替换当前寄存器。我写的代码是:

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

当我使用以下命令在GDB下运行它时,它正常工作:

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.

但是当它像./a.out一样单独使用时,它不起作用!!!!请帮忙。

Thanks in advance.

提前致谢。

2 个解决方案

#1


4  

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

尝试更改代码以直接在C源代码中包含汇编指令,如下所示:

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.

上面的代码确保proc1的参数在正确的寄存器中(尽管你的代码似乎与abi一致)。注意jmp参数前面的*,这是我第一次尝试你的代码时我的gnu版本所警告的。

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

使用上面的函数和用-g编译的代码,你应该能够正确地调试它(使用proc1和info寄存器上的断点指令来检查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:

问题实际上是在%rsp指针上,它必须始终等于或大于%rbp(堆栈向下增长)。只需将bp而不是sp传递给main中的proc1即可解决问题:

 proc1(&proc2, bp, bp);

2 small remarks:

2个小言论:

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

    不要忘记在asm版本的C代码中给出proc1原型:

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

    sleep libc函数只取无符号long,而不是float。

    sleep(1);
    

#2


2  

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

你的程序集顶部的movq是(好吧,编辑之前的“是”:-))写成

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.

但你在jmp之前的movq写的是movq%rax,%rsp和%rsp显然是所需的dst。这显然是错的,不确定其他任何事情。

#1


4  

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

尝试更改代码以直接在C源代码中包含汇编指令,如下所示:

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.

上面的代码确保proc1的参数在正确的寄存器中(尽管你的代码似乎与abi一致)。注意jmp参数前面的*,这是我第一次尝试你的代码时我的gnu版本所警告的。

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

使用上面的函数和用-g编译的代码,你应该能够正确地调试它(使用proc1和info寄存器上的断点指令来检查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:

问题实际上是在%rsp指针上,它必须始终等于或大于%rbp(堆栈向下增长)。只需将bp而不是sp传递给main中的proc1即可解决问题:

 proc1(&proc2, bp, bp);

2 small remarks:

2个小言论:

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

    不要忘记在asm版本的C代码中给出proc1原型:

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

    sleep libc函数只取无符号long,而不是float。

    sleep(1);
    

#2


2  

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

你的程序集顶部的movq是(好吧,编辑之前的“是”:-))写成

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.

但你在jmp之前的movq写的是movq%rax,%rsp和%rsp显然是所需的dst。这显然是错的,不确定其他任何事情。