Compare commits
No commits in common. "v2" and "main" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,4 +2,3 @@
|
||||||
lang/target
|
lang/target
|
||||||
lang/libcompiler/target
|
lang/libcompiler/target
|
||||||
/target/
|
/target/
|
||||||
/stages/build/
|
|
||||||
|
|
|
||||||
127
flake.lock
127
flake.lock
|
|
@ -20,11 +20,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780243769,
|
"lastModified": 1760524057,
|
||||||
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
|
"narHash": "sha256-EVAqOteLBFmd7pKkb0+FIUyzTF61VKi7YmvP1tw4nEw=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
|
"rev": "544961dfcce86422ba200ed9a0b00dd4b1486ec5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -35,22 +35,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1769861202,
|
|
||||||
"narHash": "sha256-JwhbEHmbFFH754loOPbpjPHf9EAi+oMm2RsFXnlbt1g=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "f214de98544a6acf0d9917ba265ac50849048fcb",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_3": {
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744536153,
|
"lastModified": 1744536153,
|
||||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||||
|
|
@ -66,95 +50,23 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pwndbg": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs_2",
|
|
||||||
"pyproject-build-systems": "pyproject-build-systems",
|
|
||||||
"pyproject-nix": "pyproject-nix",
|
|
||||||
"uv2nix": "uv2nix"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1780751340,
|
|
||||||
"narHash": "sha256-MPZ+qTH0xK0zCWefximm2rAje6Zr8outa6Sj5uP9bLw=",
|
|
||||||
"owner": "pwndbg",
|
|
||||||
"repo": "pwndbg",
|
|
||||||
"rev": "0ad76cb7cc74fc80d64520596429f2fff9865c62",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "pwndbg",
|
|
||||||
"repo": "pwndbg",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pyproject-build-systems": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"pwndbg",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"pyproject-nix": [
|
|
||||||
"pwndbg",
|
|
||||||
"pyproject-nix"
|
|
||||||
],
|
|
||||||
"uv2nix": [
|
|
||||||
"pwndbg",
|
|
||||||
"uv2nix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1763662255,
|
|
||||||
"narHash": "sha256-4bocaOyLa3AfiS8KrWjZQYu+IAta05u3gYZzZ6zXbT0=",
|
|
||||||
"owner": "pyproject-nix",
|
|
||||||
"repo": "build-system-pkgs",
|
|
||||||
"rev": "042904167604c681a090c07eb6967b4dd4dae88c",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "pyproject-nix",
|
|
||||||
"repo": "build-system-pkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pyproject-nix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"pwndbg",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1769936401,
|
|
||||||
"narHash": "sha256-kwCOegKLZJM9v/e/7cqwg1p/YjjTAukKPqmxKnAZRgA=",
|
|
||||||
"owner": "pyproject-nix",
|
|
||||||
"repo": "pyproject.nix",
|
|
||||||
"rev": "b0d513eeeebed6d45b4f2e874f9afba2021f7812",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "pyproject-nix",
|
|
||||||
"repo": "pyproject.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pwndbg": "pwndbg",
|
|
||||||
"rust-overlays": "rust-overlays"
|
"rust-overlays": "rust-overlays"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-overlays": {
|
"rust-overlays": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs_3"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780456895,
|
"lastModified": 1760582142,
|
||||||
"narHash": "sha256-CvRZn3Ut0scqLJ1xwQFkZwKGVBUUNBPrFVXRTMZpbfU=",
|
"narHash": "sha256-RSLRjAoS75szOc9fFzRi9/jzPbYsiqPISSLZTloaKtM=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "7cc96a6a3fd6613cafd633250a3934483479b9a1",
|
"rev": "9ea094253b9389ba7dd4f18637f66b5824276d1d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -177,31 +89,6 @@
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"uv2nix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"pwndbg",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"pyproject-nix": [
|
|
||||||
"pwndbg",
|
|
||||||
"pyproject-nix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1769957392,
|
|
||||||
"narHash": "sha256-6PkqwwYf5K2CHi2V+faI/9pqjfz/HxUkI/MVid6hlOY=",
|
|
||||||
"owner": "pyproject-nix",
|
|
||||||
"repo": "uv2nix",
|
|
||||||
"rev": "d18bc50ae1c3d4be9c41c2d94ea765524400af75",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "pyproject-nix",
|
|
||||||
"repo": "uv2nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
|
||||||
12
flake.nix
12
flake.nix
|
|
@ -5,16 +5,12 @@
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
rust-overlays.url = "github:oxalica/rust-overlay";
|
rust-overlays.url = "github:oxalica/rust-overlay";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
pwndbg.url = "github:pwndbg/pwndbg";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils, rust-overlays, pwndbg, ...}:
|
outputs = { self, nixpkgs, flake-utils, rust-overlays, ...}:
|
||||||
flake-utils.lib.eachDefaultSystem (system: let
|
flake-utils.lib.eachDefaultSystem (system: let
|
||||||
overlays = [
|
overlays = [
|
||||||
(import rust-overlays)
|
(import rust-overlays)
|
||||||
(final: prev: {
|
|
||||||
pwndbg = pwndbg.packages.${system}.default;
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
inherit system overlays;
|
inherit system overlays;
|
||||||
|
|
@ -35,17 +31,11 @@
|
||||||
just-formatter
|
just-formatter
|
||||||
just-lsp
|
just-lsp
|
||||||
gdb
|
gdb
|
||||||
gdbgui
|
|
||||||
seer
|
|
||||||
rr
|
rr
|
||||||
nasm
|
nasm
|
||||||
nasmfmt
|
nasmfmt
|
||||||
git
|
git
|
||||||
rust
|
rust
|
||||||
pkgs.pwndbg
|
|
||||||
|
|
||||||
radare2
|
|
||||||
iaito
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
default rel
|
|
||||||
%include "src/ast.inc"
|
|
||||||
|
|
||||||
section .text
|
|
||||||
extern vec_init_with
|
|
||||||
extern vec_push
|
|
||||||
extern vec_get
|
|
||||||
extern vec_insert_sorted
|
|
||||||
extern vec_get_or
|
|
||||||
extern panic
|
|
||||||
extern memcpy
|
|
||||||
extern strcmp
|
|
||||||
extern vec_binary_search_by
|
|
||||||
extern vec_insert
|
|
||||||
|
|
||||||
extern bump_alloc
|
|
||||||
|
|
||||||
;; rdi: *Ctx
|
|
||||||
;; rsi: *Ast
|
|
||||||
;; rdx: node
|
|
||||||
;; rcx: scope
|
|
||||||
;; define-fn fn typeck_for_each(ctx: *mut TypeCheckCtx, ast: *mut Ast, node_index: u64, scope: u64)
|
|
||||||
typeck_for_each:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
; start-structs
|
|
||||||
; struct TypeCheckCtx {
|
|
||||||
; type_graph: TCGraph,
|
|
||||||
; ast_to_type_map: Vec<(u64, u64)>,
|
|
||||||
; }
|
|
||||||
; struct TCEdge<E> {
|
|
||||||
; nodes: [u64; 2],
|
|
||||||
; next_edge: [u64; 2],
|
|
||||||
; weight: E,
|
|
||||||
; }
|
|
||||||
; struct TCNode<N> {
|
|
||||||
; next_edge: [u64; 2],
|
|
||||||
; weight: N,
|
|
||||||
; }
|
|
||||||
; struct TCGraph {
|
|
||||||
; types: Vec<TCNode<Type>>,
|
|
||||||
; type_vars: Vec<TCNode<()>>,
|
|
||||||
; edges: Vec<TCEdge<bool>>,
|
|
||||||
; }
|
|
||||||
; end-structs
|
|
||||||
|
|
||||||
; In the pre-pass, the typechecker graph is built according to the typing rules of each ast node.
|
|
||||||
; E.g., for a binary expression, both operands must be of the same type, and the result type must be a supertype of the operand type:
|
|
||||||
; E1(Add, E2(..), E3(..))
|
|
||||||
; E1: T1, E2: T2, E3: T3
|
|
||||||
; T2 == T3, T1 >= T2, T1 >= T3
|
|
||||||
|
|
||||||
; In a subsequent pass, the type-variable graph is simplified and equal type nodes are merged to make traversal easier.
|
|
||||||
; The resulting type graph should be a DAG.
|
|
||||||
|
|
||||||
sub rsp, 32
|
|
||||||
mov [rsp], rsi ; Ast
|
|
||||||
mov [rsp + 8], rdi ; TypeCheckCtx
|
|
||||||
mov [rsp + 16], rdx ; node_index
|
|
||||||
mov [rsp + 24], rcx ; scope
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
;; } typeck_for_each
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
BUILD = build
|
|
||||||
|
|
||||||
all: $(BUILD)/stage1-hex1 $(BUILD)/stage1-elf $(BUILD)/stage0
|
|
||||||
|
|
||||||
stage0-hex0: $(BUILD)/stage0-hex0
|
|
||||||
|
|
||||||
# We need an initial compiler to convert the hex0.in file into an executable that can be used to generate the stage0 compiler.
|
|
||||||
$(BUILD)/stage0-bootstrap: hex0/hex0.in
|
|
||||||
for x in `sed '/^;/d;s/;.*//;s/ *//' hex0/hex0.in`; do printf \\x$$x; done > $@
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
# We use the stage0-bootstrap compiler to generate the stage0-hex0 compiler.
|
|
||||||
$(BUILD)/stage0-hex0: $(BUILD)/stage0-bootstrap
|
|
||||||
$< < hex0/hex0.in > $@
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
# Finally, we use the stage0-hex0 compiler to generate the stage0 compiler, which should be identical to the stage0-hex0 compiler.
|
|
||||||
$(BUILD)/stage0: $(BUILD)/stage0-hex0
|
|
||||||
$< < hex0/hex0.in > $@
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
verify-hex0: $(BUILD)/stage0
|
|
||||||
cmp $(BUILD)/stage0 $(BUILD)/stage0-hex0
|
|
||||||
|
|
||||||
$(BUILD)/stage1-elf: $(BUILD)/stage0
|
|
||||||
$< < hex1/elf.in > $@
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
$(BUILD)/stage1-hex1.text: $(BUILD)/stage0
|
|
||||||
$< < hex1/hex1.in > $@
|
|
||||||
|
|
||||||
$(BUILD)/stage1-hex1: $(BUILD)/stage1-elf $(BUILD)/stage1-hex1.text
|
|
||||||
$(BUILD)/stage1-elf $(BUILD)/stage1-hex1 $(BUILD)/stage1-hex1.text
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
verify-hex1: $(BUILD)/stage1-elf $(BUILD)/stage1-hex1
|
|
||||||
$(BUILD)/stage1-hex1 < hex1/helloworld.h1 > $(BUILD)/helloworld.text
|
|
||||||
$(BUILD)/stage1-elf $(BUILD)/helloworld.elf $(BUILD)/helloworld.text
|
|
||||||
chmod +x $(BUILD)/helloworld.elf
|
|
||||||
$(BUILD)/helloworld.elf
|
|
||||||
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(BUILD)
|
|
||||||
mkdir -p $(BUILD)
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
|
|
||||||
test.bin: test.rs as0.o
|
|
||||||
rustc -Clink-arg=-fuse-ld=mold -Clink-arg=as0.o --edition=2024 --test -g $< -o $@
|
|
||||||
|
|
||||||
as0.o: as0.asm
|
|
||||||
nasm -g -f elf64 -o as0.o as0.asm
|
|
||||||
|
|
||||||
test: test.bin
|
|
||||||
./test.bin
|
|
||||||
|
|
||||||
as0: as0.o
|
|
||||||
ld -m elf_x86_64 -e _entry_as0 -o as0 as0.o
|
|
||||||
|
|
||||||
print_table: as0.o
|
|
||||||
ld -m elf_x86_64 -e _entry_print_table -o print_table as0.o
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f as0.o test.bin as0 print_table
|
|
||||||
2723
stages/as0/as0.asm
2723
stages/as0/as0.asm
File diff suppressed because it is too large
Load diff
|
|
@ -1,385 +0,0 @@
|
||||||
section .bss
|
|
||||||
token_buf resb 0x100
|
|
||||||
buf resb 0x100
|
|
||||||
labels resb 0x1000
|
|
||||||
|
|
||||||
section .text
|
|
||||||
extern panic_abort
|
|
||||||
;; returns the next character from the source without advancing.
|
|
||||||
extern peekc
|
|
||||||
;; returns the next character from the source and advances.
|
|
||||||
extern getc
|
|
||||||
;; advances past the next character and peeks the next.
|
|
||||||
extern consuming_peekc
|
|
||||||
|
|
||||||
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
|
||||||
fasthash:
|
|
||||||
mov eax, 0x811c9dc5 ; FNV offset basis
|
|
||||||
.loop:
|
|
||||||
test rsi, rsi
|
|
||||||
jz .done
|
|
||||||
movzx ecx, byte [rdi] ; get next byte
|
|
||||||
xor eax, ecx
|
|
||||||
imul eax, 0x1000193 ; FNV prime
|
|
||||||
inc rdi
|
|
||||||
dec rsi
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; @param lhs: (rdi, rsi)
|
|
||||||
;; @param rhs: (rdx, rcx)
|
|
||||||
;; @return al
|
|
||||||
strcmp:
|
|
||||||
cmp rcx, rsi
|
|
||||||
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
|
|
||||||
xor eax, eax
|
|
||||||
.loop:
|
|
||||||
cmp rsi, rax
|
|
||||||
je .equal
|
|
||||||
movzx ecx, byte [rdx + rax]
|
|
||||||
cmp byte [rdi + rax], cl
|
|
||||||
lea rax, [rax + 1]
|
|
||||||
je .loop
|
|
||||||
seta al ; al = lhs > rhs
|
|
||||||
sbb al, 0 ; al = al - CF
|
|
||||||
ret
|
|
||||||
.equal:
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
memcpy:
|
|
||||||
xor rax, rax
|
|
||||||
.loop:
|
|
||||||
cmp rax, rdx
|
|
||||||
je .done
|
|
||||||
mov al, byte [rdi + rax]
|
|
||||||
mov byte [rsi + rax], al
|
|
||||||
lea rax, [rax + 1]
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; returns 1 if the result of `peekc()` is $sil
|
|
||||||
;; treats all characters less than ' ' as spaces.
|
|
||||||
is_ch:
|
|
||||||
push rsi
|
|
||||||
call peekc
|
|
||||||
pop rsi
|
|
||||||
cmp al, ' '
|
|
||||||
setbe cl ; cl = al <= ' '
|
|
||||||
movzx ecx, cl
|
|
||||||
mov edx, ' '
|
|
||||||
mul ecx, edx ; cl = (al < ' ') ? ' ' : 0
|
|
||||||
cmp sil, ' '
|
|
||||||
cmove ax, cx ; al = (sil < ' ') ? cl : al
|
|
||||||
cmp al, sil
|
|
||||||
setz al ; al = al == sil
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; struct packed LabelEntry {
|
|
||||||
;; hash: u64,
|
|
||||||
;; file_offset: Option<NonMaxU32>,
|
|
||||||
;; }
|
|
||||||
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
|
||||||
push_lbl:
|
|
||||||
push r12
|
|
||||||
lea r12, [rel labels]
|
|
||||||
mov rax, dword [r12 + 0x1000 - 4] ; get current label count
|
|
||||||
cmp rax, 341
|
|
||||||
jge panic_abort
|
|
||||||
lea rax, [rax + rax*2]
|
|
||||||
shl rax, 2 ; $rax = num_labels + size_of::<label_entry>()
|
|
||||||
add r12, rax
|
|
||||||
call fasthash
|
|
||||||
mov qword [r12], rax ; store hash in labels array
|
|
||||||
mov dword [r12 + 8], -1 ; initialize file offset to -1 (invalid)
|
|
||||||
inc dword [r12 + 0x1000 - 4] ; increment label count
|
|
||||||
pop r12
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; searches for the offset of the label with the name pointed at by rdi with length rsi, and returns it in rax. If the label is not found, returns 0 in rax.
|
|
||||||
find_lbl_ptr:
|
|
||||||
call fasthash
|
|
||||||
lea rdx, [r15 + 0x5010] ; rdx = pointer to start of labels array
|
|
||||||
mov ebx, dword [r15 + 0x6000]
|
|
||||||
.loop:
|
|
||||||
test rbx, rbx
|
|
||||||
jz .not_found
|
|
||||||
dec rbx
|
|
||||||
cmp qword [rdx], rax
|
|
||||||
je .found
|
|
||||||
add rdx, 12 ; move to next label entry
|
|
||||||
jmp .loop
|
|
||||||
.found:
|
|
||||||
mov rax, rdx
|
|
||||||
ret
|
|
||||||
.not_found:
|
|
||||||
xor rax, rax
|
|
||||||
ret
|
|
||||||
|
|
||||||
push_or_find_lbl:
|
|
||||||
call fasthash
|
|
||||||
push rax
|
|
||||||
mov rdi, rax
|
|
||||||
call find_lbl_ptr
|
|
||||||
test rax, rax
|
|
||||||
jz .push
|
|
||||||
add rsp, 8
|
|
||||||
ret
|
|
||||||
.push:
|
|
||||||
pop rdi
|
|
||||||
call push_lbl
|
|
||||||
ret
|
|
||||||
|
|
||||||
find_lbl_offset:
|
|
||||||
call find_lbl_ptr
|
|
||||||
test rax, rax
|
|
||||||
jz .not_found
|
|
||||||
mov eax, dword [rax + 8] ; get file offset of label
|
|
||||||
ret
|
|
||||||
.not_found:
|
|
||||||
xor rdi, rdi
|
|
||||||
call panic_abort
|
|
||||||
|
|
||||||
set_lbl_offset:
|
|
||||||
push rsi
|
|
||||||
call find_lbl_ptr
|
|
||||||
pop rsi
|
|
||||||
mov dword [rax + 8], esi ; set file offset of label
|
|
||||||
ret
|
|
||||||
|
|
||||||
is_alpha:
|
|
||||||
movzx edi, dil
|
|
||||||
and edi, 0x1fffdf ; ignore bit 5 (case)
|
|
||||||
add edi, -65 ; subtract 'A'
|
|
||||||
cmp edi, 26 ; check if in range 0-25
|
|
||||||
setb al
|
|
||||||
ret
|
|
||||||
|
|
||||||
is_digit:
|
|
||||||
movzx edi, dil
|
|
||||||
lea eax, [rdi - 48] ; subtract '0'
|
|
||||||
cmp eax, 10 ; check if in range 0-9
|
|
||||||
setb al
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; returns 1 if $dil is a valid identifier character (alphanumeric, '-' or '_'), and 0 otherwise.
|
|
||||||
;; clobbers rdi, rax, rcx
|
|
||||||
is_id_cont:
|
|
||||||
movzx rdi, dil
|
|
||||||
; is_digit {
|
|
||||||
lea eax, [rdi - 48]
|
|
||||||
cmp eax, 10
|
|
||||||
setb al
|
|
||||||
; } || is_alpha {
|
|
||||||
mov ecx, edi
|
|
||||||
and ecx, 0x1fffdf
|
|
||||||
add ecx, -65
|
|
||||||
cmp ecx, 26
|
|
||||||
setb cl
|
|
||||||
; } is_alpha
|
|
||||||
or al, cl
|
|
||||||
cmp edi, 45 ; || '-'
|
|
||||||
sete cl
|
|
||||||
or al, cl
|
|
||||||
cmp edi, 95 ; || '_'
|
|
||||||
sete cl
|
|
||||||
or al, cl
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx.
|
|
||||||
;; $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
|
||||||
to_digit:
|
|
||||||
lea eax, [rsi - 2]
|
|
||||||
cmp eax, 35
|
|
||||||
jae .invalid
|
|
||||||
movzx rdi, dil
|
|
||||||
lea edx, [rdi - 65] ; 'A' = 65
|
|
||||||
and edx, -33 ; convert to uppercase
|
|
||||||
add edx, 10 ; 'A' should map to 10
|
|
||||||
lea eax, [rdi - 48] ; '0' = 48
|
|
||||||
cmp esi, 11
|
|
||||||
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
|
||||||
cmp edi, 58
|
|
||||||
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
|
||||||
xor eax, eax
|
|
||||||
cmp edx, esi
|
|
||||||
setb al ; al = edx < radix
|
|
||||||
ret
|
|
||||||
.invalid:
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; reads from $rdi and parses digits with radix $rsi until a non-digit is encountered.
|
|
||||||
parse_digits:
|
|
||||||
sub rsp, 24
|
|
||||||
mov qword [rsp + 16], rsi ; save the radix
|
|
||||||
mov qword [rsp + 8], rdi ; save the source iterator
|
|
||||||
mov qword [rsp], 0
|
|
||||||
.loop:
|
|
||||||
mov rdi, qword [rsp + 8] ; restore the source iterator
|
|
||||||
call peekc
|
|
||||||
mov dil, al
|
|
||||||
mov rsi, qword [rsp + 16] ; restore the radix
|
|
||||||
call to_digit
|
|
||||||
test al, al
|
|
||||||
jz .done
|
|
||||||
mov rax, qword [rsp]
|
|
||||||
mov rsi, qword [rsp + 16] ; restore the radix
|
|
||||||
imul rax, rsi
|
|
||||||
add rax, rdx
|
|
||||||
mov qword [rsp], rax
|
|
||||||
mov rdi, qword [rsp + 8] ; restore the source iterator
|
|
||||||
call getc
|
|
||||||
jmp .loop
|
|
||||||
|
|
||||||
.done:
|
|
||||||
mov rax, [rsp]
|
|
||||||
add rsp, 24
|
|
||||||
ret
|
|
||||||
|
|
||||||
parse_num:
|
|
||||||
sub rsp, 24
|
|
||||||
mov qword [rsp], 0 ; acc
|
|
||||||
mov qword [rsp + 8], rdi ; source iterator
|
|
||||||
mov dword [rsp + 16], 10 ; radix
|
|
||||||
call peekc
|
|
||||||
cmp al, `-`
|
|
||||||
jne .skip_sign
|
|
||||||
mov qword [rsp], -1 ; acc = -1
|
|
||||||
call consuming_peekc
|
|
||||||
.skip_sign:
|
|
||||||
cmp al, `0`
|
|
||||||
jne .skip_radix
|
|
||||||
call consuming_peekc
|
|
||||||
cmp al, `x`
|
|
||||||
jne .skip_radix
|
|
||||||
mov dword [rsp + 16], 16 ; radix = 16
|
|
||||||
call consuming_peekc
|
|
||||||
.skip_radix:
|
|
||||||
mov dil, al
|
|
||||||
call to_digit
|
|
||||||
test al, al
|
|
||||||
jz .done
|
|
||||||
mov rax, qword [rsp] ; acc
|
|
||||||
mov rsi, qword [rsp + 16] ; radix
|
|
||||||
mov rcx, rdx
|
|
||||||
imul rax, rsi
|
|
||||||
add rax, rcx
|
|
||||||
mov qword [rsp], rax ; acc = acc * radix + digit
|
|
||||||
mov rdi, qword [rsp + 8] ; source iterator
|
|
||||||
call consuming_peekc
|
|
||||||
jmp .skip_radix
|
|
||||||
.done:
|
|
||||||
mov rax, qword [rsp] ; move the result into rax
|
|
||||||
add rsp, 24
|
|
||||||
ret
|
|
||||||
|
|
||||||
parse_label:
|
|
||||||
push r12
|
|
||||||
lea r12, [rel buf]
|
|
||||||
sub rsp, 8
|
|
||||||
mov qword [rsp], rdi ; source iterator
|
|
||||||
call consuming_peekc
|
|
||||||
mov dil, al
|
|
||||||
call is_alpha
|
|
||||||
test al, al
|
|
||||||
jz .done
|
|
||||||
mov byte [r12], al
|
|
||||||
inc r12
|
|
||||||
mov rdi, qword [rsp] ; restore source iterator
|
|
||||||
call consuming_peekc
|
|
||||||
mov dil, al
|
|
||||||
call is_id_cont
|
|
||||||
test al, al
|
|
||||||
jz .done
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
lea rdi, [rel buf]
|
|
||||||
mov rsi, r12
|
|
||||||
sub rsi, rdi
|
|
||||||
call push_or_find_lbl
|
|
||||||
add rsp, 8
|
|
||||||
pop r12
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; reads the next token into `token_buf`
|
|
||||||
next_token:
|
|
||||||
sub rsp, 8
|
|
||||||
mov qword [rsp], 0
|
|
||||||
.skip_spaces:
|
|
||||||
mov rsi, ' '
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jz .skipped_spaces:
|
|
||||||
call getc
|
|
||||||
jmp .skip_spaces
|
|
||||||
.skipped_spaces:
|
|
||||||
; leading [ is a memory access
|
|
||||||
mov rsi, `[`
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jnz .mem_access
|
|
||||||
; leading @ is label-def
|
|
||||||
mov rsi, '@'
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jnz .label_def
|
|
||||||
; leading ' is a label-ref
|
|
||||||
mov rsi, `'`
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jnz .label_ref
|
|
||||||
; leading digit is a number
|
|
||||||
call peekc
|
|
||||||
mov rdi, rax
|
|
||||||
call is_digit
|
|
||||||
test al, al
|
|
||||||
jnz .number
|
|
||||||
; leading ; or # is a comment
|
|
||||||
mov rsi, `;`
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jnz .comment
|
|
||||||
mov rsi, `#`
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jnz .comment
|
|
||||||
; leading , is a separator
|
|
||||||
mov rsi, `,`
|
|
||||||
call is_ch
|
|
||||||
test al, al
|
|
||||||
jz .ident
|
|
||||||
; just skip the comma since its not important for parsing
|
|
||||||
call getc
|
|
||||||
jmp .skip_spaces
|
|
||||||
.ident:
|
|
||||||
; now we have some identifier
|
|
||||||
|
|
||||||
; (mov rax (byte rdi))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; registers:
|
|
||||||
;; r0/rax
|
|
||||||
;; r1/rcx
|
|
||||||
;; r2/rdx
|
|
||||||
;; r3/rbx
|
|
||||||
;; r4/rsp
|
|
||||||
;; r5/rbp
|
|
||||||
;; r6/rsi
|
|
||||||
;; r7/rdi
|
|
||||||
;; r8
|
|
||||||
;; r9
|
|
||||||
;; r10
|
|
||||||
;; r11
|
|
||||||
;; r12
|
|
||||||
;; r13
|
|
||||||
;; r14
|
|
||||||
;; r15
|
|
||||||
;;
|
|
||||||
;; registers of the form `rNUM` may have an optional suffix b, w, d, or q
|
|
||||||
;; legacy registers may also be referenced by their 8-bit, 16-bit, or 32-bit names (e.g. al, ax, eax, rax or spl, sp, esp, rsp)
|
|
||||||
;; high-byte registers (ah, bh, ch, dh) are not supported
|
|
||||||
|
|
||||||
|
|
@ -1,415 +0,0 @@
|
||||||
unsafe extern "C" {
|
|
||||||
// unsafe fn parse_gpr(src: *mut Source, prefix: u8) -> (u64, u64);
|
|
||||||
// unsafe fn parse_reg(src: *mut Source) -> (u64, u64);
|
|
||||||
|
|
||||||
unsafe fn try_parse_reg(_: *const u8) -> (usize, Register);
|
|
||||||
unsafe fn try_parse_mem(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
|
||||||
#[link_name = "try_parse_operand"]
|
|
||||||
unsafe fn try_parse_operand_impl(_: *const u8, _: *mut Operand) -> (usize, *const Operand);
|
|
||||||
|
|
||||||
#[link_name = "try_parse_inst"]
|
|
||||||
unsafe fn try_parse_inst_impl(_: *const u8, _: *mut Instruction)
|
|
||||||
-> (usize, *const Instruction);
|
|
||||||
|
|
||||||
#[link_name = "encode_inst"]
|
|
||||||
unsafe fn encode_inst_impl(_: *mut Instruction) -> usize;
|
|
||||||
|
|
||||||
unsafe fn init_label_tables();
|
|
||||||
|
|
||||||
unsafe fn fasthash(_: *const u8, _: usize) -> u32;
|
|
||||||
|
|
||||||
#[link_name = "buf"]
|
|
||||||
static mut BUF: [u8; 0x100];
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
struct Register {
|
|
||||||
size: u32,
|
|
||||||
num: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
enum OperandKind {
|
|
||||||
None = 0,
|
|
||||||
Reg = 1,
|
|
||||||
Mem = 2,
|
|
||||||
Imm = 4,
|
|
||||||
MemLabel = 2 | 8,
|
|
||||||
ImmLabel = 4 | 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C, align(8))]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
struct Operand {
|
|
||||||
kind: OperandKind,
|
|
||||||
size: u32,
|
|
||||||
reg: Register,
|
|
||||||
index: Register,
|
|
||||||
scale: u32,
|
|
||||||
disp: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Operand {
|
|
||||||
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
|
|
||||||
let mut op = core::mem::MaybeUninit::<Operand>::uninit();
|
|
||||||
let (n, ptr) = unsafe { try_parse_operand_impl(bytes.as_ptr(), op.as_mut_ptr()) };
|
|
||||||
if n == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((n, unsafe { op.assume_init() }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn label(name: &[u8]) -> Self {
|
|
||||||
let mut this = Self {
|
|
||||||
kind: OperandKind::ImmLabel,
|
|
||||||
size: 2,
|
|
||||||
reg: Register::invalid(),
|
|
||||||
index: Register::invalid(),
|
|
||||||
scale: 0,
|
|
||||||
disp: 0,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
let hash = fasthash(name.as_ptr(), name.len());
|
|
||||||
(&raw mut this).byte_add(8).cast::<u32>().write(hash as u32);
|
|
||||||
}
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
fn imm(value: i64) -> Self {
|
|
||||||
let size = if value.abs() == 0 {
|
|
||||||
value as u32
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
(value.abs() as u64)
|
|
||||||
.checked_ilog2()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
.saturating_sub(1)
|
|
||||||
.checked_ilog2()
|
|
||||||
.unwrap_or(0)
|
|
||||||
+ 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut this = Self {
|
|
||||||
kind: OperandKind::Imm,
|
|
||||||
size,
|
|
||||||
reg: Register::invalid(),
|
|
||||||
index: Register::invalid(),
|
|
||||||
scale: 0,
|
|
||||||
disp: 0,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
(&raw mut this).byte_add(8).cast::<i64>().write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
struct Instruction {
|
|
||||||
mnemonic_offset: i32,
|
|
||||||
num_operands: u32,
|
|
||||||
operands: [Operand; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instruction {
|
|
||||||
fn parse(bytes: &[u8]) -> Option<(usize, Self)> {
|
|
||||||
let mut inst = core::mem::MaybeUninit::<Instruction>::uninit();
|
|
||||||
let (n, ptr) = unsafe { try_parse_inst_impl(bytes.as_ptr(), inst.as_mut_ptr()) };
|
|
||||||
if n == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((n, unsafe { inst.assume_init() }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Register {
|
|
||||||
fn new(num: u32, size: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
num,
|
|
||||||
size: size.ilog2(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn invalid() -> Self {
|
|
||||||
Self { num: !0, size: !0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rbp() -> Self {
|
|
||||||
Self { num: 5, size: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut TABLES_INIT: std::cell::LazyCell<()> =
|
|
||||||
std::cell::LazyCell::new(|| unsafe { init_label_tables() });
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
|
|
||||||
fn write_to_buf(b: &[u8]) {
|
|
||||||
assert!(b.len() <= 0x100, "Buffer overflow");
|
|
||||||
unsafe {
|
|
||||||
core::ptr::copy_nonoverlapping(b.as_ptr(), (&raw mut BUF).cast(), b.len());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_inst() {
|
|
||||||
_ = unsafe { *TABLES_INIT };
|
|
||||||
let res = Instruction::parse(b"mov byte ['rax], 4\0");
|
|
||||||
// assert_eq!(
|
|
||||||
// res,
|
|
||||||
// Some((
|
|
||||||
// 12,
|
|
||||||
// Instruction {
|
|
||||||
// mnemonic_offset: 0,
|
|
||||||
// num_operands: 2,
|
|
||||||
// operands: [
|
|
||||||
// Operand {
|
|
||||||
// kind: OperandKind::Reg,
|
|
||||||
// size: 3,
|
|
||||||
// reg: Register::new(0, 8),
|
|
||||||
// index: Register::invalid(),
|
|
||||||
// scale: 0,
|
|
||||||
// disp: 0,
|
|
||||||
// },
|
|
||||||
// Operand {
|
|
||||||
// kind: OperandKind::Reg,
|
|
||||||
// size: 3,
|
|
||||||
// reg: Register::new(3, 8),
|
|
||||||
// index: Register::invalid(),
|
|
||||||
// scale: 0,
|
|
||||||
// disp: 0,
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// }
|
|
||||||
// ))
|
|
||||||
// );
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut inst = res.unwrap().1;
|
|
||||||
encode_inst_impl((&raw mut inst).cast());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_mem() {
|
|
||||||
let mut op = MaybeUninit::<Operand>::uninit();
|
|
||||||
let (n, _) =
|
|
||||||
unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) };
|
|
||||||
assert_eq!(
|
|
||||||
(n, unsafe { op.assume_init_ref() }),
|
|
||||||
(
|
|
||||||
28,
|
|
||||||
&Operand {
|
|
||||||
kind: OperandKind::Mem,
|
|
||||||
size: 3,
|
|
||||||
reg: Register::new(0, 8),
|
|
||||||
index: Register::new(3, 8),
|
|
||||||
scale: 2,
|
|
||||||
disp: 0x10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let (n, _) =
|
|
||||||
unsafe { try_parse_mem(b"byte [r12 + r13 * 4 - 0x20]\0".as_ptr(), op.as_mut_ptr()) };
|
|
||||||
assert_eq!(
|
|
||||||
(n, unsafe { op.assume_init_ref() }),
|
|
||||||
(
|
|
||||||
27,
|
|
||||||
&Operand {
|
|
||||||
kind: OperandKind::Mem,
|
|
||||||
size: 0,
|
|
||||||
reg: Register::new(12, 8),
|
|
||||||
index: Register::new(13, 8),
|
|
||||||
scale: 4,
|
|
||||||
disp: -0x20,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let (n, _) = unsafe { try_parse_mem(b"word [r8 * 8 + 5]\0".as_ptr(), op.as_mut_ptr()) };
|
|
||||||
assert_eq!(
|
|
||||||
(n, unsafe { op.assume_init_ref() }),
|
|
||||||
(
|
|
||||||
17,
|
|
||||||
&Operand {
|
|
||||||
kind: OperandKind::Mem,
|
|
||||||
size: 1,
|
|
||||||
reg: Register::rbp(),
|
|
||||||
index: Register::new(8, 8),
|
|
||||||
scale: 8,
|
|
||||||
disp: 5,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_operand() {
|
|
||||||
let cases = [
|
|
||||||
(
|
|
||||||
&b"rax\0"[..],
|
|
||||||
Operand {
|
|
||||||
kind: OperandKind::Reg,
|
|
||||||
size: 3,
|
|
||||||
reg: Register::new(0, 8),
|
|
||||||
index: Register::invalid(),
|
|
||||||
scale: 0,
|
|
||||||
disp: 0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
&b"qword [rax + rbx * 2 + 0x10]\0"[..],
|
|
||||||
Operand {
|
|
||||||
kind: OperandKind::Mem,
|
|
||||||
size: 3,
|
|
||||||
reg: Register::new(0, 8),
|
|
||||||
index: Register::new(3, 8),
|
|
||||||
scale: 2,
|
|
||||||
disp: 0x10,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
&b"byte [r12 + r13 * 4 - 0x20]\0"[..],
|
|
||||||
Operand {
|
|
||||||
kind: OperandKind::Mem,
|
|
||||||
size: 0,
|
|
||||||
reg: Register::new(12, 8),
|
|
||||||
index: Register::new(13, 8),
|
|
||||||
scale: 4,
|
|
||||||
disp: -0x20,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
&b"word [r8 * 8 + 5]\0"[..],
|
|
||||||
Operand {
|
|
||||||
kind: OperandKind::Mem,
|
|
||||||
size: 1,
|
|
||||||
reg: Register::rbp(),
|
|
||||||
index: Register::new(8, 8),
|
|
||||||
scale: 8,
|
|
||||||
disp: 5,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(&b"0x12345678\0"[..], Operand::imm(0x12345678)),
|
|
||||||
(&b"-0x12345678\0"[..], Operand::imm(-0x12345678)),
|
|
||||||
(&b"0\0"[..], Operand::imm(0)),
|
|
||||||
(&b"-1\0"[..], Operand::imm(-1)),
|
|
||||||
(&b"1\0"[..], Operand::imm(1)),
|
|
||||||
(&b"255\0"[..], Operand::imm(255)),
|
|
||||||
(&b"-255\0"[..], Operand::imm(-255)),
|
|
||||||
(&b"'wazzaah\0"[..], Operand::label(b"wazzaah")),
|
|
||||||
(
|
|
||||||
&b"qword [rsp + 'hii]"[..],
|
|
||||||
Operand {
|
|
||||||
kind: OperandKind::MemLabel,
|
|
||||||
size: 3,
|
|
||||||
reg: Register::new(4, 8),
|
|
||||||
index: Register::invalid(),
|
|
||||||
scale: 0,
|
|
||||||
disp: unsafe { fasthash(b"hii".as_ptr(), 3) as i32 },
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (text, expected) in cases {
|
|
||||||
let (n, result) = Operand::parse(text).expect("Failed to parse operand");
|
|
||||||
assert_eq!(
|
|
||||||
result,
|
|
||||||
expected,
|
|
||||||
"Failed to parse '{}'",
|
|
||||||
std::str::from_utf8(text).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_reg() {
|
|
||||||
let cases = [
|
|
||||||
(&b"rax\0"[..], (0, 8)),
|
|
||||||
(&b"rcx\0"[..], (1, 8)),
|
|
||||||
(&b"rdx\0"[..], (2, 8)),
|
|
||||||
(&b"rbx\0"[..], (3, 8)),
|
|
||||||
(&b"rsp\0"[..], (4, 8)),
|
|
||||||
(&b"rbp\0"[..], (5, 8)),
|
|
||||||
(&b"rsi\0"[..], (6, 8)),
|
|
||||||
(&b"rdi\0"[..], (7, 8)),
|
|
||||||
(&b"r8\0"[..], (8, 8)),
|
|
||||||
(&b"r9\0"[..], (9, 8)),
|
|
||||||
(&b"r10\0"[..], (10, 8)),
|
|
||||||
(&b"r11\0"[..], (11, 8)),
|
|
||||||
(&b"r12\0"[..], (12, 8)),
|
|
||||||
(&b"r13\0"[..], (13, 8)),
|
|
||||||
(&b"r14\0"[..], (14, 8)),
|
|
||||||
(&b"r15\0"[..], (15, 8)),
|
|
||||||
(&b"rip\0"[..], (!0, 8)),
|
|
||||||
(&b"eax\0"[..], (0, 4)),
|
|
||||||
(&b"ecx\0"[..], (1, 4)),
|
|
||||||
(&b"edx\0"[..], (2, 4)),
|
|
||||||
(&b"ebx\0"[..], (3, 4)),
|
|
||||||
(&b"esp\0"[..], (4, 4)),
|
|
||||||
(&b"ebp\0"[..], (5, 4)),
|
|
||||||
(&b"esi\0"[..], (6, 4)),
|
|
||||||
(&b"edi\0"[..], (7, 4)),
|
|
||||||
(&b"r8d\0"[..], (8, 4)),
|
|
||||||
(&b"r9d\0"[..], (9, 4)),
|
|
||||||
(&b"r10d\0"[..], (10, 4)),
|
|
||||||
(&b"r11d\0"[..], (11, 4)),
|
|
||||||
(&b"r12d\0"[..], (12, 4)),
|
|
||||||
(&b"r13d\0"[..], (13, 4)),
|
|
||||||
(&b"r14d\0"[..], (14, 4)),
|
|
||||||
(&b"r15d\0"[..], (15, 4)),
|
|
||||||
(&b"r8w\0"[..], (8, 2)),
|
|
||||||
(&b"r9w\0"[..], (9, 2)),
|
|
||||||
(&b"r10w\0"[..], (10, 2)),
|
|
||||||
(&b"r11w\0"[..], (11, 2)),
|
|
||||||
(&b"r12w\0"[..], (12, 2)),
|
|
||||||
(&b"r13w\0"[..], (13, 2)),
|
|
||||||
(&b"r14w\0"[..], (14, 2)),
|
|
||||||
(&b"r15w\0"[..], (15, 2)),
|
|
||||||
(&b"r8b\0"[..], (8, 1)),
|
|
||||||
(&b"r9b\0"[..], (9, 1)),
|
|
||||||
(&b"r10b\0"[..], (10, 1)),
|
|
||||||
(&b"r11b\0"[..], (11, 1)),
|
|
||||||
(&b"r12b\0"[..], (12, 1)),
|
|
||||||
(&b"r13b\0"[..], (13, 1)),
|
|
||||||
(&b"r14b\0"[..], (14, 1)),
|
|
||||||
(&b"r15b\0"[..], (15, 1)),
|
|
||||||
(&b"ax\0"[..], (0, 2)),
|
|
||||||
(&b"cx\0"[..], (1, 2)),
|
|
||||||
(&b"dx\0"[..], (2, 2)),
|
|
||||||
(&b"bx\0"[..], (3, 2)),
|
|
||||||
(&b"sp\0"[..], (4, 2)),
|
|
||||||
(&b"bp\0"[..], (5, 2)),
|
|
||||||
(&b"si\0"[..], (6, 2)),
|
|
||||||
(&b"di\0"[..], (7, 2)),
|
|
||||||
(&b"al\0"[..], (0, 1)),
|
|
||||||
(&b"cl\0"[..], (1, 1)),
|
|
||||||
(&b"dl\0"[..], (2, 1)),
|
|
||||||
(&b"bl\0"[..], (3, 1)),
|
|
||||||
(&b"spl\0"[..], (4, 1)),
|
|
||||||
(&b"bpl\0"[..], (5, 1)),
|
|
||||||
(&b"sil\0"[..], (6, 1)),
|
|
||||||
(&b"dil\0"[..], (7, 1)),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (text, (reg, size)) in cases {
|
|
||||||
let (n, result) = unsafe { try_parse_reg(text.as_ptr()) };
|
|
||||||
assert_eq!(
|
|
||||||
(n, result),
|
|
||||||
(text.len() - 1, Register::new(reg, size)),
|
|
||||||
"Failed to parse '{}' n={n}",
|
|
||||||
std::str::from_utf8(text).unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
@_start:
|
|
||||||
mov rdi, 0xa21444C52
|
|
||||||
push rdi
|
|
||||||
mov rdi, 0x4F57204F
|
|
||||||
shl rdi, 32
|
|
||||||
mov rsi, 0x4C4C4548
|
|
||||||
or rdi, rsi
|
|
||||||
push rdi
|
|
||||||
@init:
|
|
||||||
lea rsi, qword [rsp]
|
|
||||||
mov edx, 13
|
|
||||||
mov eax, 1
|
|
||||||
mov edi, 1
|
|
||||||
syscall
|
|
||||||
jmp 'init
|
|
||||||
jmp '_start
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
const PackedOperandKind = enum(u3) {
|
|
||||||
reg = 0,
|
|
||||||
imm = 1,
|
|
||||||
mem = 2,
|
|
||||||
label = 3,
|
|
||||||
mem_label = 4,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const Register = enum(u4) {
|
|
||||||
rax = 0,
|
|
||||||
rbx = 1,
|
|
||||||
rcx = 2,
|
|
||||||
rdx = 3,
|
|
||||||
rsi = 4,
|
|
||||||
rdi = 5,
|
|
||||||
rsp = 6,
|
|
||||||
rbp = 7,
|
|
||||||
r8 = 8,
|
|
||||||
r9 = 9,
|
|
||||||
r10 = 10,
|
|
||||||
r11 = 11,
|
|
||||||
r12 = 12,
|
|
||||||
r13 = 13,
|
|
||||||
r14 = 14,
|
|
||||||
r15 = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
const PackedRegister = packed struct {
|
|
||||||
reg: Register,
|
|
||||||
size: u4,
|
|
||||||
};
|
|
||||||
|
|
||||||
const PackedOperand = union(PackedOperandKind) {
|
|
||||||
reg: PackedRegister,
|
|
||||||
imm: i64,
|
|
||||||
label: u32,
|
|
||||||
mem_label: u32,
|
|
||||||
mem: struct {
|
|
||||||
base: PackedRegister,
|
|
||||||
index: PackedRegister,
|
|
||||||
scale: u2,
|
|
||||||
disp: i32,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
BUILD = ../build
|
|
||||||
|
|
||||||
all: $(BUILD)/stage0
|
|
||||||
|
|
||||||
stage0: $(BUILD)/stage0
|
|
||||||
|
|
||||||
stage0-bootstrap: $(BUILD)/stage0-bootstrap
|
|
||||||
|
|
||||||
$(BUILD)/stage0-bootstrap: hex0.in
|
|
||||||
for x in `sed '/^;/d;s/;.*//;s/ *//' hex0.in`; do printf \\x$$x; done > $@
|
|
||||||
chmod +x $@
|
|
||||||
|
|
||||||
$(BUILD)/stage0: $(BUILD)/stage0-bootstrap
|
|
||||||
$< < hex0.in > $@
|
|
||||||
chmod +x $@
|
|
||||||
|
|
@ -1,221 +0,0 @@
|
||||||
; ELF header
|
|
||||||
; e_ident: u128
|
|
||||||
; Magic number: 0x7f454c46 = "\x7fELF"
|
|
||||||
7f 45 4c 46
|
|
||||||
; EI_CLASS: u8 = ELFCLASS64
|
|
||||||
02
|
|
||||||
; EI_DATA: u8 = ELFDATA2LSB
|
|
||||||
01
|
|
||||||
; EI_VERSION: u8 = EV_CURRENT
|
|
||||||
01
|
|
||||||
; EI_OSABI: u8 = ELFOSABI_NONE
|
|
||||||
00
|
|
||||||
; EI_ABIVERSION: u8 = 0
|
|
||||||
00
|
|
||||||
; EI_PAD: [u8; 7]
|
|
||||||
00 00 00 00 00 00 00
|
|
||||||
; e_type: u16 = E_EXEC
|
|
||||||
02 00
|
|
||||||
; e_machine: u16 = EM_X86_64
|
|
||||||
3e 00
|
|
||||||
; e_version: u32 = EV_CURRENT
|
|
||||||
01 00 00 00
|
|
||||||
; e_entry: u64 = 0x400078
|
|
||||||
78 00 40 00 00 00 00 00
|
|
||||||
; e_phoff: u64 = 0x40
|
|
||||||
40 00 00 00 00 00 00 00
|
|
||||||
; e_shoff: u64 = 0x0
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
; e_flags: u32 = 0
|
|
||||||
00 00 00 00
|
|
||||||
; e_ehsize: u16 = 64
|
|
||||||
40 00
|
|
||||||
; e_phentsize: u16 = 56
|
|
||||||
38 00
|
|
||||||
; e_phnum: u16 = 1
|
|
||||||
01 00
|
|
||||||
; e_shentsize: u16 = 0
|
|
||||||
00 00
|
|
||||||
; e_shnum: u16 = 0
|
|
||||||
00 00
|
|
||||||
; e_shstrndx: u16 = 0
|
|
||||||
00 00
|
|
||||||
; End of ELF header
|
|
||||||
;
|
|
||||||
; Program header
|
|
||||||
; p_type: u32 = PT_LOAD
|
|
||||||
01 00 00 00
|
|
||||||
; p_flags: u32 = PF_R | PF_X
|
|
||||||
05 00 00 00
|
|
||||||
; p_offset: u64 = 0
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
; p_vaddr: u64 = 0x400000
|
|
||||||
00 00 40 00 00 00 00 00
|
|
||||||
; p_paddr: u64 = 0x0
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
; p_filesz: u64 = 0x167
|
|
||||||
67 01 00 00 00 00 00 00
|
|
||||||
; p_memsz: u64 = 0x167
|
|
||||||
67 01 00 00 00 00 00 00
|
|
||||||
; p_align: u64 = 0x1000
|
|
||||||
00 10 00 00 00 00 00 00
|
|
||||||
; End of program header
|
|
||||||
;
|
|
||||||
; Code segment (offset 0x40, virtual address 0x400000)
|
|
||||||
;
|
|
||||||
; _start=120:
|
|
||||||
; ; alloca 0x1000 bytes on the stack
|
|
||||||
; mov rbp, rsp
|
|
||||||
48 89 e5
|
|
||||||
; sub rsp, 0x10000, increase this if you want to read more than 0x10000 bytes
|
|
||||||
48 81 ec 00 00 01 00
|
|
||||||
; ; read 0x10000 bytes from stdin into the stack
|
|
||||||
; .READ: (130)
|
|
||||||
; xor rax, rax
|
|
||||||
48 31 c0
|
|
||||||
; mov edi, 0 (stdin)
|
|
||||||
48 c7 c7 00 00 00 00
|
|
||||||
; mov rsi, rsp
|
|
||||||
48 89 e6
|
|
||||||
; mov rdx, 0x10000, increase this if you want to read more than 0x10000 bytes
|
|
||||||
48 c7 c2 00 00 01 00
|
|
||||||
; syscall (sys_read)
|
|
||||||
0f 05
|
|
||||||
; cmp rax, 0
|
|
||||||
48 83 f8 00
|
|
||||||
; jle die
|
|
||||||
0f 8e b3 00 00 00
|
|
||||||
;# (162)
|
|
||||||
; ; end = r8
|
|
||||||
; mov r8, rax
|
|
||||||
49 89 c0
|
|
||||||
; add r8, rsp
|
|
||||||
49 01 e0
|
|
||||||
; ; pos = r9
|
|
||||||
; mov r9, rsp
|
|
||||||
49 89 e1
|
|
||||||
; ; dst = r10
|
|
||||||
; mov r10, rsp
|
|
||||||
49 89 e2
|
|
||||||
; add r10, 0x0000
|
|
||||||
49 81 c2 00 00 00 00
|
|
||||||
; ; while (pos < end) {
|
|
||||||
; ; .L0: (181)
|
|
||||||
; cmp r9, r8
|
|
||||||
4d 39 c1
|
|
||||||
; jge .L3
|
|
||||||
0f 8d 72 00 00 00
|
|
||||||
; (190)
|
|
||||||
;
|
|
||||||
; ; if (*pos == ';') {
|
|
||||||
; mov al , byte [r9]
|
|
||||||
41 8a 41 00
|
|
||||||
; cmp al, '\n'
|
|
||||||
3c 0a
|
|
||||||
; je .L2.1:
|
|
||||||
0f 84 15 00 00 00
|
|
||||||
; (202)
|
|
||||||
; cmp al, ';'
|
|
||||||
3c 3b
|
|
||||||
; jne .L1
|
|
||||||
0f 85 15 00 00 00
|
|
||||||
; ; while (*pos != '\n') pos++;
|
|
||||||
; ; .L2: (210)
|
|
||||||
; inc r9
|
|
||||||
49 ff c1
|
|
||||||
; cmp byte [r9], '\n'
|
|
||||||
41 80 39 0a
|
|
||||||
; jne .L2
|
|
||||||
0f 85 f3 ff ff ff
|
|
||||||
; .L2.1: (223)
|
|
||||||
; inc r9
|
|
||||||
49 ff c1
|
|
||||||
; jmp .L0
|
|
||||||
e9 ce ff ff ff
|
|
||||||
; (231)
|
|
||||||
; ; .L1: (231)
|
|
||||||
; ; convert *pos to high word
|
|
||||||
; mov al, byte [r9]
|
|
||||||
41 8a 01
|
|
||||||
; mov bl, al
|
|
||||||
88 c3
|
|
||||||
; sub bl, 'a'
|
|
||||||
80 eb 61
|
|
||||||
; add bl, 10
|
|
||||||
80 c3 0a
|
|
||||||
; sub al, '0'
|
|
||||||
2c 30
|
|
||||||
; cmp al, 10
|
|
||||||
3c 0a
|
|
||||||
; cmovge eax, ebx
|
|
||||||
0f 43 c3
|
|
||||||
; shl al, 4
|
|
||||||
c0 e0 04
|
|
||||||
; ; pos++
|
|
||||||
; inc r9
|
|
||||||
49 ff c1
|
|
||||||
; ; convert *pos to low word
|
|
||||||
; mov bl, byte [r9]
|
|
||||||
41 8a 19
|
|
||||||
; mov cl, bl
|
|
||||||
88 d9
|
|
||||||
; sub cl, 'a'
|
|
||||||
80 e9 61
|
|
||||||
; add cl, 10
|
|
||||||
80 c1 0a
|
|
||||||
; sub bl, '0'
|
|
||||||
80 eb 30
|
|
||||||
; cmp bl, 10
|
|
||||||
80 fb 0a
|
|
||||||
; cmovge ebx, ecx
|
|
||||||
0f 4d d9
|
|
||||||
; ; combine high and low word
|
|
||||||
; or al, bl
|
|
||||||
08 d8
|
|
||||||
; ; write the byte to dst
|
|
||||||
; mov byte [r10], al
|
|
||||||
41 88 02
|
|
||||||
; inc r10
|
|
||||||
49 ff c2
|
|
||||||
; inc r9
|
|
||||||
49 ff c1
|
|
||||||
; ; if (*pos++ == '\n') goto .L1;
|
|
||||||
; cmp byte [r9], '\n'
|
|
||||||
41 80 39 0a
|
|
||||||
; je .L0
|
|
||||||
0f 84 8d ff ff ff
|
|
||||||
;# (296)
|
|
||||||
; inc r9
|
|
||||||
49 ff c1
|
|
||||||
; jmp .L1 ;
|
|
||||||
e9 b7 ff ff ff
|
|
||||||
;# (304)
|
|
||||||
; ; }
|
|
||||||
; .L3: (304)
|
|
||||||
; ; write the decoded bytes to stdout
|
|
||||||
; mov rax, 1 (sys_write)
|
|
||||||
48 c7 c0 01 00 00 00
|
|
||||||
; mov rdi, 1 (stdout)
|
|
||||||
48 c7 c7 01 00 00 00
|
|
||||||
; mov rsi, rsp
|
|
||||||
48 89 e6
|
|
||||||
; add rsi, 0x000
|
|
||||||
48 81 c6 00 00 00 00
|
|
||||||
; mov rdx, r10
|
|
||||||
4c 89 d2
|
|
||||||
; sub rdx, rsi
|
|
||||||
48 29 f2
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; jmp .READ ;
|
|
||||||
e9 2d ff ff ff
|
|
||||||
;# (341)
|
|
||||||
;
|
|
||||||
; die: (341)
|
|
||||||
; mov rax, 60 (sys_exit)
|
|
||||||
48 c7 c0 3c 00 00 00
|
|
||||||
; xor rdi, rdi
|
|
||||||
48 31 ff
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; _start+215
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
;; Program that takes an output file to which it writes elf64 headers and the contents of a second file which is assumed to be a text section.
|
|
||||||
|
|
||||||
die:
|
|
||||||
mov rax, 60 ; syscall: exit
|
|
||||||
mov rdi, 0 ; status: 0
|
|
||||||
syscall
|
|
||||||
ret
|
|
||||||
|
|
||||||
fopen:
|
|
||||||
mov rax, 2 ; syscall: open
|
|
||||||
mov rsi, 0 ; flags: O_RDONLY
|
|
||||||
mov rdx, 0 ; mode
|
|
||||||
syscall
|
|
||||||
ret
|
|
||||||
|
|
||||||
fopen_write:
|
|
||||||
mov rax, 2 ; syscall: open
|
|
||||||
mov rsi, 0x241 ; flags: O_WRONLY | O_CREAT | O_TRUNC
|
|
||||||
mov rdx, 0o644 ; mode: rw-r--r--
|
|
||||||
syscall
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; rdi: fd
|
|
||||||
;; rsi: buffer
|
|
||||||
fstat:
|
|
||||||
mov rax, 5 ; syscall: fstat
|
|
||||||
syscall
|
|
||||||
ret
|
|
||||||
|
|
||||||
fsize:
|
|
||||||
push rdi
|
|
||||||
push rsi
|
|
||||||
call fstat
|
|
||||||
pop rsi
|
|
||||||
pop rdi
|
|
||||||
mov rax, [rsi + 48] ; st_size
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; rdi: src
|
|
||||||
;; rsi: dst
|
|
||||||
;; rdx: len
|
|
||||||
memcpy:
|
|
||||||
.memcpy_loop:
|
|
||||||
test rdx, rdx
|
|
||||||
jz .memcpy_done
|
|
||||||
mov al, byte [rdi]
|
|
||||||
mov byte [rsi], al
|
|
||||||
inc rsi
|
|
||||||
inc rdi
|
|
||||||
dec rdx
|
|
||||||
jmp .memcpy_loop
|
|
||||||
|
|
||||||
.memcpy_done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; variables:
|
|
||||||
;; +0 buffer: [u8; 0x1000]
|
|
||||||
;; +0x1000 bytes_read: u32
|
|
||||||
;; +0x1004 in_size: u32
|
|
||||||
;; +0x1008 in_fd: u32
|
|
||||||
;; +0x100c out_fd: u32
|
|
||||||
;; +0x1010 argc: u32
|
|
||||||
;; +0x1018 argv: *const *const u8
|
|
||||||
|
|
||||||
global _start
|
|
||||||
_start:
|
|
||||||
mov eax, dword [rsp] ; argc
|
|
||||||
lea rbx, [rsp + 8] ; argv
|
|
||||||
sub rsp, 0x4000 ; reserve stack space
|
|
||||||
mov r15, rsp ; r15 = pointer to our state
|
|
||||||
mov dword [r15 + 0x1010], eax ; store argc
|
|
||||||
mov qword [r15 + 0x1018], rbx ; store argv
|
|
||||||
cmp eax, 3
|
|
||||||
jl die
|
|
||||||
|
|
||||||
;; open output file
|
|
||||||
mov rdi, [r15 + 0x1018] ; rdi = argv
|
|
||||||
add rdi, 8 ; rdi = argv[1]
|
|
||||||
mov rdi, [rdi] ; rdi = pointer to argv[1]
|
|
||||||
call fopen_write
|
|
||||||
mov dword [r15 + 0x100c], eax ; store out_fd
|
|
||||||
|
|
||||||
;; open input file and get size
|
|
||||||
mov rdi, [r15 + 0x1018] ; rdi = argv
|
|
||||||
add rdi, 16 ; rdi = argv[2]
|
|
||||||
mov rdi, [rdi] ; rdi = pointer to argv[2]
|
|
||||||
call fopen
|
|
||||||
mov dword [r15 + 0x1008], eax ; store in_fd
|
|
||||||
mov edi, eax ; rdi = in_fd
|
|
||||||
lea rsi, [r15] ; rsi = pointer to statbuf
|
|
||||||
call fsize
|
|
||||||
mov dword [r15 + 0x1004], eax ; store in_size
|
|
||||||
|
|
||||||
;; copy elf header to scratch space
|
|
||||||
mov rdi, 0x400000
|
|
||||||
mov rsi, r15
|
|
||||||
mov rdx, 0x40
|
|
||||||
call memcpy
|
|
||||||
|
|
||||||
;; copy program header to scratch space
|
|
||||||
mov rdi, 0x400040
|
|
||||||
lea rsi, [r15 + 0x40]
|
|
||||||
mov rdx, 0x38
|
|
||||||
call memcpy
|
|
||||||
|
|
||||||
;; write p_filesz and p_memsz to 0x78 + in_size
|
|
||||||
mov rax, 0x78
|
|
||||||
add eax, dword [r15 + 0x1004] ; rax = 0x78 + in_size
|
|
||||||
mov dword [r15 + 0x60], eax ; write p_filesz
|
|
||||||
mov dword [r15 + 0x68], eax ; write p_memsz
|
|
||||||
|
|
||||||
;; write e_entry to point at 0x40 + 0x38 + in_size - 5
|
|
||||||
sub rax, 5
|
|
||||||
add rax, 0x400000
|
|
||||||
mov dword [r15 + 24], eax ; write e_entry
|
|
||||||
|
|
||||||
;; write Ehdr and Phdr to output file
|
|
||||||
mov edi, dword [r15 + 0x100c] ; rdi = out_fd
|
|
||||||
mov rsi, r15 ; rsi = buffer
|
|
||||||
mov rdx, 0x78 ; size of Ehdr + Phdr
|
|
||||||
mov rax, 1 ; syscall: write
|
|
||||||
syscall
|
|
||||||
|
|
||||||
;; copy text section from input file to output file
|
|
||||||
.copy_loop:
|
|
||||||
mov edi, dword [r15 + 0x1008] ; rdi = in_fd
|
|
||||||
mov rsi, r15 ; rsi = pointer to buffer
|
|
||||||
mov rdx, 0x1000 ; rdx = size of buffer
|
|
||||||
mov rax, 0 ; syscall: read
|
|
||||||
syscall
|
|
||||||
test rax, rax
|
|
||||||
jle die
|
|
||||||
mov dword [r15 + 0x1000], eax ; store number of bytes read
|
|
||||||
mov edi, dword [r15 + 0x100c] ; rdi = out_fd
|
|
||||||
mov rsi, r15 ; rsi = pointer to buffer
|
|
||||||
mov edx, dword [r15 + 0x1000] ; rdx = number of bytes read
|
|
||||||
mov rax, 1 ; syscall: write
|
|
||||||
syscall
|
|
||||||
cmp dword [r15 + 0x1000], 0x1000 ; if we read less than 0x1000 bytes, we are done
|
|
||||||
je .copy_loop
|
|
||||||
call die
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
; ELF header
|
|
||||||
; e_ident: u128
|
|
||||||
; Magic number: 0x7f454c46 = "\x7fELF"
|
|
||||||
7f 45 4c 46
|
|
||||||
; EI_CLASS: u8 = ELFCLASS64
|
|
||||||
02
|
|
||||||
; EI_DATA: u8 = ELFDATA2LSB
|
|
||||||
01
|
|
||||||
; EI_VERSION: u8 = EV_CURRENT
|
|
||||||
01
|
|
||||||
; EI_OSABI: u8 = ELFOSABI_NONE
|
|
||||||
00
|
|
||||||
; EI_ABIVERSION: u8 = 0
|
|
||||||
00
|
|
||||||
; EI_PAD: [u8; 7]
|
|
||||||
00 00 00 00 00 00 00
|
|
||||||
; e_type: u16 = E_EXEC
|
|
||||||
02 00
|
|
||||||
; e_machine: u16 = EM_X86_64
|
|
||||||
3e 00
|
|
||||||
; e_version: u32 = EV_CURRENT
|
|
||||||
01 00 00 00
|
|
||||||
; e_entry: u64 = 0x400078 + 0x5c
|
|
||||||
d4 00 40 00 00 00 00 00
|
|
||||||
; e_phoff: u64 = 0x40
|
|
||||||
40 00 00 00 00 00 00 00
|
|
||||||
; e_shoff: u64 = 0x0
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
; e_flags: u32 = 0
|
|
||||||
00 00 00 00
|
|
||||||
; e_ehsize: u16 = 64
|
|
||||||
40 00
|
|
||||||
; e_phentsize: u16 = 56
|
|
||||||
38 00
|
|
||||||
; e_phnum: u16 = 1
|
|
||||||
01 00
|
|
||||||
; e_shentsize: u16 = 0
|
|
||||||
00 00
|
|
||||||
; e_shnum: u16 = 0
|
|
||||||
00 00
|
|
||||||
; e_shstrndx: u16 = 0
|
|
||||||
00 00
|
|
||||||
; End of ELF header
|
|
||||||
;
|
|
||||||
; Program header
|
|
||||||
; p_type: u32 = PT_LOAD
|
|
||||||
01 00 00 00
|
|
||||||
; p_flags: u32 = PF_R | PF_X
|
|
||||||
05 00 00 00
|
|
||||||
; p_offset: u64 = 0
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
; p_vaddr: u64 = 0x400000
|
|
||||||
00 00 40 00 00 00 00 00
|
|
||||||
; p_paddr: u64 = 0x0
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
; p_filesz: u64 = 0x167
|
|
||||||
ef 01 00 00 00 00 00 00
|
|
||||||
; p_memsz: u64 = 0x167
|
|
||||||
ef 01 00 00 00 00 00 00
|
|
||||||
; p_align: u64 = 0x1000
|
|
||||||
00 10 00 00 00 00 00 00
|
|
||||||
; End of program header
|
|
||||||
;
|
|
||||||
; @die: +0
|
|
||||||
; mov rax, 60 (exit)
|
|
||||||
b8 3c 00 00 00
|
|
||||||
; mov rdi, 0 (exit code)
|
|
||||||
bf 00 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @fopen:
|
|
||||||
; mov rax, 2 (sys_open)
|
|
||||||
b8 02 00 00 00
|
|
||||||
; mov rsi, 0
|
|
||||||
be 00 00 00 00
|
|
||||||
; mov rdx, 0
|
|
||||||
ba 00 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; fopen_write:
|
|
||||||
; mov rax, 2 (sys_open)
|
|
||||||
b8 02 00 00 00
|
|
||||||
; mov rsi, 0x241 (O_WRONLY | O_CREAT | O_TRUNC)
|
|
||||||
be 41 02 00 00
|
|
||||||
; mov rdx, 0o644 (file mode)
|
|
||||||
ba a4 01 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @fstat:
|
|
||||||
; mov rax, 5 (sys_fstat)
|
|
||||||
b8 05 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @fsize:
|
|
||||||
; push rdi
|
|
||||||
57
|
|
||||||
; push rsi
|
|
||||||
56
|
|
||||||
; call 'fstat
|
|
||||||
e8 f1 ff ff ff
|
|
||||||
; pop rsi
|
|
||||||
5e
|
|
||||||
; pop rdi
|
|
||||||
5f
|
|
||||||
; mov rax, [rsi+48] (st_size)
|
|
||||||
48 8b 46 30
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @memcpy:
|
|
||||||
; test rdx, rdx
|
|
||||||
48 85 d2
|
|
||||||
; jz 'memcpy_exit
|
|
||||||
74 0f
|
|
||||||
; mov al, byte [rdi]
|
|
||||||
8a 07
|
|
||||||
; mov byte [rsi], al
|
|
||||||
88 06
|
|
||||||
; inc rsi
|
|
||||||
48 ff c6
|
|
||||||
; inc rdi
|
|
||||||
48 ff c7
|
|
||||||
; dec rdx
|
|
||||||
48 ff ca
|
|
||||||
; jmp 'memcpy
|
|
||||||
eb ec
|
|
||||||
; @memcpy_exit:
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @_start:
|
|
||||||
; mov eax, dword [rsp] (argc)
|
|
||||||
8b 04 24
|
|
||||||
; lea rbx, [rsp+8] (argv)
|
|
||||||
48 8d 5c 24 08
|
|
||||||
; sub rsp, 0x4000
|
|
||||||
48 81 ec 00 40 00 00
|
|
||||||
; mov r15, rsp
|
|
||||||
49 89 e7
|
|
||||||
; mov dword [r15+0x1010], eax (store argc)
|
|
||||||
41 89 87 10 10 00 00
|
|
||||||
; mov qword [r15+0x1018], rbx (store argv)
|
|
||||||
49 89 9f 18 10 00 00
|
|
||||||
; cmp eax, 3
|
|
||||||
83 f8 03
|
|
||||||
; jl 'die
|
|
||||||
0f 8c 7b ff ff ff
|
|
||||||
;
|
|
||||||
; mov rdi, qword [r15+0x1018] (argv)
|
|
||||||
49 8b bf 18 10 00 00
|
|
||||||
; add rdi, 8 (argv[1])
|
|
||||||
48 83 c7 08
|
|
||||||
; mov rdi, [rdi] (argv[1])
|
|
||||||
48 8b 3f
|
|
||||||
; call 'fopen_write
|
|
||||||
e8 87 ff ff ff
|
|
||||||
;
|
|
||||||
; mov dword [r15+0x101c], eax (store file descriptor)
|
|
||||||
41 89 87 0c 10 00 00
|
|
||||||
; mov rdi, qword [r15+0x1018] (argv)
|
|
||||||
49 8b bf 18 10 00 00
|
|
||||||
; add rdi, 16 (argv[2])
|
|
||||||
48 83 c7 10
|
|
||||||
; mov rdi, [rdi] (argv[2])
|
|
||||||
48 8b 3f
|
|
||||||
; call 'fopen
|
|
||||||
e8 5b ff ff ff
|
|
||||||
;
|
|
||||||
; mov dword [r15+0x1008], eax (store file descriptor)
|
|
||||||
41 89 87 08 10 00 00
|
|
||||||
; mov edi, eax (fd)
|
|
||||||
89 c7
|
|
||||||
; lea rsi, [r15]
|
|
||||||
49 8d 37
|
|
||||||
; call 'fsize
|
|
||||||
e8 76 ff ff ff
|
|
||||||
;
|
|
||||||
; mov dword [r15+0x1004], eax (store file size)
|
|
||||||
41 89 87 04 10 00 00
|
|
||||||
; mov edi, 0x400000
|
|
||||||
bf 00 00 40 00
|
|
||||||
; mov rsi, r15
|
|
||||||
4c 89 fe
|
|
||||||
; mov edx, 0x40 (size)
|
|
||||||
ba 40 00 00 00
|
|
||||||
; call 'memcpy
|
|
||||||
e8 6b ff ff ff
|
|
||||||
;
|
|
||||||
; mov edi, 0x400040
|
|
||||||
bf 40 00 40 00
|
|
||||||
; lea rsi, [r15+0x40]
|
|
||||||
49 8d 77 40
|
|
||||||
; mov edx, 0x38 (size)
|
|
||||||
ba 38 00 00 00
|
|
||||||
; call 'memcpy
|
|
||||||
e8 58 ff ff ff
|
|
||||||
;
|
|
||||||
; mov eax, 0x78
|
|
||||||
b8 78 00 00 00
|
|
||||||
; add eax, dword [r15+0x1004] (file size)
|
|
||||||
41 03 87 04 10 00 00
|
|
||||||
; mov dword [r15+0x60], eax (store file end)
|
|
||||||
41 89 47 60
|
|
||||||
; mov dword [r15+0x68], eax (store file end)
|
|
||||||
41 89 47 68
|
|
||||||
;
|
|
||||||
; sub rax, 5
|
|
||||||
48 83 e8 05
|
|
||||||
; add rax, 0x400000
|
|
||||||
48 05 00 00 40 00
|
|
||||||
; mov dword [r15+0x18], eax (store file end - 5)
|
|
||||||
41 89 47 18
|
|
||||||
;
|
|
||||||
; mov edi, dword [r15+0x100c] (fd)
|
|
||||||
41 8b bf 0c 10 00 00
|
|
||||||
; mov rsi, r15
|
|
||||||
4c 89 fe
|
|
||||||
; mov edx, 0x78 (size)
|
|
||||||
ba 78 00 00 00
|
|
||||||
; mov eax, 1 (sys_write)
|
|
||||||
b8 01 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; @write_loop:
|
|
||||||
; mov edi, dword [r15+0x1008] (fd)
|
|
||||||
41 8b bf 08 10 00 00
|
|
||||||
; mov rsi, r15
|
|
||||||
4c 89 fe
|
|
||||||
; mov edx, 0x1000 (size)
|
|
||||||
ba 00 10 00 00
|
|
||||||
; mov eax, 0 (sys_read)
|
|
||||||
b8 00 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; test rax, rax
|
|
||||||
48 85 c0
|
|
||||||
; jle 'die
|
|
||||||
0f 8e ba fe ff ff
|
|
||||||
;
|
|
||||||
; mov dword [r15+0x1000], eax (store bytes read)
|
|
||||||
41 89 87 00 10 00 00
|
|
||||||
; mov edi, dword [r15+0x100c] (fd)
|
|
||||||
41 8b bf 0c 10 00 00
|
|
||||||
; mov rsi, r15
|
|
||||||
4c 89 fe
|
|
||||||
; mov edx, dword [r15+0x1000] (bytes read)
|
|
||||||
41 8b 97 00 10 00 00
|
|
||||||
; mov eax, 1 (sys_write)
|
|
||||||
b8 01 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; cmp dword [r15+0x1000], 0x1000
|
|
||||||
41 81 bf 00 10 00 00 00 10 00 00
|
|
||||||
; jz 'write_loop
|
|
||||||
74 b5
|
|
||||||
; call 'die
|
|
||||||
e8 89 fe ff ff
|
|
||||||
; end
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
; Code segment
|
|
||||||
;
|
|
||||||
; _start
|
|
||||||
@hello_world:
|
|
||||||
68 65 6c 6c 6f 20 77 6f 72 6c 64 21
|
|
||||||
@_start:
|
|
||||||
; mov rax, 1
|
|
||||||
48 c7 c0 01 00 00 00
|
|
||||||
; mov rdi, 1
|
|
||||||
48 c7 c7 01 00 00 00
|
|
||||||
; mov rsi, [@hello_world]
|
|
||||||
48 8d 35 'hello_world
|
|
||||||
; mov rdx, 12
|
|
||||||
48 c7 c2 0c 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; mov rax, 60 (sys_exit)
|
|
||||||
48 c7 c0 3c 00 00 00
|
|
||||||
; xor rdi, rdi
|
|
||||||
48 31 ff
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
;; This is the 2nd stage of the hex compiler.
|
|
||||||
;; It still requires hex input, but it can automatically calculate rip-relative offsets to labels
|
|
||||||
|
|
||||||
;; we need the following variables:
|
|
||||||
;; +0 = buf: [u8; 0x1000]
|
|
||||||
;; +0x1000 = write_buf: [u8; 0x4000]
|
|
||||||
;; +0x5000 = read_cursor: u32
|
|
||||||
;; +0x5004 write_cursor: u32
|
|
||||||
;; +0x5008 read_end: u32
|
|
||||||
;; +0x500c file_offset: u32 (the offset of the current file in the overall input, used for calculating label offsets)
|
|
||||||
;; +0x5010 labels: [(u64, u32); 340]
|
|
||||||
;; +0x6000 num_labels: u32
|
|
||||||
;; +0x6004 scratch space
|
|
||||||
|
|
||||||
section .data
|
|
||||||
_start_lbl db "_start", 0
|
|
||||||
|
|
||||||
section .text
|
|
||||||
;; exits the program
|
|
||||||
panic:
|
|
||||||
mov rax, 60 ; syscall: exit
|
|
||||||
mov rdi, 1 ; status: 1
|
|
||||||
syscall
|
|
||||||
|
|
||||||
;; exits the program
|
|
||||||
die:
|
|
||||||
mov rax, 60 ; syscall: exit
|
|
||||||
mov rdi, 0 ; status: 0
|
|
||||||
syscall
|
|
||||||
|
|
||||||
;; calculates a quick hash of a byte sequence given by rdi (pointer) and rsi (length)
|
|
||||||
fasthash:
|
|
||||||
mov eax, 0x811c9dc5 ; FNV offset basis
|
|
||||||
.fashhash_loop:
|
|
||||||
test rsi, rsi
|
|
||||||
jz .fashhash_exit
|
|
||||||
movzx ecx, byte [rdi] ; get next byte
|
|
||||||
xor eax, ecx
|
|
||||||
imul eax, 0x1000193 ; FNV prime
|
|
||||||
inc rdi
|
|
||||||
dec rsi
|
|
||||||
jmp .fashhash_loop
|
|
||||||
|
|
||||||
.fashhash_exit:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; push the label pointed at by rdi with length rsi, and the current file offset onto the labels array.
|
|
||||||
push_lbl:
|
|
||||||
mov eax, dword [r15 + 0x6000]
|
|
||||||
cmp eax, 340
|
|
||||||
jge panic
|
|
||||||
lea edx, [rax + rax*2]
|
|
||||||
shl rdx, 2
|
|
||||||
push rdx
|
|
||||||
call fasthash
|
|
||||||
pop rdx ; rdx = offset of label entry in labels array
|
|
||||||
lea rdi, [r15 + 0x5010]
|
|
||||||
mov qword [rdi + rdx], rax ; store hash in labels array
|
|
||||||
mov eax, dword [r15 + 0x500c] ; get current file offset
|
|
||||||
mov dword [rdi + rdx + 8], eax ; store file offset in labels array
|
|
||||||
inc dword [r15 + 0x6000]
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; searches for the offset of the label with the name pointed at by rdi with length rsi, and returns it in rax. If the label is not found, dies.
|
|
||||||
find_lbl:
|
|
||||||
call fasthash
|
|
||||||
lea rdx, [r15 + 0x5010] ; rdx = pointer to start of labels array
|
|
||||||
mov ebx, dword [r15 + 0x6000]
|
|
||||||
.find_lbl_loop:
|
|
||||||
test rbx, rbx
|
|
||||||
jz .find_lbl_not_found
|
|
||||||
dec rbx
|
|
||||||
cmp qword [rdx], rax
|
|
||||||
je .find_lbl_found
|
|
||||||
add rdx, 12 ; move to next label entry
|
|
||||||
jmp .find_lbl_loop
|
|
||||||
|
|
||||||
.find_lbl_not_found:
|
|
||||||
call panic
|
|
||||||
.find_lbl_found:
|
|
||||||
mov eax, dword [rdx + 8] ; get file offset of label
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; write up to write_cursor to stdout, and then reset the write cursor to 0
|
|
||||||
wrt_stdout:
|
|
||||||
mov rax, 1 ; syscall: write
|
|
||||||
mov rdi, 1 ; fd: stdout
|
|
||||||
lea rsi, [r15 + 0x1000] ; buf: pointer to write_buf
|
|
||||||
mov edx, dword [r15 + 0x5004] ; count: write_cursor
|
|
||||||
syscall
|
|
||||||
mov dword [r15 + 0x5004], 0
|
|
||||||
ret
|
|
||||||
|
|
||||||
die_jmp_start:
|
|
||||||
;; @_start_lbl: 5F 73 74 61 72 74 00 00
|
|
||||||
;; lea rdi, [@_start_lbl]
|
|
||||||
mov rdi, _start_lbl
|
|
||||||
mov rsi, 6
|
|
||||||
call find_lbl
|
|
||||||
add dword [r15 + 0x500c], 5 ; advance the file offset by 5
|
|
||||||
sub eax, dword [r15 + 0x500c] ; add current file offset
|
|
||||||
mov edi, dword [r15 + 0x5004] ; get current write cursor
|
|
||||||
lea rdi, [r15 + rdi + 0x1000] ; rdi = pointer to write location in write_buf
|
|
||||||
mov byte [rdi], 0xe9 ; write the opcode for jmp rel32
|
|
||||||
mov dword [rdi + 1], eax ; write the offset of the label reference
|
|
||||||
add dword [r15 + 0x5004], 5 ; advance the write cursor by 5
|
|
||||||
call wrt_stdout
|
|
||||||
call die
|
|
||||||
|
|
||||||
;; reads up to 0x1000 bytes from stdin into buf, and sets read_end to the number of bytes read, and resets read_cursor to 0
|
|
||||||
;; dies if no bytes were read (if it returns, there were bytes read)
|
|
||||||
rd_stdin:
|
|
||||||
mov rax, 0 ; syscall: read
|
|
||||||
mov rdi, 0 ; fd: stdin
|
|
||||||
mov rsi, r15
|
|
||||||
mov rdx, 0x1000 ; count: size of buffer
|
|
||||||
syscall
|
|
||||||
test eax, eax
|
|
||||||
jle die_jmp_start
|
|
||||||
mov dword [r15 + 0x5008], eax ; store number of bytes read in read_end
|
|
||||||
mov dword [r15 + 0x5000], 0 ; reset read_cursor to 0
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; return the next byte from the input
|
|
||||||
;; automatically writes to stdout and reads from stdin if we are at the end of the buffer
|
|
||||||
rd_buf:
|
|
||||||
mov eax, dword [r15 + 0x5000] ; get read_cursor
|
|
||||||
cmp eax, dword [r15 + 0x5008] ; compare to read_end
|
|
||||||
jl .rd_buf_end
|
|
||||||
call wrt_stdout
|
|
||||||
call rd_stdin
|
|
||||||
jmp .rd_buf_end
|
|
||||||
|
|
||||||
.rd_buf_end:
|
|
||||||
mov edx, dword [r15 + 0x5000] ; get read_cursor
|
|
||||||
xor rax, rax
|
|
||||||
mov al, byte [r15 + rdx] ; get next byte from buf
|
|
||||||
inc dword [r15 + 0x5000] ; advance read_cursor
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; The grammar is slightly updated from hex0, as follows:
|
|
||||||
;; program = (label-def / instruction-seq / comment)*
|
|
||||||
;; comment = ";" [^\n]* "\n"
|
|
||||||
;; label-def = "@" label-name ":" comment?
|
|
||||||
;; instruction-seq = instruction+ comment?
|
|
||||||
;; instruction = label-ref / opcode
|
|
||||||
;; label-ref = "'" label-name
|
|
||||||
;; opcode = hex hex
|
|
||||||
;; label-name = [a-zA-Z0-9_-]+
|
|
||||||
;; hex = [0-9a-f][0-9a-f]
|
|
||||||
;;
|
|
||||||
;; whitespaces may occur between any tokens and are ignored.
|
|
||||||
;; whitespace = [ \t\n]+
|
|
||||||
|
|
||||||
|
|
||||||
skip_line:
|
|
||||||
cmp al, `\n`
|
|
||||||
jne .skip_line_read
|
|
||||||
call rd_buf
|
|
||||||
ret
|
|
||||||
|
|
||||||
.skip_line_read:
|
|
||||||
call rd_buf
|
|
||||||
jmp skip_line
|
|
||||||
|
|
||||||
skip_whitespaces:
|
|
||||||
cmp al, ' '
|
|
||||||
je .skip_whitespace_read
|
|
||||||
cmp al, `\t`
|
|
||||||
je .skip_whitespace_read
|
|
||||||
cmp al, `\n`
|
|
||||||
je .skip_whitespace_read
|
|
||||||
ret
|
|
||||||
|
|
||||||
.skip_whitespace_read:
|
|
||||||
call rd_buf
|
|
||||||
jmp skip_whitespaces
|
|
||||||
|
|
||||||
skip_comment:
|
|
||||||
cmp al, ";"
|
|
||||||
jne .skip_comment_ret
|
|
||||||
call skip_line
|
|
||||||
ret
|
|
||||||
.skip_comment_ret:
|
|
||||||
ret
|
|
||||||
|
|
||||||
get_next_token:
|
|
||||||
cmp al, ";"
|
|
||||||
je .get_next_token_skip
|
|
||||||
cmp al, ' '
|
|
||||||
je .get_next_token_skip
|
|
||||||
cmp al, `\t`
|
|
||||||
je .get_next_token_skip
|
|
||||||
cmp al, `\n`
|
|
||||||
je .get_next_token_skip
|
|
||||||
ret
|
|
||||||
|
|
||||||
.get_next_token_skip:
|
|
||||||
call skip_whitespaces
|
|
||||||
call skip_comment
|
|
||||||
jmp get_next_token
|
|
||||||
|
|
||||||
char_to_u4:
|
|
||||||
cmp al, '0'
|
|
||||||
jb .invalid_hex
|
|
||||||
cmp al, '9'
|
|
||||||
jbe .is_digit
|
|
||||||
cmp al, 'a'
|
|
||||||
jb .invalid_hex
|
|
||||||
cmp al, 'f'
|
|
||||||
jbe .is_lower_hex
|
|
||||||
cmp al, 'A'
|
|
||||||
jb .invalid_hex
|
|
||||||
cmp al, 'F'
|
|
||||||
jbe .is_upper_hex
|
|
||||||
jmp .invalid_hex
|
|
||||||
|
|
||||||
.invalid_hex:
|
|
||||||
call panic
|
|
||||||
.is_digit:
|
|
||||||
sub al, '0'
|
|
||||||
ret
|
|
||||||
.is_lower_hex:
|
|
||||||
sub al, 'a' - 10
|
|
||||||
ret
|
|
||||||
.is_upper_hex:
|
|
||||||
sub al, 'A' - 10
|
|
||||||
ret
|
|
||||||
|
|
||||||
octet:
|
|
||||||
sub rsp, 8
|
|
||||||
call char_to_u4
|
|
||||||
mov byte [rsp], al
|
|
||||||
call rd_buf
|
|
||||||
call char_to_u4
|
|
||||||
mov bl, byte [rsp]
|
|
||||||
shl bl, 4
|
|
||||||
or al, bl
|
|
||||||
mov edi, dword [r15 + 0x5004] ; get current write cursor
|
|
||||||
lea rdx, [r15 + 0x1000]
|
|
||||||
mov byte [rdi + rdx], al ; write the byte to the write_buf
|
|
||||||
inc dword [r15 + 0x5004] ; advance the write cursor by 1
|
|
||||||
inc dword [r15 + 0x500c] ; advance file offset by 1
|
|
||||||
add rsp, 8
|
|
||||||
ret
|
|
||||||
|
|
||||||
label_ref:
|
|
||||||
cmp al, "'"
|
|
||||||
jne .not_label_ref
|
|
||||||
xor rsi, rsi ; rsi = length of label
|
|
||||||
.label_ref_loop:
|
|
||||||
push rsi
|
|
||||||
call rd_buf
|
|
||||||
pop rsi
|
|
||||||
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
|
||||||
cmp al, ' '
|
|
||||||
je .label_ref_ret
|
|
||||||
cmp al, `\t`
|
|
||||||
je .label_ref_ret
|
|
||||||
cmp al, `\n`
|
|
||||||
je .label_ref_ret
|
|
||||||
mov byte [rdi + rsi], al
|
|
||||||
inc rsi
|
|
||||||
jmp .label_ref_loop
|
|
||||||
|
|
||||||
.label_ref_ret:
|
|
||||||
lea rdi, [r15 + 0x6004]
|
|
||||||
call find_lbl
|
|
||||||
add dword [r15 + 0x500c], 4 ; advance the file offset by 4
|
|
||||||
sub eax, dword [r15 + 0x500c] ; add current file offset
|
|
||||||
mov edi, dword [r15 + 0x5004] ; get current write cursor
|
|
||||||
mov dword [r15 + 0x1000 + rdi], eax ; write the opcode for jmp rel32
|
|
||||||
add dword [r15 + 0x5004], 4 ; advance the write cursor by 4
|
|
||||||
ret
|
|
||||||
.not_label_ref:
|
|
||||||
call octet
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; read the label into scratch, call push_lbl
|
|
||||||
label:
|
|
||||||
cmp al, '@'
|
|
||||||
jne .not_label
|
|
||||||
xor rsi, rsi ; rsi = length of label
|
|
||||||
.label_loop:
|
|
||||||
push rsi
|
|
||||||
call rd_buf
|
|
||||||
pop rsi
|
|
||||||
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
|
||||||
cmp al, ':'
|
|
||||||
je .label_ret
|
|
||||||
mov byte [rdi + rsi], al
|
|
||||||
inc rsi
|
|
||||||
jmp .label_loop
|
|
||||||
|
|
||||||
.label_ret:
|
|
||||||
lea rdi, [r15 + 0x6004] ; rdi = pointer to scratch
|
|
||||||
call push_lbl
|
|
||||||
ret
|
|
||||||
.not_label:
|
|
||||||
call label_ref
|
|
||||||
ret
|
|
||||||
|
|
||||||
global _start
|
|
||||||
_start:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 0x8000 ; allocate space for our variables + some scratch space
|
|
||||||
mov r15, rsp ; r15 will point to the start of our variables
|
|
||||||
mov dword [r15 + 0x6000], 0 ; num_labels = 0
|
|
||||||
mov dword [r15 + 0x500c], 0 ; file_offset = 0
|
|
||||||
mov dword [r15 + 0x5008], 0 ; read_end = 0
|
|
||||||
mov dword [r15 + 0x5004], 0 ; write_cursor = 0
|
|
||||||
mov dword [r15 + 0x5000], 0 ; read_cursor = 0
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
call rd_buf
|
|
||||||
call get_next_token
|
|
||||||
call label
|
|
||||||
jmp .loop
|
|
||||||
|
|
@ -1,468 +0,0 @@
|
||||||
; @panic: +0
|
|
||||||
; mov rax, 60 (exit)
|
|
||||||
b8 3c 00 00 00
|
|
||||||
; mov rdi, 1 (exit code)
|
|
||||||
bf 01 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
;
|
|
||||||
; @die: +12
|
|
||||||
; mov rax, 60 (exit)
|
|
||||||
b8 3c 00 00 00
|
|
||||||
; mov rdi, 0 (exit
|
|
||||||
bf 00 00 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
;
|
|
||||||
; @fasthash_exit: +24
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @fasthash: +25
|
|
||||||
; mov rax, 0x811c9dc5 (FNV offset basis)
|
|
||||||
b8 c5 9d 1c 81
|
|
||||||
; @fasthash_loop: +30
|
|
||||||
; test rsi, rsi
|
|
||||||
48 85 f6
|
|
||||||
; jz 'fasthash_exit (+39)
|
|
||||||
0f 84 f1 ff ff ff
|
|
||||||
; movzx ecx, byte [rdi]
|
|
||||||
0f b6 0f
|
|
||||||
; xor eax, ecx
|
|
||||||
31 c8
|
|
||||||
; imul eax, eax, 0x1000193 (FNV prime)
|
|
||||||
69 c0 93 01 00 01
|
|
||||||
; inc rdi
|
|
||||||
48 ff c7
|
|
||||||
; dec rsi
|
|
||||||
48 ff ce
|
|
||||||
; jmp 'fasthash_loop (+61)
|
|
||||||
e9 e1 ff ff ff
|
|
||||||
;
|
|
||||||
; @push_lbl: +61
|
|
||||||
; mov eax, [r15+0x6000]
|
|
||||||
41 8b 87 00 60 00 00
|
|
||||||
; cmp eax, 0x154
|
|
||||||
3d 54 01 00 00
|
|
||||||
; jge 'panic (+79)
|
|
||||||
0f 8d b1 ff ff ff
|
|
||||||
; lea edx, [rax + rax*2]
|
|
||||||
8d 14 40
|
|
||||||
; shl rdx, 2
|
|
||||||
48 c1 e2 02
|
|
||||||
; push rdx
|
|
||||||
52
|
|
||||||
; call 'fasthash (+92)
|
|
||||||
e8 bd ff ff ff
|
|
||||||
; pop rdx
|
|
||||||
5a
|
|
||||||
; lea rdi, [r15+0x5010]
|
|
||||||
49 8d bf 10 50 00 00
|
|
||||||
; mov qword [rdi+rdx], rax
|
|
||||||
48 89 04 17
|
|
||||||
; mov eax, dword [r15+0x500c]
|
|
||||||
41 8b 87 0c 50 00 00
|
|
||||||
; mov dword [rdi+rdx+8], eax
|
|
||||||
89 44 17 08
|
|
||||||
; inc dword [r15+0x6000]
|
|
||||||
41 ff 87 00 60 00 00
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @find_lbl_not_found: (+123)
|
|
||||||
; call 'panic (+128)
|
|
||||||
e8 80 ff ff ff
|
|
||||||
; @find_lbl_found: (+128)
|
|
||||||
; mov eax, dword [rdx+8]
|
|
||||||
8b 42 08
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @find_lbl: +132
|
|
||||||
; call 'fasthash (+137)
|
|
||||||
e8 90 ff ff ff
|
|
||||||
; lea rdx, [r15+0x5010]
|
|
||||||
49 8d 97 10 50 00 00
|
|
||||||
; mov edx, dword [r15+0x6000]
|
|
||||||
41 8b 9f 00 60 00 00
|
|
||||||
; @find_lbl_loop: (+151)
|
|
||||||
; test rbx, rbx
|
|
||||||
48 85 db
|
|
||||||
; jz 'find_lbl_not_found (+160)
|
|
||||||
0f 84 db ff ff ff
|
|
||||||
; dec rbx
|
|
||||||
48 ff cb
|
|
||||||
; cmp [rdx], rax
|
|
||||||
48 39 02
|
|
||||||
; jz 'find_lbl_found (+172)
|
|
||||||
0f 84 d4 ff ff ff
|
|
||||||
; add rdx, 0xc
|
|
||||||
48 83 c2 0c
|
|
||||||
; jmp 'find_lbl_loop (+181)
|
|
||||||
e9 e2 ff ff ff
|
|
||||||
;
|
|
||||||
; @wrt_stdout: (+181)
|
|
||||||
; mov rax, 1 (sys_write)
|
|
||||||
b8 01 00 00 00
|
|
||||||
; mov rdi, 1 (stdout)
|
|
||||||
bf 01 00 00 00
|
|
||||||
; lea rsi, [r15+0x1000]
|
|
||||||
49 8d b7 00 10 00 00
|
|
||||||
; mov edx, dword [r15+0x5004]
|
|
||||||
41 8b 97 04 50 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; mov dword [r15+0x5004], 0
|
|
||||||
41 c7 87 04 50 00 00 00 00 00 00
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @_start_lbl: (+219)
|
|
||||||
5f 73 74 61 72 74 00
|
|
||||||
; @die_jmp_start: (+226)
|
|
||||||
; lea rdi, [rip+@_start_lbl] (+233)
|
|
||||||
48 8d 3d f2 ff ff ff
|
|
||||||
; mov esi, 6
|
|
||||||
be 06 00 00 00
|
|
||||||
; call 'find_lbl (+243)
|
|
||||||
e8 91 ff ff ff
|
|
||||||
; add dword [r15+0x500c], 5
|
|
||||||
41 83 87 0c 50 00 00 05
|
|
||||||
; sub eax, dword [r15+0x500c]
|
|
||||||
41 2b 87 0c 50 00 00
|
|
||||||
; mov edi, dword [r15+0x5004]
|
|
||||||
41 8b bf 04 50 00 00
|
|
||||||
; lea rdi, [r15+rdi*1+0x1000]
|
|
||||||
49 8d bc 3f 00 10 00 00
|
|
||||||
; mov byte [rdi], 0xe9
|
|
||||||
c6 07 e9
|
|
||||||
; mov dword [rdi+1], eax
|
|
||||||
89 47 01
|
|
||||||
; add dword [r15+0x5004], 5
|
|
||||||
41 83 87 04 50 00 00 05
|
|
||||||
; call 'wrt_stdout (+292)
|
|
||||||
e8 91 ff ff ff
|
|
||||||
; call 'die (+297)
|
|
||||||
e8 e3 fe ff ff
|
|
||||||
;
|
|
||||||
; @rd_stdin: (+297)
|
|
||||||
; mov rax, 0 (sys_read)
|
|
||||||
b8 00 00 00 00
|
|
||||||
; mov rdi, 0 (stdin)
|
|
||||||
bf 00 00 00 00
|
|
||||||
; mov rsi, r15
|
|
||||||
4c 89 fe
|
|
||||||
; mov rdx, 0x1000
|
|
||||||
ba 00 10 00 00
|
|
||||||
; syscall
|
|
||||||
0f 05
|
|
||||||
; test rax, rax
|
|
||||||
85 c0
|
|
||||||
; jle 'die_jmp_start (+325)
|
|
||||||
0f 8e 9d ff ff ff
|
|
||||||
; mov dword [r15+0x5008], eax
|
|
||||||
41 89 87 08 50 00 00
|
|
||||||
; mov dword [r15+0x5000], 0
|
|
||||||
41 c7 87 00 50 00 00 00 00 00 00
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @rd_buf_end: (+344)
|
|
||||||
; mov edx, dword [r15+0x5000]
|
|
||||||
41 8b 97 00 50 00 00
|
|
||||||
; xor rax, rax
|
|
||||||
48 31 c0
|
|
||||||
; mov al, byte [rdi+rdx]
|
|
||||||
41 8a 04 17
|
|
||||||
; inc dword [r15+0x5000]
|
|
||||||
41 ff 87 00 50 00 00
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @rd_buf: (+366)
|
|
||||||
; mov eax, dword [r15+0x5000]
|
|
||||||
41 8b 87 00 50 00 00
|
|
||||||
; cmp eax, dword [r15+0x5008]
|
|
||||||
41 3b 87 08 50 00 00
|
|
||||||
; jl 'rd_buf_end (+386)
|
|
||||||
0f 8c d6 ff ff ff
|
|
||||||
; call 'wrt_stdout (+391)
|
|
||||||
e8 2e ff ff ff
|
|
||||||
; call 'rd_stdin (+396)
|
|
||||||
e8 9d ff ff ff
|
|
||||||
; jmp 'rd_buf_end (+401)
|
|
||||||
e9 c7 ff ff ff
|
|
||||||
;
|
|
||||||
;
|
|
||||||
; @skip_line_read: (+401)
|
|
||||||
; call 'rd_buf (+406)
|
|
||||||
e8 d8 ff ff ff
|
|
||||||
; @skip_line: (+406)
|
|
||||||
; cmp al, 0xa (newline)
|
|
||||||
3c 0a
|
|
||||||
; jnz 'skip_line_read (+414)
|
|
||||||
0f 85 f3 ff ff ff
|
|
||||||
; call 'rd_buf (+419)
|
|
||||||
e8 cb ff ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @skip_whitespace_read: (+420)
|
|
||||||
; call 'rd_buf (+425)
|
|
||||||
e8 c5 ff ff ff
|
|
||||||
; @skip_whitespace: (+425)
|
|
||||||
; cmp al, 0x20 (space)
|
|
||||||
3c 20
|
|
||||||
; jz 'skip_whitespace_read (+433)
|
|
||||||
0f 84 f3 ff ff ff
|
|
||||||
; cmp al, 0xa (newline)
|
|
||||||
3c 0a
|
|
||||||
; jz 'skip_whitespace_read (+441)
|
|
||||||
0f 84 eb ff ff ff
|
|
||||||
; cmp al, 0x9 (tab)
|
|
||||||
3c 09
|
|
||||||
; jz 'skip_whitespace_read (+449)
|
|
||||||
0f 84 e3 ff ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @skip_comment_ret: (+450)
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @skip_comment: (+451)
|
|
||||||
; cmp al, 0x3b (semicolon)
|
|
||||||
3c 3b
|
|
||||||
; jnz 'skip_comment_ret (+459)
|
|
||||||
0f 85 f7 ff ff ff
|
|
||||||
; call 'skip_line (+464)
|
|
||||||
e8 c6 ff ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @get_next_token_skip: (+465)
|
|
||||||
; call 'skip_whitespace (+470)
|
|
||||||
e8 d3 ff ff ff
|
|
||||||
; call 'skip_comment (+475)
|
|
||||||
e8 e8 ff ff ff
|
|
||||||
; @get_next_token: (+475)
|
|
||||||
; cmp al, 0x3b (semicolon)
|
|
||||||
3c 3b
|
|
||||||
; jz 'get_next_token_skip (+483)
|
|
||||||
0f 84 ee ff ff ff
|
|
||||||
; cmp al, 0xa (newline)
|
|
||||||
3c 0a
|
|
||||||
; jz 'get_next_token_skip (+491)
|
|
||||||
0f 84 e6 ff ff ff
|
|
||||||
; cmp al, 0x20 (space)
|
|
||||||
3c 20
|
|
||||||
; jz 'get_next_token_skip (+499)
|
|
||||||
0f 84 de ff ff ff
|
|
||||||
; cmp al, 0x9 (tab)
|
|
||||||
3c 09
|
|
||||||
; jz 'get_next_token_skip (+507)
|
|
||||||
0f 84 d6 ff ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @invalid_hex: (+508)
|
|
||||||
; call 'panic (+513)
|
|
||||||
e8 ff fd ff ff
|
|
||||||
; @is_digit: (+513)
|
|
||||||
; sub al, 0x30 (digit '0')
|
|
||||||
2c 30
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @is_low_alpha: (+516)
|
|
||||||
; sub al, 0x57 (letter 'a' - 10)
|
|
||||||
2c 57
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @is_up_alpha: (+519)
|
|
||||||
; sub al, 0x37 (letter 'A' - 10)
|
|
||||||
2c 37
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @char_to_u4: (+522)
|
|
||||||
; cmp al, 0x30 (digit '0')
|
|
||||||
3c 30
|
|
||||||
; jb 'invalid_hex (+530)
|
|
||||||
0f 82 ea ff ff ff
|
|
||||||
; cmp al, 0x39 (digit '9')
|
|
||||||
3c 39
|
|
||||||
; jbe 'is_digit (+538)
|
|
||||||
0f 86 e7 ff ff ff
|
|
||||||
; cmp al, 0x41 (letter 'A')
|
|
||||||
3c 41
|
|
||||||
; jb 'invalid_hex (+546)
|
|
||||||
0f 82 da ff ff ff
|
|
||||||
; cmp al, 0x46 (letter 'F')
|
|
||||||
3c 46
|
|
||||||
; jbe 'is_up_alpha (+554)
|
|
||||||
0f 86 dd ff ff ff
|
|
||||||
; cmp al, 0x61 (letter 'a')
|
|
||||||
3c 61
|
|
||||||
; jb 'invalid_hex (+562)
|
|
||||||
0f 82 ca ff ff ff
|
|
||||||
; cmp al, 0x66 (letter 'f')
|
|
||||||
3c 66
|
|
||||||
; jbe 'is_low_alpha (+570)
|
|
||||||
0f 86 ca ff ff ff
|
|
||||||
; jmp 'invalid_hex (+575)
|
|
||||||
e9 bd ff ff ff
|
|
||||||
;
|
|
||||||
; @octet: (+575)
|
|
||||||
; sub rsp, 8
|
|
||||||
48 83 ec 08
|
|
||||||
; call 'char_to_u4 (+584)
|
|
||||||
e8 ca ff ff ff
|
|
||||||
; mov byte [rsp], al
|
|
||||||
88 04 24
|
|
||||||
; call 'rd_buf (+592)
|
|
||||||
e8 1e ff ff ff
|
|
||||||
; call 'char_to_u4 (+597)
|
|
||||||
e8 b5 ff ff ff
|
|
||||||
; mov bl, byte [rsp]
|
|
||||||
8a 1c 24
|
|
||||||
; shl bl, 4
|
|
||||||
c0 e3 04
|
|
||||||
; or al, bl
|
|
||||||
08 d8
|
|
||||||
; mov edi, dword [r15+0x5004]
|
|
||||||
41 8b bf 04 50 00 00
|
|
||||||
; lea rdx, [r15+0x1000]
|
|
||||||
49 8d 97 00 10 00 00
|
|
||||||
; mov byte [rdi+rdx], al
|
|
||||||
88 04 17
|
|
||||||
; inc dword [r15+0x5004]
|
|
||||||
41 ff 87 04 50 00 00
|
|
||||||
; inc dword [r15+0x500c]
|
|
||||||
41 ff 87 0c 50 00 00
|
|
||||||
; add rsp, 8
|
|
||||||
48 83 c4 08
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
;
|
|
||||||
; @label_ref_ret: (+641)
|
|
||||||
; lea rdi, [r15+0x6004]
|
|
||||||
49 8d bf 04 60 00 00
|
|
||||||
; call 'find_lbl (+653)
|
|
||||||
e8 f7 fd ff ff
|
|
||||||
; add dword [r15+0x500c], 4
|
|
||||||
41 83 87 0c 50 00 00 04
|
|
||||||
; sub eax, dword [r15+0x500c]
|
|
||||||
41 2b 87 0c 50 00 00
|
|
||||||
; mov edi, dword [r15+0x5004]
|
|
||||||
41 8b bf 04 50 00 00
|
|
||||||
; mov dword [r15+rdi*1+0x1000], eax
|
|
||||||
41 89 84 3f 00 10 00 00
|
|
||||||
; add dword [r15+0x5004], 4
|
|
||||||
41 83 87 04 50 00 00 04
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @not_label_ref: (+692)
|
|
||||||
; call 'octet (+697)
|
|
||||||
e8 86 ff ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @label_ref: (+698)
|
|
||||||
; cmp al, 0x27 (single quote)
|
|
||||||
3c 27
|
|
||||||
; jnz 'not_label_ref (+706)
|
|
||||||
0f 85 f2 ff ff ff
|
|
||||||
; xor rsi, rsi
|
|
||||||
48 31 f6
|
|
||||||
; @label_ref_loop: (+709)
|
|
||||||
; push rsi
|
|
||||||
56
|
|
||||||
; call 'rd_buf (+715)
|
|
||||||
e8 a3 fe ff ff
|
|
||||||
; pop rsi
|
|
||||||
5e
|
|
||||||
; lea rdi, [r15+0x6004]
|
|
||||||
49 8d bf 04 60 00 00
|
|
||||||
; cmp al, 0x20 (space)
|
|
||||||
3c 20
|
|
||||||
; jz 'label_ref_ret (+731)
|
|
||||||
0f 84 a6 ff ff ff
|
|
||||||
; cmp al, 0x9 (tab)
|
|
||||||
3c 09
|
|
||||||
; jz 'label_ref_ret (+739)
|
|
||||||
0f 84 9e ff ff ff
|
|
||||||
; cmp al, 0xa (newline)
|
|
||||||
3c 0a
|
|
||||||
; jz 'label_ref_ret (+747)
|
|
||||||
0f 84 96 ff ff ff
|
|
||||||
; mov byte [rdi+rsi*1], al
|
|
||||||
88 04 37
|
|
||||||
; inc rsi
|
|
||||||
48 ff c6
|
|
||||||
; jmp 'label_ref_loop (+758)
|
|
||||||
e9 cf ff ff ff
|
|
||||||
;
|
|
||||||
; @label_ret: (+758)
|
|
||||||
; lea rdi, [r15+0x6004]
|
|
||||||
49 8d bf 04 60 00 00
|
|
||||||
; call 'push_lbl (+770)
|
|
||||||
e8 3b fd ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @not_label: (+771)
|
|
||||||
; call 'label_ref (+776)
|
|
||||||
e8 b2 ff ff ff
|
|
||||||
; ret
|
|
||||||
c3
|
|
||||||
; @label: (+777)
|
|
||||||
; cmp al, 0x40 (at sign)
|
|
||||||
3c 40
|
|
||||||
; jnz 'not_label (+785)
|
|
||||||
0f 85 f2 ff ff ff
|
|
||||||
; xor rsi, rsi
|
|
||||||
48 31 f6
|
|
||||||
; @label_loop: (+788)
|
|
||||||
; push rsi
|
|
||||||
56
|
|
||||||
; call 'rd_buf (+794)
|
|
||||||
e8 54 fe ff ff
|
|
||||||
; pop rsi
|
|
||||||
5e
|
|
||||||
; lea rdi, [r15+0x6004]
|
|
||||||
49 8d bf 04 60 00 00
|
|
||||||
; cmp al, 0x3a (colon)
|
|
||||||
3c 3a
|
|
||||||
; jz 'label_ret (+810)
|
|
||||||
0f 84 cc ff ff ff
|
|
||||||
; mov byte [rdi+rsi*1], al
|
|
||||||
88 04 37
|
|
||||||
; inc rsi
|
|
||||||
48 ff c6
|
|
||||||
; jmp 'label_loop (+821)
|
|
||||||
e9 df ff ff ff
|
|
||||||
;
|
|
||||||
; @_start: (+821)
|
|
||||||
; push rbp
|
|
||||||
55
|
|
||||||
; mov rbp, rsp
|
|
||||||
48 89 e5
|
|
||||||
; sub rsp, 0x8000
|
|
||||||
48 81 ec 00 80 00 00
|
|
||||||
; mov r15, rsp
|
|
||||||
49 89 e7
|
|
||||||
; mov dword [r15+0x6000], 0
|
|
||||||
41 c7 87 00 60 00 00 00 00 00 00
|
|
||||||
; mov dword [r15+0x500c], 0
|
|
||||||
41 c7 87 0c 50 00 00 00 00 00 00
|
|
||||||
; mov dword [r15+0x5008], 0
|
|
||||||
41 c7 87 08 50 00 00 00 00 00 00
|
|
||||||
; mov dword [r15+0x5004], 0
|
|
||||||
41 c7 87 04 50 00 00 00 00 00 00
|
|
||||||
; mov dword [r15+0x5000], 0
|
|
||||||
41 c7 87 00 50 00 00 00 00 00 00
|
|
||||||
; @main_loop:
|
|
||||||
; call 'rd_buf (+895)
|
|
||||||
e8 ef fd ff ff
|
|
||||||
; call 'get_next_token (+900)
|
|
||||||
e8 57 fe ff ff
|
|
||||||
; call 'label (+905)
|
|
||||||
e8 80 ff ff ff
|
|
||||||
; jmp 'main_loop (+910)
|
|
||||||
e9 ec ff ff ff
|
|
||||||
; jmp 'main
|
|
||||||
e9 a2 ff ff ff
|
|
||||||
; end
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# test.bin: lisp.rs lisp.o
|
|
||||||
# rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp.o --edition=2024 --test -g $< -o $@
|
|
||||||
|
|
||||||
# lisp.o: lisp.asm
|
|
||||||
# nasm -g -f elf64 -o lisp.o lisp.asm
|
|
||||||
|
|
||||||
# test: test.bin
|
|
||||||
# ./test.bin
|
|
||||||
|
|
||||||
test.bin: test.rs lisp1.o
|
|
||||||
rustc -Clink-arg=-fuse-ld=mold -Clink-arg=lisp1.o --edition=2024 --test -g $< -o $@
|
|
||||||
|
|
||||||
lisp1.o: lisp1.asm
|
|
||||||
nasm -g -f elf64 -o lisp1.o lisp1.asm
|
|
||||||
|
|
||||||
test: test.bin
|
|
||||||
./test.bin --nocapture --test-threads=1
|
|
||||||
|
|
||||||
lisp1: lisp1.o
|
|
||||||
ld -m elf_x86_64 -e _interp_entry -o lisp1 lisp1.o
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f lisp.o test.bin lisp1.o test1.bin lisp1
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,56 +0,0 @@
|
||||||
;; Atom String Table
|
|
||||||
; t
|
|
||||||
116
|
|
||||||
; quote
|
|
||||||
113 117 111 116 101
|
|
||||||
; +
|
|
||||||
43
|
|
||||||
; -
|
|
||||||
45
|
|
||||||
; *
|
|
||||||
42
|
|
||||||
; /
|
|
||||||
47
|
|
||||||
; %
|
|
||||||
37
|
|
||||||
; <
|
|
||||||
60
|
|
||||||
; =
|
|
||||||
61
|
|
||||||
; car
|
|
||||||
99 97 114
|
|
||||||
; cdr
|
|
||||||
99 100 114
|
|
||||||
; not
|
|
||||||
100 111 116
|
|
||||||
; lambda
|
|
||||||
108 97 109 98 100 97
|
|
||||||
;define
|
|
||||||
100 101 102 105 100 101
|
|
||||||
; eval
|
|
||||||
101 118 97 108
|
|
||||||
; if
|
|
||||||
105 102
|
|
||||||
; let
|
|
||||||
108 101 116
|
|
||||||
; let*
|
|
||||||
108 101 116 42
|
|
||||||
; str-len
|
|
||||||
115 116 114 45 108 101 100
|
|
||||||
; list-len
|
|
||||||
108 105 115 116 45 108 101 100
|
|
||||||
; str-parts
|
|
||||||
115 116 114 45 112 97 114 116 115
|
|
||||||
; syscall
|
|
||||||
115 121 115 99 97 108 108
|
|
||||||
; print-env
|
|
||||||
112 114 105 100 116 45 101 100 118
|
|
||||||
; \Space
|
|
||||||
92 83 112 97 99 101
|
|
||||||
; \NL
|
|
||||||
92 78 76
|
|
||||||
; \Tab
|
|
||||||
92 84 97 98
|
|
||||||
;; Atom Table
|
|
||||||
; ref-count| length | pointer to string |
|
|
||||||
01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
unsafe extern "C" {
|
|
||||||
fn init_atoms();
|
|
||||||
fn parse_next_token(src: *mut Source<'_>) -> Object;
|
|
||||||
fn init_env() -> Object;
|
|
||||||
fn eval(expr: Object, env: Object) -> Object;
|
|
||||||
fn get_env() -> Object;
|
|
||||||
|
|
||||||
#[link_name = "nil"]
|
|
||||||
static NIL: ();
|
|
||||||
#[link_name = "env"]
|
|
||||||
static mut GENV: ();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Source<'a> {
|
|
||||||
iter: core::iter::Peekable<core::slice::Iter<'a, u8>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a [u8]> for Source<'a> {
|
|
||||||
fn from(value: &'a [u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
iter: value.iter().peekable(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn peekc(src: *mut Source) -> u8 {
|
|
||||||
unsafe { (*src).iter.peek().cloned().unwrap_or(&0).clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn getc(src: *mut Source) -> u8 {
|
|
||||||
unsafe { (*src).iter.next().unwrap_or(&0).clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn alloc(size: usize, align: usize) -> *mut u8 {
|
|
||||||
let layout = core::alloc::Layout::from_size_align(size, align).unwrap();
|
|
||||||
unsafe { std::alloc::alloc(layout) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn dealloc(ptr: *mut u8, size: usize, align: usize) {
|
|
||||||
let layout = core::alloc::Layout::from_size_align(size, align).unwrap();
|
|
||||||
unsafe { std::alloc::dealloc(ptr, layout) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn panic_abort(cstr: *const u8) -> ! {
|
|
||||||
if cstr.is_null() {
|
|
||||||
panic!("abort");
|
|
||||||
} else {
|
|
||||||
let mut len = 0;
|
|
||||||
while unsafe { *cstr.add(len) } != 0 {
|
|
||||||
len += 1;
|
|
||||||
}
|
|
||||||
let slice = unsafe { core::slice::from_raw_parts(cstr, len) };
|
|
||||||
let message = core::str::from_utf8(slice).unwrap_or("abort");
|
|
||||||
panic!("{}", message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn print(obj: Object) {
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut ENV_INIT: std::cell::LazyCell<Object> =
|
|
||||||
std::cell::LazyCell::new(|| unsafe { init_env() });
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Object(*mut ());
|
|
||||||
|
|
||||||
// impl Clone for Object {
|
|
||||||
// fn clone(&self) -> Self {
|
|
||||||
// unsafe {
|
|
||||||
// *self.ptr().cast::<u32>().as_mut().unwrap() += 1;
|
|
||||||
// }
|
|
||||||
// Self(self.0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl Drop for Object {
|
|
||||||
// fn drop(&mut self) {
|
|
||||||
// unsafe {
|
|
||||||
// *self.ptr().cast::<u32>().as_mut().unwrap() -= 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
impl fmt::Debug for Object {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
if self.0 as *const () == &raw const NIL {
|
|
||||||
return write!(f, "nil");
|
|
||||||
}
|
|
||||||
match self.tag() {
|
|
||||||
0 => write!(f, "'{}'", (self.0.addr() >> 8) as u8 as char),
|
|
||||||
1 => unsafe {
|
|
||||||
let car = self.ptr().byte_add(8).cast::<Object>().read();
|
|
||||||
let cdr = self.ptr().byte_add(16).cast::<Object>().read();
|
|
||||||
write!(f, "({car:?} . {cdr:?})")
|
|
||||||
},
|
|
||||||
2 => unsafe {
|
|
||||||
let car = self.ptr().byte_add(8).cast::<Object>().read();
|
|
||||||
let env = self.ptr().byte_add(16).cast::<Object>().read();
|
|
||||||
assert_eq!(
|
|
||||||
car.tag(),
|
|
||||||
1,
|
|
||||||
"Expected a pair for lambda cdr, got tag {}",
|
|
||||||
car.tag()
|
|
||||||
);
|
|
||||||
let params = car.ptr().byte_add(8).cast::<Object>().read();
|
|
||||||
let body = car.ptr().byte_add(16).cast::<Object>().read();
|
|
||||||
|
|
||||||
write!(f, "λ ")?;
|
|
||||||
if env.0 as *const () == &raw const GENV || env.0 as *const () == &raw const NIL {
|
|
||||||
write!(f, " #<g_env> ++ ")?;
|
|
||||||
} else {
|
|
||||||
write!(f, " {env:?} ++ ")?;
|
|
||||||
}
|
|
||||||
write!(f, "{params:?} {body:?})")
|
|
||||||
},
|
|
||||||
3 => unsafe {
|
|
||||||
let str_ptr = self.ptr().byte_add(8).cast::<*const u8>().read();
|
|
||||||
let len = self.ptr().byte_add(4).cast::<u32>().read() as usize;
|
|
||||||
let slice = core::slice::from_raw_parts(str_ptr, len);
|
|
||||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
|
||||||
write!(f, "'{string}")
|
|
||||||
},
|
|
||||||
6 => unsafe {
|
|
||||||
let str_ptr = self.ptr().byte_add(8).cast::<*const u8>().read();
|
|
||||||
let len = self.ptr().byte_add(4).cast::<u32>().read() as usize;
|
|
||||||
let slice = core::slice::from_raw_parts(str_ptr, len);
|
|
||||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
|
||||||
write!(f, "\"{string}\"")
|
|
||||||
},
|
|
||||||
4 => unsafe {
|
|
||||||
let num = self.ptr().byte_add(8).cast::<u64>().read();
|
|
||||||
write!(f, "{num}")
|
|
||||||
},
|
|
||||||
5 => unsafe { write!(f, "#<prim>") },
|
|
||||||
_ => write!(f, "#<unknown tag {}>", self.tag()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Object {
|
|
||||||
fn tag(&self) -> u8 {
|
|
||||||
self.0.addr() as u8 & 0b111
|
|
||||||
}
|
|
||||||
fn ptr(&self) -> *mut () {
|
|
||||||
self.0.map_addr(|addr| addr & !0b111)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_list() {
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
println!("env: {:?}\n", env);
|
|
||||||
|
|
||||||
// let input = b"(let ((x 10) (y 20)) (+ x y))";
|
|
||||||
let input = b"(if () 1 2)";
|
|
||||||
let hello_world = b"(let* (
|
|
||||||
(msg \"hello world\\n\")
|
|
||||||
(msg-parts (str-parts msg))
|
|
||||||
(ptr (car msg-parts))
|
|
||||||
(len (cdr msg-parts))
|
|
||||||
(fd 1))
|
|
||||||
(syscall 1 fd ptr len))";
|
|
||||||
// let math = br#"
|
|
||||||
// (let (
|
|
||||||
// (a (< 1 2))
|
|
||||||
// (b (< 2 1))
|
|
||||||
// (c (+ 1 2 3))
|
|
||||||
// (d (* 2 3 4))
|
|
||||||
// (e (/ 10 2))
|
|
||||||
// (f (% 10 3))
|
|
||||||
// )
|
|
||||||
// (print-env)
|
|
||||||
// )
|
|
||||||
// "#;
|
|
||||||
let mut src = Source::from(&hello_world[..]);
|
|
||||||
let sexp = unsafe { parse_next_token(&raw mut src) };
|
|
||||||
println!("> {:?}", sexp);
|
|
||||||
let result = unsafe { eval(sexp, get_env()) };
|
|
||||||
println!("{:?}\n", result);
|
|
||||||
println!("env: {:?}", unsafe { get_env() });
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,568 +0,0 @@
|
||||||
#![feature(allocator_api, box_as_ptr)]
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn heap_alloc(size: usize, align: usize) -> *mut u8;
|
|
||||||
fn heap_dealloc(ptr: *mut u8, size: usize, align: usize);
|
|
||||||
|
|
||||||
fn init_source(source: *mut Source, fd: i32) -> *mut Source;
|
|
||||||
fn getc() -> SourceIterResult;
|
|
||||||
fn peekc() -> SourceIterResult;
|
|
||||||
|
|
||||||
fn eval(expr: Object, env: Object) -> Object;
|
|
||||||
fn init_env() -> Object;
|
|
||||||
|
|
||||||
fn parse_next_token() -> Object;
|
|
||||||
|
|
||||||
#[link_name = "ifile"]
|
|
||||||
static mut IFILE: Source;
|
|
||||||
#[link_name = "nil"]
|
|
||||||
static NIL: ();
|
|
||||||
#[link_name = "env"]
|
|
||||||
static GENV: ();
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Object(*mut ());
|
|
||||||
|
|
||||||
impl Object {
|
|
||||||
const BYTE: u8 = 0;
|
|
||||||
const NUM: u8 = 1;
|
|
||||||
const PRIM: u8 = 2;
|
|
||||||
const CONS: u8 = 3;
|
|
||||||
const CLOS: u8 = 4;
|
|
||||||
const ATOM: u8 = 5;
|
|
||||||
const ARR: u8 = 6;
|
|
||||||
|
|
||||||
const NUM_MAGIC: u32 = 0x5555;
|
|
||||||
|
|
||||||
fn is_nil(&self) -> bool {
|
|
||||||
self.0 as *const () == &raw const NIL
|
|
||||||
}
|
|
||||||
fn tag(&self) -> u8 {
|
|
||||||
self.0.addr() as u8 & 0b111
|
|
||||||
}
|
|
||||||
fn bits(&self) -> usize {
|
|
||||||
self.0.addr()
|
|
||||||
}
|
|
||||||
fn ptr(&self) -> *mut () {
|
|
||||||
self.0.map_addr(|addr| addr & !0b111)
|
|
||||||
}
|
|
||||||
fn is_g_env(&self) -> bool {
|
|
||||||
self.0 as *const () == unsafe { *ENV_INIT }.0 as *const ()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct DebugObjectExplicitGenv(Object);
|
|
||||||
|
|
||||||
impl std::fmt::Debug for DebugObjectExplicitGenv {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
#[repr(C)]
|
|
||||||
struct Cons {
|
|
||||||
refc: u64,
|
|
||||||
car: Object,
|
|
||||||
cdr: Object,
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.0.is_g_env() {
|
|
||||||
let mut proxy = unsafe {
|
|
||||||
Cons {
|
|
||||||
refc: 1,
|
|
||||||
car: self.0.ptr().byte_add(8).cast::<Object>().read(),
|
|
||||||
cdr: self.0.ptr().byte_add(16).cast::<Object>().read(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let obj =
|
|
||||||
Object((&raw const proxy as *mut ()).map_addr(|addr| addr | Object::CONS as usize));
|
|
||||||
write!(f, "{:?}", obj)
|
|
||||||
} else {
|
|
||||||
return write!(f, "{:?}", self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Object {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if self.is_nil() {
|
|
||||||
return write!(f, "nil");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_g_env() {
|
|
||||||
return write!(f, "#<g_env>");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_byte(byte: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let c = unsafe { byte.cast::<u8>().read() };
|
|
||||||
if c.is_ascii_graphic() || c == b' ' {
|
|
||||||
write!(f, "'{}'", c as char)
|
|
||||||
} else {
|
|
||||||
write!(f, "'\\x{:02x}'", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_num(num: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let num_ptr = unsafe { num.cast::<i64>().read() };
|
|
||||||
write!(f, "{}", num_ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_prim(prim: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let prim_ptr = unsafe { prim.cast::<*const ()>().read() };
|
|
||||||
write!(f, "#<prim {:?}>", prim_ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_cons(cons: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let (car, cdr) = unsafe { cons.cast::<(Object, Object)>().read() };
|
|
||||||
|
|
||||||
if !cdr.is_nil() && cdr.tag() == Object::CONS {
|
|
||||||
// If the cdr is a cons cell, we can print it as a list
|
|
||||||
write!(f, "({car:?}")?;
|
|
||||||
let mut cdr = cdr;
|
|
||||||
while !cdr.is_nil() && cdr.tag() == Object::CONS {
|
|
||||||
let (next_car, next_cdr) =
|
|
||||||
unsafe { cdr.ptr().byte_add(8).cast::<(Object, Object)>().read() };
|
|
||||||
write!(f, " {next_car:?}")?;
|
|
||||||
cdr = next_cdr;
|
|
||||||
}
|
|
||||||
if cdr.is_nil() {
|
|
||||||
write!(f, ")")
|
|
||||||
} else {
|
|
||||||
write!(f, " {cdr:?})")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
write!(f, "({car:?} . {cdr:?})")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_clos(clos: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let (car, env) = unsafe { clos.cast::<(Object, Object)>().read() };
|
|
||||||
assert_eq!(
|
|
||||||
car.tag(),
|
|
||||||
Object::CONS,
|
|
||||||
"Expected a pair for lambda cdr, got tag {}",
|
|
||||||
car.tag()
|
|
||||||
);
|
|
||||||
let (params, body) = unsafe { car.ptr().cast::<(Object, Object)>().read() };
|
|
||||||
write!(f, "λ ")?;
|
|
||||||
if env.0 as *const () == &raw const NIL {
|
|
||||||
write!(f, " #<g_env> ++ ")
|
|
||||||
} else {
|
|
||||||
write!(f, " {env:?} ++ ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_atom(atom: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let (ptr, len) = unsafe {
|
|
||||||
(
|
|
||||||
atom.cast::<*const u8>().read(),
|
|
||||||
atom.byte_add(8).cast::<usize>().read(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
|
|
||||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
|
||||||
write!(f, "'{string}")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_arr(arr: *const (), f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let len = unsafe { arr.cast::<u32>().read() } as usize;
|
|
||||||
let cap = unsafe { arr.byte_add(4).cast::<u32>().read() } as usize;
|
|
||||||
let data = unsafe { arr.byte_add(8).cast::<Object>().read() };
|
|
||||||
|
|
||||||
let data_ptr = data.ptr();
|
|
||||||
let data_tag = data.tag();
|
|
||||||
|
|
||||||
match data_tag {
|
|
||||||
Object::BYTE => {
|
|
||||||
let slice = unsafe { core::slice::from_raw_parts(data_ptr as *const u8, len) };
|
|
||||||
let string = core::str::from_utf8(slice).unwrap_or("<invalid utf-8>");
|
|
||||||
write!(f, "\"{string}\"")
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
const SIZES: [usize; 7] = [1, 8, 8, 16, 16, 16, 16];
|
|
||||||
let _fn = match data_tag {
|
|
||||||
Object::BYTE => fmt_byte,
|
|
||||||
Object::NUM => fmt_num,
|
|
||||||
Object::PRIM => fmt_prim,
|
|
||||||
Object::CONS => fmt_cons,
|
|
||||||
Object::CLOS => fmt_clos,
|
|
||||||
Object::ATOM => fmt_atom,
|
|
||||||
Object::ARR => fmt_arr,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "[")?;
|
|
||||||
for i in 0..len {
|
|
||||||
let elem = unsafe { data_ptr.byte_add(i * SIZES[data_tag as usize]) };
|
|
||||||
_fn(elem, f)?;
|
|
||||||
if i + 1 < len {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
match self.tag() {
|
|
||||||
Object::BYTE => {
|
|
||||||
let byte = self.ptr().addr() >> 8 as u8;
|
|
||||||
fmt_byte(&raw const byte as _, f)
|
|
||||||
}
|
|
||||||
Object::NUM => {
|
|
||||||
let MASK: usize = 0x5555_0000_0000_0000 | Object::NUM as usize;
|
|
||||||
|
|
||||||
if self.bits() & MASK == MASK {
|
|
||||||
let num = (self.bits() >> 8) as i32 as i64;
|
|
||||||
fmt_num(&raw const num as _, f)
|
|
||||||
} else {
|
|
||||||
fmt_num(self.ptr().byte_add(8), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object::PRIM => fmt_prim(self.ptr().byte_add(8), f),
|
|
||||||
Object::CONS => fmt_cons(self.ptr().byte_add(8), f),
|
|
||||||
Object::CLOS => fmt_clos(self.ptr().byte_add(8), f),
|
|
||||||
Object::ATOM => fmt_atom(self.ptr().byte_add(8), f),
|
|
||||||
Object::ARR => fmt_arr(self.ptr().byte_add(8), f),
|
|
||||||
_ => write!(f, "#<unknown tag {}>", self.tag()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct Source {
|
|
||||||
fd: i32,
|
|
||||||
peeked: (u8, u8),
|
|
||||||
buf: NonNull<u8>,
|
|
||||||
buf_cur: usize,
|
|
||||||
buf_end: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(align(8))]
|
|
||||||
struct Bool(bool);
|
|
||||||
|
|
||||||
impl core::ops::Deref for Bool {
|
|
||||||
type Target = bool;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct SourceIterResult {
|
|
||||||
c: u8,
|
|
||||||
some: Bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct HeapAlloc;
|
|
||||||
|
|
||||||
unsafe impl std::alloc::Allocator for HeapAlloc {
|
|
||||||
fn allocate(
|
|
||||||
&self,
|
|
||||||
layout: std::alloc::Layout,
|
|
||||||
) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
|
|
||||||
let ptr = unsafe { heap_alloc(layout.size(), layout.align()) };
|
|
||||||
if ptr.is_null() {
|
|
||||||
Err(std::alloc::AllocError)
|
|
||||||
} else {
|
|
||||||
Ok(std::ptr::NonNull::slice_from_raw_parts(
|
|
||||||
std::ptr::NonNull::new(ptr).unwrap(),
|
|
||||||
layout.size(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn deallocate(&self, ptr: std::ptr::NonNull<u8>, layout: std::alloc::Layout) {
|
|
||||||
heap_dealloc(ptr.as_ptr(), layout.size(), layout.align());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut ENV_INIT: std::cell::LazyCell<Object> =
|
|
||||||
std::cell::LazyCell::new(|| unsafe { init_env() });
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::mem::ManuallyDrop;
|
|
||||||
use std::os::fd::AsRawFd;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_heap_alloc_dealloc() {
|
|
||||||
let small = Box::new_in(42u8, HeapAlloc);
|
|
||||||
let ptr_sized = Box::new_in(42usize, HeapAlloc);
|
|
||||||
let big = Box::new_in([0u8; 48], HeapAlloc);
|
|
||||||
let large = Box::new_in([0u8; 1024], HeapAlloc);
|
|
||||||
let page_sized = Box::new_in([0u8; 4096], HeapAlloc);
|
|
||||||
assert_eq!(*small, 42);
|
|
||||||
assert_ne!(
|
|
||||||
Box::as_ptr(&small) as *const (),
|
|
||||||
Box::as_ptr(&ptr_sized) as *const ()
|
|
||||||
);
|
|
||||||
let small_ptr = Box::as_ptr(&small) as *const ();
|
|
||||||
drop(small);
|
|
||||||
let small2 = Box::new_in(43u8, HeapAlloc);
|
|
||||||
assert_eq!(*small2, 43);
|
|
||||||
assert_eq!(small_ptr, Box::as_ptr(&small2) as *const ());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn source() {
|
|
||||||
let mut file = std::fs::File::open("lisp1.asm").unwrap();
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let SourceIterResult { c, some } = getc();
|
|
||||||
assert!(*some);
|
|
||||||
assert_eq!(c, b'd');
|
|
||||||
let SourceIterResult { c, some } = peekc();
|
|
||||||
assert!(*some);
|
|
||||||
assert_eq!(c, b'e');
|
|
||||||
let SourceIterResult { c, some } = getc();
|
|
||||||
assert!(*some);
|
|
||||||
assert_eq!(c, b'e');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_list() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/list.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let obj = parse_next_token();
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_quoted_list() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/quoted-list.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let obj = parse_next_token();
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_string() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/string.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let obj = parse_next_token();
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_atom() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/atom.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let obj = parse_next_token();
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_pair() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/pair.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let obj = parse_next_token();
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_big_number() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/big-number.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let obj = parse_next_token();
|
|
||||||
println!("{:?}", obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn global_env() {
|
|
||||||
unsafe {
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
println!("{:?}", DebugObjectExplicitGenv(env));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_add() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/add.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_let() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/let.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_letstar() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/letstar.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_print() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/print.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_if() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/if.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_define() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/define.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_typeof() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/typeof.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_eval() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/eval.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_comment() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/comment.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
eprintln!("parsed: {:?}", expr);
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_mapcar() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/mapcar.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_setq() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/setq.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_equal() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/equal.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn complex() {
|
|
||||||
let file = ManuallyDrop::new(File::open("tests/print-args.l").unwrap());
|
|
||||||
unsafe {
|
|
||||||
init_source(&raw mut IFILE, file.as_raw_fd());
|
|
||||||
let expr = parse_next_token();
|
|
||||||
let env = unsafe { *ENV_INIT };
|
|
||||||
let result = eval(expr, env);
|
|
||||||
eprint!("done: ");
|
|
||||||
eprintln!("{result:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(+ 1 2 3)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
'shrigma
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(0xdeadcafebabe 11223344556677)
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
;; this is a comment
|
|
||||||
(define x 10) ; this is another comment
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
(progn
|
|
||||||
(define gt (lambda (a b)
|
|
||||||
(if (< b a)
|
|
||||||
't ())))
|
|
||||||
(gt 1 2)
|
|
||||||
)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(= 1 1 1 1)
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
(let ((x 10)
|
|
||||||
(y 20))
|
|
||||||
(eval '(cons x y)))
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(if () 1 2)
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
(let ((x 10)
|
|
||||||
(y 20))
|
|
||||||
(+ x y))
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
(let* ((x 10)
|
|
||||||
(y (+ x 10)))
|
|
||||||
(+ x y))
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
(1 2 3)
|
|
||||||
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
(progn
|
|
||||||
(define inc (lambda (x) (+ x 1)))
|
|
||||||
(mapcar 'inc '(1 2 3 4 5))
|
|
||||||
)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(1 . 2)
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
(define print
|
|
||||||
(lambda (msg)
|
|
||||||
(let*
|
|
||||||
((msg-parts (str-parts msg))
|
|
||||||
(ptr (car msg-parts))
|
|
||||||
(len (cdr msg-parts))
|
|
||||||
(fd 1))
|
|
||||||
(syscall 1 fd ptr len)
|
|
||||||
)))
|
|
||||||
|
|
||||||
(define println
|
|
||||||
(lambda (msg)
|
|
||||||
(progn
|
|
||||||
(print msg)
|
|
||||||
(print "\n"))))
|
|
||||||
|
|
||||||
(define print-list (lambda (args)
|
|
||||||
(if (nil? args)
|
|
||||||
()
|
|
||||||
(mapcar 'println args))))
|
|
||||||
|
|
||||||
;; (print-list argv)
|
|
||||||
(println "Arguments: ")
|
|
||||||
(progn
|
|
||||||
(print-list argv)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
(let*
|
|
||||||
((msg "Hello, World!\n")
|
|
||||||
(msg-parts (str-parts msg))
|
|
||||||
(ptr (car msg-parts))
|
|
||||||
(len (cdr msg-parts))
|
|
||||||
(fd 1))
|
|
||||||
(syscall 1 fd ptr len))
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
'(1 2 3 4)
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
(progn
|
|
||||||
(define x 10)
|
|
||||||
(setq x 20)
|
|
||||||
(eval 'x)
|
|
||||||
)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
("hello, World!" \a \b \NL \Space)
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
(progn
|
|
||||||
(define exit (lambda (code)
|
|
||||||
(syscall 60 code)))
|
|
||||||
(define assert (lambda (cond)
|
|
||||||
(if (nil? cond)
|
|
||||||
(exit 1) ())))
|
|
||||||
(assert (= (typeof 1) 'NUMBER))
|
|
||||||
(assert (= (typeof 'symbol) 'ATOM))
|
|
||||||
(assert (= (typeof '()) 'NIL))
|
|
||||||
(assert (= (typeof '(1 2 3)) 'CONS))
|
|
||||||
(assert (= (typeof exit) 'CLOSURE))
|
|
||||||
)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
lib.o: lib.asm
|
|
||||||
nasm -g -f elf64 -o lib.o lib.asm
|
|
||||||
|
|
||||||
alloc.o: alloc.asm
|
|
||||||
nasm -g -f elf64 -o alloc.o alloc.asm
|
|
||||||
|
|
||||||
vec.o: vec.asm
|
|
||||||
nasm -g -f elf64 -o vec.o vec.asm
|
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
;; Allocator
|
|
||||||
global alloc_pages
|
|
||||||
global dealloc_pages
|
|
||||||
global realloc_pages
|
|
||||||
|
|
||||||
global heap_alloc
|
|
||||||
global heap_dealloc
|
|
||||||
global heap_realloc
|
|
||||||
|
|
||||||
extern memcpy
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
heap resq 1
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
oom_msg: db "Out of memory!", 10
|
|
||||||
oom:
|
|
||||||
lea rdi, [abs oom_msg]
|
|
||||||
mov esi, 15
|
|
||||||
mov rax, 1 ; syscall: write
|
|
||||||
mov rdi, 2 ; fd: stderr
|
|
||||||
syscall
|
|
||||||
mov rax, 60 ; syscall: exit
|
|
||||||
mov rdi, -1 ; status: -1
|
|
||||||
syscall
|
|
||||||
|
|
||||||
;; allocates $rdi bytes worth of pages via mmap
|
|
||||||
alloc_pages:
|
|
||||||
mov rax, 9 ; syscall: mmap
|
|
||||||
mov rsi, rdi ; length: rdi
|
|
||||||
xor rdi, rdi ; addr: NULL
|
|
||||||
mov rdx, 3 ; prot: PROT_READ | PROT_WRITE
|
|
||||||
mov r10, 34 ; flags: MAP_PRIVATE | MAP_ANONYMOUS
|
|
||||||
mov r8, -1 ; fd: -1
|
|
||||||
xor r9, r9 ; offset: 0
|
|
||||||
syscall
|
|
||||||
cmp rax, -1
|
|
||||||
jle oom
|
|
||||||
ret
|
|
||||||
|
|
||||||
dealloc_pages:
|
|
||||||
mov rax, 11 ; syscall: munmap
|
|
||||||
mov rdi, rsi ; addr: rsi
|
|
||||||
mov rsi, rdx ; length: rdx
|
|
||||||
syscall
|
|
||||||
cmp rax, -1
|
|
||||||
jle oom
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; reallocates memory at $rdi[..$rsi] to a new location of size $rdx.
|
|
||||||
realloc_pages:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 32
|
|
||||||
mov qword [rsp], rsi
|
|
||||||
mov qword [rsp + 8], rdi
|
|
||||||
mov rdi, rdx
|
|
||||||
call alloc_pages
|
|
||||||
mov rsi, rdi
|
|
||||||
mov rdi, qword [rsp + 8]
|
|
||||||
mov rdx, qword [rsp]
|
|
||||||
mov qword [rsp + 16], rax
|
|
||||||
call memcpy
|
|
||||||
mov rdi, qword [rsp + 8]
|
|
||||||
mov rsi, qword [rsp]
|
|
||||||
call dealloc_pages
|
|
||||||
mov rax, qword [rsp + 16]
|
|
||||||
add rsp, 32
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
make_slab:
|
|
||||||
push rdi
|
|
||||||
mov rdi, 0x4000
|
|
||||||
call alloc_pages
|
|
||||||
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
|
|
||||||
pop rdi
|
|
||||||
mov qword [rdi], 0 ; tail_end = 0
|
|
||||||
mov qword [rdi + 8], 0 ; free = NULL
|
|
||||||
mov qword [rdi + 16], rax ; first_block = allocated
|
|
||||||
mov qword [rdi + 24], rax ; last_block = allocated
|
|
||||||
mov rax, rdi
|
|
||||||
ret
|
|
||||||
|
|
||||||
init_heap:
|
|
||||||
push r14
|
|
||||||
xor r14, r14
|
|
||||||
mov rax, qword [rel heap]
|
|
||||||
test rax, rax
|
|
||||||
jnz .done
|
|
||||||
mov rdi, 0x1000
|
|
||||||
call alloc_pages
|
|
||||||
mov qword [rel heap], rax
|
|
||||||
.loop:
|
|
||||||
cmp r14, 9
|
|
||||||
jge .done
|
|
||||||
mov rdi, r14
|
|
||||||
shl rdi, 5 ; idx * 32
|
|
||||||
mov rax, qword [rel heap]
|
|
||||||
lea rdi, [rax + rdi] ; &heap.slabs[idx]
|
|
||||||
call make_slab
|
|
||||||
inc r14
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
pop r14
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; finds the correct slab for an allocation with size $rdi and align $rsi
|
|
||||||
slab_bucket:
|
|
||||||
xor rax, rax
|
|
||||||
dec rdi ; if size is a power of two, dec so we can later inc
|
|
||||||
dec rsi ; ^^
|
|
||||||
or rdi, rsi ; we only care about the log2, so just gather all the bits
|
|
||||||
bsr rsi, rdi ; log2((size-1) | (align-1))
|
|
||||||
sub rsi, 2 ; +1 for the dec, -3 to collapse the first 3 slabs into one
|
|
||||||
cmovae rax, rsi ; saturating sub
|
|
||||||
ret
|
|
||||||
|
|
||||||
slab_alloc:
|
|
||||||
push rbx
|
|
||||||
mov rax, qword [rdi + 8] ; free
|
|
||||||
test rax, rax
|
|
||||||
jz .no_free
|
|
||||||
mov rdx, qword [rax] ; next free chunk
|
|
||||||
mov qword [rdi + 8], rdx ; free = next
|
|
||||||
pop rbx
|
|
||||||
ret
|
|
||||||
.no_free:
|
|
||||||
add esi, 3 ; undo the -3 from slab_bucket to get the actual log2(size)
|
|
||||||
and esi, 63 ; clamp for safety
|
|
||||||
mov edx, 16376 ; 0x4000 - 8
|
|
||||||
mov ecx, esi
|
|
||||||
shr rdx, cl ; 0x4000 - 8 >> log2(size)
|
|
||||||
mov rax, qword [rdi] ; tail_end
|
|
||||||
mov rbx, qword [rdi + 24] ; last_block
|
|
||||||
cmp rax, rdx
|
|
||||||
jb .alloc_from_block
|
|
||||||
push rsi
|
|
||||||
push rax
|
|
||||||
push rdi
|
|
||||||
mov rdi, 0x4000 ; allocate a new block
|
|
||||||
call alloc_pages
|
|
||||||
mov qword [rax + 0x4000 - 8], 0 ; initialize the tail pointer to NULL
|
|
||||||
pop rdi
|
|
||||||
mov rsi, qword [rdi + 24] ; last_block
|
|
||||||
mov qword [rsi + 0x4000 - 8], rax
|
|
||||||
mov qword [rdi + 24], rax ; last_block = new block
|
|
||||||
mov rbx, rax
|
|
||||||
pop rax
|
|
||||||
pop rsi
|
|
||||||
.alloc_from_block:
|
|
||||||
inc qword [rdi] ; tail_end++
|
|
||||||
mov ecx, esi
|
|
||||||
shl rax, cl
|
|
||||||
add rax, rbx
|
|
||||||
pop rbx
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; allocate a chunk of memory of size $rdi and alignment $rsi
|
|
||||||
heap_alloc:
|
|
||||||
mov rax, qword [rel heap]
|
|
||||||
cmp rax, 0
|
|
||||||
je .init
|
|
||||||
.is_init:
|
|
||||||
push rdi
|
|
||||||
call slab_bucket
|
|
||||||
cmp rax, 9
|
|
||||||
jge .mmap
|
|
||||||
mov rsi, rax
|
|
||||||
shl rax, 5 ; idx * 32
|
|
||||||
mov rdi, qword [rel heap]
|
|
||||||
lea rdi, [rdi + rax] ; &heap.slabs[idx]
|
|
||||||
call slab_alloc
|
|
||||||
pop rdi
|
|
||||||
ret
|
|
||||||
.init:
|
|
||||||
push rdi
|
|
||||||
push rsi
|
|
||||||
call init_heap
|
|
||||||
pop rsi
|
|
||||||
pop rdi
|
|
||||||
jmp .is_init
|
|
||||||
.mmap:
|
|
||||||
pop rdi
|
|
||||||
call alloc_pages
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; deallocates a chunk of memory at $rdi of size $rsi and alignment $rdx
|
|
||||||
heap_dealloc:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
push rsi
|
|
||||||
push rdi
|
|
||||||
mov rdi, rsi
|
|
||||||
mov rsi, rdx
|
|
||||||
call slab_bucket
|
|
||||||
cmp rax, 9
|
|
||||||
jge .mmap
|
|
||||||
mov rsi, rax
|
|
||||||
shl rax, 5 ; idx * 32
|
|
||||||
mov rdi, qword [rel heap]
|
|
||||||
lea rdi, [rdi + rax] ; &heap.slabs[idx]
|
|
||||||
mov rax, qword [rdi + 8] ; free
|
|
||||||
pop rsi
|
|
||||||
mov qword [rsi], rax
|
|
||||||
mov qword [rdi + 8], rsi
|
|
||||||
pop rsi
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
.mmap:
|
|
||||||
pop rdi
|
|
||||||
pop rsi
|
|
||||||
call dealloc_pages
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; reallocates a chunk of memory at $rdi of size $rsi and alignment $rdx to a new location of size $rcx
|
|
||||||
heap_realloc:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
push rbx
|
|
||||||
push r12
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
|
|
||||||
mov rbx, rdi
|
|
||||||
mov r12, rsi
|
|
||||||
mov r13, rdx
|
|
||||||
mov r14, rcx
|
|
||||||
|
|
||||||
mov rdi, r14
|
|
||||||
mov rsi, r13
|
|
||||||
call heap_alloc
|
|
||||||
mov r14, rax
|
|
||||||
|
|
||||||
mov rdi, rbx
|
|
||||||
mov rsi, rax
|
|
||||||
mov rdx, r12
|
|
||||||
call memcpy
|
|
||||||
|
|
||||||
mov rdi, rbx
|
|
||||||
mov rsi, r12
|
|
||||||
mov rdx, r13
|
|
||||||
call heap_dealloc
|
|
||||||
|
|
||||||
mov rax, r14
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbx
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
@ -1,428 +0,0 @@
|
||||||
@oom_msg:
|
|
||||||
4f 75 74 20 6f 66 20 6d 65 6d 6f 72 79 21 0a
|
|
||||||
@oom:
|
|
||||||
# lea rdi, ['oom_msg]
|
|
||||||
48 8d 3d 'oom_msg
|
|
||||||
# mov esi, 15
|
|
||||||
be 0f 00 00 00
|
|
||||||
# mov eax, 1
|
|
||||||
b8 01 00 00 00
|
|
||||||
# mov edi, 2
|
|
||||||
bf 02 00 00 00
|
|
||||||
# syscall
|
|
||||||
0f 05
|
|
||||||
# mov eax, 60
|
|
||||||
b8 3c 00 00 00
|
|
||||||
# mov rdi, -1
|
|
||||||
48 c7 c7 ff ff ff ff
|
|
||||||
# syscall
|
|
||||||
0f 05
|
|
||||||
|
|
||||||
@alloc_pages:
|
|
||||||
# mov rax, 9
|
|
||||||
b8 09 00 00 00
|
|
||||||
# mov rsi, rdi
|
|
||||||
48 89 fe
|
|
||||||
# xor rdi, rdi
|
|
||||||
48 31 ff
|
|
||||||
# mov edi, 3
|
|
||||||
ba 03 00 00 00
|
|
||||||
# mov r10, 0x22
|
|
||||||
41 ba 22 00 00 00
|
|
||||||
# mov r8, -1
|
|
||||||
49 c7 c0 ff ff ff ff
|
|
||||||
# xor r9, r9
|
|
||||||
4d 31 c9
|
|
||||||
# syscall
|
|
||||||
0f 05
|
|
||||||
# cmp rax, -1
|
|
||||||
48 83 f8 ff
|
|
||||||
# jle 'oom
|
|
||||||
0f 8e 'oom
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@dealloc_pages:
|
|
||||||
# mov rax, 11
|
|
||||||
b8 0b 00 00 00
|
|
||||||
# mov rdi, rsi
|
|
||||||
48 89 f7
|
|
||||||
# mov rsi, rdx
|
|
||||||
48 89 d6
|
|
||||||
# syscall
|
|
||||||
0f 05
|
|
||||||
# cmp rax, -1
|
|
||||||
48 83 f8 ff
|
|
||||||
# jle 'oom
|
|
||||||
0f 8e 'oom
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@realloc_pages:
|
|
||||||
# push rbp
|
|
||||||
55
|
|
||||||
# mov rbp, rsp
|
|
||||||
48 89 e5
|
|
||||||
# sub rsp, 32
|
|
||||||
48 83 ec 20
|
|
||||||
# mov qword [rsp], rsi
|
|
||||||
48 89 34 24
|
|
||||||
# mov qword [rsp + 8], rdi
|
|
||||||
48 89 7c 24 08
|
|
||||||
# mov rdi, rdx
|
|
||||||
48 89 d7
|
|
||||||
# call 'alloc_pages
|
|
||||||
e8 'alloc_pages
|
|
||||||
# mov rsi, rax
|
|
||||||
48 89 fe
|
|
||||||
# mov rdi, qword [rsp + 8]
|
|
||||||
48 8b 7c 24 08
|
|
||||||
# mov rdx, qword [rsp]
|
|
||||||
48 8b 14 24
|
|
||||||
# mov qword [rsp + 16], rax
|
|
||||||
48 89 44 24 10
|
|
||||||
# call 'memcpy
|
|
||||||
e8 'memcpy
|
|
||||||
# mov rdi, qword [rsp + 8]
|
|
||||||
48 8b 7c 24 08
|
|
||||||
# mov rsi, qword [rsp]
|
|
||||||
48 8b 34 24
|
|
||||||
# call 'dealloc_pagesA
|
|
||||||
e8 'dealloc_pages
|
|
||||||
# mov rax, qword [rsp + 16]
|
|
||||||
48 8b 44 24 10
|
|
||||||
# add rsp, 32
|
|
||||||
48 83 c4 20
|
|
||||||
# pop rbp
|
|
||||||
5d
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@make_slab:
|
|
||||||
# push rdi
|
|
||||||
57
|
|
||||||
# mov rdi, 0x4000
|
|
||||||
bf 00 40 00 00
|
|
||||||
# call 'alloc_pages
|
|
||||||
e8 'alloc_pages
|
|
||||||
# mov qword [rsp + 0x4000 - 8], 0
|
|
||||||
48 c7 80 f8 3f 00 00 00 00 00 00
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# mov qword [rdi], 0
|
|
||||||
48 c7 07 00 00 00 00
|
|
||||||
# mov qword [rdi + 8], 0
|
|
||||||
48 c7 47 08 00 00 00 00
|
|
||||||
# mov qword [rdi + 16], rax
|
|
||||||
48 89 47 10
|
|
||||||
# mov qword [rdi + 24], rax
|
|
||||||
48 89 47 18
|
|
||||||
# mov rax, rdi
|
|
||||||
48 89 f8
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@heap:
|
|
||||||
00 00 00 00 00 00 00 00
|
|
||||||
@init_heap.done:
|
|
||||||
# pop r14
|
|
||||||
41 5e
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@init_heap:
|
|
||||||
# push r14
|
|
||||||
41 56
|
|
||||||
# xor r14, r14
|
|
||||||
4d 31 f6
|
|
||||||
# mov rax, ['heap]
|
|
||||||
48 8b 05 'heap
|
|
||||||
# test rax, rax
|
|
||||||
48 85 c0
|
|
||||||
# jne .done
|
|
||||||
0f 85 'init_heap.done
|
|
||||||
# mov rdi, 0x1000
|
|
||||||
bf 00 10 00 00
|
|
||||||
# call alloc_pages
|
|
||||||
e8 'alloc_pages
|
|
||||||
# mov ['heap], rax
|
|
||||||
48 89 05 'heap
|
|
||||||
@init_heap.loop:
|
|
||||||
# cmp r14, 9
|
|
||||||
49 83 fe 09
|
|
||||||
# jge .done
|
|
||||||
0f 8d 'init_heap.done
|
|
||||||
# mov rdi, r14
|
|
||||||
4c 89 f7
|
|
||||||
# shl rdi, 12
|
|
||||||
48 c1 e7 05
|
|
||||||
# mov rax, ['heap]
|
|
||||||
48 8b 05 'heap
|
|
||||||
# lea rdi, [rax + rdi]
|
|
||||||
48 8d 3c 38
|
|
||||||
# call make_slab
|
|
||||||
e8 'make_slab
|
|
||||||
# inc r14
|
|
||||||
49 ff c6
|
|
||||||
# jmp .loop
|
|
||||||
e9 'init_heap.loop
|
|
||||||
|
|
||||||
@slab_bucket:
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# dec rdi
|
|
||||||
48 ff cf
|
|
||||||
# dec rsi
|
|
||||||
48 ff ce
|
|
||||||
# or rdi, rsi
|
|
||||||
48 09 f7
|
|
||||||
# bsr rsi, rdi
|
|
||||||
48 0f bd f7
|
|
||||||
# sub rsi, 2
|
|
||||||
48 83 ee 02
|
|
||||||
# cmovae rax, rsi
|
|
||||||
48 0f 43 c6
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@slab_alloc.alloc_from_block:
|
|
||||||
# inc qword [rdi]
|
|
||||||
48 ff 07
|
|
||||||
#mov ecx, esi
|
|
||||||
89 f1
|
|
||||||
# shl rax, cl
|
|
||||||
48 d3 e0
|
|
||||||
# add rax, rbx
|
|
||||||
48 01 d8
|
|
||||||
# pop rbx
|
|
||||||
5b
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@slab_alloc.no_free:
|
|
||||||
# add esi, 3
|
|
||||||
83 c6 03
|
|
||||||
# and esi, 63
|
|
||||||
83 e6 3f
|
|
||||||
# mov edx, 0x4000 - 8
|
|
||||||
ba f8 3f 00 00
|
|
||||||
# mov ecx, esi
|
|
||||||
89 f1
|
|
||||||
# shr rdx, cl
|
|
||||||
48 d3 ea
|
|
||||||
# mov rax, qword [rdi]
|
|
||||||
48 8b 07
|
|
||||||
# mov rbx, qword [rdi + 24]
|
|
||||||
48 8b 5f 18
|
|
||||||
# cmp rax, rdx
|
|
||||||
48 39 d0
|
|
||||||
# jb .alloc_from_block
|
|
||||||
0f 82 'slab_alloc.alloc_from_block
|
|
||||||
# push rsi
|
|
||||||
56
|
|
||||||
# push rax
|
|
||||||
50
|
|
||||||
# push rdi
|
|
||||||
57
|
|
||||||
# mov rdi, 0x4000
|
|
||||||
bf 00 40 00 00
|
|
||||||
# call 'alloc_pages
|
|
||||||
e8 'alloc_pages
|
|
||||||
# mov qword [rax + 0x4000 - 8], 0
|
|
||||||
48 c7 80 f8 3f 00 00 00 00 00 00
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# mov rsi, qword [rdi + 24]
|
|
||||||
48 8b 77 18
|
|
||||||
# mov qword [rdi + 24], rax
|
|
||||||
48 89 86 f8 3f 00 00
|
|
||||||
# mov qword [rdi + 24], rax
|
|
||||||
48 89 47 18
|
|
||||||
# mov rbx, rax
|
|
||||||
48 89 c3
|
|
||||||
# pop rax
|
|
||||||
58
|
|
||||||
# pop rsi
|
|
||||||
5e
|
|
||||||
@slab_alloc:
|
|
||||||
# push rbx
|
|
||||||
53
|
|
||||||
# mov rax, qword [rdi + 8]
|
|
||||||
48 8b 47 08
|
|
||||||
# test rax, rax
|
|
||||||
48 85 c0
|
|
||||||
# je .no_free
|
|
||||||
0f 84 'slab_alloc.no_free
|
|
||||||
# mov rdx, qword [rax]
|
|
||||||
48 8b 10
|
|
||||||
# mov qword [rdi + 8], rdx
|
|
||||||
48 89 57 08
|
|
||||||
# pop rbx
|
|
||||||
5b
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@heap_alloc.mmap:
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# call 'alloc_pages
|
|
||||||
e8 'alloc_pages
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@heap_alloc.is_init:
|
|
||||||
# push rdi
|
|
||||||
57
|
|
||||||
# call slab_bucket
|
|
||||||
e8 'slab_bucket
|
|
||||||
# cmp rax, 9
|
|
||||||
48 83 f8 09
|
|
||||||
# jge .mmap
|
|
||||||
0f 8d 'heap_alloc.mmap
|
|
||||||
# mov rsi, rax
|
|
||||||
48 89 c6
|
|
||||||
# shl rax, 5
|
|
||||||
48 c1 e0 05
|
|
||||||
# mov rdi, qword ['heap]
|
|
||||||
48 8b 3d 'heap
|
|
||||||
# lea rdi, [rdi + rax]
|
|
||||||
48 8d 3c 07
|
|
||||||
# call 'slab_alloc
|
|
||||||
e8 'slab_alloc
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@heap_alloc.init:
|
|
||||||
# push rdi
|
|
||||||
57
|
|
||||||
# push rsi
|
|
||||||
56
|
|
||||||
# call 'init_heap
|
|
||||||
e8 'init_heap
|
|
||||||
# pop rsi
|
|
||||||
5e
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# jmp 'heap_alloc.is_init
|
|
||||||
e9 'heap_alloc.is_init
|
|
||||||
@heap_alloc:
|
|
||||||
# mov rax, qword ['heap]
|
|
||||||
48 8b 05 'heap
|
|
||||||
# cmp rax, 0
|
|
||||||
48 83 f8 00
|
|
||||||
# je .init
|
|
||||||
0f 84 'heap_alloc.init
|
|
||||||
# jmp 'heap_alloc.is_init
|
|
||||||
e9 'heap_alloc.is_init
|
|
||||||
|
|
||||||
@heap_dealloc.mmap:
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# pop rsi
|
|
||||||
5e
|
|
||||||
# call 'dealloc_pages
|
|
||||||
e8 'dealloc_pages
|
|
||||||
# pop rbp
|
|
||||||
5d
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@heap_dealloc:
|
|
||||||
# push rbp
|
|
||||||
55
|
|
||||||
# mov rbp, rsp
|
|
||||||
48 89 e5
|
|
||||||
# push rsi
|
|
||||||
56
|
|
||||||
# push rdi
|
|
||||||
57
|
|
||||||
# mov rdi, rsi
|
|
||||||
48 89 f7
|
|
||||||
# mov rsi, rdx
|
|
||||||
48 89 d6
|
|
||||||
# call 'slab_bucket
|
|
||||||
e8 'slab_bucket
|
|
||||||
# cmp rax, 9
|
|
||||||
48 83 f8 09
|
|
||||||
# jge 'heap_dealloc.mmap
|
|
||||||
0f 8d 'heap_dealloc.mmap
|
|
||||||
# mov rsi, rax
|
|
||||||
48 89 c6
|
|
||||||
# shl rax, 5
|
|
||||||
48 c1 e0 05
|
|
||||||
# mov rdi, qword ['heap]
|
|
||||||
48 8b 3d 'heap
|
|
||||||
# lea rdi, [rdi + rax]
|
|
||||||
48 8d 3c 07
|
|
||||||
# mov rax, qword [rdi + 8]
|
|
||||||
48 8b 47 08
|
|
||||||
# pop rsi
|
|
||||||
5e
|
|
||||||
# mov qword [rsi], rax
|
|
||||||
48 89 06
|
|
||||||
# mov qword [rdi + 8], rsi
|
|
||||||
48 89 77 08
|
|
||||||
# pop rsi
|
|
||||||
5e
|
|
||||||
# pop rbp
|
|
||||||
5d
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
|
|
||||||
@heap_realloc:
|
|
||||||
# push rbp
|
|
||||||
55
|
|
||||||
# mov rbp, rsp
|
|
||||||
48 89 e5
|
|
||||||
# push rbx
|
|
||||||
53
|
|
||||||
# push r12
|
|
||||||
41 54
|
|
||||||
# push r13
|
|
||||||
41 55
|
|
||||||
# push r14
|
|
||||||
41 56
|
|
||||||
# mov rbx, rdi
|
|
||||||
48 89 fb
|
|
||||||
# mov r12, rsi
|
|
||||||
49 89 f4
|
|
||||||
# mov r13, rdx
|
|
||||||
49 89 d5
|
|
||||||
# mov r14, rcx
|
|
||||||
49 89 ce
|
|
||||||
# mov rdi, r14
|
|
||||||
4c 89 f7
|
|
||||||
# mov rsi, r13
|
|
||||||
4c 89 ee
|
|
||||||
# call 'heap_alloc
|
|
||||||
e8 'heap_alloc
|
|
||||||
# mov r14, rax
|
|
||||||
49 89 c6
|
|
||||||
# mov rdi, rbx
|
|
||||||
48 89 df
|
|
||||||
# mov rsi, rax
|
|
||||||
48 89 c6
|
|
||||||
# mov rdx, r12
|
|
||||||
4c 89 e2
|
|
||||||
# call 'memcpy
|
|
||||||
e8 'memcpy
|
|
||||||
# mov rdi, rbx
|
|
||||||
48 89 df
|
|
||||||
# mov rsi, r12
|
|
||||||
4c 89 e6
|
|
||||||
# mov rdx, r13
|
|
||||||
4c 89 ea
|
|
||||||
# call 'heap_dealloc
|
|
||||||
e8 'heap_dealloc
|
|
||||||
# mov rax, r14
|
|
||||||
4c 89 f0
|
|
||||||
# pop r14
|
|
||||||
41 5e
|
|
||||||
# pop r13
|
|
||||||
41 5d
|
|
||||||
# pop r12
|
|
||||||
41 5c
|
|
||||||
# pop rbx
|
|
||||||
5b
|
|
||||||
# pop rbp
|
|
||||||
5d
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
@ -1,343 +0,0 @@
|
||||||
;; Common Library Functions
|
|
||||||
global strlen
|
|
||||||
global strstr
|
|
||||||
global strcmp
|
|
||||||
global strfind
|
|
||||||
global memcpy
|
|
||||||
global is_alpha
|
|
||||||
global is_digit
|
|
||||||
global is_alphanumeric
|
|
||||||
global is_id_start
|
|
||||||
global is_id_cont
|
|
||||||
global to_digit
|
|
||||||
global parse_i64
|
|
||||||
global parse_i64_cstr
|
|
||||||
global exit
|
|
||||||
global fasthash
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
;; fn strlen(s: *const u8) -> usize
|
|
||||||
strlen:
|
|
||||||
xor eax, eax
|
|
||||||
.loop:
|
|
||||||
cmp byte [rdi + rax], 0
|
|
||||||
je .done
|
|
||||||
inc rax
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; find in the first $rsi bytes of $rdi (haystack) the first $rcx bytes of $rdx (needle).
|
|
||||||
;; returns the index in $rax, or -1 if not found.
|
|
||||||
;; fn strstr(haystack: *const u8, haystack_len: usize, needle: *const u8, needle_len: usize) -> isize
|
|
||||||
strstr:
|
|
||||||
push rbx
|
|
||||||
push r12
|
|
||||||
push r13
|
|
||||||
mov r13, rdi ; save original haystack pointer
|
|
||||||
mov rax, rsi ; remaining haystack length
|
|
||||||
.loop_haystack:
|
|
||||||
cmp rax, rcx
|
|
||||||
jb .not_found ; not enough haystack left for needle
|
|
||||||
|
|
||||||
xor rbx, rbx ; needle index
|
|
||||||
.loop_needle:
|
|
||||||
cmp rbx, rcx
|
|
||||||
jz .found ; found the needle
|
|
||||||
|
|
||||||
mov r12b, byte [rdx + rbx] ; needle[needle_index]
|
|
||||||
cmp r12b, byte [rdi + rbx] ; haystack[haystack_index + needle_index]
|
|
||||||
jne .next_haystack ; mismatch, move to next haystack index
|
|
||||||
inc rbx
|
|
||||||
jmp .loop_needle
|
|
||||||
.next_haystack:
|
|
||||||
inc rdi ; move to next haystack index
|
|
||||||
dec rax ; decrease remaining haystack length
|
|
||||||
jmp .loop_haystack
|
|
||||||
.not_found:
|
|
||||||
xor eax, eax
|
|
||||||
dec rax
|
|
||||||
jmp .done
|
|
||||||
.found:
|
|
||||||
sub rdi, r13 ; calculate the index of the found needle
|
|
||||||
mov rax, rdi
|
|
||||||
.done:
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbx
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
;; fn strcmp(s1: *const u8, s1_len: usize, s2: *const u8, s2_len: usize) -> i32
|
|
||||||
strcmp:
|
|
||||||
cmp rcx, rsi
|
|
||||||
cmovb rsi, rcx ; if rhs is shorter, use its length for the loop
|
|
||||||
xor eax, eax
|
|
||||||
.strcmp_loop:
|
|
||||||
cmp rsi, rax
|
|
||||||
jz .strcmp_equal
|
|
||||||
movzx ecx, byte [rdx + rax]
|
|
||||||
cmp byte [rdi + rax], cl
|
|
||||||
lea rax, [rax + 1]
|
|
||||||
je .strcmp_loop
|
|
||||||
seta al ; al = lhs > rhs
|
|
||||||
sbb al, 0 ; al = al - CF
|
|
||||||
ret
|
|
||||||
.strcmp_equal:
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn strfind(s: *const u8, len: usize, c: u8) -> isize
|
|
||||||
strfind:
|
|
||||||
strch:
|
|
||||||
xor eax, eax
|
|
||||||
.loop:
|
|
||||||
cmp rax, rsi
|
|
||||||
jge .not_found
|
|
||||||
mov cl, byte [rdi + rax]
|
|
||||||
cmp cl, dl
|
|
||||||
je .found
|
|
||||||
inc rax
|
|
||||||
jmp .loop
|
|
||||||
.not_found:
|
|
||||||
xor eax, eax
|
|
||||||
dec rax
|
|
||||||
.found:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn memcpy(src: *const u8, dst: *mut u8, len: usize)
|
|
||||||
memcpy:
|
|
||||||
.loop:
|
|
||||||
test rdx, rdx
|
|
||||||
jz .done
|
|
||||||
mov al, byte [rdi]
|
|
||||||
mov byte [rsi], al
|
|
||||||
inc rsi
|
|
||||||
inc rdi
|
|
||||||
dec rdx
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn is_alpha(c: u8) -> bool
|
|
||||||
is_alpha:
|
|
||||||
movzx eax, dil
|
|
||||||
and eax, 0x1fffdf ; ignore bit 5 (case)
|
|
||||||
add eax, -65 ; subtract 'A'
|
|
||||||
cmp eax, 26 ; check if in range 0-25
|
|
||||||
setb al
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn is_digit(c: u8) -> bool
|
|
||||||
is_digit:
|
|
||||||
movzx edi, dil
|
|
||||||
lea eax, [rdi - 48] ; subtract '0'
|
|
||||||
cmp eax, 10 ; check if in range 0-9
|
|
||||||
setb al
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn is_alphanumeric(c: u8) -> bool
|
|
||||||
is_alphanumeric:
|
|
||||||
lea eax, [rdi - 48]
|
|
||||||
cmp eax, 10
|
|
||||||
setb cl
|
|
||||||
and dil, 0xdf
|
|
||||||
add dil, -65
|
|
||||||
cmp dil, 26
|
|
||||||
setb al
|
|
||||||
or al, cl
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn is_id_start(c: u8) -> bool
|
|
||||||
is_id_start:
|
|
||||||
call is_alpha
|
|
||||||
cmp dil, 95
|
|
||||||
sete cl
|
|
||||||
or al, cl
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn is_id_continue(c: u8) -> bool
|
|
||||||
is_id_cont:
|
|
||||||
call is_alphanumeric
|
|
||||||
cmp dil, 45
|
|
||||||
sete cl
|
|
||||||
or al, cl
|
|
||||||
cmp dil, 95
|
|
||||||
sete cl
|
|
||||||
or al, cl
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; converts char $dil to a digit with radix $rsi, returning it in $edx. $al is set to 1 if the char is a valid digit, and 0 otherwise.
|
|
||||||
;; fn to_digit(c: u8, radix: u8) -> (is_valid: bool, digit: u8)
|
|
||||||
to_digit:
|
|
||||||
lea eax, [rsi - 2]
|
|
||||||
cmp eax, 35
|
|
||||||
jae .invalid
|
|
||||||
movzx rdi, dil
|
|
||||||
lea edx, [rdi - 65] ; 'A' = 65
|
|
||||||
and edx, -33 ; convert to uppercase
|
|
||||||
add edx, 10 ; 'A' should map to 10
|
|
||||||
lea eax, [rdi - 48] ; '0' = 48
|
|
||||||
cmp esi, 11
|
|
||||||
cmovb edx, eax ; if radix <= 10, then take the difference from '0'
|
|
||||||
cmp edi, 58
|
|
||||||
cmovb edx, eax ; or if char < '9', then take the difference from '0'
|
|
||||||
xor eax, eax
|
|
||||||
cmp edx, esi
|
|
||||||
setb al ; al = edx < radix
|
|
||||||
ret
|
|
||||||
.invalid:
|
|
||||||
xor eax, eax
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn parse_i64(s: *const u8) -> (is_valid: bool, value: i64)
|
|
||||||
parse_i64_cstr:
|
|
||||||
call strlen
|
|
||||||
mov rsi, rax
|
|
||||||
jmp parse_i64
|
|
||||||
|
|
||||||
;; fn parse_i64(s: *const u8, len: usize) -> (is_valid: bool, value: i64)
|
|
||||||
parse_i64:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
push rbx ; buf
|
|
||||||
push r12 ; radix
|
|
||||||
push r13 ; acc
|
|
||||||
push r14 ; end
|
|
||||||
|
|
||||||
push rdi
|
|
||||||
|
|
||||||
sub rsp, 8
|
|
||||||
mov dword [rsp], 1 ; sign = 1
|
|
||||||
mov rbx, rdi ; buf
|
|
||||||
mov r12, 10 ; radix
|
|
||||||
xor r13, r13 ; acc
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
mov r14, rsi
|
|
||||||
add r14, rdi ; end = buf + len
|
|
||||||
|
|
||||||
cmp rbx, r14
|
|
||||||
jge .done ; if buf >= end, return 0
|
|
||||||
|
|
||||||
; eat leading -
|
|
||||||
cmp byte [rbx], '-'
|
|
||||||
sete al
|
|
||||||
lea rbx, [rbx + rax]
|
|
||||||
shl eax, 1
|
|
||||||
sub dword [rsp], eax
|
|
||||||
|
|
||||||
cmp rbx, r14
|
|
||||||
jge .done
|
|
||||||
|
|
||||||
; eat leading +
|
|
||||||
cmp byte [rbx], '+'
|
|
||||||
sete al
|
|
||||||
lea rbx, [rbx + rax]
|
|
||||||
|
|
||||||
cmp rbx, r14
|
|
||||||
jge .done
|
|
||||||
|
|
||||||
mov dil, byte [rbx]
|
|
||||||
mov esi, 10
|
|
||||||
call to_digit
|
|
||||||
jnb .not_numeric
|
|
||||||
|
|
||||||
; radix
|
|
||||||
cmp dil, '0'
|
|
||||||
jne .loop
|
|
||||||
inc rbx
|
|
||||||
|
|
||||||
cmp rbx, r14
|
|
||||||
jge .done
|
|
||||||
|
|
||||||
mov dil, byte [rbx]
|
|
||||||
and dil, 0xdf ; convert to uppercase
|
|
||||||
cmp dil, 'X'
|
|
||||||
sete al
|
|
||||||
lea ecx, [eax + eax * 2]
|
|
||||||
shl ecx, 1 ; multiply by 6
|
|
||||||
add r12d, ecx ; radix += 'x' ? 6 : 0
|
|
||||||
|
|
||||||
cmp dil, 'O'
|
|
||||||
sete al
|
|
||||||
shl al, 1 ; multiply by 2
|
|
||||||
sub r12d, eax ; radix -= 'o' ? 2 : 0
|
|
||||||
|
|
||||||
cmp dil, 'B'
|
|
||||||
sete al
|
|
||||||
shl al, 3 ; multiply by 8
|
|
||||||
sub r12d, eax ; radix -= 'b' ? 8 : 0
|
|
||||||
|
|
||||||
cmp r12d, 10
|
|
||||||
setne al
|
|
||||||
lea rbx, [rbx + rax] ; skip the radix prefix if it was present
|
|
||||||
|
|
||||||
.loop:
|
|
||||||
cmp rbx, r14
|
|
||||||
jge .done
|
|
||||||
|
|
||||||
mov dil, byte [rbx]
|
|
||||||
inc rbx
|
|
||||||
|
|
||||||
cmp dil, '_'
|
|
||||||
je .loop
|
|
||||||
mov esi, r12d
|
|
||||||
call to_digit
|
|
||||||
mov ecx, edx
|
|
||||||
test al, al
|
|
||||||
jz .done
|
|
||||||
mov rax, r13
|
|
||||||
imul r12
|
|
||||||
add rax, rcx
|
|
||||||
mov r13, rax
|
|
||||||
jmp .loop
|
|
||||||
.not_numeric:
|
|
||||||
xor eax, eax
|
|
||||||
xor edx, edx
|
|
||||||
add rsp, 8
|
|
||||||
pop rdi
|
|
||||||
jmp .epilogue
|
|
||||||
.done:
|
|
||||||
|
|
||||||
mov rdx, rax
|
|
||||||
neg r13
|
|
||||||
cmp dword [rsp], 0
|
|
||||||
cmovl rdx, r13
|
|
||||||
|
|
||||||
add rsp, 8
|
|
||||||
pop rdi
|
|
||||||
|
|
||||||
sub rbx, rdi
|
|
||||||
mov rax, rbx
|
|
||||||
|
|
||||||
.epilogue:
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbx
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn exit() -> !
|
|
||||||
exit:
|
|
||||||
xor edi, edi
|
|
||||||
mov rax, 60
|
|
||||||
syscall
|
|
||||||
|
|
||||||
;; fn fasthash(s: *const u8, len: usize) -> u32
|
|
||||||
fasthash:
|
|
||||||
mov eax, 0x811C9DC5 ; FNV-1a 32-bit offset basis
|
|
||||||
.loop:
|
|
||||||
test rsi, rsi
|
|
||||||
jz .done
|
|
||||||
movzx ecx, byte [rdi]
|
|
||||||
xor eax, ecx
|
|
||||||
imul eax, 0x01000193 ; FNV prime
|
|
||||||
inc rdi
|
|
||||||
dec rsi
|
|
||||||
jmp .loop
|
|
||||||
.done:
|
|
||||||
ret
|
|
||||||
|
|
@ -1,449 +0,0 @@
|
||||||
@strlen.done:
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@strlen:
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
@strlen.loop:
|
|
||||||
# cmp byte [rdi + rax], 0
|
|
||||||
80 3c 07 00
|
|
||||||
# je .done:
|
|
||||||
0f 84 'strlen.done
|
|
||||||
# inc rax
|
|
||||||
48 ff c0
|
|
||||||
# jmp .loop
|
|
||||||
e9 'strlen.loop
|
|
||||||
|
|
||||||
|
|
||||||
@strstr.found:
|
|
||||||
# sub rdi, r13
|
|
||||||
4c 29 ef
|
|
||||||
# mov rax, rdi
|
|
||||||
48 89 f8
|
|
||||||
@strstr.done:
|
|
||||||
# pop r13
|
|
||||||
41 5d
|
|
||||||
# pop r12
|
|
||||||
41 5c
|
|
||||||
# pop rbx
|
|
||||||
5b
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@strstr.notfound
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# dec rax
|
|
||||||
48 ff c8
|
|
||||||
# jmp .done
|
|
||||||
e9 'strstr.done
|
|
||||||
@strstr.next_hackstack:
|
|
||||||
# inc rdi
|
|
||||||
48 ff c7
|
|
||||||
# dec rax
|
|
||||||
48 ff c8
|
|
||||||
@strstr.loop_haystack:
|
|
||||||
# cmp rax, rcx
|
|
||||||
48 39 c8
|
|
||||||
# jb 'strstr.notfound
|
|
||||||
0f 82 'strstr.notfound
|
|
||||||
# xor ebx, ebx
|
|
||||||
31 db
|
|
||||||
@strstr.loop_needle:
|
|
||||||
# cmp rbx, rcx
|
|
||||||
48 39 cb
|
|
||||||
# je .found
|
|
||||||
0f 84 'strstr.found
|
|
||||||
# mov r12b, byte [rdx + rbx]
|
|
||||||
44 8a 24 1a
|
|
||||||
# cmp r12b, byte [rdi + rbx]
|
|
||||||
44 3a 24 1f
|
|
||||||
# jne .next_haystack
|
|
||||||
0f 85 'strstr.next_haystack
|
|
||||||
# inc rbx
|
|
||||||
48 ff c3
|
|
||||||
# jmp .loop_needle
|
|
||||||
e9 'strstr.loop_needle
|
|
||||||
|
|
||||||
@strstr:
|
|
||||||
# push rbx
|
|
||||||
53
|
|
||||||
# push r12
|
|
||||||
41 54
|
|
||||||
# push r13
|
|
||||||
41 55
|
|
||||||
# mov r13, rdi
|
|
||||||
49 89 fd
|
|
||||||
# mov rax, rsi
|
|
||||||
48 89 f0
|
|
||||||
# jmp .loop_haystack
|
|
||||||
e9 'strstr.loop_haystack
|
|
||||||
|
|
||||||
@strcmp.eq:
|
|
||||||
31 c0
|
|
||||||
c3
|
|
||||||
@strcmp:
|
|
||||||
# cmp rcx, rsi
|
|
||||||
48 39 f1
|
|
||||||
# cmovb rsi, rcx
|
|
||||||
48 0f 42 f1
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
@strcmp.loop:
|
|
||||||
# cmp rsi, rax
|
|
||||||
48 39 c6
|
|
||||||
# je .eq
|
|
||||||
0f 84 'strcmp.eq
|
|
||||||
# movzx ecx, byte [rdx + rax]
|
|
||||||
0f b6 0c 02
|
|
||||||
# cmp byte [rdi + rax, cl
|
|
||||||
38 0c 07
|
|
||||||
# inc rax
|
|
||||||
48 ff c0
|
|
||||||
# je .loop
|
|
||||||
0f 84 'strcmp.loop
|
|
||||||
# seta al
|
|
||||||
0f 97 c0
|
|
||||||
# sbb al, 0
|
|
||||||
1c 00
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
|
|
||||||
@strfind.nf:
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# dec rax
|
|
||||||
48 ff c8
|
|
||||||
@strfind.f:
|
|
||||||
c3
|
|
||||||
@strfind:
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# cmp rax, rsi
|
|
||||||
48 39 f0
|
|
||||||
# jge .nf
|
|
||||||
0f 8d 'strfind.nf
|
|
||||||
# mov cl, byte [rdi + rax]
|
|
||||||
8a 0c 07
|
|
||||||
# cmp cl, dl
|
|
||||||
38 d1
|
|
||||||
# je .f
|
|
||||||
0f 84 'strfind.f
|
|
||||||
# inc rax
|
|
||||||
48 ff c0
|
|
||||||
# jmp strfind
|
|
||||||
e9 'strfind
|
|
||||||
|
|
||||||
@memcpy.done:
|
|
||||||
c3
|
|
||||||
@memcpy:
|
|
||||||
# test rdx, rdx
|
|
||||||
48 85 d2
|
|
||||||
# jz .done
|
|
||||||
0f 84 'memcpy.done
|
|
||||||
# mov al, byte [rdi]
|
|
||||||
8a 07
|
|
||||||
# mov byte [rsi], al
|
|
||||||
88 06
|
|
||||||
# inc rsi
|
|
||||||
48 ff c6
|
|
||||||
# inc rdi
|
|
||||||
48 ff c7
|
|
||||||
# dec rdx
|
|
||||||
48 ff ca
|
|
||||||
# jmp .loop
|
|
||||||
e9 'memcpy
|
|
||||||
|
|
||||||
@is_alpha:
|
|
||||||
40 0f b6 c7
|
|
||||||
25 df ff 1f 00
|
|
||||||
83 c0 bf
|
|
||||||
83 f8 1a
|
|
||||||
0f 92 c0
|
|
||||||
c3
|
|
||||||
|
|
||||||
@is_digit:
|
|
||||||
40 0f b6 ff
|
|
||||||
8d 47 d0
|
|
||||||
83 f8 0a
|
|
||||||
0f 92 c0
|
|
||||||
c3
|
|
||||||
|
|
||||||
@is_alphanumeric:
|
|
||||||
8d 47 d0
|
|
||||||
83 f8 0a
|
|
||||||
0f 92 c1
|
|
||||||
40 80 e7 df
|
|
||||||
40 80 c7 bf
|
|
||||||
40 80 ff 1a
|
|
||||||
0f 92 c0
|
|
||||||
08 c8
|
|
||||||
c3
|
|
||||||
|
|
||||||
@is_id_start:
|
|
||||||
# call is_alpha
|
|
||||||
e8 'is_alpha
|
|
||||||
40 80 ff 5f
|
|
||||||
0f 94 c1
|
|
||||||
08 c8
|
|
||||||
c3
|
|
||||||
|
|
||||||
@is_id_cont:
|
|
||||||
# call is_alphanumeric
|
|
||||||
e8 'is_alphanumeric
|
|
||||||
40 80 ff 2d
|
|
||||||
0f 94 c1
|
|
||||||
08 c8
|
|
||||||
40 80 ff 5f
|
|
||||||
0f 94 c1
|
|
||||||
08 c8
|
|
||||||
c3
|
|
||||||
|
|
||||||
@to_digit.inv:
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@to_digit:
|
|
||||||
8d 46 fe
|
|
||||||
83 f8 23
|
|
||||||
# jae .inv
|
|
||||||
0f 83 'to_digit.inv
|
|
||||||
48 0f b6 ff
|
|
||||||
8d 57 bf
|
|
||||||
83 e2 df
|
|
||||||
83 c2 0a
|
|
||||||
8d 47 d0
|
|
||||||
83 fe 0b
|
|
||||||
0f 42 d0
|
|
||||||
83 ff 3a
|
|
||||||
0f 42 d0
|
|
||||||
31 c0
|
|
||||||
39 f2
|
|
||||||
0f 92 c0
|
|
||||||
c3
|
|
||||||
|
|
||||||
@parse_i64.done:
|
|
||||||
# mov rdx, rax
|
|
||||||
48 89 c2
|
|
||||||
# neg r13
|
|
||||||
49 f7 dd
|
|
||||||
# cmp dword [rsp], 0
|
|
||||||
83 3c 24 00
|
|
||||||
# cmovl rdx, r13
|
|
||||||
49 0f 4c d5
|
|
||||||
# add rsp, 8
|
|
||||||
48 83 c4 08
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# sub rbx, rdi
|
|
||||||
48 29 fb
|
|
||||||
# mov rax, rbx
|
|
||||||
48 89 d8
|
|
||||||
@parse_i64.epilogue:
|
|
||||||
# pop r14
|
|
||||||
41 5e
|
|
||||||
# pop r13
|
|
||||||
41 5d
|
|
||||||
# pop r12
|
|
||||||
41 5c
|
|
||||||
# pop rbx
|
|
||||||
5b
|
|
||||||
# pop rbp
|
|
||||||
5d
|
|
||||||
# ret
|
|
||||||
c3
|
|
||||||
@parse_i64.not_num:
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# xor edx, edx
|
|
||||||
31 d2
|
|
||||||
# add rsp, 8
|
|
||||||
48 83 c4 08
|
|
||||||
# pop rdi
|
|
||||||
5f
|
|
||||||
# jmp .epilogue
|
|
||||||
e9 'parse_i64.epilogue
|
|
||||||
|
|
||||||
@parse_i64_cstr:
|
|
||||||
e8 'strlen
|
|
||||||
# mov rsi, rax
|
|
||||||
48 89 c6
|
|
||||||
|
|
||||||
@parse_i64:
|
|
||||||
# push rbp
|
|
||||||
55
|
|
||||||
# mov rbp, rsp
|
|
||||||
48 89 e5
|
|
||||||
# push rbx
|
|
||||||
53
|
|
||||||
# push r12
|
|
||||||
41 54
|
|
||||||
# push r13
|
|
||||||
41 55
|
|
||||||
# push r14
|
|
||||||
41 56
|
|
||||||
# push rdi
|
|
||||||
57
|
|
||||||
# sub rsp, 8
|
|
||||||
48 83 ec 08
|
|
||||||
# mov dword [rsp], 1
|
|
||||||
c7 04 24 01 00 00 00
|
|
||||||
# mov rbx, rdi
|
|
||||||
48 89 fb
|
|
||||||
# mov r12, 10
|
|
||||||
41 bc 0a 00 00 00
|
|
||||||
# xor r13, r13
|
|
||||||
4d 31 ed
|
|
||||||
# xor eax, eax
|
|
||||||
31 c0
|
|
||||||
# mov r14, rsi
|
|
||||||
49 89 f6
|
|
||||||
# add r14, rdi
|
|
||||||
49 01 fe
|
|
||||||
# cmp rbx, r14
|
|
||||||
4c 39 f3
|
|
||||||
# jge .done
|
|
||||||
0f 8d 'parse_i64.done
|
|
||||||
# cmp byte [rbx], '-'
|
|
||||||
80 3b 2d
|
|
||||||
# sete al
|
|
||||||
0f 94 c0
|
|
||||||
# lea rbx, [rbx + rax]
|
|
||||||
48 8d 1c 03
|
|
||||||
# shl eax, 1
|
|
||||||
d1 e0
|
|
||||||
# sub dword [rsp], eax
|
|
||||||
29 04 24
|
|
||||||
# cmp rbx, r14
|
|
||||||
4c 39 f3
|
|
||||||
# jge .done
|
|
||||||
0f 8d 'parse_i64.done
|
|
||||||
# cmp byte [rbx], '+'
|
|
||||||
80 3b 2b
|
|
||||||
# sete al
|
|
||||||
0f 94 c0
|
|
||||||
# lea rbx, [rbx + rax]
|
|
||||||
48 8d 1c 03
|
|
||||||
# cmp rbx, r14
|
|
||||||
4c 39 f3
|
|
||||||
# jge .done
|
|
||||||
0f 8d 'parse_i64.done
|
|
||||||
# mov dil, byte [rbx]
|
|
||||||
40 8a 3b
|
|
||||||
# mov esi, 10
|
|
||||||
be 0a 00 00 00
|
|
||||||
# call to_digit
|
|
||||||
e8 'to_digit
|
|
||||||
# jae .not_num
|
|
||||||
0f 83 'parse_i64.not_num
|
|
||||||
# cmp dil, '0'
|
|
||||||
40 80 ff 30
|
|
||||||
# jne .loop
|
|
||||||
0f 85 'parse_i64.loop
|
|
||||||
# inc rbx
|
|
||||||
48 ff c3
|
|
||||||
# cmp rbx, r14
|
|
||||||
4c 39 f3
|
|
||||||
# jge .done
|
|
||||||
0f 8d 'parse_i64.done
|
|
||||||
# mov dil, byte [rbx]
|
|
||||||
40 8a 3b
|
|
||||||
# and dil, 0xdf
|
|
||||||
40 80 e7 df
|
|
||||||
# cmp dil, 'X'
|
|
||||||
40 80 ff 58
|
|
||||||
# sete al
|
|
||||||
0f 94 c0
|
|
||||||
# lea ecx, [eax + eax * 2]
|
|
||||||
67 8d 0c 40
|
|
||||||
# shl ecx, 1
|
|
||||||
d1 e1
|
|
||||||
# add r12d, ecx
|
|
||||||
41 01 cc
|
|
||||||
# cmp dil, 'O'
|
|
||||||
40 80 ff 4f
|
|
||||||
# sete al
|
|
||||||
0f 94 c0
|
|
||||||
# shl al, 1
|
|
||||||
d0 e0
|
|
||||||
# sub r12d, eax
|
|
||||||
41 29 c4
|
|
||||||
# cmp dil, 'B'
|
|
||||||
40 80 ff 42
|
|
||||||
# sete al
|
|
||||||
0f 94 c0
|
|
||||||
# shl al, 3
|
|
||||||
c0 e0 03
|
|
||||||
# sub r12d, eax
|
|
||||||
41 29 c4
|
|
||||||
# cmp r12d, 10
|
|
||||||
41 83 fc 0a
|
|
||||||
# setne al
|
|
||||||
0f 95 c0
|
|
||||||
# lea rbx, [rbx + rax]
|
|
||||||
48 8d 1c 03
|
|
||||||
|
|
||||||
@parse_i64.loop:
|
|
||||||
# cmp rbx, r14
|
|
||||||
4c 39 f3
|
|
||||||
# jge .done
|
|
||||||
0f 8d 'parse_i64.done
|
|
||||||
# mov dil, byte [rbx]
|
|
||||||
40 8a 3b
|
|
||||||
# inc rbx
|
|
||||||
48 ff c3
|
|
||||||
# cmp dil, '_'
|
|
||||||
40 80 ff 5f
|
|
||||||
# je .loop
|
|
||||||
0f 84 'parse_i64.loop
|
|
||||||
# mov esi, r12d
|
|
||||||
44 89 e6
|
|
||||||
# call to_digit
|
|
||||||
e8 'to_digit
|
|
||||||
# mov ecx, edx
|
|
||||||
89 d1
|
|
||||||
# test al, al
|
|
||||||
84 c0
|
|
||||||
# je .done
|
|
||||||
0f 84 'parse_i64.done
|
|
||||||
# mov rax, r13
|
|
||||||
4c 89 e8
|
|
||||||
# imul r12
|
|
||||||
49 f7 ec
|
|
||||||
# add rax, rcx
|
|
||||||
48 01 c8
|
|
||||||
mov r13, rax
|
|
||||||
49 89 c5
|
|
||||||
# jmp .loop
|
|
||||||
e9 'parse_i64.loop
|
|
||||||
|
|
||||||
@exit:
|
|
||||||
# xor edi, edi
|
|
||||||
31 ff
|
|
||||||
# mov eax, 60
|
|
||||||
b8 3c 00 00 00
|
|
||||||
# syscall
|
|
||||||
0f 05
|
|
||||||
|
|
||||||
@fasthash.done:
|
|
||||||
c3
|
|
||||||
@fasthash:
|
|
||||||
# mov eax, 0x811c9dc5
|
|
||||||
b8 c5 9d 1c 81
|
|
||||||
@fasthash.loop:
|
|
||||||
# test rsi, rsi
|
|
||||||
48 85 f6
|
|
||||||
# jz .done
|
|
||||||
0f 84 'fasthash.done
|
|
||||||
# movzx ecx, byte [rdi]
|
|
||||||
0f b6 0f
|
|
||||||
# xor eax, ecx
|
|
||||||
31 c8
|
|
||||||
# imul eax, 0x01000193
|
|
||||||
69 c0 93 01 00 01
|
|
||||||
# inc rdi
|
|
||||||
48 ff c7
|
|
||||||
# dec rsi
|
|
||||||
48 ff ce
|
|
||||||
# jmp .loop
|
|
||||||
e9 'fasthash.loop
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
;; Vec / Dynamic Array Structure
|
|
||||||
extern heap_realloc
|
|
||||||
|
|
||||||
global vec_init
|
|
||||||
global vec_get_nth
|
|
||||||
global vec_reserve
|
|
||||||
|
|
||||||
struc Vec
|
|
||||||
.data resq 1 ; pointer to elements
|
|
||||||
.size resd 1 ; size in element count
|
|
||||||
.cap resd 1 ; cap in element count
|
|
||||||
.ele_size resd 1 ; size of each element in bytes
|
|
||||||
.ele_align resd 1 ; alignment of each element in bytes
|
|
||||||
endstruc
|
|
||||||
|
|
||||||
;; fn vec_init(vec: *mut Vec, ele_size: usize, ele_align: usize) -> *mut Vec
|
|
||||||
vec_init:
|
|
||||||
mov qword [rdi + Vec.data], 0
|
|
||||||
mov dword [rdi + Vec.size], 0
|
|
||||||
mov dword [rdi + Vec.cap], 0
|
|
||||||
mov dword [rdi + Vec.ele_size], esi
|
|
||||||
mov dword [rdi + Vec.ele_align], edx
|
|
||||||
mov rax, rdi
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; fn vec_get_nth(vec: *const Vec, idx: usize) -> *const u8
|
|
||||||
vec_get_nth:
|
|
||||||
mov rax, rsi
|
|
||||||
mov ecx, dword [rdi + Vec.ele_size]
|
|
||||||
mul ecx
|
|
||||||
add rax, qword [rdi + Vec.data]
|
|
||||||
ret
|
|
||||||
|
|
||||||
;; reserve space for $rsi additional entries in vec $rdi.
|
|
||||||
;; fn vec_reserve(vec: *mut Vec, count: usize)
|
|
||||||
vec_reserve:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
sub rsp, 16
|
|
||||||
|
|
||||||
mov edx, dword [rdi + Vec.ele_size]
|
|
||||||
mov ecx, dword [rdi + Vec.ele_align]
|
|
||||||
mov dword [rsp], edx ; obj-size
|
|
||||||
mov dword [rsp + 4], ecx ; alignment
|
|
||||||
mov qword [rsp + 8], rdi ; table pointer
|
|
||||||
|
|
||||||
mov eax, dword [rdi + Vec.size]
|
|
||||||
add eax, esi
|
|
||||||
cmp eax, dword [rdi + Vec.cap]
|
|
||||||
jl .done
|
|
||||||
|
|
||||||
mov eax, esi
|
|
||||||
add eax, dword [rdi + Vec.size]
|
|
||||||
mov ecx, dword [rdi + Vec.cap]
|
|
||||||
mov edx, ecx
|
|
||||||
shl edx, 1
|
|
||||||
cmp eax, edx
|
|
||||||
cmovl eax, edx
|
|
||||||
; new cap
|
|
||||||
mul dword [rsp] ; obj-size
|
|
||||||
|
|
||||||
; old cap
|
|
||||||
xchg eax, ecx
|
|
||||||
mul dword [rsp] ; obj-size
|
|
||||||
|
|
||||||
mov rdi, qword [rdi + Vec.data] ; old pointer
|
|
||||||
mov esi, eax ; old size
|
|
||||||
mov edx, ecx ; new size
|
|
||||||
mov ecx, dword [rsp + 4] ; alignment
|
|
||||||
call heap_realloc
|
|
||||||
mov rdi, qword [rsp + 8] ; table pointer
|
|
||||||
mov qword [rdi + Vec.data], rax
|
|
||||||
|
|
||||||
.done:
|
|
||||||
add rsp, 16
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
51
test.asm
51
test.asm
|
|
@ -1,51 +0,0 @@
|
||||||
section .text
|
|
||||||
|
|
||||||
global _start
|
|
||||||
_start:
|
|
||||||
mov rdi, 42
|
|
||||||
call main
|
|
||||||
mov rdi, rax
|
|
||||||
mov rax, 60 ; syscall: exit
|
|
||||||
syscall
|
|
||||||
|
|
||||||
global main
|
|
||||||
main:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
jmp .prologue
|
|
||||||
.args:
|
|
||||||
mov qword [rbp -8], rdi
|
|
||||||
.body:
|
|
||||||
mov rbx, 42
|
|
||||||
mov rcx, qword [rbp -8]
|
|
||||||
mov rax, rbx
|
|
||||||
cmp rcx, rax
|
|
||||||
sete bl
|
|
||||||
cmp bl, 0
|
|
||||||
je .Be
|
|
||||||
mov rbx, 7
|
|
||||||
mov qword [rbp -16], rbx
|
|
||||||
mov rdi, qword [rbp -16]
|
|
||||||
mov rax, rsp
|
|
||||||
sub rsp, 8
|
|
||||||
and rsp, -16
|
|
||||||
mov [rsp], rax
|
|
||||||
call main
|
|
||||||
pop rsp
|
|
||||||
mov rbx, rax
|
|
||||||
mov rax, rbx
|
|
||||||
jmp .epilogue
|
|
||||||
jmp .PHI4
|
|
||||||
.Be:
|
|
||||||
lea rax, main
|
|
||||||
jmp .epilogue
|
|
||||||
.PHI4:
|
|
||||||
.epilogue:
|
|
||||||
pop rbx
|
|
||||||
add rsp, 16
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
.prologue:
|
|
||||||
sub rsp, 16
|
|
||||||
push rbx
|
|
||||||
jmp .args
|
|
||||||
Loading…
Reference in a new issue