Skip to content

Repository files navigation

openkal

openkal is a portable kernel application binary interface. This repository contains the normative specification, the declarations in both of the forms it distributes them in, and the suite an implementation runs against itself.

The specification is SPEC.md.

What this package contains

Declarations, and no definitions. Every function declared here is supplied by an implementation package; building this package alone produces a library with undefined references, which is the intended outcome.

Module Header Interface Class
openkal.types openkal/types.h machine word, error values, endpoint
openkal.version openkal/version.h what the implementation says about itself every
openkal.abort openkal/abort.h termination core
openkal.stream openkal/stream.h byte streams core
openkal.memory openkal/memory.h allocation, and the environment's granularity core
openkal.env openkal/env.h the parameters a program receives at inception optional
openkal.time openkal/time.h monotonic and wall time sources optional
openkal.fs openkal/fs.h directories and open files, relative throughout optional
openkal.process openkal/process.h starting a program and waiting for it optional
openkal.task openkal/task.h execution contexts, and the primitive they are built upon optional
openkal.random openkal/random.h a source of unpredictable bytes optional
openkal.exec openkal/exec.h a region of the address space a program may execute optional
openkal.terminal openkal/terminal.h an interactive stream's treatment of what is typed optional
openkal.net openkal/net.h a connection, and a listener for connections optional
openkal.datagram openkal/datagram.h a message with a boundary optional
openkal.space openkal/space.h an address space, and a context executing in one optional
openkal.timeout openkal/timeout.h a bound upon operations that would otherwise wait optional

There were three classes and there are two. Version 0.8 named a middle one — standard, "an interface an implementation hosting a C library provides" — and it was false: an implementation for a machine with firmware and no operating system provides none of openkal.fs, openkal.process or openkal.task, and a C library is hosted above it. Clause 6.1 makes an interface's absence a fact a consumer learns from the linker; no class was needed to predict it, and the prediction was wrong.

openkal.version is in the table and is not an interface: it provides no resource, and every conforming implementation exports its two operations so that a consumer with no linker to ask can ask before it calls.

One statement of the declarations, two ways to reach it

The contract is a C application binary interface, and a C translation unit has no import. The canonical consumer of this contract — a C library ported onto openkal — is a C translation unit, so the declarations are distributed in both forms.

The header is the statement. The module includes it in its global module fragment and exports the names, so a consumer that imports and a consumer that includes obtain the same entities rather than two declarations that agree today. What the module adds is not a second declaration: it is what C++ can check and C cannot — the layouts clause 5.3 freezes are asserted there, and the capability words become types that cannot be mixed.

SURFACE.txt is normative, and both forms are compared against it:

tools/check-declarations.sh compiles a translation unit naming every entity, with -nostdinc — because the consumer the header exists for is compiled that way
a test in each implementation the same list, reached through import openkal.*
conformance/src/declarations.c the same list again, compiled by every toolchain that builds the suite

The header includes nothing. openkal must be usable on a freestanding target, and a consumer compiled with -nostdinc has nothing to include.

How a program uses openkal

A program declares two dependencies. The first fixes the version of the contract it is written against; the second selects an implementation and is ordinarily conditional on the target.

[dependencies]
openkal = "0.13.0"

[target.'cfg(os = "linux")'.dependencies]
openkal-linux = "0.13.0"

[target.'cfg(os = "macos")'.dependencies]
openkal-macos = "0.10.0"

[target.'cfg(windows)'.dependencies]
openkal-windows = "0.8.0"

The program imports the interface and names no implementation.

import openkal.stream;

int main() {
    const char greeting[] = "hello\n";
    kal::write(kal::out(), greeting, sizeof(greeting) - 1);
    return 0;
}

Changing the implementation is a change to one line of the manifest. The source does not change, and this property is the reason the specification exists.

What a program built on openkal may assume

A build that selects openkal resolves several layers, and each guarantees only its own. The layer names are those of the build tool (mcpp, The Target Side).

Layer Supplied by Guarantees Does not guarantee
kernel-abi = openkal this specification and one implementation per target the behaviour of every kal_* operation is independent of the platform; a missing interface is a link failure; a varying property is answered by an enquiry which C library is present; whether a platform SDK is available; that the rest of the program is portable
c-abi = musl openkal-musl a POSIX-shaped C environment built on kal_*; what it cannot provide it refuses and lists the platform's own C runtime headers (io.h, TargetConditionals.h); facilities openkal does not define (epoll, signal handlers)
c++-abi = libc++ openkal-llvm-runtime a C++ runtime configured for that C library
build tool mcpp a layer supplied by the dependency graph is supplied wholly by it: the host's headers and libraries are not searched whether a package's source compiles against that C library

Four rules follow, and they bind packages rather than this specification.

  1. openkal is not openkal-musl. The headers of this package include nothing and conflict with no platform header. A package that fails to find winsock2.h or TargetConditionals.h has met the C library layer, and adapts on cfg(c-abi = "musl"), not on kernel-abi.
  2. A platform dependency is permitted and is the package's own. A package may call a platform's system interfaces, provided the headers and libraries it needs come from the dependency graph (a feature-scoped dependency) and are not visible to its consumers. openkal is responsible only for what crosses kal_*.
  3. One C runtime and one C++ runtime per image. A platform dependency means the platform's system interfaces, across which only handles and values pass. It does not include a static library compiled against the platform's C runtime, nor passing an object that runtime owns (FILE*, memory released by the other runtime, errno) across the boundary. A context created by a platform library rather than by kal_task_start has no C library state, and code reached from such a context shall not rely on it.
  4. Source does not identify the implementation. No macro states that a program is built on openkal. A difference in headers is adapted to on c-abi; a difference in facilities (for example, the absence of epoll) is adapted to by a feature of the package, or where a manifest must select it, on cfg(all(kernel-abi = "openkal", c-abi = "musl")). Both are decided when dependencies are resolved, not when the source is compiled.

How an implementation is written

An implementation provides definitions and no modules. It reaches the declarations it is defining and exports nothing: the interface belongs to this package.

openkal-linux on the kernel's own system-call interface
openkal-macos on the kernel's own calls, and two names no C library defines
openkal-windows on Win32 and the object manager beneath it, using no C runtime symbol

Clause 4 states how the declarations are organised, clause 6 states how the absence of an interface is expressed, and clause 7 states the requirements an implementation must satisfy.

Conformance

Clause 9 has two halves, and both are here.

The artefact. tools/check-surface.sh compares an implementation's exported names against SURFACE.txt. It detects the one freedom an implementation retains after the language has removed the others: the addition of names.

The behaviour. conformance/ is a program an implementation runs against itself — 193 observations across fifteen interfaces, in four kinds: behaviour, ABI, stability and cost.

# from an implementation's working tree
bash /path/to/openkal/tools/run-conformance.sh openkal-linux . full

It is composable, because openkal is: an implementation provides an interface in whole or not at all, so each interface is a feature and a run reports on what was selected. It reports three counts, and the third is the one to read — 190 held, 0 did not hold, 3 not observed — because a suite that reported only the first two cannot distinguish an interface that behaved from one it never examined.

It depends on openkal and the language. There is no import std: the suite must run against an implementation in a program that carries no other runtime, and a suite resting on facilities that implementation may be the only supplier of would be reporting on itself.

What is built, and by what

Three compiler families across three systems, because a contract that holds only under the compiler its author used is a description of that compiler.

Linux macOS Windows
gcc ✓ (PE, GNU CRT)
llvm ✓ (MSVC ABI)
msvc

License

Apache-2.0.

openkal-kit

kit/ holds facilities composed from the interfaces this specification defines. It is a separate package, mcpplibs/openkal-kit, and it is not part of the specification.

The specification admits an interface only when it is a minimal capability every kernel has and cannot be composed from the interfaces already present. That rule is what keeps openkal implementable on a machine with firmware and nothing else, and it leaves a gap: a program that wants to carry bytes between two of its own contexts, or to turn "127.0.0.1:8080" into an endpoint, has an answer in POSIX and no answer here — because both are composed rather than primitive.

That gap was being filled by the port layer. There is one port layer today and what it composes is POSIX, so a native openkal program either wrote the composition again or took a whole C library. The kit is where the composition is written once.

The contract form is what makes the two unmistakable. Clause 10 states that openkal's contract is a C application binary interface. The kit deliberately is not one: it is C++ modules in namespace kal::kit, and it exports no name beginning with kal_. Measured on its objects: the defined names are C++ mangled module initialisers such as _ZGIW7openkalW3kitW7channel, and the operations are inline and emitted into consumers rather than exported at all. So tools/check-surface.sh --complete does not read a program that links the kit as an implementation which has added names — the rule that checker enforces is about the C surface, and the kit has none.

So "is this normative" is answered by the shape of what is exported rather than by a sentence saying it is not. A sentence can be overlooked; a mangled name cannot become a C symbol.

The consequence is the one that matters. Clause 8 forbids the specification from altering a declaration it has published, which is what makes openkal safe to depend upon and what makes it the wrong place for a facility still finding its shape. The kit may evolve.

About

openkal: a portable kernel ABI specification, with the C++ modules that declare it

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages