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
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ jobs:
|| { echo "::error::a weak import made the image require its interface"; exit 1; }
# 6 MiB of frames on the main thread, the runner's on Windows (0.5.2).
bash examples/build-image.sh "$arch" "out/$arch/stack.oke" examples/stack/app.c
# The convention's floating-point state at the entry and in a context (0.5.3).
bash examples/build-image.sh "$arch" "out/$arch/fpstate.oke" examples/fpstate/app.c
cp "out/$arch/hello.oke" "out/$arch/signed.oke"
# The library examples/bind binds for a program of each system.
bash examples/build-image.sh "$arch" "out/$arch/libmathlib.okso" examples/bind/mathlib/image/mathlib.c
Expand Down Expand Up @@ -281,6 +283,10 @@ jobs:
# reservation, which was the linker's 1 MiB default.
st=$("$OKRUN" "out/$a/stack.oke" | tr -d '\r')
test "$st" = 'stack: 6 MiB on the main thread' || { echo "::error::the image's main thread has less than a thread's stack: $st"; exit 1; }
# THE CONVENTION'S FLOATING-POINT STATE (0.5.3): on Windows a thread
# begins with the x87 at double precision.
fp=$("$OKRUN" "out/$a/fpstate.oke" | tr -d '\r')
test "$fp" = "fp: the convention's at the entry, the convention's in a context" || { echo "::error::the image's floating-point state: $fp"; exit 1; }
w=$("$OKRUN" "out/$a/weak.oke" | tr -d '\r')
echo "$w"
grep -q "weak: openkal.net bound" <<<"$w" || { echo "::error::a weak import the implementation provides was not bound"; exit 1; }
Expand Down Expand Up @@ -476,7 +482,7 @@ jobs:
run: |
for a in x86_64 aarch64; do mkdir -p "examples/bind/mathlib/lib/$a"; cp "out/$a/libmathlib.okso" "examples/bind/mathlib/lib/$a/"; done
(cd examples/bind/app && mcpp run) | tr -d '\r' | tee bind.txt
for want in "add: 42" "scale: 6.00" "mirror: -3 7" "length: 7" "each: 4 visited, sum 10"; do
for want in "add: 42" "scale: 6.00" "mirror: -3 7" "length: 7" "each: 4 visited, sum 10" "fp: the convention's in the image, the caller's here"; do
grep -q "$want" bind.txt || { echo "::error::the binding: $want"; exit 1; }
done
bash examples/bind/check-refusals.sh
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ thread's default under openkal-windows, as Linux and macOS give the main thread
8 MiB. The `stack` example recurses through 6 MiB on the main thread on every
system.

Version 0.5.3 enters an image in its convention's floating-point state (SPEC
12.2): on x86-64 the x87 control word 0x037F and MXCSR 0x1F80, on AArch64 FPCR
zero. A thread of Windows begins with the x87 at double precision (0x027F),
under which a C library that computes in long double read decimal numbers
otherwise (`strtod("0.4")` gave 0x1.99999999998p-2). The runner establishes the
state at the entry, around initialisers and finalisers, and in each context the
image starts; a binding makes each call from a program of the system in it and
restores the caller's after it (`okrun_call_enter`, `okrun_call_leave`). The
`fpstate` example and the `bind` example check it on every system.

## An image

```sh
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.5.2"
version = "0.5.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
9 changes: 8 additions & 1 deletion bind/src/openkal_bind.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,14 @@ std::string generate(const declarations& d, const std::string& header_name, cons
o += std::format("okbind_{0}_r {0}({1}) {{\n", f.name, params);
o += std::format(" okbind_{0}_t f = (okbind_{0}_t)__atomic_load_n(&okbind_{0}_entry, __ATOMIC_ACQUIRE);\n", f.name);
o += std::format(" if (!f) {{ f = (okbind_{0}_t)okbind_symbol(\"{0}\"); __atomic_store_n(&okbind_{0}_entry, (void*)f, __ATOMIC_RELEASE); }}\n", f.name);
o += std::format(" {}f({});\n}}\n\n", f.ret == "void" ? "" : "return ", args);
// In the image's floating-point state, and the caller's again after it (SPEC 12.2).
o += " const unsigned long long okbind_state = okrun_call_enter();\n";
if (f.ret == "void") {
o += std::format(" f({});\n okrun_call_leave(okbind_state);\n}}\n\n", args);
} else {
o += std::format(" okbind_{}_r okbind_r = f({});\n okrun_call_leave(okbind_state);\n"
" return okbind_r;\n}}\n\n", f.name, args);
}
}
// The entries' slots, declared before the functions that use them.
std::string slots;
Expand Down
1 change: 1 addition & 0 deletions docs/bind.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ llvm toolchain's clang converts the call.
| 0.5.0 | The binding format is unchanged; the runner selects OpenKal 0.24 and binds its four process-unit operations. |
| 0.5.1 | No change of its own; released with the archive, whose runner is built over openkal-macos 0.22.1 (confinement rules sized to their grants). |
| 0.5.2 | No change of its own; released with the archive, whose runner on Windows reserves a thread's stack for the image's main thread. |
| 0.5.3 | Each call is made in the image's floating-point state (SPEC 12.2) and restores the caller's after it, through `okrun_call_enter` and `okrun_call_leave`; a callback the image calls runs in the image's state. |
| 0.4.0 | No change of its own; released with the archive, whose loader places an image at the base `okld place` chose for it (openkal 0.23, `kal_exec_alloc_at`) and then applies only the relocations that name a symbol. |
| 0.4.1 | No change of its own; released with the archive, whose runner on macOS is built over openkal-macos 0.21.2 (a sleep is no longer a spin of the processor; the granularity is the same at every start). |
| 0.4.2 | No change of its own; released with the archive, whose runner on macOS is built over openkal-macos 0.21.3 (asked before the implementation's initialiser ran, it answers from the loader's record). |
Expand Down
27 changes: 26 additions & 1 deletion examples/bind/app/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ namespace {
// A callback the image calls: on x86-64 Windows it is reached through a
// System V entry the binding gives the image.
void visit(int32_t value, void* context) { *static_cast<long long*>(context) += value; }

// The caller's floating-point state, which the binding restores after a call.
// On x86-64 it is set as a thread of Windows begins, the x87 at double
// precision, so that every system shows the change and its restoration.
#if defined(__x86_64__)
unsigned short x87_word() {
unsigned short word = 0;
__asm__ volatile("fnstcw %0" : "=m"(word));
return word;
}
void set_x87_word(unsigned short word) { __asm__ volatile("fldcw %0" : : "m"(word)); }
#endif
}

int main() {
Expand All @@ -18,5 +30,18 @@ int main() {
long long total = 0;
const int32_t n = mathlib_each(values, 4, &visit, &total);
std::printf("each: %d visited, sum %lld\n", n, total);
return sum == 42 && p.x == -3 && p.y == 7 && n == 4 && total == 10 ? 0 : 1;
#if defined(__x86_64__)
const unsigned short own = x87_word();
set_x87_word(0x027F);
const int32_t in_image = mathlib_conventional_fp();
const bool restored = x87_word() == 0x027F;
set_x87_word(own);
#else
const int32_t in_image = mathlib_conventional_fp();
const bool restored = true;
#endif
const bool fp = in_image && restored;
std::printf("fp: %s in the image, %s here\n", in_image ? "the convention's" : "another",
restored ? "the caller's" : "another");
return sum == 42 && p.x == -3 && p.y == 7 && n == 4 && total == 10 && fp ? 0 : 1;
}
14 changes: 14 additions & 0 deletions examples/bind/mathlib/image/mathlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,17 @@ MATHLIB_API int32_t mathlib_each(const int32_t* values, size_t count, mathlib_vi
for (size_t i = 0; i < count; ++i) visit(values[i], context);
return (int32_t)count;
}
MATHLIB_API int32_t mathlib_conventional_fp(void) {
#if defined(__x86_64__)
uint16_t x87;
uint32_t mxcsr;
__asm__ volatile("fnstcw %0\n\tstmxcsr %1" : "=m"(x87), "=m"(mxcsr));
return x87 == 0x037F && (mxcsr & 0xFFC0) == 0x1F80;
#elif defined(__aarch64__)
uint64_t fpcr;
__asm__ volatile("mrs %0, fpcr" : "=r"(fpcr));
return fpcr == 0;
#else
return 1;
#endif
}
3 changes: 3 additions & 0 deletions examples/bind/mathlib/include/mathlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ double mathlib_scale(double value, double by);
struct mathlib_point mathlib_mirror(struct mathlib_point p);
size_t mathlib_length(const char* text);
int32_t mathlib_each(const int32_t* values, size_t count, mathlib_visit visit, void* context);
/* Non-zero where the call runs in the convention's floating-point state
* (SPEC 12.2): what a C library in the image that computes in long double needs. */
int32_t mathlib_conventional_fp(void);

#ifdef __cplusplus
}
Expand Down
46 changes: 46 additions & 0 deletions examples/fpstate/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* The image is entered in its convention's floating-point state (SPEC 12.2,
* openkal-image 0.5.3): on x86-64 the x87 control word 0x037F and MXCSR
* 0x1F80, on AArch64 FPCR zero --- at its entry and in each context it starts.
* Windows starts a thread with the x87 at double precision (0x027F), under
* which a C library that computes in long double reads "0.4" as
* 0x1.99999999998p-2. The entry is `int ok_main(void)' (SPEC 12.2). */
typedef unsigned long kal_uintptr;
typedef long kal_intptr;
struct kal_stream { kal_uintptr h; };
struct kal_task { kal_uintptr h; };

struct kal_stream kal_stdout(void);
kal_intptr kal_stream_write(struct kal_stream, const void*, kal_uintptr);
int kal_task_start(void (*entry)(void*), void* arg, struct kal_task* out);
int kal_task_join(struct kal_task);

static void put(const char* s) {
unsigned long n = 0; while (s[n]) ++n;
kal_stream_write(kal_stdout(), s, n);
}

/* Non-zero where the state is the convention's. */
static int conventional(void) {
#if defined(__x86_64__)
unsigned short x87; unsigned mxcsr;
__asm__ volatile("fnstcw %0\n\tstmxcsr %1" : "=m"(x87), "=m"(mxcsr));
return x87 == 0x037F && (mxcsr & 0xFFC0) == 0x1F80;
#elif defined(__aarch64__)
unsigned long fpcr;
__asm__ volatile("mrs %0, fpcr" : "=r"(fpcr));
return fpcr == 0;
#else
return 1;
#endif
}

static void context(void* seen) { *(int*)seen = conventional(); }

__attribute__((visibility("default"))) int ok_main(void) {
int at_entry = conventional(), in_context = 0;
struct kal_task t;
if (kal_task_start(context, &in_context, &t) == 0) kal_task_join(t);
put(at_entry ? "fp: the convention's at the entry" : "fp: another at the entry");
put(in_context ? ", the convention's in a context\n" : ", another in a context\n");
return at_entry && in_context ? 0 : 3;
}
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.5.2"
version = "0.5.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
7 changes: 7 additions & 0 deletions run/include/okrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ void* okrun_symbol(struct okrun_library* library, const char* name);
/* Runs the library's finalisers and unloads it, and the images it needed that
* nothing else holds. */
void okrun_close(struct okrun_library* library);
/* A call into a library's function from a thread of this system (0.5.3) is
* made in the image's floating-point state (SPEC 12.2), and the caller's is
* restored after it: `okrun_call_enter' establishes the image's and returns the
* caller's, which `okrun_call_leave' restores. A binding (openkal-bind) makes
* each call between the two. */
unsigned long long okrun_call_enter(void);
void okrun_call_leave(unsigned long long state);

/* --- a measurement (0.2) --------------------------------------------------- */

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.5.2"
version = "0.5.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
3 changes: 3 additions & 0 deletions run/src/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ struct counted<F, I> {
using image_entry = void (OKRUN_IMAGE_ABI*)(void*);
struct box { image_entry fn; void* arg; };

// The context is the image's from its start, in the convention's
// floating-point state (SPEC 12.2).
void enter_image(void* p) {
const box b = *static_cast<box*>(p);
kal_free(p, sizeof(box), alignof(box));
okrun_fp_enter();
b.fn(b.arg);
}

Expand Down
39 changes: 39 additions & 0 deletions run/src/image_abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,52 @@
// architecture appears in this library.
#pragma once

// THE CONVENTION'S FLOATING-POINT STATE (SPEC 12.2, 0.5.3) is part of the
// convention: on x86-64 the x87 control word 0x037F (extended precision, every
// exception masked) and MXCSR 0x1F80, on AArch64 FPCR zero. `okrun_fp_enter'
// establishes it and returns the state it found, which `okrun_fp_leave'
// restores; each writes only a word that differs. A thread of Windows begins
// with the x87 at double precision (0x027F), under which a C library that
// computes in long double reads decimal numbers otherwise.
#if defined(__x86_64__)
#define OKRUN_IMAGE_ABI __attribute__((sysv_abi))
inline constexpr char okrun_arch[] = "x86_64";
inline unsigned long long okrun_fp_enter() {
unsigned short x87 = 0;
unsigned mxcsr = 0;
__asm__ volatile("fnstcw %0\n\tstmxcsr %1" : "=m"(x87), "=m"(mxcsr));
const unsigned short image_x87 = 0x037F;
const unsigned image_mxcsr = 0x1F80;
if (x87 != image_x87) __asm__ volatile("fldcw %0" : : "m"(image_x87));
if ((mxcsr & 0xFFC0) != image_mxcsr) __asm__ volatile("ldmxcsr %0" : : "m"(image_mxcsr));
return static_cast<unsigned long long>(mxcsr) << 16 | x87;
}
inline void okrun_fp_leave(unsigned long long state) {
const unsigned short x87 = static_cast<unsigned short>(state & 0xFFFF);
const unsigned mxcsr = static_cast<unsigned>(state >> 16);
unsigned short now_x87 = 0;
unsigned now_mxcsr = 0;
__asm__ volatile("fnstcw %0\n\tstmxcsr %1" : "=m"(now_x87), "=m"(now_mxcsr));
if (now_x87 != x87) __asm__ volatile("fldcw %0" : : "m"(x87));
if ((now_mxcsr & 0xFFC0) != (mxcsr & 0xFFC0)) __asm__ volatile("ldmxcsr %0" : : "m"(mxcsr));
}
#elif defined(__aarch64__)
#define OKRUN_IMAGE_ABI
inline constexpr char okrun_arch[] = "aarch64";
inline unsigned long long okrun_fp_enter() {
unsigned long long fpcr = 0;
__asm__ volatile("mrs %0, fpcr" : "=r"(fpcr));
if (fpcr != 0) __asm__ volatile("msr fpcr, %0" : : "r"(0ULL));
return fpcr;
}
inline void okrun_fp_leave(unsigned long long state) {
unsigned long long fpcr = 0;
__asm__ volatile("mrs %0, fpcr" : "=r"(fpcr));
if (fpcr != state) __asm__ volatile("msr fpcr, %0" : : "r"(state));
}
#else
#define OKRUN_IMAGE_ABI
inline constexpr char okrun_arch[] = "";
inline unsigned long long okrun_fp_enter() { return 0; }
inline void okrun_fp_leave(unsigned long long) {}
#endif
2 changes: 2 additions & 0 deletions run/src/okrun.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export using ::okrun_open_library;
export using ::okrun_open_library_bytes;
export using ::okrun_symbol;
export using ::okrun_close;
export using ::okrun_call_enter;
export using ::okrun_call_leave;
export using ::okrun_profile;
export using ::okrun_cache_list;
export using ::okrun_cache_reclaim;
13 changes: 12 additions & 1 deletion run/src/run.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1250,7 +1250,13 @@ void start_making(const local_place& lp) {

using image_function = void (OKRUN_IMAGE_ABI*)(void);
using image_main = int (OKRUN_IMAGE_ABI*)(void);
void call_image(void* fn, void*) { reinterpret_cast<image_function>(fn)(); }
// An initialiser or finaliser, in the image's floating-point state and then the
// caller's again: a library's are called on a thread of the program that opens it.
void call_image(void* fn, void*) {
const unsigned long long state = okrun_fp_enter();
reinterpret_cast<image_function>(fn)();
okrun_fp_leave(state);
}

// --- the runner that makes a snapshot ------------------------------------------------

Expand Down Expand Up @@ -1607,6 +1613,8 @@ int enter(search& s, const void* bytes, unsigned long long size, const char* nam
profile_note("snapshot", "not used (OKRUN_SNAPSHOT=0)", 0);
}
profile_begin_run();
// The image's floating-point state on this thread, from here to its end.
okrun_fp_enter();
if (from_snapshot) {
// THE SECOND PHASE: the C library takes what this start supplies. A
// measurement of the start's phases names it.
Expand Down Expand Up @@ -1938,6 +1946,9 @@ bool has_separator(const char* s) { for (; *s; ++s) if (separator(*s)) return tr

} // namespace

extern "C" unsigned long long okrun_call_enter(void) { return okrun_fp_enter(); }
extern "C" void okrun_call_leave(unsigned long long state) { okrun_fp_leave(state); }

extern "C" okrun_library* okrun_open_library(const char* path, const okrun_options* options,
char* why, unsigned long long cap) {
okrun_library* lib = new_library(options);
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.5.2"
version = "0.5.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