1//------------------------------------------------------------------------------
2// Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st>
3//------------------------------------------------------------------------------
4// This file is part of thread-bites.
5//
6// thread-bites is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// thread-bites is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with thread-bites. If not, see <http://www.gnu.org/licenses/>.
18//------------------------------------------------------------------------------
19
20#include "tb.h"
21#include "tb-private.h"
22
23#include <linux/time.h>
24#include <asm-generic/param.h>
25#include <string.h>
26#include <stdarg.h>
27#include <stdint.h>
28
29//------------------------------------------------------------------------------
30// Print unsigned int to a string
31//------------------------------------------------------------------------------
32static void printNum(uint64_t num, int base)
33{
34 if(base <= 0 || base > 16)
35 return;
36 if(num == 0) {
37 tbwrite(1, "0", 1);
38 return;
39 }
40 uint64_t n = num;
41 char str[32]; str[31] = 0;
42 char *cursor = str+30;
43 char digits[] = "0123456789abcdef";
44 while(n && cursor != str) {
45 int rem = n % base;
46 *cursor = digits[rem];
47 n /= base;
48 --cursor;
49 }
50 ++cursor;
51 tbwrite(1, cursor, 31-(cursor-str));
52}
53
54//------------------------------------------------------------------------------
55// Print signed int to a string
56//------------------------------------------------------------------------------
57static void printNumS(int64_t num)
58{
59 if(num == 0) {
60 tbwrite(1, "0", 1);
61 return;
62 }
63 uint64_t n = num;
64 char str[32]; str[31] = 0;
65 char *cursor = str+30;
66 char digits[] = "0123456789";
67 while(n && cursor != str) {
68 int rem = n % 10;
69 *cursor = digits[rem];
70 n /= 10;
71 --cursor;
72 }
73 ++cursor;
74 tbwrite(1, cursor, 31-(cursor-str));
75}
76
77//------------------------------------------------------------------------------
78// Print something to stdout
79//------------------------------------------------------------------------------
80static int print_lock;
81void tbprint(const char *format, ...)
82{
83 tb_futex_lock(&print_lock);
84 va_list ap;
85 int length = 0;
86 int sz = 0;
87 int base = 0;
88 int sgn = 0;
89 const char *cursor = format;
90 const char *start = format;
91
92 va_start(ap, format);
93 while(*cursor) {
94 if(*cursor == '%') {
95 tbwrite(1, start, length);
96 ++cursor;
97 if(*cursor == 0)
98 break;
99
100 if(*cursor == 's') {
101 const char *str = va_arg(ap, const char*);
102 tbwrite(1, str, strlen(str));
103 }
104
105 else {
106 while(*cursor == 'l') {
107 ++sz;
108 ++cursor;
109 }
110 if(sz > 2) sz = 2;
111
112 if(*cursor == 'x')
113 base = 16;
114 else if(*cursor == 'u')
115 base = 10;
116 else if(*cursor == 'o')
117 base = 8;
118 else if(*cursor == 'd')
119 sgn = 1;
120
121 if(!sgn) {
122 uint64_t num;
123 if(sz == 0) num = va_arg(ap, unsigned);
124 else if(sz == 1) num = va_arg(ap, unsigned long);
125 else num = va_arg(ap, unsigned long long);
126 printNum(num, base);
127 }
128 else {
129 int64_t num;
130 if(sz == 0) num = va_arg(ap, int);
131 else if(sz == 1) num = va_arg(ap, long);
132 else num = va_arg(ap, long long);
133 printNumS(num);
134 }
135 sz = 0; base = 0; sgn = 0;
136 }
137 ++cursor;
138 start = cursor;
139 length = 0;
140 continue;
141 }
142 ++length;
143 ++cursor;
144 }
145 if(length)
146 tbwrite(1, start, length);
147 va_end (ap);
148 tb_futex_unlock(&print_lock);
149}
150
151//------------------------------------------------------------------------------
152// Write
153//------------------------------------------------------------------------------
154int tbwrite(int fd, const char *buffer, unsigned long len)
155{
156 return SYSCALL3(__NR_write, fd, buffer, len);
157}
158
159//------------------------------------------------------------------------------
160// Sleep
161//------------------------------------------------------------------------------
162void tbsleep(int secs)
163{
164 struct timespec ts, rem;
165 ts.tv_sec = secs; ts.tv_nsec = 0;
166 rem.tv_sec = 0; rem.tv_nsec = 0;
167 while(SYSCALL2(__NR_nanosleep, &ts, &rem) == -EINTR)
168 {
169 ts.tv_sec = rem.tv_sec;
170 ts.tv_nsec = rem.tv_nsec;
171 }
172}
173
174//------------------------------------------------------------------------------
175// Time
176//------------------------------------------------------------------------------
177uint64_t tbtime()
178{
179 return SYSCALL1(__NR_time, 0);
180}
181
182//------------------------------------------------------------------------------
183// Random
184//------------------------------------------------------------------------------
185uint32_t tbrandom(uint32_t *seed)
186{
187 *seed = 1103515245 * (*seed) + 12345;
188 return *seed;
189}
190
191//------------------------------------------------------------------------------
192// Mmap
193//------------------------------------------------------------------------------
194void *tbmmap(void *addr, unsigned long length, int prot, int flags, int fd,
195 unsigned long offset)
196{
197 return (void *)SYSCALL6(__NR_mmap, addr, length, prot, flags, fd, offset);
198}
199
200//------------------------------------------------------------------------------
201// Munmap
202//------------------------------------------------------------------------------
203int tbmunmap(void *addr, unsigned long length)
204{
205 return SYSCALL2(__NR_munmap, addr, length);
206}
207
208//------------------------------------------------------------------------------
209// Brk
210//------------------------------------------------------------------------------
211void *tbbrk(void *addr)
212{
213 return (void *)SYSCALL1(__NR_brk, addr);
214}
215
216//------------------------------------------------------------------------------
217// Malloc helper structs
218//------------------------------------------------------------------------------
219typedef struct memchunk
220{
221 struct memchunk *next;
222 uint64_t size;
223} memchunk_t;
224
225static memchunk_t head;
226static void *heap_limit;
227#define MEMCHUNK_USED 0x4000000000000000
228
229//------------------------------------------------------------------------------
230// Malloc
231//------------------------------------------------------------------------------
232static int memory_lock;
233void *malloc(size_t size)
234{
235 tb_futex_lock(&memory_lock);
236
237 //----------------------------------------------------------------------------
238 // Allocating anything less than 16 bytes is kind of pointless, the
239 // book-keeping overhead is too big. We will also align to 8 bytes.
240 //----------------------------------------------------------------------------
241 size_t alloc_size = (((size-1)>>3)<<3)+8;
242 if(alloc_size < 16)
243 alloc_size = 16;
244
245 //----------------------------------------------------------------------------
246 // Try to find a suitable chunk that is unused
247 //----------------------------------------------------------------------------
248 memchunk_t *cursor = &head;
249 memchunk_t *chunk = 0;
250 while(cursor->next) {
251 chunk = cursor->next;
252 if(!(chunk->size & MEMCHUNK_USED) && chunk->size >= alloc_size)
253 break;
254 cursor = cursor->next;
255 }
256
257 //----------------------------------------------------------------------------
258 // No chunk found, ask Linux for more memory
259 //----------------------------------------------------------------------------
260 if (!cursor->next) {
261 //--------------------------------------------------------------------------
262 // We have been called for the first time and don't know the heap limit yet.
263 // On Linux, the brk syscall will return the previous heap limit on error.
264 // We try to set the heap limit at 0, which is obviously wrong, so that we
265 // could figure out what the current heap limit is.
266 //--------------------------------------------------------------------------
267 if(!heap_limit)
268 heap_limit = tbbrk(0);
269
270 //--------------------------------------------------------------------------
271 // We will allocate at least one page at a time
272 //--------------------------------------------------------------------------
273 size_t chunk_size = (size+sizeof(memchunk_t)-1)/EXEC_PAGESIZE;
274 chunk_size *= EXEC_PAGESIZE;
275 chunk_size += EXEC_PAGESIZE;
276
277 void *new_heap_limit = tbbrk((char*)heap_limit + chunk_size);
278 uint64_t new_chunk_size = (char *)new_heap_limit - (char *)heap_limit;
279
280 if(heap_limit == new_heap_limit)
281 {
282 tb_futex_unlock(&memory_lock);
283 return 0;
284 }
285
286 cursor->next = heap_limit;
287 chunk = cursor->next;
288 chunk->size = new_chunk_size-sizeof(memchunk_t);
289 chunk->next = 0;
290 heap_limit = new_heap_limit;
291 }
292
293 //----------------------------------------------------------------------------
294 // Split the chunk if it's big enough to contain one more header and at least
295 // 16 more bytes
296 //----------------------------------------------------------------------------
297 if(chunk->size > alloc_size + sizeof(memchunk_t) + 16)
298 {
299 memchunk_t *new_chunk = (memchunk_t *)((char *)chunk+sizeof(memchunk_t)+alloc_size);
300 new_chunk->size = chunk->size-alloc_size-sizeof(memchunk_t);
301 new_chunk->next = chunk->next;
302 chunk->next = new_chunk;
303 chunk->size = alloc_size;
304 }
305
306 //----------------------------------------------------------------------------
307 // Mark the chunk as used and return the memory
308 //----------------------------------------------------------------------------
309 chunk->size |= MEMCHUNK_USED;
310 tb_futex_unlock(&memory_lock);
311 return (char*)chunk+sizeof(memchunk_t);
312}
313
314//------------------------------------------------------------------------------
315// Free
316//------------------------------------------------------------------------------
317void free(void *ptr)
318{
319 if(!ptr)
320 return;
321
322 tb_futex_lock(&memory_lock);
323 memchunk_t *chunk = (memchunk_t *)((char *)ptr-sizeof(memchunk_t));
324 chunk->size &= ~MEMCHUNK_USED;
325 tb_futex_unlock(&memory_lock);
326}
327
328//------------------------------------------------------------------------------
329// Calloc
330//------------------------------------------------------------------------------
331void *calloc(size_t nmemb, size_t size)
332{
333 size_t alloc_size = nmemb*size;
334 void *ptr = malloc(alloc_size);
335 char *cptr = ptr;
336 if(!ptr)
337 return ptr;
338 for(int i = 0; i < alloc_size; ++i, *cptr++ = 0);
339 return ptr;
340}
341
342//------------------------------------------------------------------------------
343// Realloc
344//------------------------------------------------------------------------------
345void *realloc(void *ptr, size_t size)
346{
347 memchunk_t *chunk = (memchunk_t *)((char *)ptr-sizeof(memchunk_t));
348 void *new_ptr = malloc(size);
349 char *s = ptr;
350 char *d = new_ptr;
351 size_t min = chunk->size > size ? size : chunk->size;
352
353 for(int i = 0; i < min; ++i, *d++ = *s++);
354 free(ptr);
355 return new_ptr;
356}
357
358//------------------------------------------------------------------------------
359// Heap state for diagnostics
360//------------------------------------------------------------------------------
361void tb_heap_state(uint64_t *total, uint64_t *allocated)
362{
363 memchunk_t *cursor = &head;
364 memchunk_t *chunk = 0;
365
366 *total = 0;
367 *allocated = 0;
368
369 while(cursor->next) {
370 chunk = cursor->next;
371 if(chunk->size & MEMCHUNK_USED)
372 ++(*allocated);
373 ++(*total);
374 cursor = cursor->next;
375 }
376}
377
378//------------------------------------------------------------------------------
379// Add an element
380//------------------------------------------------------------------------------
381int list_add_elem(list_t *list, void *element, int front)
382{
383 list_t *node = malloc(sizeof(list_t));
384 if(!node)
385 return -ENOMEM;
386 node->element = element;
387 list_add(list, node, front);
388 return 0;
389};
390
391//------------------------------------------------------------------------------
392// Add a node
393//------------------------------------------------------------------------------
394void list_add(list_t *list, list_t *node, int front)
395{
396 list_t *cursor = list;
397 if(!front)
398 for(; cursor->next; cursor = cursor->next);
399 node->next = cursor->next;
400 cursor->next = node;
401 node->prev = cursor;
402 if(node->next)
403 node->next->prev = node;
404}
405
406//------------------------------------------------------------------------------
407// Add a node here
408//------------------------------------------------------------------------------
409void list_add_here(list_t *list, list_t *node, int (*here)(void*, void*))
410{
411 list_t *cursor = list;
412 for(; cursor->next && !(*here)(node->element, cursor->next->element);
413 cursor = cursor->next);
414 node->next = cursor->next;
415 cursor->next = node;
416 node->prev = cursor;
417 if(node->next)
418 node->next->prev = node;
419}
420
421//------------------------------------------------------------------------------
422// Remove a node
423//------------------------------------------------------------------------------
424void list_rm(list_t *node)
425{
426 node->prev->next = node->next;
427 if(node->next)
428 node->next->prev = node->prev;
429}
430
431//------------------------------------------------------------------------------
432// Find an element
433//------------------------------------------------------------------------------
434list_t *list_find_elem(list_t *list, void *element)
435{
436 list_t *cursor = list;
437 for(; cursor->next && cursor->next->element != element;
438 cursor = cursor->next);
439 return cursor->next;
440}
441
442//------------------------------------------------------------------------------
443// Find an element using comparator fucntion
444//------------------------------------------------------------------------------
445list_t *list_find_elem_func(list_t *list, void *element,
446 int (*func)(void*, void*))
447{
448 list_t *cursor = list;
449 for(; cursor->next && !(*func)(element, cursor->next->element);
450 cursor = cursor->next);
451 return cursor->next;
452}
453
454//------------------------------------------------------------------------------
455// Invoke a function for each element
456//------------------------------------------------------------------------------
457void list_for_each_elem(list_t *list, void (*func)(void *))
458{
459 for(list_t *cursor = list->next; cursor; cursor = cursor->next)
460 func(cursor->element);
461}
462
463//------------------------------------------------------------------------------
464// Delete all the nodes
465//------------------------------------------------------------------------------
466void list_clear(list_t *list)
467{
468 while(list->next) {
469 list_t *node = list->next;
470 list->next = list->next->next;
471 free(node);
472 }
473}
474
475//------------------------------------------------------------------------------
476// Translate errno to a message
477//------------------------------------------------------------------------------
478struct errinfo
479{
480 int errno;
481 const char *msg;
482};
483
484static struct errinfo errors[] = {
485 {E2BIG, "Argument list too long"},
486 {EACCES, "Permission denied"},
487 {EADDRINUSE, "Address already in use"},
488 {EADDRNOTAVAIL, "Address not available"},
489 {EAFNOSUPPORT, "Address family not supported"},
490 {EAGAIN, "Resource temporarily unavailable"},
491 {EALREADY, "Connection already in progress"},
492 {EBADE, "Invalid exchange"},
493 {EBADF, "Bad file descriptor"},
494 {EBADFD, "File descriptor in bad state"},
495 {EBADMSG, "Bad message"},
496 {EBADR, "Invalid request descriptor"},
497 {EBADRQC, "Invalid request code"},
498 {EBADSLT, "Invalid slot"},
499 {EBUSY, "Device or resource busy"},
500 {ECANCELED, "Operation canceled"},
501 {ECHILD, "No child processes"},
502 {ECHRNG, "Channel number out of range"},
503 {ECOMM, "Communication error on send"},
504 {ECONNABORTED, "Connection aborted"},
505 {ECONNREFUSED, "Connection refused"},
506 {ECONNRESET, "Connection reset"},
507 {EDEADLK, "Resource deadlock avoided"},
508 {EDEADLOCK, "Synonym for EDEADLK"},
509 {EDESTADDRREQ, "Destination address required"},
510 {EDOM, "Mathematics argument out of domain of function"},
511 {EDQUOT, "Disk quota exceeded"},
512 {EEXIST, "File exists"},
513 {EFAULT, "Bad address"},
514 {EFBIG, "File too large"},
515 {EHOSTDOWN, "Host is down"},
516 {EHOSTUNREACH, "Host is unreachable"},
517 {EIDRM, "Identifier removed"},
518 {EILSEQ, "Illegal byte sequence"},
519 {EINPROGRESS, "Operation in progress"},
520 {EINTR, "Interrupted function call"},
521 {EINVAL, "Invalid argument"},
522 {EIO, "Input/output error"},
523 {EISCONN, "Socket is connected"},
524 {EISDIR, "Is a directory"},
525 {EISNAM, "Is a named type file"},
526 {EKEYEXPIRED, "Key has expired"},
527 {EKEYREJECTED, "Key was rejected by service"},
528 {EKEYREVOKED, "Key has been revoked"},
529 {EL2HLT, "Level 2 halted"},
530 {EL2NSYNC, "Level 2 not synchronized"},
531 {EL3HLT, "Level 3 halted"},
532 {EL3RST, "Level 3 halted"},
533 {ELIBACC, "Cannot access a needed shared library"},
534 {ELIBBAD, "Accessing a corrupted shared library"},
535 {ELIBMAX, "Attempting to link in too many shared libraries"},
536 {ELIBSCN, "lib section in a.out corrupted"},
537 {ELIBEXEC, "Cannot exec a shared library directly"},
538 {ELOOP, "Too many levels of symbolic links"},
539 {EMEDIUMTYPE, "Wrong medium type"},
540 {EMFILE, "Too many open files"},
541 {EMLINK, "Too many links"},
542 {EMSGSIZE, "Message too long"},
543 {EMULTIHOP, "Multihop attempted"},
544 {ENAMETOOLONG, "Filename too long"},
545 {ENETDOWN, "Network is down"},
546 {ENETRESET, "Connection aborted by network"},
547 {ENETUNREACH, "Network unreachable"},
548 {ENFILE, "Too many open files in system"},
549 {ENOBUFS, "No buffer space available"},
550 {ENODATA, "No message is available on the STREAM head read queue"},
551 {ENODEV, "No such device"},
552 {ENOENT, "No such file or directory"},
553 {ENOEXEC, "Exec format error"},
554 {ENOKEY, "Required key not available"},
555 {ENOLCK, "No locks available"},
556 {ENOLINK, "Link has been severed"},
557 {ENOMEDIUM, "No medium found"},
558 {ENOMEM, "Not enough space"},
559 {ENOMSG, "No message of the desired type"},
560 {ENONET, "Machine is not on the network"},
561 {ENOPKG, "Package not installed"},
562 {ENOPROTOOPT, "Protocol not available"},
563 {ENOSPC, "No space left on device"},
564 {ENOSR, "No STREAM resources"},
565 {ENOSTR, "Not a STREAM"},
566 {ENOSYS, "Function not implemented"},
567 {ENOTBLK, "Block device required"},
568 {ENOTCONN, "The socket is not connected"},
569 {ENOTDIR, "Not a directory"},
570 {ENOTEMPTY, "Directory not empty"},
571 {ENOTSOCK, "Not a socket"},
572 {ENOTTY, "Inappropriate I/O control operation"},
573 {ENOTUNIQ, "Name not unique on network"},
574 {ENXIO, "No such device or address"},
575 {EOPNOTSUPP, "Operation not supported on socket"},
576 {EOVERFLOW, "Value too large to be stored in data type"},
577 {EPERM, "Operation not permitted"},
578 {EPFNOSUPPORT, "Protocol family not supported"},
579 {EPIPE, "Broken pipe"},
580 {EPROTO, "Protocol error"},
581 {EPROTONOSUPPORT, "Protocol not supported"},
582 {EPROTOTYPE, "Protocol wrong type for socket"},
583 {ERANGE, "Result too large"},
584 {EREMCHG, "Remote address changed"},
585 {EREMOTE, "Object is remote"},
586 {EREMOTEIO, "Remote I/O error"},
587 {ERESTART, "Interrupted system call should be restarted"},
588 {EROFS, "Read-only filesystem"},
589 {ESHUTDOWN, "Cannot send after transport endpoint shutdown"},
590 {ESPIPE, "Invalid seek"},
591 {ESOCKTNOSUPPORT, "Socket type not supported"},
592 {ESRCH, "No such process"},
593 {ESTALE, "Stale file handle"},
594 {ESTRPIPE, "Streams pipe error"},
595 {ETIME, "Timer expired"},
596 {ETIMEDOUT, "Connection timed out"},
597 {ETXTBSY, "Text file busy"},
598 {EUCLEAN, "Structure needs cleaning"},
599 {EUNATCH, "Protocol driver not attached"},
600 {EUSERS, "Too many users"},
601 {EWOULDBLOCK, "Operation would block"},
602 {EXDEV, "Improper link"},
603 {EXFULL, "Exchange full"},
604 {0, "Unknown"}};
605
606const char *tbstrerror(int errno)
607{
608 int i = 0;
609 for(; errors[i].errno && errors[i].errno != errno; ++i);
610 return errors[i].msg;
611}
612
613//------------------------------------------------------------------------------
614// Sigaction
615//------------------------------------------------------------------------------
616void __restore_rt();
617#define SA_RESTORER 0x04000000
618
619int tbsigaction(int signum, struct sigaction *act, struct sigaction *old)
620{
621 act->sa_flags |= SA_RESTORER;
622 act->sa_restorer = __restore_rt;
623 return SYSCALL4(__NR_rt_sigaction, signum, act, old, sizeof(sigset_t));
624}
625