diff options
| author | LLLL Colonq <llll@colonq> | 2025-12-25 23:42:59 -0500 |
|---|---|---|
| committer | LLLL Colonq <llll@colonq> | 2025-12-25 23:42:59 -0500 |
| commit | c149c76ad8b251c45a0e6532850cf5dfd388f55c (patch) | |
| tree | 166d187e9488bba0af111381337d7d96de715a2d | |
Initial commit
| -rw-r--r-- | .envrc | 1 | ||||
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | Makefile | 55 | ||||
| -rw-r--r-- | flake.lock | 171 | ||||
| -rw-r--r-- | flake.nix | 48 | ||||
| -rw-r--r-- | include/lcq/royaljelly.h | 3 | ||||
| -rw-r--r-- | include/lcq/royaljelly/library.h | 8 | ||||
| -rw-r--r-- | src/library.c | 142 | ||||
| -rw-r--r-- | src/main.c | 62 | ||||
| -rw-r--r-- | test.lisp | 7 |
10 files changed, 502 insertions, 0 deletions
@@ -0,0 +1 @@ +use_flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c79a0f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build_*/ +.direnv/ +TAGS +*.a +/royaljelly diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7dcaf1 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +CC ?= gcc +AR ?= ar +CHK_SOURCES ?= $(SRCS) +CPPFLAGS ?= -MMD -MP +CFLAGS ?= -flto -ffat-lto-objects -march=native --std=c89 -g -Ideps/ -Isrc/ -Iinclude/ -Wall -Wextra -Wpedantic -Wconversion -Wformat-security -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wfloat-equal -Wundef -Wpointer-arith -Wbad-function-cast -Wmissing-braces -Wcast-align -Wstrict-overflow=5 -ftrapv +LDFLAGS ?= -flto -g -static -lcolonq-pit -lcolonq-elf + +BUILD = build_$(CC) + +SRCS := src/library.c +OBJECTS := $(SRCS:src/%.c=$(BUILD)/%.o) +EXE := royaljelly +LIB := libcolonq-royaljelly.a + +prefix ?= /usr/local +exec_prefix ?= $(prefix) +bindir ?= $(exec_prefix)/bin +includedir ?= $(prefix)/include +libdir ?= $(exec_prefix)/lib + +.PHONY: all clean install check-syntax + +all: $(EXE) $(LIB) + +$(EXE): $(BUILD)/main.o $(LIB) + $(CC) -o $@ $^ $(LDFLAGS) + +$(LIB): $(OBJECTS) + ar rcs $@ $^ + +$(BUILD): + mkdir $(BUILD)/ + +$(BUILD)/%.o: src/%.c | $(BUILD) + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< + +clean: + -rm $(EXE) + -rm $(LIB) + -rm -r $(BUILD)/ + +TAGS: $(SRCS) + ctags --output-format=etags $^ + +install: $(EXE) $(LIB) + mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir) + install $(EXE) $(DESTDIR)$(bindir)/$(EXE) + install $(LIB) $(DESTDIR)$(libdir)/$(LIB) + cp -r include/* $(DESTDIR)$(includedir) + +check-syntax: TAGS + gcc $(CFLAGS) -fsyntax-only $(CHK_SOURCES) + +-include $(BUILD)/main.d +-include $(OBJECTS:.o=.d) diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f655d49 --- /dev/null +++ b/flake.lock @@ -0,0 +1,171 @@ +{ + "nodes": { + "elf": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1766203113, + "narHash": "sha256-TCBQ0LeA+htvnwJ5B+z2BBfur5PfFMU4aZq/wdqAEeM=", + "owner": "lcolonq", + "repo": "libcolonq-elf", + "rev": "dc3508da30a577fc8f3247a5b571c8ab5fc5564b", + "type": "github" + }, + "original": { + "owner": "lcolonq", + "repo": "libcolonq-elf", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1762482733, + "narHash": "sha256-g/da4FzvckvbiZT075Sb1/YDNDr+tGQgh4N8i5ceYMg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e1ebeec86b771e9d387dd02d82ffdc77ac753abc", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pit": { + "inputs": { + "flake-utils": "flake-utils_3", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1766198852, + "narHash": "sha256-DZ0nqM4BwZwm0KzzJt74YVJmQYJDFR1gxuD+GIWDNQk=", + "owner": "lcolonq", + "repo": "pit", + "rev": "e6329f2ce1df83fd729e79f7e92e55fe96a2e826", + "type": "github" + }, + "original": { + "owner": "lcolonq", + "repo": "pit", + "type": "github" + } + }, + "root": { + "inputs": { + "elf": "elf", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs", + "pit": "pit" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c12be18 --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + pit = { + url = "github:lcolonq/pit"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + elf = { + url = "github:lcolonq/libcolonq-elf"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, ... }@inputs: + inputs.flake-utils.lib.eachDefaultSystem + (system: + let + nm = "royaljelly"; + pkgs = nixpkgs.legacyPackages.${system}; + p = pkgs.pkgsMusl.stdenv.mkDerivation { + pname = nm; + version = "git"; + src = ./.; + hardeningDisable = ["all"]; + installPhase = '' + make prefix=$out install + ''; + }; + in { + packages = { + "${nm}" = p; + default = p; + }; + devShells.default = pkgs.mkShell { + hardeningDisable = ["all"]; + NIX_ENFORCE_NO_NATIVE = ""; + buildInputs = [ + pkgs.musl + pkgs.valgrind + pkgs.universal-ctags + inputs.pit.packages.x86_64-linux.default + inputs.elf.packages.x86_64-linux.default + ]; + }; + } + ); +} diff --git a/include/lcq/royaljelly.h b/include/lcq/royaljelly.h new file mode 100644 index 0000000..862cdc2 --- /dev/null +++ b/include/lcq/royaljelly.h @@ -0,0 +1,3 @@ +#ifndef LCOLONQ_ROYALJELLY_H +#define LCOLONQ_ROYALJELLY_H +#endif diff --git a/include/lcq/royaljelly/library.h b/include/lcq/royaljelly/library.h new file mode 100644 index 0000000..f8fc65d --- /dev/null +++ b/include/lcq/royaljelly/library.h @@ -0,0 +1,8 @@ +#ifndef LCOLONQ_ROYALJELLY_LIBRARY_H +#define LCOLONQ_ROYALJELLY_LIBRARY_H + +#include <lcq/pit/runtime.h> + +void rj_install_library(pit_runtime *rt); + +#endif diff --git a/src/library.c b/src/library.c new file mode 100644 index 0000000..6e1eb64 --- /dev/null +++ b/src/library.c @@ -0,0 +1,142 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <lcq/royaljelly/library.h> + +#include <lcq/pit/types.h> +#include <lcq/pit/runtime.h> + +#include <lcq/elf.h> + +struct elf_builder { + i64 cap; + elf_ctx ctx; +}; + +static void builder_ensure_size(struct elf_builder *e, i64 sz) { + i64 cur = e->ctx.len; + i64 cap = e->cap; + if (sz <= cur) return; + while (cap < sz) cap <<= 1; + e->ctx.buf = realloc(e->ctx.buf, (size_t) cap); + memset(e->ctx.buf + e->cap, 0, (size_t) (cap - e->cap)); + e->ctx.len = sz; +} + +static pit_value impl_elf_new(pit_runtime *rt, pit_value kwargs) { + /* + i64 vclass = pit_as_integer(rt, pit_plist_get(rt, pit_intern_cstr(rt, ":class"), kwargs)); + i64 vendianness = pit_as_integer(rt, pit_plist_get(rt, pit_intern_cstr(rt, ":endianness"), kwargs)); + */ + i64 vclass = -1; + i64 vendianness = -1; + struct elf_builder *ret = malloc(sizeof(struct elf_builder)); + i64 len = 256; + u8 *buf = calloc((size_t) len, 1); + ret->cap = len; + ret->ctx = elf_ctx_new(buf, len, + vclass > 0 ? vclass : ELF_CLASS_64, + vendianness > 0 ? vendianness : ELF_ENDIANNESS_LITTLE + ); + return pit_nativedata_new(rt, pit_intern_cstr(rt, "elf"), (void *) ret); +} +static i64 get_kwargs_i64(pit_runtime *rt, i64 def, pit_value kwargs, char *kw) { + pit_value v = pit_plist_get(rt, pit_intern_cstr(rt, kw), kwargs); + if (v != PIT_NIL && pit_is_integer(rt, v)) { + return pit_as_integer(rt, v); + } else { + return def; + } +} +static pit_value impl_elf_write_header(pit_runtime *rt, pit_value args) { + pit_value velf = pit_car(rt, args); + pit_value kwargs = pit_cdr(rt, args); + elf_header h = {0}; + struct elf_builder *e = pit_nativedata_get(rt, pit_intern_cstr(rt, "elf"), velf); + if (!e) return PIT_NIL; + builder_ensure_size(e, elf_header_size(&e->ctx)); + h.type = get_kwargs_i64(rt, ELF_TYPE_EXEC, kwargs, ":type"); + h.machine = get_kwargs_i64(rt, ELF_MACHINE_AMD64, kwargs, ":machine"); + h.version = (u32) get_kwargs_i64(rt, 1, kwargs, ":version"); + h.entry = (u64) get_kwargs_i64(rt, 0x401000, kwargs, ":entry"); + h.program_header_offset = (u64) get_kwargs_i64(rt, 0, kwargs, ":program-header-offset"); + h.program_header_entry_size = (u16) get_kwargs_i64(rt, elf_program_header_size(&e->ctx), kwargs, ":program-header-entry-size"); + h.program_header_entries = (u16) get_kwargs_i64(rt, 1, kwargs, ":program-header-entries"); + h.section_header_offset = (u64) get_kwargs_i64(rt, 0, kwargs, ":section-header-offset"); + h.section_header_entry_size = (u16) get_kwargs_i64(rt, elf_section_header_size(&e->ctx), kwargs, ":section-header-entry-size"); + h.section_header_entries = (u16) get_kwargs_i64(rt, 0, kwargs, ":section-header-entries"); + h.section_name_table_index = (u16) get_kwargs_i64(rt, 1, kwargs, ":section-name-table-index"); + elf_write_header(&h, &e->ctx); + return PIT_NIL; +} +static pit_value impl_elf_write_section_header(pit_runtime *rt, pit_value args) { + pit_value velf = pit_car(rt, args); + pit_value voff = pit_car(rt, pit_cdr(rt, args)); + i64 off = pit_as_integer(rt, voff); + pit_value kwargs = pit_cdr(rt, pit_cdr(rt, args)); + elf_section_header h = {0}; + struct elf_builder *e = pit_nativedata_get(rt, pit_intern_cstr(rt, "elf"), velf); + if (!e) return PIT_NIL; + if (off < 0) { pit_error(rt, "negative offset: %d", off); return PIT_NIL; } + builder_ensure_size(e, off + elf_header_size(&e->ctx)); + h.name_index = (u32) get_kwargs_i64(rt, ELF_SECTION_TYPE_NULL, kwargs, ":name-index"); + h.type = (u32) get_kwargs_i64(rt, ELF_SECTION_TYPE_NULL, kwargs, ":type"); + h.flags = (u64) get_kwargs_i64(rt, 0, kwargs, ":flags"); + h.addr = (u64) get_kwargs_i64(rt, 0, kwargs, ":addr"); + h.offset = (u64) get_kwargs_i64(rt, 0, kwargs, ":offset"); + h.size = (u64) get_kwargs_i64(rt, 0, kwargs, ":size"); + h.link = (u32) get_kwargs_i64(rt, 0, kwargs, ":link"); + h.info = (u32) get_kwargs_i64(rt, 0, kwargs, ":info"); + h.addr_alignment = (u64) get_kwargs_i64(rt, 0, kwargs, ":addr-alignment"); + h.entry_size = (u64) get_kwargs_i64(rt, 0, kwargs, ":entry-size"); + elf_write_section_header(&h, &e->ctx, (u64) off); + return PIT_NIL; +} +static pit_value impl_elf_spit(pit_runtime *rt, pit_value args) { + char pathbuf[1024] = {0}; + i64 len = 0, actual = 0; + FILE *f = NULL; + struct elf_builder *e = NULL; + pit_value path = pit_car(rt, args); + pit_value velf = pit_car(rt, pit_cdr(rt, args)); + len = pit_as_bytes(rt, path, (u8 *) pathbuf, sizeof(pathbuf) - 1); + if (len < 0) { pit_error(rt, "path was not a string"); return PIT_NIL; } + pathbuf[len] = 0; + e = pit_nativedata_get(rt, pit_intern_cstr(rt, "elf"), velf); + if (!e) return PIT_NIL; + f = fopen(pathbuf, "w+"); + if (!f) { + pit_error(rt, "failed to open file: %s", pathbuf); + return PIT_NIL; + } + actual = (i64) fwrite(e->ctx.buf, 1, (size_t) e->ctx.len, f); + fclose(f); + if (e->ctx.len != actual) { + pit_error(rt, "failed to write ELF file"); + return PIT_NIL; + } + return velf; +} +void rj_install_library(pit_runtime *rt) { + /* constants */ + pit_set(rt, pit_intern_cstr(rt, "elf/CLASS_INVALID"), pit_integer_new(rt, 0)); + pit_set(rt, pit_intern_cstr(rt, "elf/CLASS_32"), pit_integer_new(rt, 1)); + pit_set(rt, pit_intern_cstr(rt, "elf/CLASS_64"), pit_integer_new(rt, 2)); + pit_set(rt, pit_intern_cstr(rt, "elf/ENDIANNESS_INVALID"), pit_integer_new(rt, 0)); + pit_set(rt, pit_intern_cstr(rt, "elf/ENDIANNESS_LITTLE"), pit_integer_new(rt, 1)); + pit_set(rt, pit_intern_cstr(rt, "elf/ENDIANNESS_BIG"), pit_integer_new(rt, 2)); + pit_set(rt, pit_intern_cstr(rt, "elf/TYPE_NONE"), pit_integer_new(rt, 0)); + pit_set(rt, pit_intern_cstr(rt, "elf/TYPE_REL"), pit_integer_new(rt, 1)); + pit_set(rt, pit_intern_cstr(rt, "elf/TYPE_EXEC"), pit_integer_new(rt, 2)); + pit_set(rt, pit_intern_cstr(rt, "elf/TYPE_DYN"), pit_integer_new(rt, 3)); + pit_set(rt, pit_intern_cstr(rt, "elf/TYPE_CORE"), pit_integer_new(rt, 4)); + pit_set(rt, pit_intern_cstr(rt, "elf/MACHINE_NONE"), pit_integer_new(rt, 0)); + pit_set(rt, pit_intern_cstr(rt, "elf/MACHINE_X86"), pit_integer_new(rt, 3)); + pit_set(rt, pit_intern_cstr(rt, "elf/MACHINE_AMD64"), pit_integer_new(rt, 62)); + + pit_fset(rt, pit_intern_cstr(rt, "elf/new!"), pit_nativefunc_new(rt, impl_elf_new)); + pit_fset(rt, pit_intern_cstr(rt, "elf/write-header!"), pit_nativefunc_new(rt, impl_elf_write_header)); + pit_fset(rt, pit_intern_cstr(rt, "elf/write-section-header!"), pit_nativefunc_new(rt, impl_elf_write_section_header)); + pit_fset(rt, pit_intern_cstr(rt, "elf/spit!"), pit_nativefunc_new(rt, impl_elf_spit)); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5ca07f2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,62 @@ +#include <stdlib.h> +#include <stdio.h> + +#include <lcq/royaljelly/library.h> + +#include <lcq/pit/utils.h> +#include <lcq/pit/lexer.h> +#include <lcq/pit/parser.h> +#include <lcq/pit/runtime.h> +#include <lcq/pit/library.h> + +int main(int argc, char **argv) { + pit_runtime *rt = pit_runtime_new(); + pit_install_library_essential(rt); + pit_install_library_io(rt); + pit_install_library_plist(rt); + pit_install_library_bytestring(rt); + rj_install_library(rt); + if (argc < 2) { /* run repl */ + char buf[1024] = {0}; + i64 len = 0; + pit_runtime_freeze(rt); + if (pit_runtime_print_error(rt)) { exit(1); } + setbuf(stdout, NULL); + printf("> "); + while (len < (i64) sizeof(buf) && (buf[len++] = (char) getchar()) != EOF) { + if (buf[len - 1] == '\n') { + pit_value bs, prog, res; + buf[len - 1] = 0; + bs = pit_bytes_new_cstr(rt, buf); + prog = pit_read_bytes(rt, bs); + res = pit_eval(rt, prog); + if (pit_runtime_print_error(rt)) { + rt->error = PIT_NIL; + printf("> "); + } else { + char dumpbuf[1024] = {0}; + pit_dump(rt, dumpbuf, sizeof(dumpbuf) - 1, res, true); + printf("%s\n> ", dumpbuf); + } + len = 0; + } + } + } else { /* run file */ + pit_value bs = pit_bytes_new_file(rt, argv[1]); + pit_lexer lex; + pit_parser parse; + bool eof = false; + pit_value p = PIT_NIL; + if (!pit_lexer_from_bytes(rt, &lex, bs)) { + pit_error(rt, "failed to initialize lexer"); + } + pit_parser_from_lexer(&parse, &lex); + while (p = pit_parse(rt, &parse, &eof), !eof) { + pit_eval(rt, p); + if (pit_runtime_print_error(rt)) { + exit(1); + } + } + } + return 0; +} diff --git a/test.lisp b/test.lisp new file mode 100644 index 0000000..4353ef3 --- /dev/null +++ b/test.lisp @@ -0,0 +1,7 @@ +(let ((elf (elf/new!))) + (print! elf) + (elf/write-header! elf + :type elf/TYPE_EXEC + :machine elf/MACHINE_AMD64) + (elf/spit! "test.elf" elf) + ) |
