main
1#include "./notlibc.h"
2
3/******************************************************************************/
4/* COMMON CODE TO ALL PLATFORMS */
5/******************************************************************************/
6
7/*============================== Compiler ====================================*/
8
9/* Compiler-specific debugging/checking system implementations */
10#if (COMPILER == COMPILER_GCC || COMPILER == COMPILER_CLANG) && defined(NOTLIBC_IMPLEMENT)
11 /* This is so that GCC can detect stack smashing via canaries and crash the program when it happens. */
12
13 void PRESERVE_WITH_LTO __stack_chk_fail(void) {
14 assert(0);
15 }
16#endif
17
18/*=============================== Memory =====================================*/
19void *memset( void* ptr, int value, unsigned long count ) {
20 mem_set( ptr, count, value );
21 return ptr;
22}
23
24
25void *memcpy( void *dest, const void *src, unsigned long n ) {
26 char *d = dest;
27 const char *s = src;
28 while(n--)
29 *d++ = *s++;
30 return dest;
31}
32
33/*================================ Algorithms ================================*/
34
35usize algo_binary_search( Status *s, const void *restrict el, usize el_size, const void *restrict data, usize count, CmpFunc *cmp ) {
36 usize ret = 0;
37 END_IF_NOK( *s );
38
39 if( !data ) {
40 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
41 goto end;
42 }
43
44 if( !count ) {
45 *s = S_COMPLETION;
46 return 0;
47 }
48
49 if( !el ) {
50 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_4 );
51 goto end;
52 }
53
54 if( !el_size ) {
55 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_5 );
56 goto end;
57 }
58
59 if( !cmp ) {
60 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_6 );
61 goto end;
62 }
63
64 usize start = 0;
65 usize end = count;
66 usize middle;
67
68 while( start < end ) {
69 middle = start + (end - start)/2;
70 void *middle_el = (u8*)data + middle*el_size;
71
72 s8 eq = cmp( middle_el, el );
73
74 if( eq < 0 ) {
75 start = middle + 1;
76 } else if( eq > 0 ) {
77 end = middle;
78 } else {
79 *s = S_SUCCESS;
80 ret = middle;
81 goto end;
82 }
83 }
84 *s = S_COMPLETION;
85 ret = start;
86
87 end:
88 return ret;
89}
90
91void algo_sort_generic(Status *s, void *const data, usize count, usize el_size, CmpFunc *cmp) {
92 algo_quicksort( s, data, count, el_size, cmp, 16, algo_insertionsort );
93}
94
95void algo_insertionsort(Status *s, void *const data, usize count, usize el_size, CmpFunc *cmp) {
96 END_IF_NOK( *s );
97
98 if( count <= 1 ) {
99 return;
100 }
101
102 if( !data ) {
103 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
104 }
105
106 if( !el_size ) {
107 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_4 );
108 }
109
110 if( !cmp ) {
111 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_5 );
112 }
113
114 for( usize i = 1; i < count; ++i ) {
115 for( usize j = i; j > 0 && cmp( (u8*)data + (j-1)*el_size, (u8*)data + j*el_size ) > 0; j-- ) {
116 swap( (u8*)data + j*el_size, (u8*)data + (j-1)*el_size, el_size );
117 }
118 }
119
120 end:
121 return;
122}
123
124usize _quicksort_partition( Status *s, void *const data, usize count, usize el_size, CmpFunc *cmp ) {
125 if( count < 1 ) {
126 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
127 }
128
129 usize pivot_index = count/2;
130 usize hi = count - 1;
131
132 swap( (u8*)data + pivot_index*el_size, (u8*)data + hi*el_size, el_size);
133
134 /* This two-way partitioning isn't the best, because it can yield unbalanced partitions if there are many duplicates in the input */
135 usize i = 0;
136 for( usize j = 0; j < hi; ++j ) {
137 if( cmp( (u8*)data + j*el_size, (u8*)data + hi*el_size ) < 0 ) {
138 swap( (u8*)data + i*el_size, (u8*)data + j*el_size, el_size);
139 i++;
140 }
141 }
142
143 swap( (u8*)data + hi*el_size, (u8*)data + i*el_size, el_size);
144 return i;
145}
146
147void algo_quicksort(Status *s, void *const data, usize count, usize el_size, CmpFunc *cmp, usize fallback_at_count, SortFunc* fallback_algo) {
148 END_IF_NOK( *s );
149
150 if( count == 0 ) {
151 return;
152 }
153
154 if( !data ) {
155 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
156 }
157
158 if( !el_size ) {
159 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_4 );
160 }
161
162 if( !cmp ) {
163 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_5 );
164 }
165
166 if( count <= fallback_at_count ) {
167 fallback_algo( s, data, count, el_size, cmp );
168 return;
169 }
170
171 usize pivot = _quicksort_partition( s, data, count, el_size, cmp );
172 END_IF_NOK( *s );
173
174 usize left_count = pivot;
175 usize right_count = count - pivot - 1;
176
177 void* right_data = (u8*)data + (pivot + 1)*el_size;
178
179 if (left_count < right_count) {
180 algo_quicksort( s, data, left_count, el_size, cmp, fallback_at_count, fallback_algo );
181 END_IF_NOK(*s);
182 algo_quicksort( s, right_data, right_count, el_size, cmp, fallback_at_count, fallback_algo );
183 } else {
184 algo_quicksort( s, right_data, right_count, el_size, cmp, fallback_at_count, fallback_algo );
185 END_IF_NOK(*s);
186 algo_quicksort( s, data, left_count, el_size, cmp, fallback_at_count, fallback_algo );
187 }
188
189 END_IF_NOK( *s );
190
191end:
192 return;
193}
194
195
196/*================================ Arena =====================================*/
197
198Arena Arena_create( Status *s, usize size ) {
199 Arena ret = {0};
200 END_IF_NOK( *s );
201
202 size = u64_round_up( s, size, sizeof(Page));
203 ret.start = mem_claim( s, size/sizeof(Page) );
204
205 END_IF_NOK( *s );
206 ret.claimed = size;
207
208end:
209 return ret;
210}
211
212void Arena_destroy( Status *s, Arena *a ) {
213 Status substatus = S_SUCCESS;
214
215 /* Object should be destroyed */
216 if( a && a->start && a->claimed ) {
217 mem_relinquish( &substatus, a->start, a->claimed/sizeof(Page) );
218 mem_set( a, sizeof(*a), 0 );
219 }
220
221 if( S_OK(*s) && S_NOK( substatus ) ) {
222 *s = substatus;
223 }
224}
225
226void Arena_align( Status *s, Arena *a, usize alignment ) {
227 END_IF_NOK( *s );
228
229 u64 take_amount = u64_round_up( s, a->taken, alignment ) - a->taken;
230
231 if( take_amount ) {
232 (void)Arena_take( s, a, take_amount );
233 }
234
235end:
236 return;
237}
238
239void* Arena_take( Status *s, Arena *a, usize amount ) {
240 void *ret = 0;
241 END_IF_NOK( *s );
242
243 if( amount == 0 ) {
244 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
245 return 0;
246 }
247
248 if( a->claimed - a->taken < amount ) { /* no overflow risk this way */
249 ERRORING_FAIL( S_FAILURE | ERR_OUT_OF_BOUND );
250 return 0;
251 }
252
253 u64 new_taken = a->taken + amount;
254
255 if( new_taken > a->occupied ) {
256 u64 occupy_amount = u64_round_up( s, new_taken - a->occupied, sizeof(Page) );
257 mem_occupy( s, a->start + a->occupied/sizeof(Page), occupy_amount/0x1000 );
258
259 END_IF_NOK( *s );
260 a->occupied += occupy_amount;
261 }
262
263 ret = ((u8*)a->start + a->taken);
264 a->taken = new_taken;
265end:
266 return ret;
267}
268
269void Arena_give( Status *s, Arena *a, usize amount ) {
270 END_IF_NOK( *s );
271
272 if( amount > a->taken ) {
273 DEBUG(breakpoint());
274 *s = S_FAILURE | ERR_OUT_OF_BOUND;
275 return;
276 }
277
278 a->taken -= amount;
279end:
280 return;
281}
282
283
284
285/*================================ Heap ======================================*/
286
287Heap Heap_create( Status *s, usize size ) {
288 Heap ret = {0};
289 END_IF_NOK( *s );
290
291 if( size == 0 ) {
292 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
293 /* notice no separate return, cause ret is alread 0 */
294 } else {
295 ret.arena = Arena_create( s, size );
296 }
297
298end:
299 return ret;
300}
301
302void Heap_destroy( Status *s, Heap *h ) {
303 if( h == 0 ) {
304 return;
305 }
306
307 Status local_status = S_SUCCESS;
308 Arena_destroy( &local_status, &h->arena );
309
310 END_IF_NOK( *s );
311 mem_set( h, sizeof(*h), 0 );
312
313end:
314 if( S_OK(*s) && S_NOK( local_status ) ) {
315 *s = local_status;
316 }
317 return;
318}
319
320#if BUILD_TYPE == BUILD_DEBUG
321 static inline Status _Heap_find_block( Heap *h, HeapEntry *e, HeapEntry **out_prev ) {
322 u8 ptr_correct = 0;
323 HeapEntry *prev = &h->blocklist;
324
325 for( HeapEntry *i = h->blocklist.next; i != 0; i = i->next ) {
326 if( e == i ) {
327 ptr_correct = 1;
328 break;
329 }
330 prev = i;
331 }
332
333 /* The pointer is not pointing to a previously allocated block if this fails or perhaps it is a double-free. */
334 assert( ptr_correct );
335 if( !ptr_correct ) {
336 return S_FAILURE;
337 }
338
339 *out_prev = prev;
340 return S_SUCCESS;
341 }
342
343 static inline Status _Heap_check_entry( HeapEntry *e ) {
344 u8 ok = 1;
345
346 assert( e->checksum == (e->size ^ e->allowed_size ^ (usize)e->next ^ HEAP_XOR_KEY) || (ok = 0) );
347 assert( e->header_start_guard == HEAP_HEADER_GUARD_NUMBER || (ok = 0) );
348 assert( e->header_end_guard == HEAP_HEADER_GUARD_NUMBER || (ok = 0) );
349 assert( e->allowed_size >= e->size || (ok = 0) );
350
351 u8* data = (u8*)(&e->header_end_guard + 1);
352 assert( *(usize*)(data + e->allowed_size) = HEAP_OVERRUN_GUARD_NUMBER || (ok = 0) );
353
354 if( !ok ) {
355 return S_FAILURE;
356 } else {
357 return S_SUCCESS;
358 }
359 }
360
361 static inline void _Heap_unlink_entry( HeapEntry *prev, HeapEntry *e ) {
362 prev->next = e->next;
363 prev->checksum = prev->size ^ prev->allowed_size ^ (usize)prev->next ^ HEAP_XOR_KEY;
364 }
365
366 static inline void _Heap_link_free_entry( Heap *h, HeapEntry *e ) {
367 e->next = h->freelist.next;
368 h->freelist.next = e;
369 e->checksum = e->size ^ e->allowed_size ^ (usize)e->next ^ HEAP_XOR_KEY;
370 }
371
372
373 static inline void _Heap_link_block( Heap *h, HeapEntry *e ) {
374 e->next = h->blocklist.next;
375 h->blocklist.next = e;
376 e->checksum = e->size ^ e->allowed_size ^ (usize)e->next ^ HEAP_XOR_KEY;
377 }
378
379 usize Heap_get_asize( Heap *h, const void *ptr ) { /* asize = allocation size */
380 HeapEntry *e = (HeapEntry*)( (u8*)ptr - sizeof(HeapEntry) );
381 HeapEntry *prev = 0;
382 _Heap_check_entry( e );
383 _Heap_find_block( h, e, &prev );
384 return e->size;
385 }
386
387 void* Heap_alloc( Status *s, Heap *h, usize amount ) {
388 if( h == 0 ) {
389 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
390 return 0;
391 }
392
393 if( amount == 0 ) {
394 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
395 return 0;
396 }
397
398 void *ret = 0;
399
400 amount = usize_round_up( s, amount, sizeof(usize) );
401 usize total_amount = amount + sizeof(HeapEntry) + sizeof(usize); /* usize for end guard */
402
403 HeapEntry *new = 0;
404 HeapEntry *prev = &h->freelist;
405
406 usize max_size = usize_mul(s, total_amount, 4 );
407 END_IF_NOK( *s );
408
409 for( HeapEntry *i = h->freelist.next; i != 0; i = i->next ) {
410
411 u8* data = (u8*)(&i->header_end_guard + 1);
412 /* If any of these fails, then your freelist has been corrupted somehow - it should not be iterated */
413 _Heap_check_entry(i);
414
415 if( i->allowed_size >= amount && i->allowed_size <= max_size ) {
416 new = i;
417 for( usize i = 0; i < new->allowed_size; ++i ) {
418 assert( data[i] == MAGIC_FREED_MEM );
419 }
420 _Heap_unlink_entry( prev, new );
421 new->next = 0;
422 break;
423 }
424 prev = i;
425 }
426
427 if( !new ) {
428 new = Arena_take( s, &h->arena, total_amount );
429 END_IF_NOK( *s );
430 new->allowed_size = amount;
431 }
432
433 new->size = amount;
434
435 u8 *data = (u8*)(&new->header_end_guard + 1);
436 mem_set( data, new->size, MAGIC_UNINITIALIZED_MEM );
437 mem_set( data + new->size, new->allowed_size - new->size, MAGIC_POISONED_MEM );
438
439 *(usize*)(data + new->allowed_size) = HEAP_OVERRUN_GUARD_NUMBER;
440
441 new->size = amount;
442 new->header_start_guard = HEAP_HEADER_GUARD_NUMBER;
443 new->header_end_guard = HEAP_HEADER_GUARD_NUMBER;
444 _Heap_link_block( h, new );
445
446 ret = data;
447 end:
448 return ret;
449 }
450
451 void Heap_free( Status *s, Heap *h, void *ptr ) {
452
453 if( ptr == 0 ) {
454 return;
455 } else {
456
457 if( h == 0 ) {
458 if( S_OK( *s ) ) *s = S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2;
459 DEBUG(breakpoint());
460 return;
461 }
462
463 HeapEntry *e = (HeapEntry*)( (u8*)ptr - sizeof(HeapEntry) );
464
465 HeapEntry *prev = 0;
466
467 assert(S_OK( _Heap_find_block( h, e, &prev ) ));
468 assert(S_OK( _Heap_check_entry( e ) ));
469
470 _Heap_unlink_entry( prev, e );
471 _Heap_link_free_entry( h, e );
472
473 u8* data = (u8*)(&e->header_end_guard + 1);
474 mem_set( data, e->allowed_size, MAGIC_FREED_MEM );
475 *(usize*)(data + e->allowed_size) = HEAP_OVERRUN_GUARD_NUMBER;
476
477 }
478 }
479
480#elif BUILD_TYPE == BUILD_RELEASE
481
482 usize Heap_get_asize( Heap *h, const void *ptr ) {
483 (void)h;
484 return *((usize*)ptr - 1);
485 }
486
487 void* Heap_alloc( Status *s, Heap *h, usize amount ) {
488
489 if( amount == 0 ) {
490 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
491 return 0;
492 }
493
494 usize required_amount = usize_max(amount, sizeof(void*)); /* A pointer must fit into data, otherwise it can not be linked into the freelist when freed. */
495 required_amount = usize_add( s, required_amount, sizeof(HeapEntry) );
496 required_amount = usize_round_up( s, required_amount, sizeof(usize) ); /* word-align */
497
498
499 void *ret = 0;
500 HeapEntry *new = 0;
501 HeapEntry *prev = &h->freelist;
502 usize max_size = usize_mul( s, required_amount, 4 );
503
504 END_IF_NOK( *s )
505
506 for( HeapEntry *i = h->freelist_data; i != 0; i = *(HeapEntry**)(&i->size + 1) ) {
507 usize cur_entry_size = i->size+sizeof(HeapEntry);
508 if( cur_entry_size + sizeof(HeapEntry) >= required_amount && cur_entry_size + sizeof(HeapEntry) <= max_size ) {
509 new = i;
510 *(HeapEntry**)(&prev->size + 1) = *(HeapEntry**)(&new->size + 1); /* unlink entry */
511 break;
512 }
513 prev = i;
514 }
515
516 /* New allocation */
517 if( !new ) {
518 new = Arena_take( s, &h->arena, required_amount);
519
520 END_IF_NOK( *s )
521
522 new->size = required_amount - sizeof(HeapEntry);
523 } /* otherwise we leave original size */
524 ret = (u8*)(&new->size + 1);
525 end:
526 return ret;
527 }
528
529 void Heap_free( Status *s, Heap *h, void *ptr ) {
530 if( ptr == 0 ) {
531 return;
532 } else {
533 if( h == 0 ) {
534 if( S_OK( *s ) ) *s = S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2;
535 return;
536 }
537
538 HeapEntry *e = (HeapEntry*)( (u8*)ptr - sizeof(HeapEntry) );
539
540 /* Link into freelist */
541 *((HeapEntry**)(&e->size + 1)) = h->freelist_data;
542 h->freelist_data = e;
543 }
544 }
545
546#endif
547
548/*=============================== Arrays =====================================*/
549
550AnyArr* AnyArr_create( Status *s, usize count, Heap *h, usize elem_size ) {
551 AnyArr *ret = 0;
552 END_IF_NOK( *s );
553
554 usize alloc_count = count;
555 DEBUG( alloc_count++ ); /* additional poisoned element in debug */
556
557 usize alloc_size = sizeof(AnyArr) + alloc_count*elem_size;
558
559 ret = Heap_alloc( s, h, alloc_size );
560
561 END_IF_NOK( *s );
562
563 ret->len = 0;
564 DEBUG(
565 ret->heap = h;
566 ret->sorted = 0;
567 ret->sortfunc = 0;
568 _AnyArr_update_checksum( ret )
569 );
570
571end:
572 return ret;
573}
574
575void AnyArr_destroy( Status *s, AnyArr** arr, Heap *h ) {
576
577 DEBUG(
578 assert( h == (*arr)->heap );
579 _AnyArr_validate( *arr );
580 )
581
582 Heap_free( s, h, *arr );
583}
584
585#if BUILD_TYPE == BUILD_DEBUG
586 void _AnyArr_update_checksum( AnyArr *arr ) {
587 usize heapblock_size = Heap_get_asize( arr->heap, arr );
588 arr->checksum = heapblock_size ^ (usize)arr->heap ^ arr->len ^ ARRAY_XOR_KEY;
589 }
590
591 void _AnyArr_validate( const AnyArr *arr ) {
592 usize heapblock_size = Heap_get_asize( arr->heap, arr );
593 usize checksum = heapblock_size ^ (usize)arr->heap ^ arr->len ^ ARRAY_XOR_KEY;
594 assert( arr->checksum == checksum );
595 }
596#endif
597
598AnyArr* AnyArr_inflate( Status *s, AnyArr *arr, usize additional_count, Heap *h, usize elem_size ) {
599 AnyArr *ret = arr;
600
601 END_IF_NOK( *s );
602
603 DEBUG(
604 assert( h == arr->heap );
605 _AnyArr_validate( arr );
606 )
607
608 if( additional_count == 0 ) {
609 *s = S_COMPLETION;
610 return arr;
611 }
612
613 usize newsize = (arr->len + additional_count)*elem_size + sizeof(AnyArr);
614 DEBUG( newsize += elem_size; )
615
616 ret = Heap_alloc( s, h, newsize );
617 END_IF_NOK( *s );
618
619 usize total_size = arr->len*elem_size + sizeof(AnyArr);
620 DEBUG( total_size += elem_size; )
621
622 mem_copy( arr, ret, total_size );
623
624 DEBUG(
625 _AnyArr_update_checksum( ret );
626 mem_set( (u8*)(ret+1) + (ret->len)*elem_size, elem_size, MAGIC_POISONED_MEM );
627 );
628
629 /* Don't want to free the original if we couldn't grow it, hence it's
630 * jumped over if the heap alloc fails - otherwise we risk double-free
631 * when it finally propagates down the chain to the destructor. */
632 Heap_free( s, h, arr );
633
634end:
635 return ret;
636}
637
638void AnyArr_truncate( Status *s, AnyArr *arr, usize cutlen, usize elem_size ) {
639 END_IF_NOK( *s );
640
641 DEBUG(
642 _AnyArr_validate( arr );
643 usize old_len = arr->len;
644 )
645 RELEASE(
646 (void)elem_size;
647 )
648
649 if( cutlen > arr->len ) {
650 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_2 );
651 return;
652 }
653
654 arr->len -= cutlen;
655
656 DEBUG(
657 u8 *data = (u8*)(arr + 1);
658 u8 *raticide = data + arr->len*elem_size;
659 mem_set( raticide, (old_len - arr->len)*elem_size, MAGIC_POISONED_MEM );
660
661 _AnyArr_update_checksum( arr );
662 )
663
664end:
665 return;
666}
667
668AnyArr* AnyArr_deflate( Status *s, AnyArr *arr, Heap *h, usize elem_size ) {
669 AnyArr *ret = arr;
670 END_IF_NOK( *s );
671
672 DEBUG(
673 assert( h == arr->heap );
674 _AnyArr_validate( arr );
675 )
676
677 usize total_size = arr->len*elem_size + sizeof(AnyArr);
678 DEBUG( total_size += elem_size; )
679
680 ret = voidp_by_status( s, Heap_alloc( s, h, total_size ), arr ); /* standard C guarantees that function arguments are evaluated before the function call, so s's state should propagate correctly */
681 END_IF_NOK( *s );
682
683 mem_copy( arr, ret, total_size );
684 Heap_free( s, h, arr );
685
686 DEBUG( _AnyArr_update_checksum( ret ) );
687
688end:
689 return ret;
690}
691
692static inline AnyArr* _AnyArr_ensure_space_for_one_more_element( Status *s, AnyArr *arr, Heap *h, usize elem_size ) {
693 AnyArr *ret = arr;
694 END_IF_NOK( *s );
695
696 DEBUG(
697 assert( h == arr->heap );
698 _AnyArr_validate( arr );
699 )
700
701 usize asize = Heap_get_asize(h, arr);
702 usize maxcount = (asize - sizeof(AnyArr))/elem_size;
703
704 DEBUG(
705 maxcount--; /* Additional poisoned element */
706 );
707
708 if( arr->len == maxcount ) {
709 ret = AnyArr_inflate( s, arr, arr->len, h, elem_size );
710 }
711
712 END_IF_NOK( *s );
713
714end:
715 return ret;
716}
717
718AnyArr* AnyArr_push( Status *s, AnyArr *arr, void* el, Heap *h, usize elem_size ) {
719 AnyArr *ret = _AnyArr_ensure_space_for_one_more_element( s, arr, h, elem_size);
720 END_IF_NOK( *s );
721
722 u8 *data = (u8*)(ret + 1);
723 void *new_elem = data + ret->len*elem_size;
724
725 mem_copy( el, new_elem, elem_size );
726 ret->len++;
727
728 DEBUG(
729 _AnyArr_update_checksum( ret );
730 ret->sorted = 0;
731 ret->sortfunc = 0;
732 );
733
734 DEBUG( arr->sorted = 0; )
735
736end:
737 return ret;
738}
739
740void AnyArr_pop( Status *s, AnyArr *restrict arr, void *restrict el_out, usize elem_size ) {
741 END_IF_NOK( *s );
742
743 DEBUG(
744 _AnyArr_validate( arr );
745 )
746
747 if( arr->len == 0 ) {
748 ERRORING_FAIL( S_FAILURE | ERR_OUT_OF_BOUND );
749 return;
750 }
751
752 arr->len--;
753
754 DEBUG(
755 _AnyArr_update_checksum( arr );
756 )
757
758 u8 *data = (u8*)(arr + 1);
759 usize offset = elem_size*arr->len;
760
761 mem_copy( data + offset, el_out, elem_size );
762
763 DEBUG(
764 mem_set( data + offset, elem_size, MAGIC_POISONED_MEM );
765 )
766
767end:
768 return;
769}
770
771AnyArr* AnyArr_insert_at( Status *s, AnyArr *arr, usize index, const void *element, Heap *h, usize elem_size) {
772 AnyArr *ret = arr;
773 END_IF_NOK( *s );
774
775 if( arr->len <= index ) {
776 *s = S_FAILURE | ERR_OUT_OF_BOUND | ERRSRC_ARG_3;
777 goto end;
778 }
779 ret = _AnyArr_ensure_space_for_one_more_element( s, arr, h, elem_size );
780
781 u8 *data = (u8*)(arr + 1);
782 usize insertion_offset = elem_size*arr->len;
783 usize move_amount = (arr->len - index)*elem_size;
784
785 mem_move( data + insertion_offset , data + insertion_offset + elem_size, move_amount );
786 mem_copy( element, data + insertion_offset, elem_size );
787
788 DEBUG( arr->sorted = 0; )
789
790end:
791 return ret;
792}
793
794AnyArr* AnyArr_insert_sorted( Status *s, AnyArr *arr, const void *elem, CmpFunc *cmp, Heap *h, usize elem_size ) {
795 AnyArr *ret = 0;
796
797 END_IF_NOK( *s );
798 DEBUG( assert( arr->sorted ) );
799 DEBUG( assert( arr->sortfunc == cmp ) );
800
801 usize index = algo_binary_search( s, arr + 1, arr->len, elem, elem_size, cmp );
802 ret = AnyArr_insert_at( s, arr, index, elem, h, elem_size );
803
804 DEBUG( ret->sorted = 1; )
805
806end:
807 return ret;
808}
809
810AnyArr* AnyArr_dup( Status *s, const AnyArr *arr, Heap *h, usize elem_size ) {
811 AnyArr *ret = 0;
812 END_IF_NOK( *s );
813
814 DEBUG( _AnyArr_validate( arr );)
815 usize total_size = arr->len*elem_size + sizeof(AnyArr);
816
817 ret = Heap_alloc( s, h, total_size );
818 END_IF_NOK( *s );
819
820 mem_copy( arr, ret, total_size );
821
822end:
823 return ret;
824}
825
826
827void AnyArr_sort( Status *s, AnyArr *arr, CmpFunc *cmp, usize elem_size ) {
828 algo_sort_generic(s, arr + 1, arr->len, elem_size, cmp );
829 END_IF_NOK( *s );
830
831 DEBUG(
832 arr->sorted = 1;
833 arr->sortfunc = cmp;
834 )
835
836end:
837 return;
838}
839
840/*================================= String ===================================*/
841
842u8 Str_equals( const Str *s1, const Str *s2 ) {
843 if( s1->len != s2->len ) return 0;
844
845 for( usize i = 0; i < s1->len; ++i ) {
846 if( s1->data[i] != s2->data[i] ) return 0;
847 }
848 return 1;
849}
850
851StrSlc StrSlc_from_Str( Status *s, const Str *str, usize start, usize len ) {
852 StrSlc ret = {0};
853 END_IF_NOK( *s );
854
855 if( start > str->len ) {
856 ERRORING_FAIL( S_FAILURE | ERR_OUT_OF_BOUND | ERRSRC_ARG_3 );
857 goto end;
858 }
859
860 if( str->len >= len && str->len - len < start ) {
861 ERRORING_FAIL( S_FAILURE | ERR_OUT_OF_BOUND | ERRSRC_ARG_3 );
862 goto end;
863 }
864
865 ret.data = &str->data[start];
866 ret.len = len;
867
868end:
869 return ret;
870}
871
872u8 StrSlc_equals( const StrSlc sl1, const StrSlc sl2 ) {
873 if( sl1.len != sl2.len ) {
874 return 0;
875 }
876
877 if( sl1.data == sl2.data ) return 1; /* If the pointers are the same
878 the data has got to be the same */
879
880 for( usize i = 0; i < sl1.len; ++i ) {
881 if( sl1.data[i] != sl2.data[i] ) {
882 return 0;
883 }
884 }
885
886 return 1;
887}
888
889u8 StrSlc_equals_by_ref( const StrSlc *sl1, const StrSlc *sl2 ) {
890 if( sl1->len != sl2->len ) {
891 return 0;
892 }
893
894 if( sl1->data == sl2->data ) return 1;
895
896 for( usize i = 0; i < sl1->len; ++i ) {
897 if( sl1->data[i] != sl2->data[i] ) {
898 return 0;
899 }
900 }
901
902 return 1;
903}
904
905
906u8 StrSlc_equals_Str( const StrSlc sl, const Str* s ) {
907 if( sl.len != s->len ) {
908 return 0;
909 }
910
911 for( usize i = 0; i < sl.len; ++i ) {
912 if( sl.data[i] != s->data[i] ) {
913 return 0;
914 }
915 }
916
917 return 1;
918}
919
920u8 StrSlc_equals_Str_by_ref( const StrSlc *sl, const Str *s ) {
921 if( sl->len != s->len ) {
922 return 0;
923 }
924
925 for( usize i = 0; i < sl->len; ++i ) {
926 if( sl->data[i] != s->data[i] ) {
927 return 0;
928 }
929 }
930
931 return 1;
932}
933
934StrSlcs* StrSlc_split_by_u8( Status *s, const StrSlc *sl, u8 separator, Heap *h ) {
935 if( !S_OK(*s) ) return 0;
936
937 StrSlcs *ret = StrSlcs_create( s, h, 16 );
938
939 END_IF_NOK( *s );
940
941 StrSlc new_sl = {
942 .data = sl->data,
943 .len = 0
944 };
945
946 for( usize i = 0; i < sl->len; ++i ) {
947 if( sl->data[i] == separator ) {
948 ret = StrSlcs_push( s, ret, &new_sl, h );
949 if( !S_OK(*s) ) return ret;
950 new_sl.data = &(sl->data[i+1]);
951 new_sl.len = 0;
952 } else {
953 new_sl.len++;
954 }
955 }
956
957end:
958 return ret;
959}
960
961/* String Assembler */
962
963StrAsm StrAsm_create( Status *s, usize max_size ) {
964 StrAsm ret = {0};
965 END_IF_NOK( *s );
966
967 ret.a = Arena_create( s, max_size );
968
969end:
970 return ret;
971}
972
973void StrAsm_reset( Status *s, StrAsm *sa ) {
974 END_IF_NOK( *s );
975 Arena_give( s, &sa->a, sa->a.taken );
976end:
977 return;
978}
979
980void StrAsm_destroy( Status *s, StrAsm *sa ) {
981 Status ls = S_SUCCESS;
982
983 if( sa ) {
984 Arena_destroy( &ls, &sa->a );
985 }
986
987 if( S_OK(*s) && S_NOK(ls) ) {
988 *s = ls;
989 }
990}
991
992void StrAsm_store_into_arena( Status *s, StrAsm *sa, Arena *dst, Str **out ) {
993 END_IF_NOK( *s );
994
995 usize len = sa->a.taken;
996
997 #if OS != OS_NONE
998 len++;
999 #endif
1000
1001 u8 *p = 0;
1002 p = Arena_take( s, dst, len );
1003 END_IF_NOK( *s );
1004
1005 #if OS != OS_NONE
1006 p[len] = 0; /* We need the NUL in case this string is used in a syscall
1007 - it has to be an actual C string */
1008 #endif
1009
1010 *out = (Str*)p;
1011
1012 #if OS != OS_NONE
1013 (*out)->len = len - 1;
1014 #else
1015 (*out)->len = len;
1016 #endif
1017
1018 mem_copy( sa->a.start, &(*out)->data, len );
1019
1020end:
1021 return;
1022}
1023
1024void StrAsm_store_onto_heap( Status *s, StrAsm *sa, Heap *dst, Str **out ) {
1025 if( !S_OK(*s) ) return;
1026
1027 usize len = sa->a.taken;
1028
1029 #if OS != OS_NONE
1030 len++;
1031 #endif
1032
1033 void *p = 0;
1034 p = Heap_alloc( s, dst, len );
1035 END_IF_NOK( *s )
1036
1037 #if OS != OS_NONE
1038 ((u8*)p)[len] = 0;
1039 #endif
1040
1041 *out = (Str*)p;
1042
1043 #if OS != OS_NONE
1044 (*out)->len = len - 1;
1045 #else
1046 (*out)->len = len;
1047 #endif
1048
1049 mem_copy( sa->a.start, &(*out)->data, len );
1050
1051end:
1052 return;
1053}
1054
1055StrSlc StrAsm_as_StrSlc( const StrAsm *sa ) {
1056 return (StrSlc){ .len = sa->a.taken, .data = (u8*)sa->a.start };
1057}
1058
1059
1060void StrAsm_cat_char ( Status *s, StrAsm *sa, u8 value ) {
1061 END_IF_NOK( *s );
1062
1063 u8 *data = Arena_take( s, &sa->a, sizeof(value) );
1064
1065 ENCHAIN( *s,
1066 *data = value;
1067 )
1068
1069end:
1070 return;
1071}
1072
1073void StrAsm_cat_Str( Status *s, StrAsm *sa, const Str *str ) {
1074 END_IF_NOK( *s );
1075
1076 u8 *data = Arena_take( s, &sa->a, str->len );
1077 END_IF_NOK( *s );
1078
1079 mem_copy( &str->data, data, str->len );
1080
1081end:
1082 return;
1083}
1084
1085void StrAsm_cat_StrSlc( Status *s, StrAsm *sa, StrSlc sl ) {
1086 END_IF_NOK( *s );
1087
1088 if( sl.len == 0 ) {
1089 *s = S_COMPLETION;
1090 return;
1091 }
1092
1093 u8 *data = 0;
1094
1095 data = Arena_take( s, &sa->a, sl.len );
1096 END_IF_NOK( *s );
1097
1098 mem_copy( sl.data, data, sl.len );
1099
1100end:
1101 return;
1102}
1103
1104void StrAsm_cat_StrSlc_by_ref( Status *s, StrAsm *sa, const StrSlc *sl ) {
1105 END_IF_NOK( *s );
1106
1107 if( sl->len == 0 ) {
1108 *s = S_COMPLETION;
1109 return;
1110 }
1111
1112 u8 *data = Arena_take( s, &sa->a, sl->len );
1113 END_IF_NOK( *s );
1114
1115 mem_copy( sl->data, data, sl->len );
1116
1117end:
1118 return;
1119}
1120
1121
1122u8 _halfbyte_to_hex_ascii[] = {
1123 [0x0] = '0',
1124 [0x1] = '1',
1125 [0x2] = '2',
1126 [0x3] = '3',
1127 [0x4] = '4',
1128 [0x5] = '5',
1129 [0x6] = '6',
1130 [0x7] = '7',
1131 [0x8] = '8',
1132 [0x9] = '9',
1133 [0xa] = 'a',
1134 [0xb] = 'b',
1135 [0xc] = 'c',
1136 [0xd] = 'd',
1137 [0xe] = 'e',
1138 [0xf] = 'f',
1139};
1140
1141static inline u8* _write_hex_prefix( u8* start ) {
1142 start[0] = '0';
1143 start[1] = 'x';
1144 return start + 2;
1145}
1146
1147static inline void _write_bin_as_hex_into_arena( Status *s, u8* data, Arena *a, usize len ) {
1148 u8 *out = Arena_take( s, a, 2*len + 2 );
1149 END_IF_NOK( *s );
1150
1151 out = _write_hex_prefix( out );
1152 _bin_to_hex_ascii( data, out, len );
1153
1154end:
1155 return;
1156}
1157
1158void StrAsm_fmtx_u8 ( Status *s, StrAsm *sa, u8 value) {
1159 _write_bin_as_hex_into_arena( s, &value, &sa->a, sizeof(value) );
1160}
1161
1162void StrAsm_fmtx_u16( Status *s, StrAsm *sa, u16 value) {
1163 _write_bin_as_hex_into_arena( s, (u8*)&value, &sa->a, sizeof(value) );
1164}
1165
1166void StrAsm_fmtx_u32( Status *s, StrAsm *sa, u32 value) {
1167 _write_bin_as_hex_into_arena( s, (u8*)&value, &sa->a, sizeof(value) );
1168}
1169
1170void StrAsm_fmtx_u64( Status *s, StrAsm *sa, u64 value) {
1171 _write_bin_as_hex_into_arena( s, (u8*)&value, &sa->a, sizeof(value) );
1172}
1173
1174static inline void _write_u64_as_dec_into_arena( Status *s, u64 value, Arena *a ) {
1175 u8 digits[21] = {0};
1176
1177 u8 *p = &digits[21];
1178
1179 do {
1180 *--p = '0' + value%10;
1181 value /= 10;
1182 } while(value);
1183
1184 usize n = &digits[21] - p;
1185 u8 *out = Arena_take( s, a, n );
1186
1187 ENCHAIN( *s,
1188 mem_copy( p, out, n );
1189 )
1190}
1191
1192void StrAsm_fmtd_u8 ( Status *s, StrAsm *sa, u8 value) {
1193 _write_u64_as_dec_into_arena( s, value, &sa->a );
1194}
1195
1196void StrAsm_fmtd_u16( Status *s, StrAsm *sa, u16 value) {
1197 _write_u64_as_dec_into_arena( s, value, &sa->a );
1198}
1199
1200void StrAsm_fmtd_u32( Status *s, StrAsm *sa, u32 value) {
1201 _write_u64_as_dec_into_arena( s, value, &sa->a );
1202}
1203
1204void StrAsm_fmtd_u64( Status *s, StrAsm *sa, u64 value) {
1205 _write_u64_as_dec_into_arena( s, value, &sa->a );
1206}
1207
1208static inline void _write_s64_as_dec_into_arena( Status *s, s64 value, Arena *a ) {
1209 u8 negative = (value < 0);
1210 u8 digits[21] = {0};
1211
1212 u8 *p = &digits[21];
1213
1214 do {
1215 s8 digit = value%10;
1216 *--p = '0' + ((digit < 0)? -digit : digit);
1217 value /= 10;
1218 } while(value);
1219
1220
1221 u8 *out = 0;
1222 usize n = &digits[21] - p;
1223 if( negative ) {
1224 n++;
1225 *--p = '-';
1226 }
1227 out = Arena_take( s, a, n );
1228 ENCHAIN( *s,
1229 mem_copy( p, out, n );
1230 )
1231}
1232
1233void StrAsm_fmtd_s8 ( Status *s, StrAsm *sa, s8 value) {
1234 _write_s64_as_dec_into_arena( s, value, &sa->a );
1235}
1236
1237void StrAsm_fmtd_s16 ( Status *s, StrAsm *sa, s16 value) {
1238 _write_s64_as_dec_into_arena( s, value, &sa->a );
1239}
1240
1241void StrAsm_fmtd_s32 ( Status *s, StrAsm *sa, s32 value) {
1242 _write_s64_as_dec_into_arena( s, value, &sa->a );
1243}
1244
1245void StrAsm_fmtd_s64 ( Status *s, StrAsm *sa, s64 value) {
1246 _write_s64_as_dec_into_arena( s, value, &sa->a );
1247}
1248
1249
1250
1251/******************************************************************************/
1252/* LINUX-SPECIFIC CODE */
1253/******************************************************************************/
1254
1255#if OS == OS_LINUX
1256/*============================= Entrypoint ===================================*/
1257#if ARCH == ARCH_X86_64
1258 u8* PRESERVE_WITH_LTO _linux_startup_sp = 0;
1259
1260 __asm__ (
1261 ".section .text\n"
1262 ".global _start\n"
1263 ".type _start, @function\n"
1264 "_start:\n"
1265 "leaq _linux_startup_sp(%rip), %rax\n"
1266 "movq %rsp, (%rax)\n" /* save original stack pointer */
1267 "xor %rax, %rax\n"
1268 "call entry\n"
1269 "ud2\n"
1270 );
1271#elif ARCH == ARCH_I386
1272 #error "i386 entrypoint not implemented"
1273#endif
1274
1275
1276
1277usize c_strlen( const u8* str ) {
1278 usize res = 0;
1279 while( str[res] )
1280 res++;
1281 return res;
1282}
1283
1284void _linux_get_args_and_env( Status *s, Heap *h, StrSlcs **args, StrSlcs **env ) {
1285 if( !S_OK(*s) ) return;
1286
1287 usize *it = (usize*)_linux_startup_sp;
1288 usize argc = *it;
1289
1290 /* start with args */
1291 it++;
1292
1293 *args = StrSlcs_create( s, h, argc );
1294
1295 if( !S_OK(*s) ) return;
1296
1297 for( usize i = 0; i < argc; ++i ) {
1298 StrSlc arg = (StrSlc){
1299 .data = (u8*)*(it + i),
1300 .len = c_strlen( (u8*)*(it + i) ),
1301 };
1302
1303 StrSlcs_push( s, *args, &arg, h );
1304 }
1305
1306 it += argc;
1307 if( *it != 0 ) {
1308 ERRORING_FAIL( S_FAILURE | ERR_GENERIC | ERRSRC_PLATFORM );
1309 return;
1310 }
1311
1312 /* env */
1313 it++;
1314 usize *envptrs = it;
1315
1316 while( *it != 0 ) {
1317 it++;
1318 }
1319
1320 usize envc = it - envptrs;
1321 *env = StrSlcs_create( s, h, envc );
1322
1323 if( !S_OK(*s) ) return;
1324
1325 for( usize i = 0; i < envc; ++i ) {
1326 StrSlc var = (*env)->data[i] = (StrSlc){
1327 .data = (u8*)*(envptrs + i),
1328 .len = c_strlen( (u8*)*(envptrs + i) ),
1329 };
1330
1331 StrSlcs_push( s, *env, &var, h );
1332 }
1333}
1334
1335#ifndef NOTLIBC_NOMAIN
1336 Heap _linux_heap = {0};
1337
1338 void PRESERVE_WITH_LTO entry(void) {
1339 StrSlcs *args = 0;
1340 StrSlcs *env = 0;
1341
1342 Status s = S_SUCCESS;
1343
1344 _linux_heap = Heap_create( &s, KiB(16) );
1345
1346 if( !S_OK(s) ) {
1347 DEBUG( breakpoint(); );
1348 terminate(2);
1349 }
1350
1351 _linux_get_args_and_env( &s, &_linux_heap, &args, &env );
1352
1353 if( !S_OK(s) ) {
1354 DEBUG( breakpoint(); );
1355 terminate(2);
1356 }
1357
1358 Status res = c_main( args, env );
1359
1360 Heap_destroy( &s, &_linux_heap );
1361
1362 if( !S_OK(s) ) {
1363 DEBUG( breakpoint(); );
1364 terminate(2);
1365 }
1366
1367 if( !S_OK(res) ) {
1368 terminate(res);
1369 }
1370
1371 terminate(0);
1372 UNREACHABLE;
1373 }
1374#endif
1375
1376/*=============================== Memory =====================================*/
1377
1378Page* mem_claim( Status *s, u32 page_count) {
1379 void *ptr_out = 0;
1380 END_IF_NOK( *s );
1381
1382 *s = linux_mmap(
1383 0,
1384 page_count*sizeof(Page),
1385 LINUX_PROT_NONE,
1386 MMAP_MAP_PRIVATE | MMAP_MAP_ANONYMOUS,
1387 -1,
1388 0,
1389 (void**)&ptr_out
1390 );
1391
1392end:
1393 return ptr_out;
1394}
1395
1396void mem_relinquish( Status *s, Page *start, u32 page_count ) {
1397 END_IF_NOK( *s );
1398
1399 *s = linux_munmap( (usize)start, page_count*sizeof(Page) );
1400end:
1401 return;
1402}
1403
1404void mem_occupy( Status *s, Page *start, u32 page_count ) {
1405 END_IF_NOK( *s );
1406
1407 *s = linux_mprotect(
1408 (usize)start,
1409 page_count*sizeof(Page),
1410 LINUX_PROT_READ | LINUX_PROT_WRITE
1411 );
1412
1413 end:
1414 return;
1415}
1416
1417void mem_vacate( Status *s, Page *start, u32 page_count ) {
1418 END_IF_NOK( *s );
1419
1420 usize amount = page_count*sizeof(Page);
1421
1422 END_IF_NOK( *s );
1423 *s = linux_mprotect( (usize)start, amount, LINUX_PROT_NONE );
1424
1425 END_IF_NOK( *s );
1426 *s = linux_madvise( (usize)start, amount, MADV_DONTNEED);
1427
1428end:
1429 return;
1430}
1431
1432
1433/*============================= Filesystem ===================================*/
1434
1435File File_open( Status *s, const Str *name, usize flags ) {
1436 File ret = { 0 };
1437 END_IF_NOK( *s );
1438
1439 DEBUG( assert( name->data[name->len] == 0 ) );
1440
1441 if(
1442 !(flags & FILE_ACCESS_WRITE) &&
1443 !(flags & FILE_ACCESS_APPEND) &&
1444 (flags & FILE_EXISTING_TRUNCATE)
1445 ) {
1446 /* Can not truncate a file without write access. */
1447 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
1448 goto end;
1449 }
1450
1451 if( (flags & FILE_EXISTING_FAIL) && (flags & FILE_MISSING_FAIL) ) {
1452 /* Useless flag combination: error if there is a file, error if there isn't one aka always error */
1453 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
1454 goto end;
1455 }
1456
1457 File_open_validate_flags( s, flags, ERRSRC_ARG_3 );
1458 END_IF_NOK( *s );
1459
1460 usize syscall_flags = O_CLOEXEC; /* Close all files on exec */
1461
1462 switch( flags & FILE_ACCESS_MASK ) {
1463 case FILE_ACCESS_READ:
1464 syscall_flags |= O_RDONLY;
1465 break;
1466
1467 case FILE_ACCESS_WRITE:
1468 syscall_flags |= O_WRONLY;
1469 break;
1470
1471 case FILE_ACCESS_APPEND:
1472 syscall_flags |= O_WRONLY | O_APPEND;
1473 break;
1474
1475 case FILE_ACCESS_READ | FILE_ACCESS_WRITE:
1476 syscall_flags |= O_RDWR;
1477 break;
1478
1479 case FILE_ACCESS_READ | FILE_ACCESS_APPEND:
1480 syscall_flags |= O_RDWR | O_APPEND;
1481 break;
1482
1483 default:
1484 UNREACHABLE;
1485 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
1486 goto end;
1487 }
1488
1489 switch( flags & FILE_EXISTING_MASK ) {
1490 case FILE_EXISTING_OPEN:
1491 /* done by default on linux */
1492 break;
1493 case FILE_EXISTING_FAIL:
1494 syscall_flags |= O_CREAT | O_EXCL;
1495 break;
1496 case FILE_EXISTING_TRUNCATE:
1497 syscall_flags |= O_TRUNC;
1498 break;
1499 default:
1500 UNREACHABLE;
1501 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3);
1502 goto end;
1503 }
1504
1505 switch( flags & FILE_MISSING_MASK ) {
1506 case FILE_MISSING_CREATE:
1507 syscall_flags |= O_CREAT;
1508 break;
1509 case FILE_MISSING_FAIL:
1510 /* done by default on linux */
1511 break;
1512 default:
1513 UNREACHABLE;
1514 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
1515 goto end;
1516 }
1517
1518 switch( flags & FILE_LINK_MASK ) {
1519 case FILE_LINK_FOLLOW:
1520 /* Default on linux */
1521 break;
1522 case FILE_LINK_FAIL:
1523 syscall_flags |= O_NOFOLLOW;
1524 break;
1525 default:
1526 UNREACHABLE;
1527 ERRORING_FAIL( S_FAILURE | ERR_INVALID_ARGUMENT | ERRSRC_ARG_3 );
1528 goto end;
1529 }
1530
1531 *s = linux_open( &name->data[0], syscall_flags, 0666, &ret.fd );
1532
1533end:
1534 return ret;
1535}
1536
1537void File_close( Status *s, File *f ) {
1538
1539 if( f && f->fd ) {
1540 Status ls = linux_close( &f->fd );
1541 f->fd = 0;
1542
1543 if( S_OK( *s ) ) {
1544 *s = ls;
1545 }
1546 }
1547}
1548
1549
1550usize File_read( Status *s, const File *f, usize max_amount, u8 *out ) {
1551 usize out_amount = 0;
1552 END_IF_NOK( *s );
1553
1554 *s = linux_read( f->fd, max_amount, &out_amount, out);
1555
1556end:
1557 return out_amount;
1558}
1559
1560usize File_write( Status *s, const File *f, usize amount, const u8 *buf ) {
1561 usize out_amount = 0;
1562 END_IF_NOK( *s );
1563
1564 *s = linux_write( f->fd, amount, &out_amount, buf );
1565end:
1566 return out_amount;
1567}
1568
1569void File_seek( Status *s, const File *f, usize position ) {
1570 END_IF_NOK( *s );
1571
1572 *s = linux_lseek( f->fd, position, SEEK_SET );
1573
1574end:
1575 return;
1576}
1577
1578void File_delete( Status *s, const Str *name ) {
1579 END_IF_NOK( *s );
1580 DEBUG( assert( name->data[name->len] == 0 ));
1581
1582 *s = linux_unlink( (const u8*)&name->data );
1583
1584end:
1585 return;
1586}
1587
1588
1589FileView FileView_from_file( Status *s, File *file, usize claim_amount, usize flags ) {
1590 FileView ret = {0};
1591 END_IF_NOK( *s );
1592
1593 DEBUG( assert( claim_amount%sizeof(Page) == 0 ) );
1594
1595 if( claim_amount%sizeof(Page) != 0 ) {
1596 ERRORING_FAIL( ERRSRC_ARG_3 | ERR_INVALID_ARGUMENT );
1597 goto end;
1598 }
1599
1600 if( !file->fd ) {
1601 ERRORING_FAIL( ERRSRC_ARG_2 | ERR_INVALID_ARGUMENT );
1602 goto end;
1603 }
1604
1605 ret.start = mem_claim( s, claim_amount/sizeof(Page) );
1606 END_IF_NOK( *s );
1607
1608 ret.claimed = claim_amount;
1609
1610 /* Get filesize */
1611 struct stat stat = {0};
1612 *s = linux_fstat( file->fd, &stat );
1613
1614 END_IF_NOK( *s );
1615
1616 ret.filesize = stat.st_size;
1617
1618 void *discard;
1619
1620 if( ret.filesize > 0 ) { /* Nothing to map in an empty file */
1621 *s = linux_mmap(
1622 (usize)ret.start,
1623 ret.filesize,
1624 LINUX_PROT_READ | LINUX_PROT_WRITE,
1625 MMAP_MAP_FIXED | MMAP_MAP_SHARED,
1626 file->fd,
1627 0,
1628 &discard
1629 );
1630 }
1631 END_IF_NOK( *s );
1632
1633 /* Finalize output */
1634 ret.file = *file;
1635 ret.flags = flags;
1636
1637 if( flags & FILEVIEW_OWN_FILE ) {
1638 file->fd = 0;
1639 }
1640
1641end:
1642 return ret;
1643}
1644
1645FileView FileView_from_path( Status *s, const Str *path, usize claim_amount, usize flags ) {
1646 File file = {0};
1647
1648 file = File_open( s, path,
1649 FILE_EXISTING_OPEN |
1650 FILE_MISSING_CREATE |
1651 FILE_LINK_FOLLOW |
1652 FILE_ACCESS_READ | FILE_ACCESS_WRITE
1653 );
1654
1655 return FileView_from_file( s, &file, claim_amount, flags );
1656}
1657
1658StrSlc FileView_as_StrSlc( const FileView *fv ) {
1659 return (StrSlc){ .len = fv->filesize, .data = (const u8*)fv->start };
1660}
1661
1662void FileView_extend_file( Status *s, FileView *fv, usize amount ) {
1663 END_IF_NOK( *s );
1664
1665 if( amount > fv->claimed - fv->filesize ) {
1666 ERRORING_FAIL( S_FAILURE | ERR_OUT_OF_BOUND );
1667 }
1668
1669 usize map_start_offset = usize_round_down( s, fv->filesize, sizeof(Page) );
1670 END_IF_NOK( *s );
1671
1672 usize map_start_addr = (u64)((u8*)fv->start + map_start_offset);
1673
1674 *s = linux_fallocate( fv->file.fd, 0, fv->filesize, amount );
1675 END_IF_NOK( *s );
1676
1677 fv->filesize += amount;
1678
1679 void *discard;
1680
1681 *s = linux_mmap(
1682 map_start_addr,
1683 fv->filesize - map_start_offset + amount,
1684 LINUX_PROT_READ | LINUX_PROT_WRITE,
1685 MMAP_MAP_FIXED | MMAP_MAP_SHARED,
1686 fv->file.fd,
1687 map_start_offset,
1688 &discard
1689 );
1690
1691end:
1692 return;
1693}
1694
1695void FileView_destroy( Status *s, FileView *fv ) {
1696 if( fv ) {
1697 if( fv->flags & FILEVIEW_OWN_FILE ) {
1698 File_close( s, &fv->file );
1699 }
1700 }
1701
1702 Status ls = linux_munmap( (usize)fv->start, fv->claimed );
1703
1704 if( S_OK( *s ) && S_NOK( ls ) ) {
1705 *s = ls;
1706 }
1707
1708 END_IF_NOK( *s );
1709
1710 fv->flags = 0;
1711 fv->filesize = 0;
1712 fv->claimed = 0;
1713 fv->start = 0;
1714
1715end:
1716 return;
1717}
1718
1719
1720/*============================ System calls ==================================*/
1721
1722/* System call wrappers */
1723#if COMPILER == COMPILER_TCC
1724 #if ARCH == ARCH_X86_64
1725 ssize linux_syscall1(usize num, usize arg1) {
1726 __asm__ volatile (
1727 "movq %[a1], %%rdi\n"
1728 "syscall\n"
1729 : [num]"+a"(num)
1730 : [a1]"m"(arg1) /* memory operands are forced, because otherwise tcc does not generate correct inline asm - it uses mm registers, which have bogus values... */
1731 : "rcx", "r11", "memory", "rdi"
1732 );
1733 return num;
1734 }
1735
1736 ssize linux_syscall2(usize num, usize arg1, usize arg2) {
1737 __asm__ volatile (
1738 "movq %[a1], %%rdi\n"
1739 "movq %[a2], %%rsi\n"
1740 "syscall\n"
1741 : [num]"+a"(num)
1742 : [a1]"m"(arg1), [a2]"m"(arg2)
1743 : "rcx", "r11", "memory", "rdi", "rsi"
1744 );
1745 return num;
1746 }
1747 ssize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3) {
1748 __asm__ volatile (
1749 "movq %[a1], %%rdi\n"
1750 "movq %[a2], %%rsi\n"
1751 "movq %[a3], %%rdx\n"
1752 "syscall\n"
1753 : [num]"+a"(num)
1754 : [a1]"m"(arg1), [a2]"m"(arg2), [a3]"m"(arg3)
1755 : "rcx", "r11", "memory", "rdi", "rsi", "rdx"
1756 );
1757 return num;
1758 }
1759 ssize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4) {
1760 __asm__ volatile (
1761 "movq %[a1], %%rdi\n"
1762 "movq %[a2], %%rsi\n"
1763 "movq %[a3], %%rdx\n"
1764 "movq %[a4], %%r10\n"
1765 "syscall\n"
1766 : [num]"+a"(num)
1767 : [a1]"m"(arg1), [a2]"m"(arg2), [a3]"m"(arg3), [a4]"m"(arg4)
1768 : "rcx", "r11", "memory", "rdi", "rsi", "rdx", "r10"
1769 );
1770 return num;
1771 }
1772 ssize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5) {
1773 __asm__ volatile (
1774 "movq %[a1], %%rdi\n"
1775 "movq %[a2], %%rsi\n"
1776 "movq %[a3], %%rdx\n"
1777 "movq %[a4], %%r10\n"
1778 "movq %[a5], %%r8\n"
1779 "syscall\n"
1780 : [num]"+a"(num)
1781 : [a1]"m"(arg1), [a2]"m"(arg2), [a3]"m"(arg3), [a4]"m"(arg4), [a5]"m"(arg5)
1782 : "rcx", "r11", "memory", "rdi", "rsi", "rdx", "r10", "r8"
1783 );
1784 return num;
1785 }
1786
1787 ssize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6) {
1788 __asm__ volatile (
1789 "movq %[a1], %%rdi\n"
1790 "movq %[a2], %%rsi\n"
1791 "movq %[a3], %%rdx\n"
1792 "movq %[a4], %%r10\n"
1793 "movq %[a5], %%r8\n"
1794 "movq %[a6], %%r9\n"
1795 "syscall\n"
1796 : [num]"+a"(num)
1797 : [a1]"m"(arg1), [a2]"m"(arg2), [a3]"m"(arg3), [a4]"m"(arg4), [a5]"m"(arg5), [a6]"m"(arg6)
1798 : "rcx", "r11", "memory", "rdi", "rsi", "rdx", "r10", "r8", "r9"
1799 );
1800 return num;
1801 }
1802
1803 #elif ARCH == ARCH_I386
1804 #error "Linux syscalls not implemented for I386."
1805 #endif
1806#elif COMPILER == COMPILER_CLANG || COMPILER == COMPILER_GCC
1807 #if ARCH == ARCH_X86_64
1808 /* All this mess, just so *maybe* the compiler can optimize register usage when inlining. Check the generic wrappers for a more typical way of going about this. This here is unusual, from what I have seen. */
1809 ssize linux_syscall1(usize num, usize arg1) {
1810 register usize ret __asm__("rax") = num;
1811 register usize a1 __asm__("rdi") = arg1;
1812
1813 __asm__ volatile(
1814 "syscall\n"
1815 : "+r"(ret)
1816 : "r"(a1)
1817 : "rcx", "r11", "memory"
1818 );
1819 return ret;
1820 }
1821 ssize linux_syscall2(usize num, usize arg1, usize arg2) {
1822 register usize ret __asm__("rax") = num;
1823 register usize a1 __asm__("rdi") = arg1;
1824 register usize a2 __asm__("rsi") = arg2;
1825
1826 __asm__ volatile(
1827 "syscall\n"
1828 : "+r"(ret)
1829 : "r"(a1), "r"(a2)
1830 : "rcx", "r11", "memory"
1831 );
1832 return ret;
1833 }
1834 ssize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3) {
1835 register usize ret __asm__("rax") = num;
1836 register usize a1 __asm__("rdi") = arg1;
1837 register usize a2 __asm__("rsi") = arg2;
1838 register usize a3 __asm__("rdx") = arg3;
1839
1840 __asm__ volatile(
1841 "syscall\n"
1842 : "+r"(ret)
1843 : "r"(a1), "r"(a2), "r"(a3)
1844 : "rcx", "r11", "memory"
1845 );
1846 return ret;
1847 }
1848 ssize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4) {
1849 register usize ret __asm__("rax") = num;
1850 register usize a1 __asm__("rdi") = arg1;
1851 register usize a2 __asm__("rsi") = arg2;
1852 register usize a3 __asm__("rdx") = arg3;
1853 register usize a4 __asm__("r10") = arg4;
1854
1855 __asm__ volatile(
1856 "syscall\n"
1857 : "+r"(ret)
1858 : "r"(a1), "r"(a2), "r"(a3), "r"(a4)
1859 : "rcx", "r11", "memory"
1860 );
1861 return ret;
1862 }
1863
1864 ssize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5) {
1865 register usize ret __asm__("rax") = num;
1866 register usize a1 __asm__("rdi") = arg1;
1867 register usize a2 __asm__("rsi") = arg2;
1868 register usize a3 __asm__("rdx") = arg3;
1869 register usize a4 __asm__("r10") = arg4;
1870 register usize a5 __asm__("r8") = arg5;
1871
1872 __asm__ volatile(
1873 "syscall\n"
1874 : "+r"(ret)
1875 : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5)
1876 : "rcx", "r11", "memory"
1877 );
1878 return ret;
1879 }
1880
1881 ssize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6) {
1882 register usize ret __asm__("rax") = num;
1883 register usize a1 __asm__("rdi") = arg1;
1884 register usize a2 __asm__("rsi") = arg2;
1885 register usize a3 __asm__("rdx") = arg3;
1886 register usize a4 __asm__("r10") = arg4;
1887 register usize a5 __asm__("r8") = arg5;
1888 register usize a6 __asm__("r9") = arg6;
1889
1890 __asm__ volatile(
1891 "syscall\n"
1892 : "+r"(ret)
1893 : "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6)
1894 : "rcx", "r11", "memory"
1895 );
1896 return ret;
1897 }
1898
1899 #elif ARCH == ARCH_I386
1900 #error "Linux syscalls not implemented for I386."
1901 #endif
1902#endif
1903
1904
1905/* Actual system calls */
1906
1907Status linux_read( u32 fd, usize max_amount, usize *out_amount, u8 *out ) {
1908 usize res = linux_syscall3( 0, fd, (usize)out, max_amount );
1909 if( !LINUX_SYSCALL_ERROR(res) ) {
1910 *out_amount = res;
1911 return S_SUCCESS;
1912 } else {
1913 switch(res) {
1914 case -EBADF:
1915 return S_FAILURE | ERRSRC_ARG_1 | ERR_INVALID_ARGUMENT;
1916 case -EFAULT:
1917 return S_FAILURE | ERRSRC_ARG_3 | ERR_OUT_OF_BOUND;
1918 default:
1919 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
1920 }
1921 }
1922}
1923
1924Status linux_write( u32 fd, usize amount, usize *out_amount, const u8 *buf ) {
1925 usize res = linux_syscall3( 1, fd, (usize)buf, amount );
1926
1927 if( !LINUX_SYSCALL_ERROR(res) ) {
1928 *out_amount = res;
1929 return S_SUCCESS;
1930 } else {
1931 switch(res) {
1932 case -EBADF:
1933 return S_FAILURE | ERRSRC_ARG_1 | ERR_INVALID_ARGUMENT;
1934 case -EFAULT:
1935 return S_FAILURE | ERRSRC_ARG_3 | ERR_OUT_OF_BOUND;
1936 default:
1937 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
1938 }
1939 }
1940}
1941
1942Status linux_open( const u8* path, s32 flags, s32 mode, s32 *out_fd ) {
1943 ssize res = linux_syscall3( 2, (usize)path, (usize)flags, (usize)mode);
1944
1945 if( !LINUX_SYSCALL_ERROR( res ) ) {
1946 *out_fd = res;
1947 return S_SUCCESS;
1948 } else {
1949 switch( res ) {
1950 case -EACCES:
1951 case -EPERM:
1952 return S_FAILURE | ERRSRC_PLATFORM | ERR_PERMISSION_DENIED;
1953 case -EEXIST:
1954 return S_FAILURE | ERRSRC_PLATFORM | ERR_FILE_ALREADY_EXISTS;
1955 case -EINVAL:
1956 return S_FAILURE | ERRSRC_PLATFORM | ERR_INVALID_ARGUMENT;
1957 case -ENOENT:
1958 return S_FAILURE | ERRSRC_PLATFORM | ERR_FILE_DOESNT_EXIST;
1959 case -ENAMETOOLONG:
1960 return S_FAILURE | ERRSRC_ARG_1 | ERR_INVALID_ARGUMENT;
1961 case -ENOSPC:
1962 return S_FAILURE | ERRSRC_PLATFORM | ERR_NO_DISK_SPACE;
1963 default:
1964 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
1965 }
1966 }
1967}
1968
1969Status linux_close( s32 *fd ) {
1970 usize res = linux_syscall1( 3, *fd );
1971
1972 if( !LINUX_SYSCALL_ERROR( res ) ) {
1973 *fd = -1;
1974 return S_SUCCESS;
1975 } else {
1976 switch( res ) {
1977 case -EBADF:
1978 return S_FAILURE | ERRSRC_ARG_1 | ERR_INVALID_ARGUMENT;
1979 default:
1980 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
1981 }
1982 }
1983}
1984
1985Status linux_lseek( u32 fd, usize offset, usize whence ) {
1986 usize res = linux_syscall3( 8, fd, offset, whence );
1987
1988 if( !LINUX_SYSCALL_ERROR(res) ) {
1989 return S_SUCCESS;
1990 } else {
1991 switch( res ) {
1992 case -EBADF:
1993 return S_FAILURE | ERRSRC_ARG_1 | ERR_INVALID_ARGUMENT;
1994 case -EOVERFLOW:
1995 return S_FAILURE | ERR_OVERFLOW;
1996 default:
1997 return S_FAILURE | ERR_GENERIC;
1998 }
1999 }
2000}
2001
2002ssize linux_stat( const char* path, struct stat *statbuf ) {
2003 return linux_syscall2( 4, (usize)path, (usize)statbuf );
2004}
2005
2006Status linux_fstat( s32 fd, struct stat *statbuf ) {
2007 ssize res = linux_syscall2( 5, (usize)fd, (usize)statbuf );
2008
2009 if( !LINUX_SYSCALL_ERROR(res) ) {
2010 return S_SUCCESS;
2011 } else {
2012 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
2013 }
2014}
2015
2016void linux_exit( s32 exitcode ) {
2017 (void)linux_syscall1( 60, exitcode );
2018 UNREACHABLE;
2019}
2020
2021
2022Status linux_mmap( usize addr, usize len, usize prot, usize flags, usize fd, usize offset, void **out ) {
2023 ssize res = linux_syscall6( 9, addr, len, prot, flags, fd, offset );
2024
2025 if( !LINUX_SYSCALL_ERROR(res) ) {
2026 *out = (void*)res;
2027 return S_SUCCESS;
2028 } else {
2029 return S_FAILURE | ERRSRC_PLATFORM | ERR_MEM_MAP_FAILURE;
2030 }
2031}
2032
2033Status linux_munmap( usize start, usize len ) {
2034 ssize res = linux_syscall2( 11, start, len );
2035
2036 if( !LINUX_SYSCALL_ERROR(res) ) {
2037 return S_SUCCESS;
2038 } else {
2039 return S_FAILURE | ERRSRC_PLATFORM | ERR_MEM_UNMAP_FAILURE;
2040 }
2041}
2042
2043Status linux_mprotect( usize start, usize len, usize prot ) {
2044 ssize res = linux_syscall3( 10, start, len, prot );
2045
2046 if( !LINUX_SYSCALL_ERROR(res) ) {
2047 return S_SUCCESS;
2048 } else {
2049 return S_FAILURE | ERRSRC_PLATFORM | ERR_MEM_SETPERM_FAILURE;
2050 }
2051}
2052
2053Status linux_madvise( usize start, usize len, s32 behaviour ) {
2054 ssize res = linux_syscall3( 11, start, len, (usize)(u32)behaviour );
2055
2056 if( !LINUX_SYSCALL_ERROR(res) ) {
2057 return S_SUCCESS;
2058 } else {
2059 return S_FAILURE | ERRSRC_PLATFORM | ERR_MEM_SETHINT_FAILURE;
2060 }
2061}
2062
2063ssize linux_unlink( const u8 *path ) {
2064 ssize res = linux_syscall1( 87, (usize)path );
2065
2066 if( !LINUX_SYSCALL_ERROR(res) ) {
2067 return S_SUCCESS;
2068 } else {
2069 switch( res ) {
2070 case -EACCES:
2071 case -EPERM:
2072 return S_FAILURE | ERRSRC_PLATFORM | ERR_PERMISSION_DENIED;
2073 case -EFAULT:
2074 return S_FAILURE | ERRSRC_ARG_1 | ERR_OUT_OF_BOUND;
2075 case -ENOENT:
2076 return S_FAILURE | ERRSRC_PLATFORM | ERR_FILE_DOESNT_EXIST;
2077 default:
2078 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
2079 }
2080 }
2081}
2082
2083Status linux_fallocate( s32 fd, s32 mode, usize offset, usize len ) {
2084 ssize res = linux_syscall4(285, fd, mode, offset, len);
2085
2086 if( !LINUX_SYSCALL_ERROR(res) ) {
2087 return S_SUCCESS;
2088 } else {
2089 return S_FAILURE | ERRSRC_PLATFORM | ERR_GENERIC;
2090 }
2091}
2092/*=========================== Core functions =================================*/
2093
2094void breakpoint(void) {
2095 __asm__ volatile (
2096 "int $3\n"
2097 );
2098}
2099
2100void terminate_forced(void) {
2101 __asm__ volatile (
2102 "ud2\n"
2103 );
2104}
2105
2106void terminate(u32 code) {
2107 linux_exit((s32)code);
2108 UNREACHABLE;
2109}
2110
2111#endif /* OS == OS_LINUX */