authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-05-22 07:47:08+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-05-23 12:04:17+02:00
log8fc0c7dce163cab311bd7f088b7a953584e24a1e
tree76f55bd405fbaa8d834b06f667d1cde1eb8edd21
parent434e69482ed29de26ceea16dbc5679f32281c502

link/macho: apply fixes to deduping logic

* test non-ObjC literal deduping logic

6 files changed, 316 insertions(+), 42 deletions(-)

src/link/MachO.zig+23-3
...@@ -1500,14 +1500,25 @@ pub fn dedupLiterals(self: *MachO) !void {...@@ -1500,14 +1500,25 @@ pub fn dedupLiterals(self: *MachO) !void {
1500 const gpa = self.base.comp.gpa;1500 const gpa = self.base.comp.gpa;
1501 var lp: LiteralPool = .{};1501 var lp: LiteralPool = .{};
1502 defer lp.deinit(gpa);1502 defer lp.deinit(gpa);
1503
1503 if (self.getZigObject()) |zo| {1504 if (self.getZigObject()) |zo| {
1504 try zo.dedupLiterals(&lp, self);1505 try zo.resolveLiterals(&lp, self);
1505 }1506 }
1506 for (self.objects.items) |index| {1507 for (self.objects.items) |index| {
1507 try self.getFile(index).?.object.dedupLiterals(&lp, self);1508 try self.getFile(index).?.object.resolveLiterals(&lp, self);
1508 }1509 }
1509 if (self.getInternalObject()) |object| {1510 if (self.getInternalObject()) |object| {
1510 try object.dedupLiterals(&lp, self);1511 try object.resolveLiterals(&lp, self);
1512 }
1513
1514 if (self.getZigObject()) |zo| {
1515 zo.dedupLiterals(lp, self);
1516 }
1517 for (self.objects.items) |index| {
1518 self.getFile(index).?.object.dedupLiterals(lp, self);
1519 }
1520 if (self.getInternalObject()) |object| {
1521 object.dedupLiterals(lp, self);
1511 }1522 }
1512}1523}
15131524
...@@ -4415,8 +4426,14 @@ pub const LiteralPool = struct {...@@ -4415,8 +4426,14 @@ pub const LiteralPool = struct {
4415 lp.data.deinit(allocator);4426 lp.data.deinit(allocator);
4416 }4427 }
44174428
4429 pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom {
4430 assert(index < lp.values.items.len);
4431 return macho_file.getAtom(lp.values.items[index]).?;
4432 }
4433
4418 const InsertResult = struct {4434 const InsertResult = struct {
4419 found_existing: bool,4435 found_existing: bool,
4436 index: Index,
4420 atom: *Atom.Index,4437 atom: *Atom.Index,
4421 };4438 };
44224439
...@@ -4434,6 +4451,7 @@ pub const LiteralPool = struct {...@@ -4434,6 +4451,7 @@ pub const LiteralPool = struct {
4434 }4451 }
4435 return .{4452 return .{
4436 .found_existing = gop.found_existing,4453 .found_existing = gop.found_existing,
4454 .index = @intCast(gop.index),
4437 .atom = &lp.values.items[gop.index],4455 .atom = &lp.values.items[gop.index],
4438 };4456 };
4439 }4457 }
...@@ -4472,6 +4490,8 @@ pub const LiteralPool = struct {...@@ -4472,6 +4490,8 @@ pub const LiteralPool = struct {
4472 return key.hash(ctx.lp);4490 return key.hash(ctx.lp);
4473 }4491 }
4474 };4492 };
4493
4494 pub const Index = u32;
4475};4495};
44764496
4477const HotUpdateState = struct {4497const HotUpdateState = struct {
src/link/MachO/Atom.zig+13-1
...@@ -113,12 +113,18 @@ pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {...@@ -113,12 +113,18 @@ pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
113 return macho_file.getThunk(extra.thunk);113 return macho_file.getThunk(extra.thunk);
114}114}
115115
116pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index {
117 if (!self.flags.literal_pool) return null;
118 return self.getExtra(macho_file).?.literal_index;
119}
120
116const AddExtraOpts = struct {121const AddExtraOpts = struct {
117 thunk: ?u32 = null,122 thunk: ?u32 = null,
118 rel_index: ?u32 = null,123 rel_index: ?u32 = null,
119 rel_count: ?u32 = null,124 rel_count: ?u32 = null,
120 unwind_index: ?u32 = null,125 unwind_index: ?u32 = null,
121 unwind_count: ?u32 = null,126 unwind_count: ?u32 = null,
127 literal_index: ?u32 = null,
122};128};
123129
124pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {130pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
...@@ -1177,7 +1183,7 @@ pub const Flags = packed struct {...@@ -1177,7 +1183,7 @@ pub const Flags = packed struct {
1177 /// Specifies whether this atom is alive or has been garbage collected.1183 /// Specifies whether this atom is alive or has been garbage collected.
1178 alive: bool = true,1184 alive: bool = true,
11791185
1180 /// Specifies if the atom has been visited during garbage collection.1186 /// Specifies if this atom has been visited during garbage collection.
1181 visited: bool = false,1187 visited: bool = false,
11821188
1183 /// Whether this atom has a range extension thunk.1189 /// Whether this atom has a range extension thunk.
...@@ -1188,6 +1194,9 @@ pub const Flags = packed struct {...@@ -1188,6 +1194,9 @@ pub const Flags = packed struct {
11881194
1189 /// Whether this atom has any unwind records.1195 /// Whether this atom has any unwind records.
1190 unwind: bool = false,1196 unwind: bool = false,
1197
1198 /// Whether this atom has LiteralPool entry.
1199 literal_pool: bool = false,
1191};1200};
11921201
1193pub const Extra = struct {1202pub const Extra = struct {
...@@ -1205,6 +1214,9 @@ pub const Extra = struct {...@@ -1205,6 +1214,9 @@ pub const Extra = struct {
12051214
1206 /// Count of relocations belonging to this atom.1215 /// Count of relocations belonging to this atom.
1207 unwind_count: u32 = 0,1216 unwind_count: u32 = 0,
1217
1218 /// Index into LiteralPool entry for this atom.
1219 literal_index: u32 = 0,
1208};1220};
12091221
1210pub const Alignment = @import("../../InternPool.zig").Alignment;1222pub const Alignment = @import("../../InternPool.zig").Alignment;
src/link/MachO/InternalObject.zig+35-20
...@@ -109,12 +109,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,...@@ -109,12 +109,9 @@ fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index,
109 return atom_index;109 return atom_index;
110}110}
111111
112pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {112pub fn resolveLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
113 const gpa = macho_file.base.comp.gpa;113 const gpa = macho_file.base.comp.gpa;
114114
115 var killed_atoms = std.AutoHashMap(Atom.Index, Atom.Index).init(gpa);
116 defer killed_atoms.deinit();
117
118 var buffer = std.ArrayList(u8).init(gpa);115 var buffer = std.ArrayList(u8).init(gpa);
119 defer buffer.deinit();116 defer buffer.deinit();
120117
...@@ -126,10 +123,9 @@ pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *...@@ -126,10 +123,9 @@ pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *
126 const res = try lp.insert(gpa, header.type(), data);123 const res = try lp.insert(gpa, header.type(), data);
127 if (!res.found_existing) {124 if (!res.found_existing) {
128 res.atom.* = atom_index;125 res.atom.* = atom_index;
129 continue;
130 }126 }
131 atom.flags.alive = false;127 atom.flags.literal_pool = true;
132 try killed_atoms.putNoClobber(atom_index, res.atom.*);128 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
133 } else if (Object.isPtrLiteral(header)) {129 } else if (Object.isPtrLiteral(header)) {
134 const atom = macho_file.getAtom(atom_index).?;130 const atom = macho_file.getAtom(atom_index).?;
135 const relocs = atom.getRelocs(macho_file);131 const relocs = atom.getRelocs(macho_file);
...@@ -145,32 +141,45 @@ pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *...@@ -145,32 +141,45 @@ pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *
145 buffer.clearRetainingCapacity();141 buffer.clearRetainingCapacity();
146 if (!res.found_existing) {142 if (!res.found_existing) {
147 res.atom.* = atom_index;143 res.atom.* = atom_index;
148 continue;
149 }144 }
150 atom.flags.alive = false;145 atom.flags.literal_pool = true;
151 try killed_atoms.putNoClobber(atom_index, res.atom.*);146 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
152 }147 }
153 }148 }
149}
154150
151pub fn dedupLiterals(self: InternalObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
155 for (self.atoms.items) |atom_index| {152 for (self.atoms.items) |atom_index| {
156 if (killed_atoms.get(atom_index)) |_| continue;
157 const atom = macho_file.getAtom(atom_index) orelse continue;153 const atom = macho_file.getAtom(atom_index) orelse continue;
158 if (!atom.flags.alive) continue;154 if (!atom.flags.alive) continue;
159 if (!atom.flags.relocs) continue;155 if (!atom.flags.relocs) continue;
160156
161 const relocs = blk: {157 const relocs = blk: {
162 const extra = atom.getExtra(macho_file).?;158 const extra = atom.getExtra(macho_file).?;
163 const relocs = slice.items(.relocs)[atom.n_sect].items;159 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
164 break :blk relocs[extra.rel_index..][0..extra.rel_count];160 break :blk relocs[extra.rel_index..][0..extra.rel_count];
165 };161 };
166 for (relocs) |*rel| switch (rel.tag) {162 for (relocs) |*rel| switch (rel.tag) {
167 .local => if (killed_atoms.get(rel.target)) |new_target| {163 .local => {
168 rel.target = new_target;164 const target = macho_file.getAtom(rel.target).?;
165 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
166 const lp_atom = lp.getAtom(lp_index, macho_file);
167 if (target.atom_index != lp_atom.atom_index) {
168 target.flags.alive = false;
169 rel.target = lp_atom.atom_index;
170 }
171 }
169 },172 },
170 .@"extern" => {173 .@"extern" => {
171 const target = rel.getTargetSymbol(macho_file);174 const target_sym = rel.getTargetSymbol(macho_file);
172 if (killed_atoms.get(target.atom)) |new_atom| {175 if (target_sym.getAtom(macho_file)) |target_atom| {
173 target.atom = new_atom;176 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
177 const lp_atom = lp.getAtom(lp_index, macho_file);
178 if (target_atom.atom_index != lp_atom.atom_index) {
179 target_atom.flags.alive = false;
180 target_sym.atom = lp_atom.atom_index;
181 }
182 }
174 }183 }
175 },184 },
176 };185 };
...@@ -179,9 +188,15 @@ pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *...@@ -179,9 +188,15 @@ pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *
179 for (self.symbols.items) |sym_index| {188 for (self.symbols.items) |sym_index| {
180 const sym = macho_file.getSymbol(sym_index);189 const sym = macho_file.getSymbol(sym_index);
181 if (!sym.flags.objc_stubs) continue;190 if (!sym.flags.objc_stubs) continue;
182 const extra = sym.getExtra(macho_file).?;191 var extra = sym.getExtra(macho_file).?;
183 if (killed_atoms.get(extra.objc_selrefs)) |new_atom| {192 const atom = macho_file.getAtom(extra.objc_selrefs).?;
184 try sym.addExtra(.{ .objc_selrefs = new_atom }, macho_file);193 if (atom.getLiteralPoolIndex(macho_file)) |lp_index| {
194 const lp_atom = lp.getAtom(lp_index, macho_file);
195 if (atom.atom_index != lp_atom.atom_index) {
196 atom.flags.alive = false;
197 extra.objc_selrefs = lp_atom.atom_index;
198 sym.setExtra(extra, macho_file);
199 }
185 }200 }
186 }201 }
187}202}
src/link/MachO/Object.zig+26-17
...@@ -527,12 +527,9 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {...@@ -527,12 +527,9 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
527 }527 }
528}528}
529529
530pub fn dedupLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {530pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
531 const gpa = macho_file.base.comp.gpa;531 const gpa = macho_file.base.comp.gpa;
532532
533 var killed_atoms = std.AutoHashMap(Atom.Index, Atom.Index).init(gpa);
534 defer killed_atoms.deinit();
535
536 var buffer = std.ArrayList(u8).init(gpa);533 var buffer = std.ArrayList(u8).init(gpa);
537 defer buffer.deinit();534 defer buffer.deinit();
538535
...@@ -548,10 +545,9 @@ pub fn dedupLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !...@@ -548,10 +545,9 @@ pub fn dedupLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !
548 const res = try lp.insert(gpa, header.type(), atom_data);545 const res = try lp.insert(gpa, header.type(), atom_data);
549 if (!res.found_existing) {546 if (!res.found_existing) {
550 res.atom.* = sub.atom;547 res.atom.* = sub.atom;
551 continue;
552 }548 }
553 atom.flags.alive = false;549 atom.flags.literal_pool = true;
554 try killed_atoms.putNoClobber(sub.atom, res.atom.*);550 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
555 }551 }
556 } else if (isPtrLiteral(header)) {552 } else if (isPtrLiteral(header)) {
557 for (subs.items) |sub| {553 for (subs.items) |sub| {
...@@ -572,33 +568,46 @@ pub fn dedupLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !...@@ -572,33 +568,46 @@ pub fn dedupLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !
572 buffer.clearRetainingCapacity();568 buffer.clearRetainingCapacity();
573 if (!res.found_existing) {569 if (!res.found_existing) {
574 res.atom.* = sub.atom;570 res.atom.* = sub.atom;
575 continue;
576 }571 }
577 atom.flags.alive = false;572 atom.flags.literal_pool = true;
578 try killed_atoms.putNoClobber(sub.atom, res.atom.*);573 try atom.addExtra(.{ .literal_index = res.index }, macho_file);
579 }574 }
580 }575 }
581 }576 }
577}
582578
579pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
583 for (self.atoms.items) |atom_index| {580 for (self.atoms.items) |atom_index| {
584 if (killed_atoms.get(atom_index)) |_| continue;
585 const atom = macho_file.getAtom(atom_index) orelse continue;581 const atom = macho_file.getAtom(atom_index) orelse continue;
586 if (!atom.flags.alive) continue;582 if (!atom.flags.alive) continue;
587 if (!atom.flags.relocs) continue;583 if (!atom.flags.relocs) continue;
588584
589 const relocs = blk: {585 const relocs = blk: {
590 const extra = atom.getExtra(macho_file).?;586 const extra = atom.getExtra(macho_file).?;
591 const relocs = slice.items(.relocs)[atom.n_sect].items;587 const relocs = self.sections.items(.relocs)[atom.n_sect].items;
592 break :blk relocs[extra.rel_index..][0..extra.rel_count];588 break :blk relocs[extra.rel_index..][0..extra.rel_count];
593 };589 };
594 for (relocs) |*rel| switch (rel.tag) {590 for (relocs) |*rel| switch (rel.tag) {
595 .local => if (killed_atoms.get(rel.target)) |new_target| {591 .local => {
596 rel.target = new_target;592 const target = macho_file.getAtom(rel.target).?;
593 if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
594 const lp_atom = lp.getAtom(lp_index, macho_file);
595 if (target.atom_index != lp_atom.atom_index) {
596 target.flags.alive = false;
597 rel.target = lp_atom.atom_index;
598 }
599 }
597 },600 },
598 .@"extern" => {601 .@"extern" => {
599 const target = rel.getTargetSymbol(macho_file);602 const target_sym = rel.getTargetSymbol(macho_file);
600 if (killed_atoms.get(target.atom)) |new_atom| {603 if (target_sym.getAtom(macho_file)) |target_atom| {
601 target.atom = new_atom;604 if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
605 const lp_atom = lp.getAtom(lp_index, macho_file);
606 if (target_atom.atom_index != lp_atom.atom_index) {
607 target_atom.flags.alive = false;
608 target_sym.atom = lp_atom.atom_index;
609 }
610 }
602 }611 }
603 },612 },
604 };613 };
src/link/MachO/ZigObject.zig+8-1
...@@ -314,7 +314,14 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !vo...@@ -314,7 +314,14 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, macho_file: *MachO) !vo
314 }314 }
315}315}
316316
317pub fn dedupLiterals(self: *ZigObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {317pub fn resolveLiterals(self: *ZigObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
318 _ = self;
319 _ = lp;
320 _ = macho_file;
321 // TODO
322}
323
324pub fn dedupLiterals(self: *ZigObject, lp: MachO.LiteralPool, macho_file: *MachO) void {
318 _ = self;325 _ = self;
319 _ = lp;326 _ = lp;
320 _ = macho_file;327 _ = macho_file;
test/link/macho.zig+211
...@@ -38,6 +38,8 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {...@@ -38,6 +38,8 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
38 macho_step.dependOn(testLayout(b, .{ .target = default_target }));38 macho_step.dependOn(testLayout(b, .{ .target = default_target }));
39 macho_step.dependOn(testLinkingStaticLib(b, .{ .target = default_target }));39 macho_step.dependOn(testLinkingStaticLib(b, .{ .target = default_target }));
40 macho_step.dependOn(testLinksection(b, .{ .target = default_target }));40 macho_step.dependOn(testLinksection(b, .{ .target = default_target }));
41 macho_step.dependOn(testMergeLiterals(b, .{ .target = aarch64_target }));
42 macho_step.dependOn(testMergeLiterals2(b, .{ .target = aarch64_target }));
41 macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target }));43 macho_step.dependOn(testMhExecuteHeader(b, .{ .target = default_target }));
42 macho_step.dependOn(testNoDeadStrip(b, .{ .target = default_target }));44 macho_step.dependOn(testNoDeadStrip(b, .{ .target = default_target }));
43 macho_step.dependOn(testNoExportsDylib(b, .{ .target = default_target }));45 macho_step.dependOn(testNoExportsDylib(b, .{ .target = default_target }));
...@@ -914,6 +916,215 @@ fn testLinksection(b: *Build, opts: Options) *Step {...@@ -914,6 +916,215 @@ fn testLinksection(b: *Build, opts: Options) *Step {
914 return test_step;916 return test_step;
915}917}
916918
919fn testMergeLiterals(b: *Build, opts: Options) *Step {
920 const test_step = addTestStep(b, "merge-literals", opts);
921
922 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
923 \\.globl _q1
924 \\.globl _s1
925 \\
926 \\.align 4
927 \\_q1:
928 \\ adrp x8, L._q1@PAGE
929 \\ ldr d0, [x8, L._q1@PAGEOFF]
930 \\ ret
931 \\
932 \\.section __TEXT,__cstring,cstring_literals
933 \\l._s1:
934 \\ .asciz "hello"
935 \\
936 \\.section __TEXT,__literal8,8byte_literals
937 \\.align 8
938 \\L._q1:
939 \\ .double 1.2345
940 \\
941 \\.section __DATA,__data
942 \\.align 8
943 \\_s1:
944 \\ .quad l._s1
945 });
946
947 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
948 \\.globl _q2
949 \\.globl _s2
950 \\.globl _s3
951 \\
952 \\.align 4
953 \\_q2:
954 \\ adrp x8, L._q2@PAGE
955 \\ ldr d0, [x8, L._q2@PAGEOFF]
956 \\ ret
957 \\
958 \\.section __TEXT,__cstring,cstring_literals
959 \\l._s2:
960 \\ .asciz "hello"
961 \\l._s3:
962 \\ .asciz "world"
963 \\
964 \\.section __TEXT,__literal8,8byte_literals
965 \\.align 8
966 \\L._q2:
967 \\ .double 1.2345
968 \\
969 \\.section __DATA,__data
970 \\.align 8
971 \\_s2:
972 \\ .quad l._s2
973 \\_s3:
974 \\ .quad l._s3
975 });
976
977 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
978 \\#include <stdio.h>
979 \\extern double q1();
980 \\extern double q2();
981 \\extern const char* s1;
982 \\extern const char* s2;
983 \\extern const char* s3;
984 \\int main() {
985 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
986 \\ return 0;
987 \\}
988 });
989
990 {
991 const exe = addExecutable(b, opts, .{ .name = "main1" });
992 exe.addObject(a_o);
993 exe.addObject(b_o);
994 exe.addObject(main_o);
995
996 const run = addRunArtifact(exe);
997 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
998 test_step.dependOn(&run.step);
999
1000 const check = exe.checkObject();
1001 check.dumpSection("__TEXT,__const");
1002 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1003 check.dumpSection("__TEXT,__cstring");
1004 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1005 test_step.dependOn(&check.step);
1006 }
1007
1008 {
1009 const exe = addExecutable(b, opts, .{ .name = "main2" });
1010 exe.addObject(b_o);
1011 exe.addObject(a_o);
1012 exe.addObject(main_o);
1013
1014 const run = addRunArtifact(exe);
1015 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
1016 test_step.dependOn(&run.step);
1017
1018 const check = exe.checkObject();
1019 check.dumpSection("__TEXT,__const");
1020 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1021 check.dumpSection("__TEXT,__cstring");
1022 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1023 test_step.dependOn(&check.step);
1024 }
1025
1026 {
1027 const c_o = addObject(b, opts, .{ .name = "c" });
1028 c_o.addObject(a_o);
1029 c_o.addObject(b_o);
1030 c_o.addObject(main_o);
1031
1032 const exe = addExecutable(b, opts, .{ .name = "main3" });
1033 exe.addObject(c_o);
1034
1035 const run = addRunArtifact(exe);
1036 run.expectStdOutEqual("hello, hello, world, 1.234500, 1.234500");
1037 test_step.dependOn(&run.step);
1038
1039 const check = exe.checkObject();
1040 check.dumpSection("__TEXT,__const");
1041 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1042 check.dumpSection("__TEXT,__cstring");
1043 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1044 test_step.dependOn(&check.step);
1045 }
1046
1047 return test_step;
1048}
1049
1050/// This particular test case will generate invalid machine code that will segfault at runtime.
1051/// However, this is by design as we want to test that the linker does not panic when linking it
1052/// which is also the case for the system linker and lld - linking succeeds, runtime segfaults.
1053/// It should also be mentioned that runtime segfault is not due to the linker but faulty input asm.
1054fn testMergeLiterals2(b: *Build, opts: Options) *Step {
1055 const test_step = addTestStep(b, "merge-literals-2", opts);
1056
1057 const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes =
1058 \\.globl _q1
1059 \\.globl _s1
1060 \\
1061 \\.align 4
1062 \\_q1:
1063 \\ adrp x0, L._q1@PAGE
1064 \\ ldr x0, [x0, L._q1@PAGEOFF]
1065 \\ ret
1066 \\
1067 \\.section __TEXT,__cstring,cstring_literals
1068 \\_s1:
1069 \\ .asciz "hello"
1070 \\
1071 \\.section __TEXT,__literal8,8byte_literals
1072 \\.align 8
1073 \\L._q1:
1074 \\ .double 1.2345
1075 });
1076
1077 const b_o = addObject(b, opts, .{ .name = "b", .asm_source_bytes =
1078 \\.globl _q2
1079 \\.globl _s2
1080 \\.globl _s3
1081 \\
1082 \\.align 4
1083 \\_q2:
1084 \\ adrp x0, L._q2@PAGE
1085 \\ ldr x0, [x0, L._q2@PAGEOFF]
1086 \\ ret
1087 \\
1088 \\.section __TEXT,__cstring,cstring_literals
1089 \\_s2:
1090 \\ .asciz "hello"
1091 \\_s3:
1092 \\ .asciz "world"
1093 \\
1094 \\.section __TEXT,__literal8,8byte_literals
1095 \\.align 8
1096 \\L._q2:
1097 \\ .double 1.2345
1098 });
1099
1100 const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes =
1101 \\#include <stdio.h>
1102 \\extern double q1();
1103 \\extern double q2();
1104 \\extern const char* s1;
1105 \\extern const char* s2;
1106 \\extern const char* s3;
1107 \\int main() {
1108 \\ printf("%s, %s, %s, %f, %f", s1, s2, s3, q1(), q2());
1109 \\ return 0;
1110 \\}
1111 });
1112
1113 const exe = addExecutable(b, opts, .{ .name = "main1" });
1114 exe.addObject(a_o);
1115 exe.addObject(b_o);
1116 exe.addObject(main_o);
1117
1118 const check = exe.checkObject();
1119 check.dumpSection("__TEXT,__const");
1120 check.checkContains("\x8d\x97n\x12\x83\xc0\xf3?");
1121 check.dumpSection("__TEXT,__cstring");
1122 check.checkContains("hello\x00world\x00%s, %s, %s, %f, %f\x00");
1123 test_step.dependOn(&check.step);
1124
1125 return test_step;
1126}
1127
917fn testMhExecuteHeader(b: *Build, opts: Options) *Step {1128fn testMhExecuteHeader(b: *Build, opts: Options) *Step {
918 const test_step = addTestStep(b, "mh-execute-header", opts);1129 const test_step = addTestStep(b, "mh-execute-header", opts);
9191130