Skip to content

Commit c6efde1

Browse files
openkal.exec —— 可写的映射,发布之后可执行 (#4)
* feat: openkal.exec —— 可写的映射,发布之后可执行 这个内核对两步都不设条件,所以这个实现无条件提供该接口 —— 不同于另一个系统, 在那里同一个接口只对以特定方式产出的制品可用,那是 6.5 覆盖的情形,不是这里的。 保留没有一上来就把映射设成可执行,尽管这个内核允许。目标环境里有两个拒绝 「既可写又可执行」的映射,所以在这里返回一块二者兼备的内存,等于给程序一个 它在别处用不了的形状 —— 而程序只会在那另一个系统上才发现。接口写的是更窄的 契约,这个实现照它来。 props 报 REPUBLISH:这个内核的保护调用不是单向的,调用方要改已发布的字节 不必丢弃整块区域。 验证:check-surface 对本实现报 56 个名字合规(此前 52,差的正是这四个); conformance --features full 报 103 held / 0 did not hold,其中包括 「写进区域并发布的指令被执行了」。 依赖改为 git + 分支,已实测可解析:mcpp 克隆 feat/openkal-closure 后构建通过。 * ci: 声明测试的模块清单改为推导,不再写出来 它此前是写出来的,而规范第一次新增接口它就脱节了:SURFACE.txt 列了四个 kal_exec_* 名字,却没有发出 import openkal.exec,生成的测试报四个未声明的 标识符 —— 那名的是症状(有名字找不到),不是原因(这里有一份清单没跟上)。 SURFACE.txt 本来就把名字按 `# openkal.<接口>` 分组,它已经带着这份清单。 types 不是那里的一个组(它不声明任何 kal_ 名字),单独写。 本地按 CI 的步骤复现:推导出十行 import(含 exec),生成的测试 1 passed。 * feat: declare the kernel-abi layer this package supplies mcpp resolves the target side per layer after the dependency graph is known, and reads which layer a package fills from its capabilities. Declaring `mcpp:kernel-abi=openkal` states that this package implements the platform interface a C library sits on, and that it answers to the name `openkal`. The engine knows the three layer names and none of the implementations that fill them, so nothing about openkal appears in mcpp for this to work. Claude-Session: https://claude.ai/code/session_01Q4ucduLuSsETHYJ1RkGVVD * chore: 0.5.2 The manifest states a capability the previous release did not, and the index serves published tarballs rather than branches. A consumer resolving through the index would otherwise get content in which this package declares no layer, and the engine would resolve its target side to the compiler payload. Claude-Session: https://claude.ai/code/session_01Q4ucduLuSsETHYJ1RkGVVD --------- Co-authored-by: speak-agent <x.d2learn.org@gmail.com>
1 parent 8684748 commit c6efde1

4 files changed

Lines changed: 89 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,21 @@ jobs:
122122
# fails to compile and the diagnostic names it.
123123
list="$(grep -vE '^[[:space:]]*(#|$)' .spec/SURFACE.txt | sort -u)"
124124
{
125-
for m in types abort stream memory env time fs process task; do
125+
# ⚠️ THE MODULE LIST IS DERIVED, NOT WRITTEN OUT.
126+
#
127+
# It used to be written out, and it fell out of step the first time
128+
# the specification gained an interface: SURFACE.txt listed four
129+
# `kal_exec_*' names, no `import openkal.exec' was emitted, and the
130+
# generated test failed with four undeclared identifiers --- which
131+
# names the symptom (a name is missing) and not the cause (a list
132+
# here was not updated).
133+
#
134+
# SURFACE.txt groups its names under `# openkal.<interface>'
135+
# headings, so the list it already carries is the list this needs.
136+
# `types' is not a group there --- it declares no kal_ name --- and
137+
# is therefore named separately.
138+
echo "import openkal.types;"
139+
for m in $(grep -oE '^# openkal\.[a-z]+' .spec/SURFACE.txt | cut -d. -f2); do
126140
echo "import openkal.$m;"
127141
done
128142
echo 'const void *const surface[] = {'

mcpp.toml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-linux"
4-
version = "0.5.1"
4+
version = "0.5.2"
55
description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one."
66
license = "Apache-2.0"
7+
8+
# The layer this package supplies, in the vocabulary the engine resolves.
9+
#
10+
# `mcpp:kernel-abi` names the platform interface a C library sits on. On a
11+
# traditional stack that seam is unnamed — a C library issues system calls or
12+
# calls the platform's own entry points directly — and naming it is what lets
13+
# one C library sit above several platforms. `=openkal` is the interface this
14+
# package answers to; several packages answer to it and the engine knows none
15+
# of them by name.
16+
provides = ["mcpp:kernel-abi=openkal"]
717
authors = ["mcpplibs"]
818
repo = "https://github.com/mcpplibs/openkal-linux"
919

1020
[dependencies]
11-
openkal = "0.5.2"
21+
openkal = { git = "https://github.com/mcpplibs/openkal", branch = "feat/openkal-closure" }
1222

1323
# The package contributes definitions and no modules. The interface it
1424
# implements is declared by the specification package, which this package

src/exec.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include "sys.h"
2+
#include <openkal/exec.h>
3+
4+
// openkal.exec on this kernel.
5+
//
6+
// A mapping obtained writable and made executable afterwards. The kernel places
7+
// no condition upon either step, so this implementation provides the interface
8+
// unconditionally --- unlike one of the other systems, where the same interface
9+
// is available only to a program produced in a particular way, which clause 6.5
10+
// covers and which is not this system's case.
11+
//
12+
// The reservation is not made executable at the outset even though this kernel
13+
// would permit it. Two of the three environments the specification targets
14+
// refuse a mapping that is both, so an implementation that returned one here
15+
// would be offering a program a shape it could not use elsewhere --- and the
16+
// program would discover that only on the other system. The interface states
17+
// the narrower contract and this implementation keeps to it.
18+
19+
namespace {
20+
21+
constexpr okl_uptr kPage = 4096;
22+
23+
okl_uptr round_up(okl_uptr n, okl_uptr to) { return (n + to - 1) & ~(to - 1); }
24+
25+
} // namespace
26+
27+
extern "C" {
28+
29+
void* kal_exec_alloc(kal_uintptr size) {
30+
if (size == 0) return nullptr;
31+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), kPage);
32+
const okl_long r = okl::sys(okl::nr_mmap, 0, static_cast<okl_long>(bytes),
33+
okl::prot_read | okl::prot_write,
34+
okl::map_private | okl::map_anonymous, -1, 0);
35+
if (okl::failed(r)) return nullptr;
36+
return reinterpret_cast<void*>(r);
37+
}
38+
39+
int kal_exec_publish(void* p, kal_uintptr size) {
40+
if (p == nullptr || size == 0) return kal_err_invalid;
41+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), kPage);
42+
const okl_long r = okl::sys(okl::nr_mprotect, reinterpret_cast<okl_long>(p),
43+
static_cast<okl_long>(bytes),
44+
okl::prot_read | okl::prot_exec);
45+
if (okl::failed(r)) return okl::translate(r);
46+
return kal_ok;
47+
}
48+
49+
void kal_exec_free(void* p, kal_uintptr size) {
50+
if (p == nullptr || size == 0) return;
51+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), kPage);
52+
okl::sys(okl::nr_munmap, reinterpret_cast<okl_long>(p),
53+
static_cast<okl_long>(bytes));
54+
}
55+
56+
// A published region may be reserved for writing again: this kernel's
57+
// protection call is not one-way. The position is set accordingly, and a
58+
// caller that must change published bytes need not abandon the region.
59+
const kal_uintptr kal_exec_props = KAL_EXEC_PROP_REPUBLISH;
60+
61+
} // extern "C"

src/sys.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ enum : okl_long {
197197
o_creat = 0100, o_excl = 0200, o_trunc = 01000, o_append = 02000,
198198
o_directory = 0200000, o_cloexec = 02000000, o_nofollow = 0400000,
199199
at_fdcwd = -100, at_removedir = 0x200, at_symlink_nofollow = 0x100,
200-
prot_read = 1, prot_write = 2, prot_none = 0,
200+
prot_read = 1, prot_write = 2, prot_exec = 4, prot_none = 0,
201201
map_private = 2, map_anonymous = 0x20, map_stack = 0x20000,
202202
clock_monotonic = 1, clock_realtime = 0,
203203
futex_wait = 0, futex_wake = 1, futex_private = 128,

0 commit comments

Comments
 (0)