libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
thread_linux.c
1#include "config.h"
2#include <clod/thread.h>
3#include "thread_impl.h"
4#include <assert.h>
5#include <errno.h>
6#include <linux/sched.h>
7#include <sched.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12#include <sys/syscall.h>
13#include <sys/mman.h>
14#include <sys/random.h>
15
16
17#define DEFAULT_STACK_SIZE (3 * 1024)
18
20 struct clod_process_common common;
21 pid_t child_tid;
22};
23
24struct stack_header;
25
35long clod_execution_bootstrap(const struct clone_args *args, struct stack_header *stack);
36int clod_execution_main(const struct stack_header *stack);
37
38#if CLOD_HAVE_X86_64
39
40struct stack_header {
41 void *stack;
42 size_t stack_size;
43 int *errno_location;
44 int error_number;
45 clod_process_main *main;
46 uint64_t stack_guard;
47 struct clod_process_args *args;
48};
49
50static_assert(offsetof(struct stack_header, stack_guard) == 0x28);
51static_assert(offsetof(struct stack_header, errno_location) == 0x10);
52
53#pragma GCC diagnostic push
54#if __clang__
55#pragma GCC diagnostic ignored "-Wlanguage-extension-token"
56#endif
57
58long clod_execution_bootstrap(const struct clone_args *args, struct stack_header *stack) {
59 long result;
60 asm volatile(
61 "mov %[args], %%rdi\n"
62 "mov %[args_size], %%rsi\n"
63 "mov %[clone3], %%eax\n"
64 "syscall\n"
65 "cmp $0, %%rax\n"
66 "jne 0f\n"
67
68 // We are the new process.
69 "push %[stack_ptr]\n"
70 "push %[stack_size]\n"
71 "mov %[stack], %%rdi\n"
72 "call *%[main]\n"
73
74 // Save return value for later
75 "mov %%rax, %%rbx\n"
76
77 // Free our own stack from underneath us.
78 "pop %%rsi\n"
79 "pop %%rdi\n"
80 "mov %[munmap], %%eax\n"
81 "syscall\n"
82
83 // Call exit with main return value.
84 "mov %%rbx, %%rdi\n"
85 "mov %[exit], %%eax\n"
86 "syscall\n"
87 "ud2\n"
88
89 "0:\n"
90 "mov %%rax, %[result]\n"
91 :
92 [result] "=r" (result)
93 :
94 [args] "r" ((long)args),
95 [stack] "r" ((long)stack),
96 [stack_ptr] "r" ((long)stack->stack),
97 [stack_size] "r" ((long)stack->stack_size),
98 [main] "r" ((long)&clod_execution_main),
99
100 [args_size] "i" ((long)sizeof(struct clone_args)),
101 [clone3] "i" (SYS_clone3),
102 [munmap] "i" (SYS_munmap),
103 [exit] "i" (SYS_exit)
104 :
105 // More are actually clobbered i.e. rbx, but it doesn't matter since we're never
106 // going to return if we're the child.
107 "rdi", "rsi", "rax", "rcx", "r11", "memory", "cc"
108 );
109 return result;
110}
111#pragma GCC diagnostic pop
112
113#else
114#error "Process bootstrap method not implemented for this architecture"
115#endif
116
117int clod_execution_main(const struct stack_header *stack) {
118 return stack->main(stack->args->arg_count, stack->args->arg_vector);
119}
120
121void *get_stack(size_t *stack_size_out) {
122 pid_t tid = gettid();
123 char buff[256];
124 snprintf(buff, sizeof(buff), "/proc/self/task/%d/maps", tid);
125 FILE *file = fopen(buff, "r");
126 if (!file) file = fopen("/proc/self/maps", "r");
127 if (!file) return nullptr;
128
129 uintptr_t stack_start = 0, stack_end = 0;
130
131 while (fgets(buff, sizeof(buff), file)) {
132 if (strstr(buff, "[stack]")) {
133 char *end;
134 stack_start = strtoul(buff, &end, 16);
135 stack_end = strtoul(end + 1, &end, 16);
136 break;
137 }
138 }
139
140 fclose(file);
141
142 if (stack_start == 0 || stack_end == 0) return nullptr;
143 *stack_size_out = stack_end - stack_start;
144 return (void*)stack_start;
145}
146
147enum clod_process_result clod_process_start_linux(struct clod_process_opts *opts, struct clod_process_common **process_out) {
148 if (!opts->main)
149 return CLOD_PROCESS_INVALID;
150 if (opts->type != CLOD_DAEMON && opts->type != CLOD_THREAD && opts->type != CLOD_THREAD_BACKGROUND)
151 return CLOD_PROCESS_INVALID;
152
153 size_t stack_header_size = sizeof(struct stack_header);
154 size_t stack_args_size = args_size(&(struct clod_process_args){
155 .arg_count = opts->arg_count,
156 .arg_sizes = opts->arg_sizes,
157 .arg_vector = opts->arg_vector
158 });
159
160 size_t stack_header_offset = 0;
161 size_t stack_args_offset = ALIGN(stack_header_offset + stack_header_size, 16);
162
163 size_t stack_offset = stack_args_offset + stack_args_size;
164 size_t stack_size = stack_offset;
165 stack_size += opts->stack_size ? opts->stack_size : DEFAULT_STACK_SIZE;
166 stack_size = ALIGN(stack_size + 4096, 4096);
167
168 char *stack = mmap(
169 nullptr, stack_size,
170 PROT_READ | PROT_WRITE,
171 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
172 -1, 0
173 );
174 if (stack == MAP_FAILED)
175 return CLOD_PROCESS_NO_MEMORY;
176 mprotect(stack, 4096, PROT_NONE);
177
178 char *stack_data = stack + stack_size - stack_offset;
179 auto header = (struct stack_header *)(stack_data + stack_header_offset);
180 auto args = (struct clod_process_args *)(stack_data + stack_args_offset);
181
182 header->main = opts->main;
183 header->errno_location = &header->error_number;
184 header->stack = stack;
185 header->stack_size = stack_size;
186 header->args = args;
187 getrandom(&header->stack_guard, sizeof(header->stack_guard), 0);
188 header->stack_guard &= ~(uint64_t)0xFF;
189 args_copy(args, &(struct clod_process_args){
190 .arg_count = opts->arg_count,
191 .arg_sizes = opts->arg_sizes,
192 .arg_vector = opts->arg_vector
193 });
194
195 struct clone_args clone_args = {0};
196 clone_args.stack = (__u64)stack;
197 clone_args.stack_size = stack_size - stack_offset;
198 clone_args.tls = (__u64)stack_data; // in the guard page.
199
200 switch (opts->type) {
201 case CLOD_THREAD: clone_args.flags =
202 CLONE_VM | CLONE_FILES | CLONE_THREAD | CLONE_SIGHAND | CLONE_FS | CLONE_IO | CLONE_SYSVSEM | CLONE_SETTLS;
203 break;
204 case CLOD_THREAD_BACKGROUND: clone_args.flags =
205 CLONE_VM | CLONE_FILES | CLONE_SIGHAND | CLONE_FS | CLONE_IO | CLONE_SYSVSEM | CLONE_SETTLS;
206 break;
207 case CLOD_DAEMON: clone_args.flags =
208 CLONE_VM | CLONE_FILES | CLONE_CLEAR_SIGHAND | CLONE_SETTLS;
209 break;
210 }
211
212 struct clod_process_linux *process = nullptr;
213 if (process_out) {
214 process = malloc(sizeof(struct clod_process_linux));
215 clone_args.child_tid = (__u64)&process->child_tid;
216 clone_args.flags |= CLONE_CHILD_SETTID;
217 *process_out = &process->common;
218 }
219
220 long res = clod_execution_bootstrap(&clone_args, header);
221 if (res < 0) {
222 if (process) free(process);
223 munmap(stack, stack_size);
224 switch (errno) {
225 case ENOMEM: return CLOD_PROCESS_NO_MEMORY;
226 default: return CLOD_PROCESS_INVALID;
227 }
228 }
229
230 return CLOD_PROCESS_OK;
231}
232
233enum clod_process_result clod_process_join_linux(struct clod_process_common *process) {
234 auto linux_process = (struct clod_process_linux*)process;
235 syscall(SYS_futex_wait, &linux_process->child_tid, 0, nullptr, nullptr, 0);
236 return CLOD_PROCESS_OK;
237}
@ CLOD_THREAD_BACKGROUND
Definition thread.h:44
@ CLOD_THREAD
Definition thread.h:32
@ CLOD_DAEMON
Definition thread.h:63
Sized string helpers.
clod_process_main * main
Process entry point.
Definition thread.h:84
int arg_count
Number of arguments passed to main.
Definition thread.h:87
char ** arg_vector
Definition thread.h:91
size_t * arg_sizes
Definition thread.h:95
size_t stack_size
Definition thread.h:81
enum clod_process_type type
Type of process to create.
Definition thread.h:77
Definition file.c:13