summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLLLL Colonq <llll@colonq>2025-12-25 23:42:59 -0500
committerLLLL Colonq <llll@colonq>2025-12-25 23:42:59 -0500
commitc149c76ad8b251c45a0e6532850cf5dfd388f55c (patch)
tree166d187e9488bba0af111381337d7d96de715a2d
Initial commit
-rw-r--r--.envrc1
-rw-r--r--.gitignore5
-rw-r--r--Makefile55
-rw-r--r--flake.lock171
-rw-r--r--flake.nix48
-rw-r--r--include/lcq/royaljelly.h3
-rw-r--r--include/lcq/royaljelly/library.h8
-rw-r--r--src/library.c142
-rw-r--r--src/main.c62
-rw-r--r--test.lisp7
10 files changed, 502 insertions, 0 deletions
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..c4b17d7
--- /dev/null
+++ b/.envrc
@@ -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)
+ )