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
|
* Compiles to x86_64 Assembly
|
||||||
* Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std)
|
* Growing [standard library](https://github.com/antpiasecki/zern/tree/main/src/std)
|
||||||
* Produces tiny static executables (~30KB with musl)
|
* Produces tiny static executables (~30KB with musl)
|
||||||
* ~~No libc required~~ (SOON; still used for memory allocation and DNS resolution)
|
* No libc required!
|
||||||
* Sometimes works
|
* Sometimes works
|
||||||
* Has the pipe operator
|
* 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
|
"section .note.GNU-stack
|
||||||
db 0
|
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
|
section .text._builtin_read64
|
||||||
_builtin_read64:
|
_builtin_read64:
|
||||||
mov rax, qword [rdi]
|
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
|
func panic[msg: str] : void
|
||||||
io.print("PANIC: ")
|
io.print("PANIC: ")
|
||||||
io.println(msg)
|
io.println(msg)
|
||||||
(0 / 0) // crashes program which is kinda better since you get a gdb backtrace
|
(0 / 0) // crashes program which is kinda better since you get a gdb backtrace
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
|
|
||||||
func mem.alloc[x: i64] : ptr
|
const MEM_BLOCK_SIZE = 24
|
||||||
return malloc(x)
|
|
||||||
|
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
|
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
|
func mem.zero_and_free[x: ptr, size: i64] : void
|
||||||
mem.zero(x, size)
|
mem.zero(x, size)
|
||||||
@@ -465,7 +574,7 @@ func array.push[xs: Array, x: i64] : void
|
|||||||
let new_capacity = 4
|
let new_capacity = 4
|
||||||
if xs->capacity != 0
|
if xs->capacity != 0
|
||||||
new_capacity = xs->capacity * 2
|
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
|
xs->capacity = new_capacity
|
||||||
|
|
||||||
mem.write64(xs->data + xs->size * 8, x)
|
mem.write64(xs->data + xs->size * 8, x)
|
||||||
@@ -661,13 +770,7 @@ func net.listen[packed_host: i64, port: i64] : i64
|
|||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
func net.connect[host: str, port: i64] : i64
|
func net.connect[packed_host: i64, port: i64] : i64
|
||||||
let he: ptr = gethostbyname(host)
|
|
||||||
if he == 0
|
|
||||||
return -1
|
|
||||||
|
|
||||||
let ip_ptr: ptr = mem.read64(mem.read64(he + 24))
|
|
||||||
|
|
||||||
let s: i64 = _builtin_syscall(SYS_socket, 2, 1, 0)
|
let s: i64 = _builtin_syscall(SYS_socket, 2, 1, 0)
|
||||||
if s < 0
|
if s < 0
|
||||||
return -1
|
return -1
|
||||||
@@ -675,12 +778,13 @@ func net.connect[host: str, port: i64] : i64
|
|||||||
let sa: ptr = mem.alloc(16)
|
let sa: ptr = mem.alloc(16)
|
||||||
mem.zero(sa, 16)
|
mem.zero(sa, 16)
|
||||||
sa[0] = 2
|
sa[0] = 2
|
||||||
|
sa[1] = 0
|
||||||
sa[2] = (port >> 8) & 255
|
sa[2] = (port >> 8) & 255
|
||||||
sa[3] = port & 255
|
sa[3] = port & 255
|
||||||
sa[4] = ip_ptr[0]
|
sa[4] = (packed_host >> 24) & 255
|
||||||
sa[5] = ip_ptr[1]
|
sa[5] = (packed_host >> 16) & 255
|
||||||
sa[6] = ip_ptr[2]
|
sa[6] = (packed_host >> 8) & 255
|
||||||
sa[7] = ip_ptr[3]
|
sa[7] = packed_host & 255
|
||||||
|
|
||||||
if _builtin_syscall(SYS_connect, s, sa, 16) < 0
|
if _builtin_syscall(SYS_connect, s, sa, 16) < 0
|
||||||
mem.free(sa)
|
mem.free(sa)
|
||||||
|
|||||||
Reference in New Issue
Block a user