sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

sea: keep ELF segments on separate pages in --build-sea output - #65564

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement
Aug 27, 2026
Merged

sea: keep ELF segments on separate pages in --build-sea output#65564
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
codebytere:fix/sea-elf-phdr-placement

Conversation

@codebytere

Copy link
Copy Markdown
Member

The sea/* failures on the rhel8-x64 CI hosts (SIGSEGV, empty stderr, https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md) come with a kernel line, elf segment at ... requested but the memory is mapped already (nodejs/build#4433 (comment)), which is execve refusing the injected executable. When node is not PIE (RHEL toolchains, and the official Linux binaries), LIEF makes room for the new program header by moving the header table into the largest gap between two PT_LOAD segments and extending the earlier segment across it; when that gap is the one between the read-only data and the read-write segment, whose boundary is not page aligned, the extended segment ends inside the next segment's first page. Linux 4.17 to 5.3 and RHEL 8's 4.18 map an executable's segments with MAP_FIXED_NOREPLACE, so the second mapping fails past the point of no return and the process is killed; newer kernels don't check. Which gap is largest depends on section sizes, so it comes and goes from build to build.

This asks LIEF for its after-.bss placement when the executable is ET_EXEC, which leaves every existing segment where the linker put it and gives the header table pages of its own; the output grows by the size of .bss (about 340 KB for node). PIE builds are unchanged. postject makes the same choice in its own copy of LIEF, so test-single-executable-application.js and test_sea_addon, which still inject with it, aren't covered by this.

Tests:

  • new test/sea/test-build-sea-elf-segments.js asserts no two PT_LOAD segments of a --build-sea executable share a page; test/sea passes
  • reproduced off CI with a non-PIE node built with RHEL 8's clang whose read-only/read-write gap is the largest: on AlmaLinux 8.10 (4.18.0-553.150.1.el8_10) the SEA built before this change segfaults with the same kernel line and the one built after it runs; both run on a 6.12 kernel

Refs: nodejs/build#4433


Disclosure: the code, test, investigation and this description were written by Claude Code, directed and reviewed by @codebytere.

When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-botnodejs-github-bot added needs-ci PRs that need a full CI run. single-executable Issues and PRs related to single-executable applications. labels Aug 26, 2026

@panvapanva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RSLGTM

@panvapanva added fast-track PRs proposed for a shorter-than-standard waiting period before landing. author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. review wanted PRs that need review. labels Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Fast-track has been requested by @panva. Please 👍 to approve.

@nodejs-github-bot

This comment has been minimized.

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

That's not ideal - merge conflict on the rhel8-x64 machine
https://ci.nodejs.org/job/node-test-commit-linux/72453/nodes=rhel8-x64/console

Attempting a test in the stress job with 100 iterations: rhel8-x64 and rhel9-x64 as we really need the results on rhel8-x64 ASAP.

@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (7b6b21a) to head (84be637).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
src/node_sea_bin.cc0.00%2 Missing and 1 partial ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #65564 +/- ##
=======================================
Coverage 90.05% 90.06% =======================================
Files 751 751 Lines 254420 254423 +3 Branches 47975 47986 +11 =======================================
+ Hits 229121 229148 +27 + Misses 16483 16444 -39 - Partials 8816 8831 +15 
Files with missing linesCoverage Δ
src/node_sea_bin.cc41.07% <0.00%> (-0.45%)⬇️

... and 35 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panvapanva added the flaky-test Issues and PRs involving tests that fail intermittently in CI. label Aug 26, 2026
sxa
sxa approved these changes Aug 26, 2026

@sxasxa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying (as usual...) but https://ci.nodejs.org/job/node-stress-single-test/nodes=rhel8-x64/848/console which was without this patch seemed to be passing too. But on the basis this has been tested in an environment where it was reproducible I'm approving.

Out of interest, did you manage to come to any conclusion abot what change could have caused this to start going wrong recently given that it doesn't seem to have been package updates?

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment was marked as resolved.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@sxa

sxa commented Aug 26, 2026

Copy link
Copy Markdown
Member

Every so often we get a problem with merge/rebase conflicts and we don't have a permanent fix for it at the moment. This one seems to have triggered it. We've had 3 PR test runs so far and I'm collating these links for my own benefit and because I'm going to try and disable one machine to let this through:

test-pr jobtest-commit jobFailures
7652191260do-rhel8-x64-1 (multiple merge conflicts) do-rhel9-x64-1 (Makefile conflict) ibm-rhel8-s390x-1osu-rhel8c-arm64-1aix73-power9
7652991268do-rhel9-x64-1 (multiple merge conflicts) rhel8-x64 passed on do-rhel8-x64-2ibm-aix-72-2l1cc-rhel9-s390x-2
7653871277ibm-rhel8-x64-3 (Multiple merge conflicts) do-rhel9-x64-1 (Multiple merge conflicts) do-f42-x64-1ibm-rhel8-s390x-3 (Makefile conflict) ibm-aix72-2

A few notes:

  • rhel8-s390x tests are all failures with parallel/test-http2-debug so assumed unrelated to this PR
  • the third PR test row above is not just a re-run of the failures in the second row - it re-run most of the platforms from what I can see including ones that passed so it has re-runrhel8-x64 which failed, but it had already passed in the second one.
  • The rhel9-x64 merge conflicts is still a problem. All three ran on the same machine which I've now taken offline so the next run will be forced to one of the other two and run a retry of the second job. It's running as per the comment that has just been posted above and the rhel9-x64 job is now executing without merge conflicts - on test-ibm-rhel9-x64-2

Hopefully that new run will get close to a pass although I'll note that between the second and third runs we do have passes on both rhel8-s390x and rhel9-s390x. Similarly for AIX we have passes on both aix72-power9 and aix73-power9](https://ci.nodejs.org/job/node-test-commit-aix/64731/) I'll keep the problematic rhel9/x64 machine offline and perhaps see if I can analyse the workspace on the machine to try and replicate the problem manually. Mentioning nodejs/build#4345 which was a similar situation seen in the past.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@nodejs-github-bot
nodejs-github-bot merged commit a2bbe4e into nodejs:mainAug 27, 2026
97 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in a2bbe4e

@nodejs-github-botnodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Aug 27, 2026
@panvapanva mentioned this pull request Aug 28, 2026
aduh95 pushed a commit that referenced this pull request Aug 29, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95 pushed a commit that referenced this pull request Sep 3, 2026
When node itself is not position independent (the official Linux
binaries, and any build with a toolchain that does not default to PIE),
LIEF made room for the extra program header by moving the header table
into the largest gap between two PT_LOAD segments and extending the
earlier segment across that gap. Whenever the gap it picked was the one
between the read-only data and the read-write segment, whose boundary
is not page aligned, the extended segment ended inside the first page
of the next one. Linux 4.17 to 5.3, and RHEL 8's 4.18 kernel, map an
executable's segments with MAP_FIXED_NOREPLACE and refuse the second
mapping, so the single executable was killed with SIGSEGV before it ran
a single instruction ('elf segment at ... requested but the memory is
mapped already' in the kernel log). Which gap is largest depends on
section sizes, so roughly one build in three produced such binaries.
Ask LIEF to place the table after .bss for non-PIE executables instead,
which leaves every existing segment as the linker laid it out; the
output grows by the size of .bss. A test checks that no two PT_LOAD
segments of a --build-sea executable share a page.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: #65564
Refs: nodejs/build#4433
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs with CI started, the required approvals, and no outstanding review comments.fast-trackPRs proposed for a shorter-than-standard waiting period before landing.flaky-testIssues and PRs involving tests that fail intermittently in CI.needs-ciPRs that need a full CI run.review wantedPRs that need review.single-executableIssues and PRs related to single-executable applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@codebytere@nodejs-github-bot@sxa@panva@jasnell