Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/tests/debug-image-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,43 @@ std::vector<Case> cases() {
++f.relocation().offset;exercise(f,false);
}
});
// An i386 image (0.6.3): ELF32 sections, symbols and REL link relocations,
// whose addend is the word the link resolved; the object holds the addend.
out.emplace_back("i386_rel_word_becomes_its_addend",[]{
Bytes b(1040,0);
auto& h=*reinterpret_cast<e::ehdr32*>(b.data());
auto* sh=reinterpret_cast<e::shdr32*>(b.data()+64);
h.ident[0]=0x7f;h.ident[1]='E';h.ident[2]='L';h.ident[3]='F';h.ident[4]=1;h.ident[5]=1;h.ident[6]=1;
h.type=e::et_dyn;h.machine=e::em_386;h.version=1;h.ehsize=52;h.shoff=64;h.shentsize=40;h.shnum=8;h.shstrndx=1;
std::string names(1,'\0');
auto name=[&](const char* s){unsigned at=names.size();names+=s;names+='\0';return at;};
sh[1]={name(".shstrtab"),3,0,0,576,0,0,0,1,0};
sh[2]={name(".text"),1,2,0x1000,768,32,0,0,16,0};
sh[3]={name(".strtab"),3,0,0,800,5,0,0,1,0};
sh[4]={name(".symtab"),2,0,0,832,32,3,1,4,16};
sh[5]={name(".debug_info"),1,0,0,896,8,0,0,4,0};
sh[6]={name(".rel.debug_info"),9,0,0,1024,16,4,5,4,8};
sh[7]={name(".bss"),8,2,0x2000,0,16,0,0,4,0};
sh[1].size=names.size();std::memcpy(b.data()+576,names.data(),names.size());
std::memcpy(b.data()+800,"\0foo",5);
const e::sym32 foo{1,0x1004,4,18,0,2};std::memcpy(b.data()+848,&foo,16);
const e::u32 absolute=0x1004+6, relative=0x1004-4-4; // S+A with A=6; S+A-P with A=-4, P=4
std::memcpy(b.data()+896,&absolute,4);std::memcpy(b.data()+900,&relative,4);
const e::rel32 r[2]={{0,(1u<<8)|1},{4,(1u<<8)|2}};std::memcpy(b.data()+1024,r,16);
const Bytes before=b;
Heap heap;auto host=heap.host();void* out=nullptr;okld_size size=0;okld_why why{};
require(okld::debugger_image(b.data(),b.size(),0x40000000,&host,&out,&size,&why),why.text);
require(b==before,"builder mutated source image");
const auto* p=static_cast<const unsigned char*>(out);
const auto* oh=reinterpret_cast<const e::shdr32*>(p+64);
const auto* os=reinterpret_cast<const e::sym32*>(p+848);
e::u32 a0=0,a1=0;std::memcpy(&a0,p+896,4);std::memcpy(&a1,p+900,4);
require(reinterpret_cast<const e::ehdr32*>(p)->type==1,"ET_REL header");
require(oh[2].addr==0x40001000 && os->value==4 && os->shndx==2,"placed section, relative symbol");
require(a0==6 && a1==static_cast<e::u32>(-4),"the words hold their addends");
host.free(out,size,host.ctx);
require(heap.live.empty() && !heap.bad_release,"allocation leak or incorrect free size");
});
out.emplace_back("none_relocation_has_no_target_write",[]{Fixture f;f.relocation().offset=~e::u64{0};f.relocation().info=0xffffffff00000000ull;exercise(f,true);});
out.emplace_back("repeated_build_release",[]{for(int i=0;i<100;++i)exercise(Fixture(true),true);});
return out;
Expand Down
19 changes: 16 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ env:
XLINGS_NON_INTERACTIVE: '1'
# 2026.11.10.1: openkal-run binds the interfaces its implementation provides
# (MCPP_KERNEL_ABI_INTERFACES).
MCPP_VERSION: 2026.11.10.1
# 0.6.3: 2026.11.13.1, the floor of openkal-run (the core runner, by `cfg(interface)').
MCPP_VERSION: 2026.11.13.1
LLVM: llvm@23.1.3

jobs:
Expand Down Expand Up @@ -76,8 +77,10 @@ jobs:
for arch in x86_64 aarch64; do
mkdir -p "out/$arch"
bash examples/build-image.sh "$arch" "out/$arch/hello.oke" examples/hello/app.c
# The declarations are compiled from the public pinned specification.
spec=$(find "$HOME/.mcpp" -path '*openkal*0.25.0*/include/openkal/process.h' -type f | head -1)
# The declarations are compiled from the public specification the
# tools resolved: any patch of its minor version (openkal clause 8),
# which is 0.25.1 since that patch was published.
spec=$(find "$HOME/.mcpp" -path '*openkal*0.25.*/include/openkal/process.h' -type f | sort -V | tail -1)
test -n "$spec"
CPATH="${spec%/openkal/process.h}" bash examples/build-image.sh "$arch" "out/$arch/process-unit.oke" examples/process-unit/app.c
bash examples/build-image.sh "$arch" "out/$arch/liboktwice.okso" examples/needed/lib.c
Expand Down Expand Up @@ -127,6 +130,16 @@ jobs:
if "$OKLD" check changed.oke 2> changed-placement.txt; then echo "::error::a changed stored segment passed okld check"; exit 1; fi
grep -q "SPEC 12.4" changed-placement.txt
done
# i386 (0.6.3): read, placed and checked here; a runner of i386 runs
# them where it is built (openkal-platform's row i686-openkal).
mkdir -p out/i686
bash examples/build-image.sh i686 out/i686/hello.oke examples/hello/app.c
bash examples/build-image.sh i686 out/i686/liboktwice.okso examples/needed/lib.c
bash examples/build-image.sh i686 out/i686/needed.oke examples/needed/app.c -- out/i686/liboktwice.okso
"$OKLD" inspect out/i686/hello.oke | grep -q '^architecture *i686$' \
|| { echo "::error::an i386 image is not named i686"; exit 1; }
"$OKLD" bundle i386.okb --version 0.1.0 out/x86_64/hello.oke out/i686/hello.oke
"$OKLD" bundle check i386.okb | grep -q 'i686 (1 image(s))'
"$OKLD" keygen "$RUNNER_TEMP/seed" > out/key.hex
for arch in x86_64 aarch64; do
"$OKLD" sign "out/$arch/signed.oke" "$RUNNER_TEMP/seed"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ env:
MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF || '' }}
# 2026.11.10.1: openkal-run binds the interfaces its implementation provides.
# mcpp_ref supplies the candidate until this release is installable.
MCPP_VERSION: 2026.11.10.1
MCPP_VERSION: 2026.11.13.1
# The platform whose hosted arrangement the programs are built over, named by
# the dispatch beside the tag: a tag's tools pin exact implementations, and the
# platform is the one whose version group holds them (0.1.4: 0.18.0; 0.1.5:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ compile_commands.json
mcpp.lock
examples/bind/mathlib/lib/
__pycache__/
okprof-*.json
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,44 @@ The runner now folds a relative name against the first directory's name ---
name beneath it, which is matched against the supplied directories as an
absolute one is. No other change.

Version 0.6.3 builds a core runner where the implementation beneath it has no
files, processes or threads --- a firmware's, such as openkal-uefi. One interface
(`include/okrun.h`, `run/src/runner.h`, the bindings) has two runners, and
`run/mcpp.toml` selects one by the interfaces the implementation provides
(`cfg(interface = "openkal.fs")`, mcpp 2026.11.13.1): with `openkal.fs`,
`openkal.process`, `openkal.task`, `openkal.random` and `openkal.map` the full
runner (`run/src/full`), unchanged; without any of them the core runner
(`run/src/core`). The core runner runs what the program carries, from memory:
an image, a group of images, or an archive of them (`okrun_run_bytes`,
`okrun_run_images`, `okrun_run_okb_bytes`), with the same admission --- the
requirements note, the architecture, the signature --- and the same loader. It
has no cache, no snapshot and no profiler, and `okrun_run_file` refuses with the
reason. A name of an interface the implementation does not provide is absent to
the image, so an image that imports it weakly runs (SPEC 12.4). openkal-run
itself requires only `openkal.abort`, `openkal.env`, `openkal.exec`,
`openkal.memory`, `openkal.stream` and `openkal.time`, and mcpp 2026.11.13.1.

Version 0.6.3 also reads, checks, places and loads an i386 image (`ELFCLASS32`,
`EM_386`; openkal 0.25.1, clauses 5.5 and 12.2), on an i386 runner only. libokld
widens the file's header, program headers and dynamic table to their
sixty-four bit forms when it opens one, and reads its symbols and relocations
through accessors of the class, so one loader, one check and one placement
serve both classes. The image states its addends (RELA, `-z rela`; i386's
default REL is refused with that option named), and its packed relative
relocations are of the four-byte word (a bitmap of 31 entries). The relocations
a loader applies are `R_386_RELATIVE`, `R_386_32`, `R_386_GLOB_DAT` and
`R_386_JMP_SLOT`. The runner enters an i386 image and its callbacks in the System
V i386 convention (arguments on the stack, the stack aligned to sixteen bytes at
a call), with the x87 control word `0x037F` and, where the processor has SSE,
MXCSR `0x1F80`. An okb names the architecture `i686`. The object a debugger
reads (`okrun --debugger`) is of the image's class: i386's retained link
relocations are REL, whose addend is the word at the place, so the object holds
each `R_386_32` and `R_386_PC32` word less the value the link resolved. An image
of the row `i686-openkal`, built through openkal-platform 0.28.0 (C++23 with
`import std`, an exception across a frame), runs under an okrun built for
`i686-linux-musl` on an x86-64 Linux, and gdb stops at a line of it and unwinds
into the runner, as for an x86-64 image (measured).

## An image

```sh
Expand Down
7 changes: 6 additions & 1 deletion docs/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ native source-debugger compatibility on those systems.
## Image requirements and limits

The debugger-object builder accepts little-endian ELF64 metadata for x86-64
and AArch64. Embedded debug sections may use ELF zlib compression
and AArch64, and ELF32 metadata for i386 (0.6.3). An i386 image's retained
link relocations are REL: their addend is the word at the place, which the link
has resolved, so the object holds each `R_386_32` and `R_386_PC32` word less the
symbol's value (and, for `R_386_PC32`, plus the place), and the debugger adds
its own. The image's run-time relocations (`.rela.dyn`, `.rela.plt`) are data in
the object. Embedded debug sections may use ELF zlib compression
(`SHF_COMPRESSED`); the loader expands them in the debugger object. The
executable image and its persistent compressed bytes remain unchanged.

Expand Down
2 changes: 1 addition & 1 deletion docs/okb.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ aarch64/libdep.okso
```

An architecture's directory is named in the manifest; `okld bundle` names it after the
architecture (`x86_64`, `aarch64`). Version 1 holds images and the two manifest members.
architecture (`x86_64`, `aarch64`, `i686`). Version 1 holds images and the two manifest members.
A program's data files are delivered beside the okb (in the package it is part of); a view
of files inside an okb, through the loader, is reserved for a later version, which the
manifest's `okb` number distinguishes.
Expand Down
12 changes: 8 additions & 4 deletions examples/build-image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# with the compiler and linker any LLVM carries (SPEC 12.2): no operating
# system in the triple, position-independent, emulated thread-local storage,
# x18 left alone on AArch64, segments aligned to 64 KiB, relative relocations
# packed, the notes linked in, the requirements written by `okld note', and the
# image placed at its base by `okld place' (openkal 0.23; PLACE=0 leaves the
# placement note empty).
# packed, the addends stated (RELA; i386's default is REL), the notes linked in,
# the requirements written by `okld note', and the image placed at its base by
# `okld place' (openkal 0.23; PLACE=0 leaves the placement note empty).
set -euo pipefail
arch="$1"; out="$2"; shift 2
sources=(); link=()
Expand All @@ -18,6 +18,10 @@ done
work=$(mktemp -d); trap 'rm -rf "$work"' EXIT
extra=()
[ "$arch" = aarch64 ] && extra=(-ffixed-x18 -mno-outline-atomics)
# i386 (0.6.3): the 486's instructions (an image runs where DOSBox does, which
# executes no CMPXCHG8B), and the stack aligned to sixteen bytes at a call
# (SPEC 12.2), which a triple with no system leaves at four.
[ "$arch" = i686 ] && extra=(-march=i486 -mstack-alignment=16)
"$OKLD" note-source > "$work/note.S"
objs=()
for src in "${sources[@]}" "$work/note.S"; do
Expand All @@ -29,7 +33,7 @@ for src in "${sources[@]}" "$work/note.S"; do
done
entry=(-e ok_main)
case "$out" in *.okso) entry=(-soname "$(basename "$out")") ;; esac
"$LLD" -shared -z max-page-size=65536 -z now -z noexecstack -z pack-relative-relocs --hash-style=both "${entry[@]}" \
"$LLD" -shared -z max-page-size=65536 -z now -z noexecstack -z pack-relative-relocs -z rela --hash-style=both "${entry[@]}" \
-o "$out" "${objs[@]}" "${link[@]}"
"$OKLD" note "$out"
[ "${PLACE:-1}" = 0 ] || "$OKLD" place "$out"
Expand Down
2 changes: 1 addition & 1 deletion ld/mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-ld"
version = "0.6.2"
version = "0.6.3"
description = "libokld: the ok format (openkal image format) --- its definition, a reader of bytes, a checker, a signer, and a loader. Freestanding: no C library, no exceptions."
license = "Apache-2.0"
authors = ["mcpplibs"]
Expand Down
Loading
Loading