From a28bf834ac88c4ed954da41490d1c05d0626dd4a Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Mon, 29 May 2023 11:23:39 +0100 Subject: [PATCH] runtime: add a basic stdlib and crt0 file --- examples/lib/crt0.s | 14 ++++ examples/lib/stdlib.s | 176 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 examples/lib/crt0.s create mode 100644 examples/lib/stdlib.s diff --git a/examples/lib/crt0.s b/examples/lib/crt0.s new file mode 100644 index 0000000..771a38d --- /dev/null +++ b/examples/lib/crt0.s @@ -0,0 +1,14 @@ +// A minimal crt0.s that works along the stdlib.s file provided to give +// some resemblance of a functioning compilation target :) +// +// Copyright (c) 2023 Anton Lydike +// SPDX-License-Identifier: MIT + +.text + +.globl _start +_start: + // TODO: read argc, argv from a0, a1 + // maybe even find envptr? + jal main + jal exit diff --git a/examples/lib/stdlib.s b/examples/lib/stdlib.s new file mode 100644 index 0000000..a12f4b5 --- /dev/null +++ b/examples/lib/stdlib.s @@ -0,0 +1,176 @@ +// A very basic implementation of a stdlib.h but in assembly. +// should(tm) work with riscemu. +// +// Copyright (c) 2023 Anton Lydike +// SPDX-License-Identifier: MIT + +.data + +_rand_seed: +.word 0x76767676 +_atexit_calls: +// leave room for 8 atexit handlers here for now +.word 0x00, 0x00, 0x00, 0x00 +.word 0x00, 0x00, 0x00, 0x00 +_atexit_count: +.word 0x00 + +_malloc_base_ptr: +// first word is a pointer to some space +// second word is the offset inside that space +// space is always MALLOC_PAGE_SIZE bytes +.word 0x00, 0x00 +.equ MALLOC_PAGE_SIZE 4069 + +.text + +// malloc/free + +.globl malloc +.globl free + +// malloc(size_t size) +malloc: + // set aside size in s0 + sw s0, -4(sp) + mv a0, s0 + la t0, _malloc_base_ptr + lw t1, 0(t0) + beq t1, zero, _malloc_init +_malloc_post_init: + // if we are here, we always have + // t0 = (&_malloc_base_ptr) + // t1 = *(&_malloc_base_ptr) + // new we load + // t2 = base_ptr_offset + lw t2, 4(t0) + // add allocated size to offset + add t2, t2, s0 + // check for overflow + li t4, MALLOC_PAGE_SIZE + bge t2, t4, _malloc_fail + // save the new offset + sw t2, 4(t0) + // calculate base_ptr + offset + add a0, t2, t1 + // return that + lw s0, -4(sp) + ret + +_malloc_init: + // call mmap2() + li a0, 0 // addr = 0, let OS choose address + li a1, 4096 // size + li a2, 3 // PROT_READ | PROT_WRITE + li a3, 5 // MAP_PRIVATE | MAP_ANONYMOUS + li a7, SCALL_MMAP2 + ecall // invoke syscall + // check for error code + li t0, -1 + beq a0, t0, _malloc_fail + // if succeeded, load &_malloc_base_ptr + la t0, _malloc_base_ptr + // put value of _malloc_base_ptr into t1 + mv a0, t1 + // save base ptr to _malloc_base_ptr + sw t1, 0(t0) + // jump to post_init + j _malloc_post_init +_malloc_fail: + li a0, 0 + ret + +// free is a nop, that's valid, but not very good^^ +free: + ret + + + +// exit, atexit +.globl exit +.globl atexit + +// we can happily use saved registers here because we don't care at all if we +// destroy the calling registers. This is __noreturn__ anyways! +// register layout: +// s0 = &_atexit_count +// s2 = &_atexit_calls +// s1 = updated value of atexit +exit: + la s0, _atexit_count // s0 = &_atexit_count + lw s1, 0(s0) // s1 = *(&_atexit_count) + // exit if no atexit() calls remain + beq s1, zero, _exit + // decrement + addi s1, s1, -4 // s1-- + // save decremented value + sw s1, 0(s0) // _atexit_count = s1 + li s2, _atexit_calls + add s1, s1, s2 // s1 = &_atexit_calls + (s1) + lw s1, 0(s1) // s1 = *s1 + la ra, exit // set ra up to point to exit + jalr zero, s1, 0 // jump to address in s1 + // jalr will call the other function, which will then return back + // to the beginning of exit. +_exit: + li a0, 0 + li a7, 93 + ecall + +// atexit a0 = funcptr +atexit: + sw t0, -4(sp) + sw t2, -8(sp) + // load _atexit_count + la t0, _atexit_count + lw t2, 0(t0) + // if >= 8, fail + li t1, 8 + bge t2, t1, _atexit_fail + // increment atexit_count by 4 (one word) + addi t2, t2, 4 + sw t2, 0(t0) + // load address of _atexit_calls + la t0, _atexit_calls + // add new _atexit_count to _atexit_calls + add t0, t0, t2 + sw a0, -4(t0) + li a0, 0 + lw t0, -4(sp) + lw t2, -8(sp) + ret + +_atexit_fail: + li a0, -1 + lw s0, -4(sp) + lw s1, -8(sp) + ret + + + + + + +// rand, srand + +.globl rand +.globl srand + +// simple xorshift rand implementation +rand: + // load seed + la t1, _rand_seed + lw a0, 0(t1) + // three rounds of shifts: + sll a0, t0, 13 // x ^= x << 13; + srl a0, t0, 17 // x ^= x >> 17; + sll a0, t0, 5 // x ^= x << 5; + sw a0, 0(t1) + ret + +srand: + la t1, _rand_seed + sw a0, 0(t1) + ret + +