commit b11597609109b3ac2f2416582aaf1a2cdf9b4caf
parent dc1dc1f96640de20daa2a4b9accfb0a26b839218
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 16 Jun 2026 15:58:12 -0700
link: restrict coalesced PT_LOAD perms-union to R/W/X (exclude SF_TLS)
A coalesced PT_LOAD's flags must carry only the p_flags-relevant bits. The
emitter treats an SF_TLS-flagged PT_LOAD specially (p_memsz collapses to
file_size), and TLS sizing is owned by the dedicated PT_TLS segment, so OR-ing
SF_TLS into a PT_LOAD is never correct. Narrow the perms-union accordingly.
Defensive: the scripted layout does not currently route SF_TLS into a coalesced
section's perms (verified: a real .tdata coalesced into a flags-less PT_LOAD
keeps memsz>filesz), so this only sharpens intent with no observable change.
Diffstat:
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/link/link_layout.c b/src/link/link_layout.c
@@ -1546,9 +1546,13 @@ static void link_layout_sections_scripted(Linker* l, LinkImage* img,
/* Bug 2: a FLAGS-less PHDRS PT_LOAD maps p_flags from seg->flags
* (perms_to_pflags); a coalesced PT_LOAD must carry the UNION of all its
* mapped sections' perms (GNU-ld behavior), not just the first section's
- * (set once at segment creation). OR in this section's perms here. The
- * load-bearing part is `perms` (SF_EXEC/SF_WRITE/SF_TLS). */
- seg->flags |= SF_ALLOC | perms;
+ * (set once at segment creation). Union only the p_flags-relevant bits
+ * (R/W/X) — a PT_LOAD's flags must never include SF_TLS, which the
+ * emitter treats specially (p_memsz collapses to file_size for SF_TLS
+ * segments); TLS sizing is owned by the dedicated PT_TLS segment.
+ * (Defensive: the scripted layout does not currently route SF_TLS into a
+ * coalesced section's perms, so this only narrows intent.) */
+ seg->flags |= SF_ALLOC | (perms & (SF_EXEC | SF_WRITE));
} else {
seg = &img->segments[img->nsegments];
memset(seg, 0, sizeof(*seg));