Skip to content

Bump vendored LIEF 0.13.0 -> 0.17.6 and migrate to its API#108

Open
cstrahan wants to merge 1 commit into
nodejs:mainfrom
cstrahan:cstrahan/bump-lief-0.17.6
Open

Bump vendored LIEF 0.13.0 -> 0.17.6 and migrate to its API#108
cstrahan wants to merge 1 commit into
nodejs:mainfrom
cstrahan:cstrahan/bump-lief-0.17.6

Conversation

@cstrahan

Copy link
Copy Markdown

postject's 3-year-old LIEF (0.13.0) corrupts the dynamic symbol table when it rebuilds a binary with a large .gnu.hash: only some symbol-table st_name offsets are updated, the rest keep stale offsets, so exported symbols resolve to truncated/garbage names. For a Node.js SEA this breaks native (raw-V8) addons at runtime with "undefined symbol: _ZN2v8...", because the dlopen'd .node resolves V8 symbols from the host executable by name.

This is upstream LIEF bug lief-project/LIEF#1241 ("Corrupted symbol table due to DT_GNU_HASH parsing"): a hardcoded NB_MAX_MASKWORD = 512 cap on the GNU-hash bloom filter. When the filter exceeds 512 maskwords (Node's binary has ~40k dynamic symbols, far past that), the symbol-table rebuild is left incomplete. Fixed by LIEF commit 2d1bbae (in the 0.16.6 -> 0.16.7 range), which removes the cap and reconstructs GnuHash properly; verified here by a version sweep (0.16.6 corrupts, 0.16.7 is clean).

Two conditions must coincide to hit it, which is why it looked PIE-specific: injecting a note into a PIE (ET_DYN) binary forces LIEF to relocate .dynsym/.dynstr (the trigger for the symbol-table rebuild), and a >512-maskword .gnu.hash is the defect (#1241). Official non-PIE/ET_EXEC Node builds never relocate .dynstr, so they dodged the bug even on old LIEF; builds that link the node binary as PIE hit it directly.

This bumps the dependency to 0.17.6 (latest) via CMake FetchContent and migrates src/postject.cpp to the LIEF 0.16+ API. The injection logic is lifted from Node.js core's own LIEF-based injector, src/node_sea_bin.cc, added in nodejs/node#61167 (Joyee Cheung's work to move SEA blob injection into Node and off the stale postject).

Also force-include globally: LIEF's bundled fmt calls global free() from a template whose definition point doesn't see under Emscripten, which otherwise fails the wasm build.

postject's 3-year-old LIEF (0.13.0) corrupts the dynamic symbol table when it
rebuilds a binary with a large .gnu.hash: only some symbol-table `st_name`
offsets are updated, the rest keep stale offsets, so exported symbols resolve
to truncated/garbage names. For a Node.js SEA this breaks native (raw-V8)
addons at runtime with "undefined symbol: _ZN2v8...", because the dlopen'd
.node resolves V8 symbols from the host executable by name.

This is upstream LIEF bug lief-project/LIEF#1241 ("Corrupted symbol table due
to DT_GNU_HASH parsing"): a hardcoded NB_MAX_MASKWORD = 512 cap on the GNU-hash
bloom filter. When the filter exceeds 512 maskwords (Node's binary has ~40k
dynamic symbols, far past that), the symbol-table rebuild is left incomplete.
Fixed by LIEF commit 2d1bbae (in the 0.16.6 -> 0.16.7 range), which removes the
cap and reconstructs GnuHash properly; verified here by a version sweep (0.16.6
corrupts, 0.16.7 is clean).

Two conditions must coincide to hit it, which is why it looked PIE-specific:
injecting a note into a PIE (ET_DYN) binary forces LIEF to relocate
.dynsym/.dynstr (the trigger for the symbol-table rebuild), and a >512-maskword
.gnu.hash is the defect (#1241). Official non-PIE/ET_EXEC Node builds never
relocate .dynstr, so they dodged the bug even on old LIEF; builds that link the
node binary as PIE hit it directly.

This bumps the dependency to 0.17.6 (latest) via CMake FetchContent and
migrates src/postject.cpp to the LIEF 0.16+ API. The injection logic is lifted
from Node.js core's own LIEF-based injector, src/node_sea_bin.cc, added in
nodejs/node#61167 (Joyee Cheung's work to move SEA blob injection into Node and
off the stale postject).

Also force-include <cstdlib> globally: LIEF's bundled fmt calls global free()
from a template whose definition point doesn't see <cstdlib> under Emscripten,
which otherwise fails the wasm build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant