Skip to content

Commit 3cfcdcc

Browse files
authored
fix(compile): bump libsui to 0.16.1 to survive eu-strip in flatpak (#35699)
Fixes #35633 ## Problem Standalone binaries produced by `deno compile` segfault (exit 139) after `eu-strip` runs over them — which `flatpak-builder` does automatically during a flatpak build. Regression in Deno 2.9, bisected to the libsui 0.16.0 bump (#35467). ## Root cause libsui's in-place `Elf::append` (new in 0.16.0) relocates the program header table past the original EOF, into the file gap just before the appended note. `eu-strip` (elfutils, as run by flatpak-builder) lays the stripped output out itself with `ELF_F_LAYOUT` and zero-fills every file gap that falls between two allocated sections. With only a note-sized `.note.sui` section past that gap, eu-strip treats the relocated program header table as dead space and zeroes it → all-NULL program headers → segfault on exec. ## Fix libsui 0.16.1 (denoland/sui#75) covers the relocated program header table with a dedicated allocated `.sui.phdrs` section so section-based strip tools preserve it. This bumps the dependency and adds a spec test that `eu-strip`s a compiled binary and asserts it still runs (skips gracefully where `eu-strip` isn't installed). ## Verification Reproduced and fixed against the exact flatpak toolchain (elfutils **0.193**, built from source) on Linux x86_64: - real `deno 2.9.0` compiled binary + eu-strip 0.193 (flatpak flags) → **segfault 139**; with 0.16.1 → **runs** - every binary shape (PIE+RELR, plain PIE, non-PIE, large `.bss`, section-header-stripped, fully stripped) × eu-strip 0.193 & 0.190 → runs, program headers intact - payload sizes 1 B – 1 MB → payload still recoverable after strip - real denort 2.9.0 base → program headers intact after strip - binutils `strip` note-survival preserved (unchanged from 0.16.0)
1 parent 7073436 commit 3cfcdcc

5 files changed

Lines changed: 66 additions & 4 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ imara-diff = "=0.2.0"
375375
lax-css = "=0.2.4"
376376
lax-markup = "=0.2.5"
377377
lax-sql = "=0.2.1"
378-
libsui = "=0.16.0"
378+
libsui = "=0.16.1"
379379
malva = "=0.15.2"
380380
markup_fmt = "=0.27.3"
381381
object = { version = "0.36.3", default-features = false, features = ["read_core", "pe"] }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"tempDir": true,
3+
"if": "linux",
4+
"steps": [{
5+
"args": "compile --output main main.ts",
6+
"output": "[WILDCARD]"
7+
}, {
8+
"args": "run --allow-run --allow-read run.ts",
9+
"output": "[WILDCARD]OK[WILDLINE]\n"
10+
}]
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log("hello from compiled binary");
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Regression test for https://github.com/denoland/deno/issues/35633
2+
//
3+
// `flatpak-builder` runs `eu-strip` (elfutils) over compiled binaries. The
4+
// standalone binary embeds its payload via a note appended past the original
5+
// EOF, together with a relocated program header table. `eu-strip` lays the
6+
// stripped output out itself and zero-fills the file gap the program header
7+
// table lives in unless an allocated section covers it, producing an all-zero
8+
// program header table that segfaults on exec. This runs `eu-strip` over the
9+
// compiled binary and asserts it still executes.
10+
const bin = "./main";
11+
12+
let strip;
13+
try {
14+
strip = await new Deno.Command("eu-strip", {
15+
args: [bin],
16+
stdout: "null",
17+
stderr: "piped",
18+
}).output();
19+
} catch (err) {
20+
if (err instanceof Deno.errors.NotFound) {
21+
// elfutils isn't installed on this machine; nothing to exercise.
22+
console.log("OK (eu-strip unavailable)");
23+
Deno.exit(0);
24+
}
25+
throw err;
26+
}
27+
28+
if (!strip.success) {
29+
// eu-strip refused the file (e.g. already stripped); treat as a skip.
30+
console.log("OK (eu-strip refused the file)");
31+
Deno.exit(0);
32+
}
33+
34+
const res = await new Deno.Command(bin, { stdout: "piped", stderr: "piped" })
35+
.output();
36+
if (!res.success) {
37+
console.error(new TextDecoder().decode(res.stderr));
38+
console.error(`stripped binary exited with ${res.code}`);
39+
Deno.exit(1);
40+
}
41+
42+
const out = new TextDecoder().decode(res.stdout).trim();
43+
if (out !== "hello from compiled binary") {
44+
console.error(
45+
`unexpected output from stripped binary: ${JSON.stringify(out)}`,
46+
);
47+
Deno.exit(1);
48+
}
49+
50+
console.log("OK");

0 commit comments

Comments
 (0)