-
Notifications
You must be signed in to change notification settings - Fork 0
253 lines (243 loc) · 11.3 KB
/
Copy pathprobe.yml
File metadata and controls
253 lines (243 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
name: probe
# What this system offers, measured rather than remembered.
#
# An implementation that sits beneath a C library must not depend upon one, or
# its calls resolve to the library above it and recur without bound. On Linux
# that requirement is met by issuing system calls directly. Whether it can be
# met here, and by what means, is a question about this system that is answered
# by asking it: every candidate below is either confirmed or eliminated, and the
# implementation is then written against what remains.
#
# The workflow is temporary. It is removed once its answers are recorded in the
# source, and it exists in history so that the answers can be re-derived.
on:
push:
branches: ['**']
workflow_dispatch:
jobs:
probe:
strategy:
fail-fast: false
matrix:
include:
# One runner. The answers this probe seeks --- which names libSystem
# exports, whether an entry point of one's own is accepted, whether the
# suspension primitive behaves --- are properties of the system rather
# than of the processor, and the x86_64 runner queues for half an hour.
- { os: macos-14, arch: arm64 }
runs-on: ${{ matrix.os }}
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- continue-on-error: true
name: What the system is
run: |
uname -a
sw_vers
clang --version
echo "arch: $(arch)"
# 1. Which names this system exports that a C library never defines.
#
# The names a C library defines are unavailable: two definitions of one
# name cannot both be reached from one program. The question is therefore
# not "does libSystem have a way to create a thread" but "does it have one
# under a name musl does not use".
- continue-on-error: true
name: Names that are available because no C library defines them
run: |
cat > names.c <<'EOF'
#include <dlfcn.h>
#include <stdio.h>
static const char *const candidates[] = {
/* threads, under names a C library does not define */
"pthread_create_from_mach_thread",
"_pthread_start", "bsdthread_create", "__bsdthread_create",
"thread_create_running", "mach_task_self_", "mach_thread_self",
"thread_terminate", "swtch_pri", "thread_switch",
/* the suspension primitive */
"__ulock_wait", "__ulock_wake", "os_sync_wait_on_address",
"os_sync_wake_by_address_any", "os_sync_wake_by_address_all",
/* time */
"mach_absolute_time", "mach_timebase_info",
"clock_gettime_nsec_np",
/* the error location, under the name this system uses */
"__error",
/* the cancellable variants, which are exported separately */
"__write_nocancel", "__read_nocancel", "__open_nocancel",
"__close_nocancel",
/* the argument vector, for an entry point that is not main */
"_NSGetArgc", "_NSGetArgv", "_NSGetEnviron", "_NSGetExecutablePath",
/* names a C library DOES define, listed so that the report shows
the check can distinguish the two cases */
"write", "malloc", "pthread_create", "pthread_join",
0
};
int main(void) {
for (int i = 0; candidates[i]; i++) {
void *p = dlsym(RTLD_DEFAULT, candidates[i]);
printf("%-32s %s\n", candidates[i], p ? "present" : "absent");
}
return 0;
}
EOF
clang -o names names.c && ./names
# 2. Whether a program can be linked without the C library's start files
# and given an entry point of its own, and what that entry receives.
- continue-on-error: true
name: A program with its own entry point, linked without a C runtime
run: |
cat > entry.c <<'EOF'
/* No C library is called here. The write is a system call issued
directly, so that what is being tested is the entry point rather
than the availability of a C library. */
static long sys_write(int fd, const void *b, unsigned long n)
{
#if defined(__aarch64__)
register long x16 __asm__("x16") = 4; /* SYS_write */
register long x0 __asm__("x0") = fd;
register long x1 __asm__("x1") = (long)b;
register long x2 __asm__("x2") = (long)n;
__asm__ __volatile__("svc #0x80" : "+r"(x0) : "r"(x16), "r"(x1), "r"(x2)
: "memory", "cc");
return x0;
#else
long r;
register long r10 __asm__("r10");
__asm__ __volatile__("syscall" : "=a"(r)
: "a"(0x2000004L), "D"((long)fd), "S"((long)b), "d"((long)n)
: "rcx", "r11", "memory", "cc");
return r;
#endif
}
static void sys_exit(int code)
{
#if defined(__aarch64__)
register long x16 __asm__("x16") = 1;
register long x0 __asm__("x0") = code;
__asm__ __volatile__("svc #0x80" :: "r"(x16), "r"(x0) : "memory", "cc");
#else
__asm__ __volatile__("syscall" :: "a"(0x2000001L), "D"((long)code)
: "rcx", "r11", "memory", "cc");
#endif
__builtin_unreachable();
}
static unsigned len(const char *s) { unsigned n = 0; while (s[n]) n++; return n; }
int okm_entry(int argc, char **argv, char **envp, char **apple);
int okm_entry(int argc, char **argv, char **envp, char **apple)
{
sys_write(1, "entry reached\n", 14);
if (argc >= 1 && argv && argv[0]) {
sys_write(1, "argv0=", 6); sys_write(1, argv[0], len(argv[0]));
sys_write(1, "\n", 1);
} else sys_write(1, "argv absent\n", 12);
int n = 0; while (envp && envp[n]) n++;
sys_write(1, n ? "envp present\n" : "envp absent\n", n ? 13 : 12);
(void)apple;
sys_exit(0);
return 0;
}
EOF
echo "--- with -nostdlib and libSystem ---"
clang -nostdlib -Wl,-e,_okm_entry -o entry entry.c -lSystem && ./entry one two || echo "FAILED"
echo "--- with -nostdlib and no libSystem at all ---"
clang -nostdlib -Wl,-e,_okm_entry -o entry2 entry.c 2>&1 | tail -5 || true
./entry2 one two || echo "a program with no dylib does not run here"
# 3. Whether the suspension primitive is reachable and behaves.
- continue-on-error: true
name: The suspension primitive
run: |
cat > ulock.c <<'EOF'
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <pthread.h>
extern int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout_us);
extern int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
#define UL_COMPARE_AND_WAIT 1
#define ULF_NO_ERRNO 0x01000000
static volatile uint32_t word = 0;
static void *waker(void *p) {
(void)p;
usleep(50000);
__atomic_store_n(&word, 1, __ATOMIC_RELEASE);
int r = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, (void *)&word, 0);
printf("wake returned %d\n", r);
return 0;
}
int main(void) {
pthread_t t; pthread_create(&t, 0, waker, 0);
int r = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, (void *)&word, 0, 0);
printf("wait returned %d, word=%u\n", r, word);
pthread_join(t, 0);
return word == 1 ? 0 : 1;
}
EOF
clang -o ulock ulock.c && ./ulock && echo "the suspension primitive works"
# 4. Whether a thread can be created under a name a C library does not use.
- continue-on-error: true
name: Creating an execution context without the name pthread_create
run: |
cat > mt.c <<'EOF'
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
extern int pthread_create_from_mach_thread(pthread_t *, const pthread_attr_t *,
void *(*)(void *), void *);
extern int __ulock_wait(uint32_t, void *, uint64_t, uint32_t);
extern int __ulock_wake(uint32_t, void *, uint64_t);
#define UL_COMPARE_AND_WAIT 1
#define ULF_NO_ERRNO 0x01000000
static volatile uint32_t done = 0;
static void *body(void *p) {
(void)p;
__atomic_store_n(&done, 1, __ATOMIC_RELEASE);
__ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, (void *)&done, 0);
return 0;
}
int main(void) {
pthread_t t;
int r = pthread_create_from_mach_thread(&t, 0, body, 0);
printf("pthread_create_from_mach_thread returned %d\n", r);
if (r) return 1;
while (!__atomic_load_n(&done, __ATOMIC_ACQUIRE))
__ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, (void *)&done, 0, 0);
printf("the context ran and was awaited without pthread_join\n");
/* Whether the thread's record can be released without naming
pthread_detach is the remaining question; it is reported rather
than assumed. */
return 0;
}
EOF
clang -o mt mt.c && ./mt
# 5. Whether the port's assembly assembles for this object format.
- continue-on-error: true
name: The assembly one file supplies for every object format
run: |
curl -fsSL -o okm_setjmp.S \
https://raw.githubusercontent.com/mcpplibs/openkal-libc/feat/musl-on-openkal/port/src/okm_setjmp.S
clang -c okm_setjmp.S -o okm_setjmp.o && nm -gU okm_setjmp.o
echo "the assembly assembles for Mach-O"
# 6. Which system calls answer.
- continue-on-error: true
name: The system calls this implementation would issue
run: |
cat > calls.c <<'EOF'
#include <stdio.h>
#include <sys/syscall.h>
int main(void) {
#define R(n) printf("%-24s %d\n", #n, n);
R(SYS_read) R(SYS_write) R(SYS_close) R(SYS_exit) R(SYS_lseek)
R(SYS_mmap) R(SYS_munmap) R(SYS_mprotect) R(SYS_ftruncate)
R(SYS_fsync) R(SYS_ioctl) R(SYS_dup) R(SYS_dup2)
R(SYS_openat) R(SYS_unlinkat) R(SYS_renameat) R(SYS_mkdirat)
R(SYS_fstatat64) R(SYS_fstat64) R(SYS_getdirentries64)
R(SYS_readlinkat) R(SYS_faccessat) R(SYS___getcwd) R(SYS_chdir)
R(SYS_gettimeofday) R(SYS_wait4) R(SYS_kill) R(SYS_execve)
R(SYS_posix_spawn) R(SYS_getpid) R(SYS_getuid) R(SYS_geteuid)
R(SYS_thread_selfid) R(SYS___semwait_signal)
R(SYS_bsdthread_create) R(SYS_bsdthread_terminate) R(SYS_bsdthread_register)
R(SYS___ulock_wait) R(SYS___ulock_wake)
return 0;
}
EOF
clang -o calls calls.c && ./calls