Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add utility functions and create simple threads
- Loading branch information
0 parents
commit b1ef686
Showing
9 changed files
with
1,599 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
cmake_minimum_required(VERSION 2.4) | ||
project (thread-bites) | ||
|
||
enable_language(ASM) | ||
include_directories(".") | ||
|
||
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") | ||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") | ||
|
||
add_library( | ||
tb SHARED | ||
tb-utils.c | ||
tb-threads.c | ||
tb-clone.S) | ||
|
||
add_executable(test-00 test-00.c) | ||
target_link_libraries(test-00 tb) | ||
|
||
add_executable(test-01 test-01.c) | ||
target_link_libraries(test-01 tb) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
Thread Bites | ||
============ | ||
|
||
Thread Bites is a simple and, admittedly, a pretty useless threading library. | ||
Its raison d'être is to be as simple as possible in order to illustrate how | ||
one can implement a threading library that somewhat resembles pthreads and plays | ||
well with x86_64 Linux. Thread Bites does not, and likely will not, support the | ||
compiler generated thread-local storage, `__thread int number;`, and other | ||
linker trickery, such as | ||
`__attribute__ ((section ("__libc_thread_freeres_fn")))`. This fact, in | ||
conjunction with glibc expecting a bunch of globals to be in TLS, makes calling | ||
anything non-trivial from glibc a rather risky business. | ||
|
||
The goal of this project is to, over time, implement all/most of pthreads | ||
functionality. | ||
|
||
See http://jany.st/post/2016-01-30-thread-bites-1.html for more details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
//------------------------------------------------------------------------------ | ||
// Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st> | ||
//------------------------------------------------------------------------------ | ||
// This file is part of thread-bites. | ||
// | ||
// thread-bites is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// thread-bites is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with thread-bites. If not, see <http://www.gnu.org/licenses/>. | ||
//------------------------------------------------------------------------------ | ||
|
||
#include <asm/unistd_64.h> // for syscall numbers | ||
|
||
//------------------------------------------------------------------------------ | ||
// All this has been derived from glibc's sysdeps/unix/sysv/linux/x86_64/clone.S | ||
// the Linux kernel sources, and a bunch of websites. | ||
// | ||
// We need to make a function callable from C that calls the clone syscall and | ||
// and starts the user function on the new task. We do this in assembly | ||
// to properly set up the call frame information (CFI) for debugging, | ||
// backtraces, exceptions and such. | ||
// | ||
// The C call has the following format: | ||
// | ||
// int tbclone(int (*fn)(void *arg), void *arg, int flags, void *child_stack | ||
// pid_t *parent_tidptr, pid_t *child_tidptr, void *tls), | ||
// | ||
// This results with the registers having the following values: | ||
// | ||
// rdi: fn | ||
// rsi: arg | ||
// rdx: flags | ||
// rcx: child_stack | ||
// r8: parent TID pointer | ||
// r9: child TID pointer | ||
// %rsp+8: TLS pointer | ||
// | ||
// The syscall has the following interface: | ||
// | ||
// SYSCALL_DEFINE5(clone, | ||
// unsigned long, clone_flags, | ||
// unsigned long, newsp, | ||
// int __user *, parent_tidptr, | ||
// int __user *, child_tidptr, | ||
// unsigned long, tls) | ||
// | ||
// So we need the registers to be: | ||
// | ||
// rax: __NR_clone | ||
// rdi: flags | ||
// rsi: child_stack | ||
// rdx: parent TID pointer | ||
// r10: child TID pointer | ||
// r8: TLS pointer | ||
// | ||
//------------------------------------------------------------------------------ | ||
|
||
.text | ||
|
||
.global tbclone // sets the symbol as externally linkable | ||
.type tbclone,@function // declare this symbol as a function in ELF | ||
.align 16 // place the next instruction at an address | ||
// divisible by 16 in the resulting ELF binary | ||
tbclone: | ||
.cfi_startproc // cfi_* is the stuff for exception frames | ||
subq $16, %rcx // decrement the new stack pointer by 16 because | ||
// we will pop stuff from it later | ||
movq %rsi, 8(%rcx) // store the function argument in the child's stack | ||
movq %rdi, 0(%rcx) // store the function pointer in the child's stack | ||
|
||
movq $__NR_clone, %rax // clone syscall number to rax | ||
movq %rdx, %rdi // flags to rdi | ||
movq %rcx, %rsi // child_stack to rsi | ||
movq %r8, %rdx // parent_tid to rdx | ||
movq %r9, %r10 // child_tid to r10 | ||
movq 8(%rsp), %r8 // TLS pointer to r8 | ||
.cfi_endproc // end FDE now to get proper unwind info in the | ||
// child | ||
syscall // call sys_clone | ||
testq %rax, %rax // compare %rax && %rax to zero | ||
jz .Lstart_thread // 0 == we're in the child | ||
ret // we're in the parent with either an error or | ||
// the child's TID | ||
|
||
.Lstart_thread: // ELF local label | ||
.cfi_startproc | ||
.cfi_undefined rip // previous value of the instruction pointer cannot | ||
// be restored anymore | ||
xorq %rbp, %rbp // clear the frame pointer | ||
|
||
popq %rax // pop the function pointer | ||
popq %rdi // pop the argument | ||
call *%rax // call the function | ||
|
||
movq %rax, %rdi // whatever the function returned goes to rdi | ||
movq $__NR_exit, %rax // exit syscall number to %rax | ||
syscall // call sys_exit | ||
.cfi_endproc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
//------------------------------------------------------------------------------ | ||
// Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st> | ||
//------------------------------------------------------------------------------ | ||
// This file is part of thread-bites. | ||
// | ||
// thread-bites is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// thread-bites is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with thread-bites. If not, see <http://www.gnu.org/licenses/>. | ||
//------------------------------------------------------------------------------ | ||
|
||
#include "tb.h" | ||
|
||
#include <stdint.h> | ||
#include <string.h> | ||
#include <stdlib.h> | ||
#include <linux/sched.h> | ||
#include <linux/mman.h> | ||
#include <asm-generic/mman-common.h> | ||
#include <asm-generic/param.h> | ||
|
||
//------------------------------------------------------------------------------ | ||
// Init the attrs to the defaults | ||
//------------------------------------------------------------------------------ | ||
void tbthread_attr_init(tbthread_attr_t *attr) | ||
{ | ||
attr->stack_size = 8192 * 1024; | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
// Thread function wrapper | ||
//------------------------------------------------------------------------------ | ||
static int start_thread(void *arg) | ||
{ | ||
tbthread_t th = (tbthread_t)arg; | ||
uint32_t stack_size = th->stack_size; | ||
void *stack = th->stack; | ||
th->fn(th->arg); | ||
free(th); | ||
|
||
//---------------------------------------------------------------------------- | ||
// Free the stack and exit. We do it this way because we remove the stack from | ||
// underneath our feet and cannot allow the C code to write on it anymore. | ||
//---------------------------------------------------------------------------- | ||
register long a1 asm("rdi") = (long)stack; | ||
register long a2 asm("rsi") = stack_size; | ||
asm volatile( | ||
"syscall\n\t" | ||
"movq $60, %%rax\n\t" // 60 = __NR_exit | ||
"movq $0, %%rdi\n\t" | ||
"syscall" | ||
: | ||
: "a" (__NR_munmap), "r" (a1), "r" (a2) | ||
: "memory", "cc", "r11", "cx"); | ||
return 0; | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
// Spawn a thread | ||
//------------------------------------------------------------------------------ | ||
int tbthread_create( | ||
tbthread_t *thread, | ||
const tbthread_attr_t *attr, | ||
void *(*f)(void *), | ||
void *arg) | ||
{ | ||
//---------------------------------------------------------------------------- | ||
// Allocate the stack with a guard page at the end so that we could protect | ||
// from overflows (by receiving a SIGSEGV) | ||
//---------------------------------------------------------------------------- | ||
void *stack = tbmmap(NULL, attr->stack_size, PROT_READ | PROT_WRITE, | ||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
long status = (long)stack; | ||
if(status < 0) | ||
return status; | ||
|
||
status = SYSCALL3(__NR_mprotect, stack, EXEC_PAGESIZE, PROT_NONE); | ||
if(status < 0) { | ||
tbmunmap(stack, attr->stack_size); | ||
return status; | ||
} | ||
|
||
//---------------------------------------------------------------------------- | ||
// Pack everything up | ||
//---------------------------------------------------------------------------- | ||
*thread = malloc(sizeof(struct tbthread)); | ||
memset(*thread, 0, sizeof(struct tbthread)); | ||
(*thread)->stack = stack; | ||
(*thread)->stack_size = attr->stack_size; | ||
(*thread)->fn = f; | ||
(*thread)->arg = arg; | ||
|
||
//---------------------------------------------------------------------------- | ||
// Spawn the thread | ||
//---------------------------------------------------------------------------- | ||
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM | CLONE_SIGHAND; | ||
flags |= CLONE_THREAD; | ||
int tid = tbclone(start_thread, *thread, flags, stack+attr->stack_size); | ||
if(tid < 0) { | ||
tbmunmap(stack, attr->stack_size); | ||
free(*thread); | ||
return tid; | ||
} | ||
|
||
return 0; | ||
} |
Oops, something went wrong.