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
17 changes: 16 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,14 @@ jobs:
out/x86_64/needed.oke out/x86_64/liboktwice.okso out/aarch64/needed.oke out/aarch64/liboktwice.okso
"$OKLD" bundle out/signed.okb --sign "$RUNNER_TEMP/seed" --entry needed.oke \
out/x86_64/needed.oke out/x86_64/liboktwice.okso out/aarch64/needed.oke out/aarch64/liboktwice.okso
for okb in hello needed signed; do "$OKLD" bundle check "out/$okb.okb"; done
# From a universal package mcpp staged (mcpp-package.json, SPEC-014 §4.5).
python3 examples/package/stage.py staged out
"$OKLD" bundle out/package.okb --package staged/tree
cp -R staged/tree staged/changed
printf 'x' >> staged/changed/lib/x86_64/needed.oke
if "$OKLD" bundle out/changed.okb --package staged/changed 2> changed.txt; then echo "::error::a package whose image is not its stated content was bundled"; exit 1; fi
grep -q "mcpp-package.json states" changed.txt
for okb in hello needed signed package; do "$OKLD" bundle check "out/$okb.okb"; done
"$OKLD" bundle check out/signed.okb --require-signature --trust "$(cat out/key.hex)"
if "$OKLD" bundle check out/hello.okb --require-signature 2> unsigned.txt; then echo "::error::an unsigned okb passed --require-signature"; exit 1; fi
grep -q "not signed" unsigned.txt
Expand Down Expand Up @@ -200,6 +207,7 @@ jobs:
grep -q "argc as kal_env_arg_count = 3" <<<"$out" || { echo "::error::the okb's entry and arguments"; exit 1; }
n=$("$OKRUN" out/needed.okb | tr -d '\r')
grep -q "twice(21) = 42, next_value() = 41" <<<"$n" || { echo "::error::an image the okb's entry needs, from the okb"; exit 1; }
"$OKRUN" out/package.okb | tr -d '\r' | grep -q "twice(21) = 42" || { echo "::error::the okb of a staged package"; exit 1; }
"$OKRUN" --require-signature --trust "$key" out/signed.okb > /dev/null || { echo "::error::a signed okb was refused"; exit 1; }
if "$OKRUN" --require-signature out/needed.okb 2> e4.txt; then echo "::error::an unsigned okb ran under --require-signature"; exit 1; fi
grep -q "okb.json is not signed" e4.txt
Expand All @@ -225,6 +233,13 @@ jobs:
grep -q "library: twice(21) = 42, next_value() = 41, 42" lib.txt || { echo "::error::okrun_open_library"; exit 1; }
"$host" okb out/needed.okb | tr -d '\r' | grep -q "twice(21) = 42" || { echo "::error::okrun_run_okb_bytes"; exit 1; }
"$host" images "out/$a/needed.oke" "out/$a/liboktwice.okso" | tr -d '\r' | grep -q "twice(21) = 42" || { echo "::error::okrun_run_images"; exit 1; }
# The archive a program that carries its images holds: no okb.json,
# the images at the root, zipalign's field and bytes between members.
python3 examples/package/stage.py staged out
"$host" archive staged/carried.zip needed.oke | tr -d '\r' | grep -q "twice(21) = 42" || { echo "::error::an archive without okb.json, from memory"; exit 1; }
python3 -c "import sys; d=bytearray(open(sys.argv[1],'rb').read()); d[65536+200]^=1; open(sys.argv[2],'wb').write(d)" staged/carried.zip staged/bad.zip
if "$host" archive staged/bad.zip needed.oke 2> crc.txt; then echo "::error::a changed member ran"; exit 1; fi
grep -q "CRC-32" crc.txt
echo " ok an okb and a group of images run from memory; a library's exports are called from here"
- name: The binding openkal-bind generates
run: |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ images are stored and aligned to 64 KiB ([docs/okb.md](docs/okb.md)):

```sh
okld bundle app.okb --version 0.1.0 x86_64/app.oke aarch64/app.oke # okb.json, the images by architecture
okld bundle app.okb --package target/dist/app-0.1.0-openkal # or from a package mcpp staged
okld bundle check app.okb # stored, aligned, digests, signatures
okrun app.okb arguments... # this machine's architecture's entry
```
Expand Down
2 changes: 1 addition & 1 deletion bind/mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-bind"
version = "0.1.2"
version = "0.1.3"
# `mcpp::xpkg_request', `mcpp::decision' (protocol 15).
mcpp = ">=2026.10.1.3"
description = "A build plugin: a library published as an ok image (.okso) and a C header is called from a program of any system, through a binding generated from the header and libokrun."
Expand Down
5 changes: 3 additions & 2 deletions docs/bind.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ sources = []
include_dirs = ["include"]

[target.'cfg(not(os = "openkal"))'.dependencies]
openkal-run = "0.1.2"
openkal-run = "0.1.3"

[build-dependencies]
openkal-bind = "0.1.2"
openkal-bind = "0.1.3"
```

```cpp
Expand Down Expand Up @@ -98,3 +98,4 @@ llvm toolchain's clang converts the call.
| openkal-bind | Statement |
|---|---|
| 0.1.2 | This document. Released with openkal-run 0.1.2, whose library interface it calls. |
| 0.1.3 | No change of its own; released with the archive. |
15 changes: 15 additions & 0 deletions docs/okb.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ has no such interface in version 0.17.

```
okld bundle <out.okb> [--name <n>] [--version <v>] [--entry <image>] [--sign <seed-file>] <image>...
okld bundle <out.okb> --package <staged-tree> [--sign <seed-file>]
okld bundle check <file.okb> [--require-signature] [--trust <hex>]...
```

Expand All @@ -128,6 +129,19 @@ architecture's directory, and writes `okb.json` with the digests; `--sign` signs
and the manifest with one key. The archive is deterministic: no time is recorded, and the same
images give the same bytes. The entry defaults to the first `.oke` named.

`--package` takes the images from a universal package that mcpp has staged (mcpp SPEC-014 §4.2,
§4.5), reading `mcpp-package.json` at the root of the tree:

- the images are the files of role `image`, at `lib/<arch>/<file>`;
- each image's architecture, from its ELF header, must be the one its directory names;
- its content must be the `sha256` stated for it;
- each architecture's entry is the file `launch.entry` names for it;
- the okb's name and version are the package's.

Files of every other role (launchers, loaders, resources, metadata) are not members of a
version 1 okb. This is the action of the `okb` package format that openkal-platform's plugin
provides.

`okld bundle check` verifies C1 to C3, the manifest, every digest, every signature (and the
trust asked for), that each architecture's entry is present, that each image is of its
directory's architecture, and that each image follows the convention (SPEC 12.2) with the
Expand All @@ -148,3 +162,4 @@ exports of the other images of its directory. It prints one line per violation.
| okb | okrun | Change |
|---|---|---|
| 1 | 0.1.2 | This document. |
| 1 | 0.1.3 | `okld bundle --package`: an okb from a package mcpp staged. |
2 changes: 1 addition & 1 deletion examples/host/mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# system of each architecture with the images built once.
[package]
name = "okrun-host"
version = "0.1.2"
version = "0.1.3"

[dependencies]
openkal-run = { path = "../../run" }
12 changes: 11 additions & 1 deletion examples/host/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// okrun-host library <dir>/liboktwice.okso the library's exports, called from here
// okrun-host okb <file.okb> [arguments...] an okb run from the bytes of memory
// okrun-host archive <file.zip> <entry> [arguments...]
// an archive of images without okb.json, as a
// program that carries its images holds one
// okrun-host images <needed.oke> <liboktwice.okso>
// a group of images run from memory
#include <okrun.h>
Expand Down Expand Up @@ -32,7 +35,7 @@ std::vector<unsigned char> read(const char* path) {
} // namespace

int main(int argc, char** argv) {
if (argc < 3) { std::fprintf(stderr, "usage: okrun-host library <okso> | okb <file.okb> | images <oke> <okso>\n"); return 2; }
if (argc < 3) { std::fprintf(stderr, "usage: okrun-host library <okso> | okb <file.okb> | archive <file.zip> <entry> | images <oke> <okso>\n"); return 2; }
okrun_options options{};
char why[512];
if (std::strcmp(argv[1], "library") == 0) {
Expand All @@ -56,6 +59,13 @@ int main(int argc, char** argv) {
std::fprintf(stderr, "okrun-host: %s\n", why);
return 127;
}
if (std::strcmp(argv[1], "archive") == 0 && argc >= 4) {
const auto bytes = read(argv[2]);
okrun_run_okb_bytes(bytes.data(), bytes.size(), argv[3], argc - 3, const_cast<const char* const*>(argv + 3),
&options, why, sizeof why);
std::fprintf(stderr, "okrun-host: %s\n", why);
return 127;
}
if (std::strcmp(argv[1], "images") == 0 && argc >= 4) {
const auto app = read(argv[2]), lib = read(argv[3]);
const okrun_image images[] = {
Expand Down
70 changes: 70 additions & 0 deletions examples/package/stage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""stage.py <out-dir> <images-dir> -- a universal package of images as mcpp
stages it (mcpp SPEC-014 §4.2, §4.5), for `okld bundle --package', and an
archive of the same images without okb.json, as a program that carries them
holds it (zipalign's field 0xD935, and bytes between members).

<out-dir>/tree/ lib/<arch>/needed.oke, liboktwice.okso, a launcher,
a resource, mcpp-package.json
<out-dir>/carried.zip needed.oke and liboktwice.okso of this machine's
architecture at the root, stored, 64 KiB-aligned
"""
import hashlib, json, os, platform, shutil, struct, sys, zlib

out, images = sys.argv[1], sys.argv[2]
tree = os.path.join(out, "tree")
files = []

def place(rel, data, role, leg=None):
path = os.path.join(tree, *rel.split("/"))
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "wb") as f:
f.write(data)
files.append({"path": rel, "role": role, "leg": leg, "sha256": hashlib.sha256(data).hexdigest()})

for arch in ("x86_64", "aarch64"):
for name in ("needed.oke", "liboktwice.okso"):
with open(os.path.join(images, arch, name), "rb") as f:
place(f"lib/{arch}/{name}", f.read(), "image", f"{arch}-openkal")
place("bin/needed", b"#!/bin/sh\n", "launcher")
place("share/needed/data.txt", b"a resource\n", "resource")
doc = {
"schema": 1, "name": "needed", "version": "0.1.0", "platform": "openkal-platform@0.17.2",
"linkage": "declared", "mode": "vendored", "legs": ["x86_64-openkal", "aarch64-openkal"], "systems": [],
"launch": {"runner": ["okrun", "{}"],
"entry": {"x86_64": "lib/x86_64/needed.oke", "aarch64": "lib/aarch64/needed.oke"}},
"files": files,
}
with open(os.path.join(tree, "mcpp-package.json"), "w") as f:
json.dump(doc, f, indent=2)

# The carried archive: this machine's images at the root.
arch = {"amd64": "x86_64", "x86_64": "x86_64", "arm64": "aarch64", "aarch64": "aarch64"}[platform.machine().lower()]
members = []
for name in ("needed.oke", "liboktwice.okso"):
with open(os.path.join(images, arch, name), "rb") as f:
members.append((name, f.read()))
blob, central = bytearray(), bytearray()
for i, (name, data) in enumerate(members):
n = name.encode()
if i == 1:
blob += b"\0" * 3 # bytes between members, which no entry names
pad = (-(len(blob) + 30 + len(n))) % 65536
if 0 < pad < 6:
blob += b"\0" * pad # too few for a field: bytes before the header
pad = 0
extra = b""
if pad:
# zipalign's field: id 0xD935, the alignment (2 bytes), zeros.
extra = struct.pack("<HHH", 0xD935, pad - 4, 0) + b"\0" * (pad - 6)
crc = zlib.crc32(data) & 0xffffffff
header = len(blob)
blob += struct.pack("<IHHHHHIIIHH", 0x04034b50, 20, 0, 0, 0, 0x21, crc, len(data), len(data), len(n), len(extra)) + n + extra
assert len(blob) % 65536 == 0, (name, len(blob) % 65536)
blob += data
central += struct.pack("<IHHHHHHIIIHHHHHII", 0x02014b50, 20, 20, 0, 0, 0, 0x21, crc, len(data), len(data), len(n), 0, 0, 0, 0, 0, header) + n
cd = len(blob)
blob += central + struct.pack("<IHHHHIIH", 0x06054b50, 0, 0, len(members), len(members), len(central), cd, 0)
with open(os.path.join(out, "carried.zip"), "wb") as f:
f.write(blob)
print(f"staged {tree} and {os.path.join(out, 'carried.zip')} ({arch})")
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.1.2"
version = "0.1.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
2 changes: 1 addition & 1 deletion run/mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-run"
version = "0.1.2"
version = "0.1.3"
description = "libokrun: the runner's core --- an ok image's names bound to this process's one openkal implementation, in the image's calling convention, and its entry entered."
license = "Apache-2.0"
authors = ["mcpplibs"]
Expand Down
8 changes: 6 additions & 2 deletions run/src/run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,12 @@ int run_archive(search& s, const char* entry, const char* name, int argc, const
s.refusal[0] = 0;
const void* bytes = archive_image(s, entry, size);
if (!bytes) {
if (s.refusal[0]) text(why, cap) << name << ": " << s.refusal;
else text(why, cap) << name << " has no member " << entry << " (in " << s.arch_dir << "/ or at its root)";
// A refusal names the member; the archive is named before it when it
// has a name of its own (a file).
text t(why, cap);
if (!same(name, entry)) t << name << ": ";
if (s.refusal[0]) t << s.refusal;
else t << "no member " << entry << " (in " << s.arch_dir << "/ or at the root)";
return -1;
}
const bool carried = s.options && s.options->carried;
Expand Down
2 changes: 1 addition & 1 deletion tools/mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-tools"
version = "0.1.2"
version = "0.1.3"
description = "okrun, which runs an ok image on this system, and okld, the format's tool on a build machine (check, note, sign, inspect)."
license = "Apache-2.0"
authors = ["mcpplibs"]
Expand Down
Loading
Loading