GitShow/denoland/sui
denoland

sui

Embed custom RO data into precompiled executables

by denoland
Star on GitHubForkWebsite

Rust

147 stars14 forks9 contributorsActive · 1w agoSince 20240.0.3MIT

Meet the team

See all 9 on GitHub →
littledivy
littledivy139 contributions
divybot
divybot8 contributions
dsherret
dsherret3 contributions
nathanwhit
nathanwhit3 contributions
bartlomieju
bartlomieju2 contributions
drahnr
drahnr2 contributions
antoniosarosi
antoniosarosi1 contribution
jlarmstrongiv
jlarmstrongiv1 contribution

Languages

View on GitHub →
Rust99.8%
Shell0.1%
Batchfile0.1%

Commit activity

Last 12 weeks · 15 commits

Full graph →

Community health

2 of 6 standards met

Community profile →
37
✓README✓License○Contributing○Code of Conduct○Issue Template○PR Template

Recent PRs & issues

Active · 5 in progress · Last activity 1w ago
See all on GitHub →
mdekstrand
Resulting binary (often) failing with SIGILL on MacOSOpenIssue

I'm using to embed Tcl scripts into standalone binaries using a Rust wrapper for Jim Tcl. On MacOS, when I embed a script into a debug build of my binary, the resulting binary usually terminates with . A stack trace under shows it failing somewhere inside panic / backtrace machinery. Embedding into release binaries does not seem to have this problem. The problem seems more likely to appear when the embedding binary has a slightly more complicated code path before using the embedded script. I have not fully isolated a minimal example, but the project in which I am encountering this problem is not large. It can be seen with: This pretty reliably fails in the tests on my M2 MacBook Air. Switching the tests to use the release binary () succeeds. I have never seen the problem appear on Linux/Elf. If helpful, the embedder code is in , and the startup code is in .

mdekstrand · 1w ago
littledivy
fix(macho): grow the header padding instead of clobbering __TEXTOpenPR

Fixes #82. Based on ; independent of #80. The reported bug adds a plus a section entry — 152 bytes of load-command space — and paid for it by dropping that many bytes of the padding the linker left after the load commands. It never checked the padding was there. A stock binary often has 48 bytes. So the tail of the new load command landed on top of the start of : Executing that is the reported , at , four bytes into the clobbered range. Two corrections to the issue as filed: it is not debug-specific (the release binary has the same 48 bytes and is corrupted identically — it only looked fine because a different function occupies the start of ), and it is not data-dependent. A second bug, already shipping While building evidence for the first fix, rejected the output — and it rejects it without any of this PR's shift logic involved, including on 's own binary: carries one per segment plus a readers require to equal the segment count. Adding made it 5 ≠ 6 and nothing updated it, so every output on modern macOS carries malformed fixups metadata. It runs only because dyld is lenient about it. Fixed in , which stands alone and is worth reviewing on its own. Approach When the padding is short, push the image down a page and patch everything defined relative to the image base — the approach LIEF's takes, which relies on for the same problem. needs nothing: the section comes out byte-identical yet FDE ranges self-correct ( → ), because the augmentation is PC-relative. The code verifies that rather than assuming it, and rejects absolute encodings. Anything unrecognised is an error, never a guess: encrypted images, section relocations, split info, populated linker optimization hints, relocation tables, unknown chained pointer formats (including arm64e), thread-state entry points, and bytes no load command describes. Evidence Differentials before vs. after, adjusted by the shift width, zero diffs: Runtime, every combination — embeds, runs, clean, valid: Rust exe, Rust dylib ( + ), ObjC (class metadata, , ), Swift (protocol witness dispatch, generics, reflection), C++ (vtables, templates, ), C++ at (classic with a 920-byte weak-bind stream), , both fixtures, and x86_64 including an ad-hoc signed input. Plus through the rebuilt export trie, , and backtraces with correct . The issue reporter's repro passes: Fuzzing Adds , which fuzzes the input image — only ever fuzzed the payload written into a known-good executable. It found seven reachable failures, all from trusting a number out of the file, including two pre-existing panics in / and a asking for 17.7 GB. All fixed in and ; ~6M executions clean afterwards. The committed suite also sweeps both fixtures through and through the whole public path. Behavior changes On the arm64 path, where a segment is actually added: A shifted image gets a new . Every code address moved, so a built for the input no longer describes it, and a debugger pairing the two reports wrong line numbers. Retiring the UUID makes the pairing fail instead; symbol-table symbolication is unaffected. Content-derived, so builds stay reproducible. Images whose entry point lives in a thread state () rather than are rejected on the shift path. Caught in review — they were previously neither patched nor rejected, so such an image would have been shifted into one that jumps at the inserted gap. Tight-padding binaries now succeed where they previously produced binaries. Inputs with enough padding are untouched by all of this — has 13,928 bytes, so it never shifts and only picks up the fix. x86_64 appends past the end of the file rather than adding a segment, so it never needs the shift at all. Known limits One machine and toolchain: macOS 15.6, one Xcode/ld-prime, one rustc. No CI matrix added here. in particular rests on linkers leaving 4 bytes between the 28-byte fixups header and , verified on four binaries from one linker; a linker that doesn't errors cleanly rather than corrupting. arm64e is rejected, not supported.

littledivy · 1w ago
DerZade
Add PortableExecutable::set_versionOpenPR

This PR adds a utility function. This was done to tackle https://github.com/denoland/deno/issues/29038. I'll hope I'll get to the impl in Deno in the next couple of days. Either way I think this is a nice addition to libsui :)

DerZade · 2w ago

Recent fixes

View closed PRs →
littledivy
test(macho): cover find_section_in_current_image on Intel Mac dylibsMergedPR

Follow-up to #74 (which shipped without a test). Adds a regression test for #73. What it does Builds a small cdylib fixture () that calls on itself. Injects a section into the built dylib with . s the injected dylib and asserts the exported probe recovers the exact payload. Gated to , so it runs on both the aarch64 () and x86_64 (sentinel-scan) paths — the x86_64 path was the unimplemented stub returning that broke on Intel Macs (#73). The nested probe build targets the host arch, so the runner exercises the fixed Intel path. Verified Native arm64: passes (). x86_64 via Rosetta (standalone repro of the same logic): recovers the payload; on unfixed code it returned .

littledivy · 3w ago
bartlomieju
fix(pe): correct SizeOfImage when appending a resource sectionMergedPR

Embedding a resource into a PE rebuilds the image and appends a new section holding the resource directory. The writer updated the optional header with , adding the unaligned raw length of the appended resource data. That under-counts SizeOfImage by up to one SectionAlignment page, and in the branch that merely extends an existing last section it was not updated at all. Per the PE spec, SizeOfImage must cover the highest section's VirtualAddress + VirtualSize rounded up to SectionAlignment. On Windows an undersized SizeOfImage leaves the tail of the appended resource section outside the mapped image. Depending on how much is clipped and how strict the loader is, this shows up either as an access violation at load time before any user code runs, or as the runtime failing to find the embedded section at all. The latter is what surfaces as "Could not find standalone binary section" from , where the icon bitmaps make the resource directory large enough to push its tail past the truncated SizeOfImage. Both were reported downstream as denoland/deno#36206 and denoland/deno#36238; 2.9.2 worked and 2.9.3 did not, purely because the base binary's layout shifted enough to start clipping, with no change to this code. The resource writer was the third-party crate (0.1.0, unmaintained), pulled in only for its PE resource path. This reduces the part libsui actually uses into an owned module, pe_edit.rs, dropping the resource parsers, icon/version/manifest readers and the second dependency, and fixes SizeOfImage there by recomputing it from the final section table rather than incrementing it. The reduced code keeps editpe's serialization verbatim; its BSD-2-Clause license and attribution are retained in LICENSE-editpe. The regression tests build a PE in memory and inspect the resulting headers, so they run on every platform rather than only Windows. They assert that SizeOfImage is a multiple of SectionAlignment and covers every section's virtual extent, across small, page-unaligned, large, and resource payloads. Reverting the fix fails all four with "SizeOfImage ... is not a multiple of SectionAlignment". A libsui version bump for release is left as a separate commit per the repo's convention.

bartlomieju · 1mo ago
littledivy
fix(elf): keep the relocated program header table reachable via AT_PHDRMergedPR

Fixes denoland/deno#35700. Problem binaries crash at startup when run directly under gVisor / Google Cloud Run: (a plain SIGSEGV for section-header-stripped binaries). Running through the interpreter explicitly — — works. Root cause relocates the program header table into a new past EOF, packed right above the highest existing vaddr. That gives the new segment a different file-offset→vaddr bias than the first (for the deno base, ~23 MB different). Loaders that recompute from the segment table (the Linux kernel, ld.so via an explicit interpreter) cope, but a loader that trusts the classic invariant — which gVisor does — lands tens of MB off the real table, reads garbage program headers, and dies before . The native-addon detail in the original report is incidental: a trivial binary reproduces it. Fix Pin the new segment's virtual address to so it carries the same bias as the first , making the naive computation correct. is bumped when needed so the vaddr still clears every existing segment (and the note still lands past ). Verified Reproduced under gVisor () with elfutils/glibc from Ubuntu; the exact assertion fires before the fix and is gone after, for PIE (+RELR), non-PIE, large , and section-header-stripped inputs. Composes with the eu-strip section cover (an eu-stripped binary also runs under gVisor). Adds a regression test asserting the invariant.

littledivy · 1mo ago
Structured data for AI agents

Repository: denoland/sui. Description: Embed custom RO data into precompiled executables Stars: 147, Forks: 14. Primary language: Rust. Languages: Rust (99.8%), Shell (0.1%), Batchfile (0.1%). License: MIT. Homepage: https://littledivy.com/sui Latest release: 0.0.3 (2y ago). Open PRs: 5, open issues: 2. Last activity: 1w ago. Community health: 37%. Top contributors: littledivy, divybot, dsherret, nathanwhit, bartlomieju, drahnr, antoniosarosi, jlarmstrongiv, cions.

·@ofershap

Replace github.com with gitshow.dev