mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
MachO: don't split subsections on N_ALT_ENTRY symbols
MachO has a mechanism where symbols can introduce "subsections", which (as I understand it) allows a linker to garbage-collect parts of sections without pulling in the heavy machinery of `-fdata-sections` and `-ffunction-sections`. Essentially, symbols can be considered to partition a section, and these boundaries are not allowed to be crossed by memory accesses, so the linker can detect symbols which are unused and drop the corresponding input section regions. However, the symbol flag `N_ALT_ENTRY` indicates that a symbol should not participate in this "splitting", and is instead an "alternate entry point" to the previous subsection, which should continue through this symbol. The Mach-O linker was failing to ignore `N_ALT_ENTRY` symbols when creating subsections, which meant that for certain link inputs, it would create additional subsection splits, and then garbage collect the extra sections (due to the `N_ALT_ENTRY` symbol being unused). Naturally, this silent dropping of parts of input sections led to miscompilations.
This commit is contained in:
@@ -328,7 +328,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
|
||||
if (isPtrLiteral(sect)) continue;
|
||||
|
||||
const nlist_start = for (nlists, 0..) |nlist, i| {
|
||||
if (nlist.nlist.n_sect - 1 == n_sect) break i;
|
||||
// We must ignore `alt_entry` (N_ALT_ENTRY) symbols here, because that flag indicates
|
||||
// that a symbol should *not* split subsections.
|
||||
if (nlist.nlist.n_sect - 1 == n_sect and !nlist.nlist.n_desc.alt_entry) break i;
|
||||
} else nlists.len;
|
||||
const nlist_end = for (nlists[nlist_start..], nlist_start..) |nlist, i| {
|
||||
if (nlist.nlist.n_sect - 1 != n_sect) break i;
|
||||
@@ -359,9 +361,24 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
|
||||
const alias_start = idx;
|
||||
const nlist = nlists[alias_start];
|
||||
|
||||
while (idx < nlist_end and
|
||||
nlists[idx].nlist.n_value == nlist.nlist.n_value) : (idx += 1)
|
||||
{}
|
||||
// Skip past any symbols which shouldn't terminate this subsection.
|
||||
while (true) {
|
||||
idx += 1;
|
||||
if (idx == nlist_end) {
|
||||
// This subsection contains the full remainder of the section.
|
||||
break;
|
||||
}
|
||||
if (nlists[idx].nlist.n_value == nlist.nlist.n_value) {
|
||||
// Multiple symbols at the same address---don't create zero-length subsections.
|
||||
continue;
|
||||
}
|
||||
if (nlists[idx].nlist.n_desc.alt_entry) {
|
||||
// N_ALT_ENTRY indicates that this symbol does not split subsections, and is
|
||||
// instead an "alternate entry point" into an existing subsection.
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const size = if (idx < nlist_end)
|
||||
nlists[idx].nlist.n_value - nlist.nlist.n_value
|
||||
@@ -385,7 +402,9 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
|
||||
});
|
||||
|
||||
for (alias_start..idx) |i| {
|
||||
self.symtab.items(.size)[nlists[i].idx] = size;
|
||||
if (!nlists[i].nlist.n_desc.alt_entry) {
|
||||
self.symtab.items(.size)[nlists[i].idx] = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user