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 */