implement our own malloc/free, finally drop libc
This commit is contained in:
@@ -7,7 +7,7 @@ A very cool language
|
||||
* Compiles to x86_64 Assembly
|
||||
* Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std)
|
||||
* Produces tiny static executables (~30KB with musl)
|
||||
* ~~No libc required~~ (SOON; still used for memory allocation and DNS resolution)
|
||||
* No libc required!
|
||||
* Sometimes works
|
||||
* Has the pipe operator
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
func main[argc: i64, argv: ptr] : i64
|
||||
if argc < 2
|
||||
panic("url missing")
|
||||
|
||||
let url: str = mem.read64(argv + 8)
|
||||
|
||||
if str.len(url) <= 7
|
||||
panic("missing url scheme")
|
||||
|
||||
if !str.equal(str.substr(url, 0, 7), "http://")
|
||||
panic("invalid url scheme")
|
||||
|
||||
let url_len: i64 = str.len(url)
|
||||
let host_start = 7
|
||||
let i: i64 = host_start
|
||||
while i < url_len
|
||||
if url[i] == '/'
|
||||
break
|
||||
i = i + 1
|
||||
|
||||
let host: str = str.substr(url, host_start, i - host_start)
|
||||
let path: str = "/"
|
||||
if i < url_len
|
||||
path = str.substr(url, i, url_len - i)
|
||||
|
||||
let s: i64 = net.connect(host, 80)
|
||||
if s < 0
|
||||
panic("failed to connect")
|
||||
|
||||
// very leaky
|
||||
let req: str = "GET "
|
||||
req = str.concat(req, path)
|
||||
req = str.concat(req, " HTTP/1.0\r\nHost: ")
|
||||
req = str.concat(req, host)
|
||||
req = str.concat(req, "\r\nConnection: close\r\n\r\n")
|
||||
net.send(s, req, str.len(req))
|
||||
mem.free(req)
|
||||
|
||||
let header_buf: str = mem.alloc(8192)
|
||||
let header_size = 0
|
||||
let found: bool = false
|
||||
let end_index: i64 = -1
|
||||
|
||||
while !found && header_size < 8192
|
||||
let n: i64 = net.read(s, header_buf + header_size, 8192 - header_size)
|
||||
if n <= 0
|
||||
break
|
||||
let current_size: i64 = header_size + n
|
||||
i = 0
|
||||
while i <= current_size - 4
|
||||
if header_buf[i] == 13 && header_buf[i + 1] == 10 && header_buf[i + 2] == 13 && header_buf[i + 3] == 10
|
||||
found = true
|
||||
end_index = i + 4
|
||||
break
|
||||
i = i + 1
|
||||
header_size = current_size
|
||||
|
||||
if end_index < header_size
|
||||
io.print_sized(header_buf + end_index, header_size - end_index)
|
||||
mem.free(header_buf)
|
||||
|
||||
let buffer: ptr = mem.alloc(4096)
|
||||
while true
|
||||
let n: i64 = net.read(s, buffer, 4096)
|
||||
if n <= 0
|
||||
break
|
||||
io.print_sized(buffer, n)
|
||||
mem.free(buffer)
|
||||
|
||||
net.close(s)
|
||||
@@ -106,6 +106,20 @@ impl<'a> CodegenX86_64<'a> {
|
||||
"section .note.GNU-stack
|
||||
db 0
|
||||
|
||||
section .bss
|
||||
_heap_head: resq 1
|
||||
_heap_tail: resq 1
|
||||
|
||||
section .text._builtin_heap_head
|
||||
_builtin_heap_head:
|
||||
lea rax, [rel _heap_head]
|
||||
ret
|
||||
|
||||
section .text._builtin_heap_tail
|
||||
_builtin_heap_tail:
|
||||
lea rax, [rel _heap_tail]
|
||||
ret
|
||||
|
||||
section .text._builtin_read64
|
||||
_builtin_read64:
|
||||
mov rax, qword [rdi]
|
||||
|
||||
144
src/std/std.zr
144
src/std/std.zr
@@ -1,19 +1,128 @@
|
||||
extern malloc
|
||||
extern realloc
|
||||
extern free
|
||||
extern gethostbyname
|
||||
|
||||
func panic[msg: str] : void
|
||||
io.print("PANIC: ")
|
||||
io.println(msg)
|
||||
(0 / 0) // crashes program which is kinda better since you get a gdb backtrace
|
||||
os.exit(1)
|
||||
|
||||
func mem.alloc[x: i64] : ptr
|
||||
return malloc(x)
|
||||
const MEM_BLOCK_SIZE = 24
|
||||
|
||||
struct mem.Block
|
||||
size: i64
|
||||
free: bool
|
||||
next: mem.Block
|
||||
|
||||
func mem._align[x: i64] : i64
|
||||
return (x + 7) & -8
|
||||
|
||||
func mem._split_block[blk: mem.Block, needed: i64] : void
|
||||
if blk->size >= needed + MEM_BLOCK_SIZE + 8
|
||||
let new_blk: mem.Block = blk + MEM_BLOCK_SIZE + needed
|
||||
new_blk->size = blk->size - needed - MEM_BLOCK_SIZE
|
||||
new_blk->free = true
|
||||
new_blk->next = blk->next
|
||||
|
||||
blk->size = needed
|
||||
blk->next = new_blk
|
||||
|
||||
if mem.read64(_builtin_heap_tail()) == blk
|
||||
mem.write64(_builtin_heap_tail(), new_blk)
|
||||
|
||||
func mem._request_space[size: i64] : mem.Block
|
||||
let blk: mem.Block = _builtin_syscall(SYS_brk, 0)
|
||||
let new_brk: ptr = blk + MEM_BLOCK_SIZE + size
|
||||
let result: ptr = _builtin_syscall(SYS_brk, new_brk)
|
||||
if result != new_brk
|
||||
return 0
|
||||
|
||||
blk->size = size
|
||||
blk->free = false
|
||||
blk->next = 0
|
||||
|
||||
let tail: mem.Block = mem.read64(_builtin_heap_tail())
|
||||
if tail
|
||||
tail->next = blk
|
||||
|
||||
mem.write64(_builtin_heap_tail(), blk)
|
||||
return blk
|
||||
|
||||
func mem._coalesce[] : void
|
||||
let cur: mem.Block = mem.read64(_builtin_heap_head())
|
||||
while cur && cur->next
|
||||
let next: mem.Block = cur->next
|
||||
if cur->free && next->free
|
||||
cur->size = cur->size + MEM_BLOCK_SIZE + next->size
|
||||
cur->next = next->next
|
||||
if !cur->next
|
||||
mem.write64(_builtin_heap_tail(), cur)
|
||||
else
|
||||
cur = cur->next
|
||||
|
||||
func mem.alloc[size: i64] : ptr
|
||||
if size == 0
|
||||
return 0
|
||||
|
||||
size = mem._align(size)
|
||||
|
||||
let cur: mem.Block = mem.read64(_builtin_heap_head())
|
||||
while cur
|
||||
if cur->free && cur->size >= size
|
||||
mem._split_block(cur, size)
|
||||
cur->free = false
|
||||
return cur + MEM_BLOCK_SIZE
|
||||
cur = cur->next
|
||||
|
||||
let blk: mem.Block = mem._request_space(size)
|
||||
if !blk
|
||||
return 0
|
||||
|
||||
if !mem.read64(_builtin_heap_head())
|
||||
mem.write64(_builtin_heap_head(), blk)
|
||||
|
||||
return blk + MEM_BLOCK_SIZE
|
||||
|
||||
func mem.free[x: ptr] : void
|
||||
free(x)
|
||||
if !x
|
||||
return 0
|
||||
|
||||
let blk: mem.Block = x - MEM_BLOCK_SIZE
|
||||
blk->free = true
|
||||
mem._coalesce()
|
||||
|
||||
func mem.realloc[x: ptr, new_size: i64] : ptr
|
||||
if !x
|
||||
return mem.alloc(new_size)
|
||||
|
||||
if new_size == 0
|
||||
mem.free(x)
|
||||
return 0
|
||||
|
||||
new_size = mem._align(new_size)
|
||||
|
||||
let blk: mem.Block = x - MEM_BLOCK_SIZE
|
||||
if blk->size >= new_size
|
||||
mem._split_block(blk, new_size)
|
||||
return x
|
||||
|
||||
let next: mem.Block = blk->next
|
||||
if next && next->free
|
||||
let combined: i64 = blk->size + MEM_BLOCK_SIZE + next->size
|
||||
if combined >= new_size
|
||||
blk->size = combined
|
||||
blk->next = next->next
|
||||
if !blk->next
|
||||
mem.write64(_builtin_heap_tail(), blk)
|
||||
mem._split_block(blk, new_size)
|
||||
return x
|
||||
|
||||
let new_ptr: ptr = mem.alloc(new_size)
|
||||
if !new_ptr
|
||||
return 0
|
||||
|
||||
for i in 0..blk->size
|
||||
new_ptr[i] = x[i]
|
||||
|
||||
mem.free(x)
|
||||
return new_ptr
|
||||
|
||||
func mem.zero_and_free[x: ptr, size: i64] : void
|
||||
mem.zero(x, size)
|
||||
@@ -465,7 +574,7 @@ func array.push[xs: Array, x: i64] : void
|
||||
let new_capacity = 4
|
||||
if xs->capacity != 0
|
||||
new_capacity = xs->capacity * 2
|
||||
xs->data = realloc(xs->data, new_capacity * 8)
|
||||
xs->data = mem.realloc(xs->data, new_capacity * 8)
|
||||
xs->capacity = new_capacity
|
||||
|
||||
mem.write64(xs->data + xs->size * 8, x)
|
||||
@@ -661,13 +770,7 @@ func net.listen[packed_host: i64, port: i64] : i64
|
||||
|
||||
return s
|
||||
|
||||
func net.connect[host: str, port: i64] : i64
|
||||
let he: ptr = gethostbyname(host)
|
||||
if he == 0
|
||||
return -1
|
||||
|
||||
let ip_ptr: ptr = mem.read64(mem.read64(he + 24))
|
||||
|
||||
func net.connect[packed_host: i64, port: i64] : i64
|
||||
let s: i64 = _builtin_syscall(SYS_socket, 2, 1, 0)
|
||||
if s < 0
|
||||
return -1
|
||||
@@ -675,12 +778,13 @@ func net.connect[host: str, port: i64] : i64
|
||||
let sa: ptr = mem.alloc(16)
|
||||
mem.zero(sa, 16)
|
||||
sa[0] = 2
|
||||
sa[1] = 0
|
||||
sa[2] = (port >> 8) & 255
|
||||
sa[3] = port & 255
|
||||
sa[4] = ip_ptr[0]
|
||||
sa[5] = ip_ptr[1]
|
||||
sa[6] = ip_ptr[2]
|
||||
sa[7] = ip_ptr[3]
|
||||
sa[4] = (packed_host >> 24) & 255
|
||||
sa[5] = (packed_host >> 16) & 255
|
||||
sa[6] = (packed_host >> 8) & 255
|
||||
sa[7] = packed_host & 255
|
||||
|
||||
if _builtin_syscall(SYS_connect, s, sa, 16) < 0
|
||||
mem.free(sa)
|
||||
|
||||
Reference in New Issue
Block a user