karnel-termux 4.18.0 → 4.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/docs/CHANGELOG.md +13 -0
- package/karnel/RELEASE_COMMIT +1 -1
- package/karnel/utils/compat.sh +60 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://github.com/karnel-termuxOFC/karnel-termux">
|
|
11
|
-
<img src="https://img.shields.io/badge/version-4.18.
|
|
11
|
+
<img src="https://img.shields.io/badge/version-4.18.2-0078D4?style=for-the-badge" alt="Version">
|
|
12
12
|
</a>
|
|
13
13
|
<a href="https://www.npmjs.com/package/karnel-termux">
|
|
14
14
|
<img src="https://img.shields.io/npm/v/karnel-termux?style=for-the-badge&logo=npm&color=cb3837" alt="npm">
|
|
@@ -78,7 +78,7 @@ Why it's great:
|
|
|
78
78
|
### Via checksummed GitHub release (recommended)
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
version=4.18.
|
|
81
|
+
version=4.18.2
|
|
82
82
|
tmpdir=$(mktemp -d) && trap 'rm -rf "$tmpdir"' EXIT
|
|
83
83
|
base="https://github.com/karnel-termuxOFC/karnel-termux/releases/download/v$version"
|
|
84
84
|
curl -fsSL "$base/karnel-termux-install.sh" -o "$tmpdir/karnel-termux-install.sh"
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ layout: base
|
|
|
6
6
|
|
|
7
7
|
# Documentation Changelog
|
|
8
8
|
|
|
9
|
+
## 4.18.2
|
|
10
|
+
|
|
11
|
+
- **Wrappers no longer hand a preload to glibc.** `login(1)` exports `LD_PRELOAD=$PREFIX/lib/libtermux-exec-ld-preload.so`, a bionic object; glibc's `ld.so` cannot load it, so every wrapped tool died before `main()` with `.../glibc/lib/libc.so: invalid ELF header`. Tier 3's own preload was fatal in the other direction: exporting the glibc `libtermux-exec.so` shim left every bionic child of the tool failing with `library "libc.so.6" not found`. Every wrapper now starts with `unset LD_PRELOAD`, and tier 3 exports `PATH` only.
|
|
12
|
+
- **Wrappers written by older Karnels are refreshed.** `compat_wrapper_is_current()` checks the invariants the current code emits; when one is missing the wrapper is rewritten in place before `compat_escalate` probes it, so a stale launcher can no longer keep a tool pinned to a tier it no longer reaches.
|
|
13
|
+
- **An orphaned parked copy no longer blocks adaptation.** Freebuff's own entry re-extracts its runtime in place, dropping a raw ELF over the wrapper and leaving `.karnel-real` behind; `compat_wrap` refused to touch that file, so the tool could never be adapted again — which is how `karnel install ai --freebuff` ended in "does not start at any tier" and then "Freebuff runtime still does not run after repair". Identical bytes are dropped (a 130 MB runtime is not copied to prove a point) and a different copy is parked as `.karnel-real.stale`.
|
|
14
|
+
- **Tests.** Four new cases pin the behaviour — no wrapper hands a preload to the loader on tier 2 or tier 3, a launcher from an older karnel is refreshed in place, an orphaned parked copy does not block adaptation — in a suite of 23 cases running in 8 seconds. Every branch was proven to fail first: removing the `unset` fails 2 cases, restoring the tier 3 preload fails 5, dropping the refresh fails 1, restoring the refusal fails 1, and always discarding the orphan fails 1.
|
|
15
|
+
|
|
16
|
+
## 4.18.1
|
|
17
|
+
|
|
18
|
+
- **The ladder is reachable from every installer, not just Freebuff:** `compat_adapt_installed()` is the single entry point behind `_tool_compat_adapt` (utils/tools.sh), `_ai_tool_compat_adapt` (tools/ai/all.sh) and the post-install loop in `cli/commands/install.sh`, so it already covered all 16 categories — but it only ever called `compat_wrap()`, which stops at the loader tier. Tiers 3 (glibc userland) and 4 (proot FHS root) were reachable solely from Freebuff's `compat_adapt()` call, which is why no other tool could ever climb above tier 2. It now escalates after wrapping, and re-probes wrappers already on disk so a tool that stops starting at its current tier climbs on the next install or update instead of staying broken behind a launcher nobody re-examines.
|
|
19
|
+
- **No cost for what already runs:** native binaries, scripts and plain files still return before any probe, so the added `--version` probe applies only to tools that actually needed a wrapper — the hundreds of native entries in a Termux prefix are untouched.
|
|
20
|
+
- **Tests:** three new cases pin the behaviour (ladder reachable from the installer path, existing wrappers re-evaluated, natives never probed — the last with a positive control so its zero cannot pass vacuously). Each was proven to fail first: dropping the escalation fails 3 cases, probing natives fails the efficiency case alone, removing tier 3's `PATH` export fails the 3 ladder cases, and letting tier 4 write a wrapper without a root fails the refusal case alone. The suite runs 20 cases in 7 seconds.
|
|
21
|
+
|
|
9
22
|
## 4.18.0
|
|
10
23
|
|
|
11
24
|
- **Three-tier compatibility layer:** a foreign glibc binary is no longer handled by one fixed trick. `compat_adapt` wraps, probes and climbs to the cheapest tier that actually starts the tool — the glibc loader alone (free, installs nothing), the glibc userland (`$PREFIX/glibc/bin`, 383 glibc tools, prepended to `PATH` together with the glibc exec shim so child processes resolve the same way), or proot over a synthetic `/lib`, `/usr` and `/etc` assembled from the sysroot already on the device. That last tier replaces `proot-distro install ubuntu` for binaries that merely hardcode an absolute path, and it downloads nothing. A tier that cannot be reached is refused before the wrapper is written, so `proot -r ''` can never ship, and every step unwraps back to `<path>.karnel-real`.
|
package/karnel/RELEASE_COMMIT
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
fff0d351f344943ef88e0745cc0f7f56c7aaf970
|
package/karnel/utils/compat.sh
CHANGED
|
@@ -206,6 +206,19 @@ compat_wrapper_tier() {
|
|
|
206
206
|
esac
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
# A wrapper written by an older karnel is still a wrapper, but it may hand
|
|
210
|
+
# LD_PRELOAD straight to the glibc loader and die in a login shell. Returns 0
|
|
211
|
+
# only when the launcher carries every invariant the current code emits.
|
|
212
|
+
compat_wrapper_is_current() {
|
|
213
|
+
local file="$1"
|
|
214
|
+
_compat_is_wrapper "$file" || return 1
|
|
215
|
+
grep -qF 'unset LD_PRELOAD' -- "$file" 2>/dev/null || return 1
|
|
216
|
+
if grep -qF 'export LD_PRELOAD' -- "$file" 2>/dev/null; then
|
|
217
|
+
return 1
|
|
218
|
+
fi
|
|
219
|
+
return 0
|
|
220
|
+
}
|
|
221
|
+
|
|
209
222
|
# Builds (once) a synthetic filesystem root out of the glibc sysroot that is
|
|
210
223
|
# already on the device. proot then maps /lib, /usr and /etc onto it, which is
|
|
211
224
|
# everything a binary that hardcodes those paths needs -- for a few kilobytes
|
|
@@ -273,24 +286,23 @@ _compat_write_wrapper() {
|
|
|
273
286
|
printf '%s\n' "#!$bash_bin"
|
|
274
287
|
printf '%s\n' "$COMPAT_WRAPPER_MARKER"
|
|
275
288
|
printf '%s\n' "$COMPAT_WRAPPER_TIER_MARKER $tier"
|
|
289
|
+
# login(1) exports LD_PRELOAD=$PREFIX/lib/libtermux-exec-ld-preload.so,
|
|
290
|
+
# a bionic object. glibc's ld.so cannot load it and dies with
|
|
291
|
+
# "libc.so: invalid ELF header" on every start. The glibc termux-exec
|
|
292
|
+
# shim is equally unusable: every bionic child this tool spawns then
|
|
293
|
+
# fails with "library libc.so.6 not found". So no preload survives.
|
|
294
|
+
printf '%s\n' "unset LD_PRELOAD"
|
|
276
295
|
case "$tier" in
|
|
277
296
|
"$COMPAT_TIER_USERLAND")
|
|
278
|
-
# The $root/$PATH
|
|
297
|
+
# The $root/$PATH below must reach the wrapper verbatim.
|
|
279
298
|
# shellcheck disable=SC2016
|
|
280
299
|
printf '%s\n' "# Tier 3: the glibc loader plus the glibc userland, so the child"
|
|
281
|
-
printf '%s\n' "# processes this tool spawns find the same coreutils
|
|
300
|
+
printf '%s\n' "# processes this tool spawns find the same coreutils."
|
|
282
301
|
# Everything below is emitted literally and expanded by the wrapper at
|
|
283
302
|
# runtime, where "root" is the assignment written on the previous line.
|
|
284
303
|
printf 'root=%q\n' "$(compat_glibc_root)"
|
|
285
304
|
# shellcheck disable=SC2016 # expands in the wrapper
|
|
286
305
|
printf 'export PATH="$root/bin:$PATH"\n'
|
|
287
|
-
# The glibc termux-exec shim is optional; a preload pointing nowhere
|
|
288
|
-
# makes ld.so complain on every start of the tool.
|
|
289
|
-
# shellcheck disable=SC2016 # expands in the wrapper
|
|
290
|
-
printf 'if [ -f "$root/lib/libtermux-exec.so" ]; then\n'
|
|
291
|
-
# shellcheck disable=SC2016 # expands in the wrapper
|
|
292
|
-
printf ' export LD_PRELOAD="$root/lib/libtermux-exec.so${LD_PRELOAD:+:$LD_PRELOAD}"\n'
|
|
293
|
-
printf 'fi\n'
|
|
294
306
|
printf 'exec %q --library-path %q %q "$@"\n' "$loader" "$libpath" "$real"
|
|
295
307
|
;;
|
|
296
308
|
"$COMPAT_TIER_PROOT")
|
|
@@ -328,22 +340,43 @@ compat_wrap() {
|
|
|
328
340
|
[[ -f "$real" ]] || return 1
|
|
329
341
|
if _compat_is_wrapper "$real"; then
|
|
330
342
|
current="$(compat_wrapper_tier "$real")"
|
|
331
|
-
[[ "$current" == "$tier" ]] &&
|
|
332
|
-
|
|
333
|
-
|
|
343
|
+
if [[ "$current" == "$tier" ]] && compat_wrapper_is_current "$real"; then
|
|
344
|
+
return 0
|
|
345
|
+
fi
|
|
346
|
+
# Re-tier an existing wrapper, or refresh one written by an older karnel:
|
|
347
|
+
# the original is already parked next to it, so only the launcher changes.
|
|
334
348
|
stored="$real.karnel-real"
|
|
335
349
|
[[ -e "$stored" ]] || return 1
|
|
336
350
|
loader="$(compat_glibc_loader)" || loader="/lib/ld-linux-aarch64.so.1"
|
|
337
351
|
_compat_write_wrapper "$real" "$stored" "$loader" "$tier" || return 1
|
|
338
|
-
|
|
352
|
+
if [[ "$current" == "$tier" ]]; then
|
|
353
|
+
log_info "compat: $real wrapper refreshed"
|
|
354
|
+
else
|
|
355
|
+
log_info "compat: $real retiered to level $tier"
|
|
356
|
+
fi
|
|
339
357
|
return 0
|
|
340
358
|
fi
|
|
341
359
|
compat_is_glibc_elf "$real" || return 1
|
|
342
360
|
loader="$(compat_glibc_loader)" || return 1
|
|
343
361
|
stored="$real.karnel-real"
|
|
344
362
|
if [[ -e "$stored" ]]; then
|
|
345
|
-
|
|
346
|
-
|
|
363
|
+
# The file on disk is a real ELF, not our wrapper, while a parked copy is
|
|
364
|
+
# already sitting beside it: something outside karnel replaced the
|
|
365
|
+
# launcher (freebuff's node entry re-extracts its runtime in place) and
|
|
366
|
+
# orphaned the copy. The file on disk is authoritative now, so the parked
|
|
367
|
+
# copy is stale. A refusal here would leave the tool permanently
|
|
368
|
+
# unrunnable, so the copy is dropped when it is the same bytes and kept
|
|
369
|
+
# as .karnel-real.stale otherwise - a 130 MB runtime must not be copied
|
|
370
|
+
# just to prove a point.
|
|
371
|
+
if cmp -s -- "$stored" "$real" 2>/dev/null; then
|
|
372
|
+
rm -f -- "$stored" || return 1
|
|
373
|
+
log_warn "compat: dropped a stale parked copy for $real"
|
|
374
|
+
elif ! mv -f -- "$stored" "$stored.stale" 2>/dev/null; then
|
|
375
|
+
log_error "compat: could not move the stale parked copy for $real"
|
|
376
|
+
return 1
|
|
377
|
+
else
|
|
378
|
+
log_warn "compat: replaced a stale parked copy for $real"
|
|
379
|
+
fi
|
|
347
380
|
fi
|
|
348
381
|
if ! mv -- "$real" "$stored"; then
|
|
349
382
|
log_error "compat: could not move $real aside"
|
|
@@ -392,6 +425,11 @@ compat_escalate() {
|
|
|
392
425
|
real="$(readlink -f -- "$target" 2>/dev/null || printf '%s' "$target")"
|
|
393
426
|
_compat_is_wrapper "$real" || return 0
|
|
394
427
|
|
|
428
|
+
# Refresh a launcher written by an older karnel before probing it: the probe
|
|
429
|
+
# has to exercise the wrapper we are about to ship, not the stale one.
|
|
430
|
+
compat_wrapper_is_current "$real" ||
|
|
431
|
+
compat_wrap "$real" "$(compat_wrapper_tier "$real")" || true
|
|
432
|
+
|
|
395
433
|
compat_probe "$real" && return 0
|
|
396
434
|
|
|
397
435
|
if compat_wrap "$real" "$COMPAT_TIER_USERLAND" && compat_probe "$real"; then
|
|
@@ -507,10 +545,17 @@ compat_adapt_installed() {
|
|
|
507
545
|
real="$(readlink -f -- "$path" 2>/dev/null || printf '%s' "$path")"
|
|
508
546
|
[[ -f "$real" && -x "$real" ]] || return 0
|
|
509
547
|
if _compat_is_wrapper "$real"; then
|
|
548
|
+
# Already wrapped: re-probe it, so a binary that later stops starting at
|
|
549
|
+
# this tier climbs instead of being handed to the user as-is. Escalation
|
|
550
|
+
# is what every installer reaches through this function; only freebuff
|
|
551
|
+
# calls compat_adapt() directly, so without this the glibc userland and
|
|
552
|
+
# proot tiers would stay unreachable for the rest of the catalog.
|
|
553
|
+
compat_escalate "$real" || true
|
|
510
554
|
return 0
|
|
511
555
|
fi
|
|
512
556
|
if compat_is_glibc_elf "$real"; then
|
|
513
557
|
compat_wrap "$real" || true
|
|
558
|
+
compat_escalate "$real" || true
|
|
514
559
|
fi
|
|
515
560
|
return 0
|
|
516
561
|
}
|
package/package.json
CHANGED