lib/tldr/src/formats/elf/link.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const allocators = @import("alloc");
  3 const root = @import("../../root.zig");
  4 const elf = @import("root.zig");
  5 const archive_selection = @import("archive/root.zig");
  6 
  7 const Allocator = std.mem.Allocator;
  8 const archive = root.archive;
  9 const incremental = root.incremental;
 10 const model = root.model;
 11 const parallel = root.parallel;
 12 const trace = root.trace;
 13 
 14 const addressing = elf.addressing;
 15 const diagnostic = elf.diagnostic;
 16 const ehframe = elf.ehframe;
 17 const format = elf.format;
 18 const got_table = elf.got_table;
 19 const group = elf.group;
 20 const icf = elf.icf;
 21 const image = elf.image;
 22 const layout = elf.layout;
 23 const liveness = elf.liveness;
 24 const note = elf.note;
 25 const output_section = elf.output_section;
 26 const parser = elf.parser;
 27 const program_headers = elf.program;
 28 const relink = elf.relink;
 29 const relocation_namespace = elf.relocation;
 30 const section_state = elf.section_state;
 31 const symbol_table = elf.symbol_table;
 32 
 33 const build_id_note = note;
 34 const collectGlobalSymbols = symbol_table.collectGlobalSymbols;
 35 const collectGotEntries = got_table.collectEntries;
 36 const countProgramHeaders = program_headers.countHeaders;
 37 const finishBuildIdNote = build_id_note.finishBuildId;
 38 const output_section_count = output_section.output_section_count;
 39 const reserveBuildIdNote = build_id_note.reserveBuildId;
 40 const sectionIsAllocated = format.sectionIsAllocated;
 41 const ObjectFile = parser.ObjectFile;
 42 const ObjectLayout = layout.ObjectLayout;
 43 const OutputSection = layout.OutputSection;
 44 const SectionContribution = layout.SectionContribution;
 45 const SymbolAddressCache = addressing.Cache;
 46 const SymbolRef = layout.SymbolRef;
 47 const GlobalSymbol = layout.GlobalSymbol;
 48 const boundarySectionNameMatches = addressing.boundarySectionNameMatches;
 49 const contributionAt = layout.contributionAt;
 50 const foldedSection = section_state.foldedSection;
 51 const parseObjectWithOptions = parser.parseObjectWithOptions;
 52 const sectionDiscarded = section_state.sectionDiscarded;
 53 const sectionName = parser.sectionName;
 54 const sectionNameOrEmpty = parser.sectionNameOrEmpty;
 55 const sectionRelocationsAffectOutput = relocation_namespace.sectionRelocationsAffectOutput;
 56 const symbolAddress = addressing.symbolAddress;
 57 const syntheticBoundaryForSymbol = addressing.boundaryForSymbol;
 58 
 59 pub fn linkExecutable(
 60     allocator: Allocator,
 61     inputs: []const model.Input,
 62     options: model.LinkOptions,
 63 ) model.Error!root.LinkedImage {
 64     const link_phase = trace.scope("link.elf");
 65     defer link_phase.end();
 66     if (options.diagnostics) |diagnostics| diagnostics.clear();
 67     if (options.requiresDynamicLinking()) return error.UnsupportedDynamicLinking;
 68     if (options.output_kind != .executable) return error.UnsupportedOutputKind;
 69     if (options.target.architecture != .x86_64 or options.target.endianness != .little) {
 70         return error.UnsupportedArchitecture;
 71     }
 72     if (inputs.len == 0) return error.NoAllocSections;
 73 
 74     var selection_state = std.heap.ArenaAllocator.init(allocator);
 75     defer selection_state.deinit();
 76     var selection_lock = allocators.LockedAllocator.init(selection_state.allocator());
 77     const selection = selection_lock.allocator();
 78 
 79     var scratch_state = std.heap.ArenaAllocator.init(allocator);
 80     defer scratch_state.deinit();
 81     const scratch = scratch_state.allocator();
 82 
 83     var manifest_builder = build_manifest: {
 84         const manifest_phase = trace.product(.manifest_recording);
 85         defer manifest_phase.end();
 86         var builder = try incremental.Builder.init(allocator, options);
 87         errdefer builder.deinit();
 88         for (inputs) |input| try builder.addInput(input);
 89         break :build_manifest builder;
 90     };
 91     errdefer manifest_builder.deinit();
 92 
 93     var objects = std.ArrayListUnmanaged(ObjectFile).empty;
 94     defer objects.deinit(scratch);
 95     try objects.ensureTotalCapacity(scratch, inputs.len);
 96 
 97     var archive_state: archive_selection.ExtractionState = .{};
 98     defer archive_state.deinit(scratch);
 99 
100     var prefetcher = archive_selection.Prefetcher.init(selection, inputs, options);
101     defer prefetcher.deinit();
102     prefetcher.advance(0);
103 
104     for (inputs, 0..) |input, input_index| {
105         if (archive.isArchive(input.bytes)) {
106             const archive_phase = trace.product(.archive_member_selection);
107             defer archive_phase.end();
108             try archive_state.observeNewObjects(scratch, objects.items);
109             try archive_selection.addCandidates(scratch, selection, input, input_index, &archive_state, options, &prefetcher);
110             prefetcher.advance(input_index + 1);
111         } else {
112             var object = try parseInputObject(scratch, input, options);
113             object.input_index = input_index;
114             try objects.append(scratch, object);
115         }
116         if (archive_state.hasCandidates()) {
117             const archive_phase = trace.product(.archive_member_selection);
118             defer archive_phase.end();
119             try archive_state.observeNewObjects(scratch, objects.items);
120             try archive_selection.extractCandidates(
121                 scratch,
122                 selection,
123                 &objects,
124                 &archive_state,
125                 options,
126             );
127         }
128     }
129     if (manifest_builder.recordsContributions()) {
130         const manifest_phase = trace.product(.manifest_recording);
131         defer manifest_phase.end();
132         try relink.recordInputLinkEvidence(&manifest_builder, scratch, inputs, objects.items, &archive_state);
133     }
134     var globals: std.StringHashMapUnmanaged(GlobalSymbol) = .{};
135     defer globals.deinit(scratch);
136     var global_symbol_refs = std.ArrayListUnmanaged(SymbolRef).empty;
137     defer global_symbol_refs.deinit(scratch);
138     {
139         const symbol_phase = trace.product(.symbol_database);
140         defer symbol_phase.end();
141         {
142             const duplicate_phase = trace.product(.duplicate_policy);
143             defer duplicate_phase.end();
144             try group.discardDuplicates(scratch, objects.items);
145         }
146         try collectGlobalSymbols(scratch, objects.items, &globals, &global_symbol_refs, options.max_link_jobs);
147         if (options.gc_sections) {
148             try liveness.discardUnreachable(scratch, objects.items, &globals, options);
149         }
150         if (options.icf == .all) {
151             const duplicate_phase = trace.product(.duplicate_policy);
152             defer duplicate_phase.end();
153             try icf.fold(scratch, objects.items);
154         }
155         try requireResolvedRelocationSymbols(scratch, objects.items, &globals, options);
156         try requireSupportedRelocations(scratch, objects.items, options);
157         try requireEntrySymbol(&globals, options);
158     }
159     if (options.commit_observer) |observer| observer.notify();
160 
161     var output_sections = [output_section_count]OutputSection{
162         .{ .kind = .build_id_note, .alignment = 4 },
163         .{ .kind = .init, .alignment = 4 },
164         .{ .kind = .text, .alignment = 16 },
165         .{ .kind = .fini, .alignment = 4 },
166         .{ .kind = .iplt, .alignment = 16 },
167         .{ .kind = .eh_frame_hdr, .alignment = 4 },
168         .{ .kind = .eh_frame, .alignment = 8 },
169         .{ .kind = .rodata, .alignment = 1 },
170         .{ .kind = .rela_iplt, .alignment = 8 },
171         .{ .kind = .preinit_array, .alignment = 8 },
172         .{ .kind = .init_array, .alignment = 8 },
173         .{ .kind = .fini_array, .alignment = 8 },
174         .{ .kind = .data, .alignment = 8 },
175         .{ .kind = .got, .alignment = 8 },
176         .{ .kind = .igot, .alignment = 8 },
177         .{ .kind = .tdata, .alignment = 1 },
178         .{ .kind = .tbss, .alignment = 1 },
179         .{ .kind = .bss, .alignment = 1 },
180         .{ .kind = .debug_abbrev, .alignment = 1 },
181         .{ .kind = .debug_addr, .alignment = 1 },
182         .{ .kind = .debug_aranges, .alignment = 1 },
183         .{ .kind = .debug_cu_index, .alignment = 1 },
184         .{ .kind = .debug_frame, .alignment = 1 },
185         .{ .kind = .debug_info, .alignment = 1 },
186         .{ .kind = .debug_line, .alignment = 1 },
187         .{ .kind = .debug_line_str, .alignment = 1 },
188         .{ .kind = .debug_loc, .alignment = 1 },
189         .{ .kind = .debug_loclists, .alignment = 1 },
190         .{ .kind = .debug_macinfo, .alignment = 1 },
191         .{ .kind = .debug_macro, .alignment = 1 },
192         .{ .kind = .debug_names, .alignment = 1 },
193         .{ .kind = .debug_pubnames, .alignment = 1 },
194         .{ .kind = .debug_pubtypes, .alignment = 1 },
195         .{ .kind = .debug_ranges, .alignment = 1 },
196         .{ .kind = .debug_rnglists, .alignment = 1 },
197         .{ .kind = .debug_str, .alignment = 1 },
198         .{ .kind = .debug_str_offsets, .alignment = 1 },
199         .{ .kind = .debug_tu_index, .alignment = 1 },
200         .{ .kind = .debug_types, .alignment = 1 },
201     };
202 
203     var layouts = try scratch.alloc(ObjectLayout, objects.items.len);
204     var layout_count: usize = 0;
205     var section_contributions: []SectionContribution = &.{};
206     defer {
207         for (layouts[0..layout_count]) |*object_layout| object_layout.deinit(scratch);
208         scratch.free(section_contributions);
209         scratch.free(layouts);
210     }
211 
212     {
213         const layout_phase = trace.product(.section_contribution_graph);
214         defer layout_phase.end();
215         const section_contribution_count = try layout.collect.countObjectSections(objects.items);
216         section_contributions = try scratch.alloc(SectionContribution, section_contribution_count);
217         try layout.collect.sections(scratch, objects.items, &output_sections, layouts, section_contributions, &layout_count, &globals, options);
218         reserveBuildIdNote(&output_sections, options);
219         try ehframe.reserveHeader(&output_sections, objects.items, layouts[0..layout_count], options);
220     }
221     var ifunc_collector: elf.ifunc.Collector = undefined;
222     var got_layout: layout.GotLayout = undefined;
223     var ifunc_layout: elf.ifunc.IfuncLayout = undefined;
224     {
225         const layout_phase = trace.product(.layout_groups);
226         defer layout_phase.end();
227         ifunc_collector = try elf.ifunc.Collector.init(scratch, objects.items, &globals, options.max_link_jobs);
228         errdefer ifunc_collector.deinit(scratch);
229         const collect_ifunc = ifunc_collector.enabled();
230         got_layout = if (collect_ifunc)
231             try got_table.collectEntriesWithIfunc(scratch, objects.items, &output_sections, &globals, &ifunc_collector)
232         else
233             try collectGotEntries(scratch, objects.items, &output_sections, &globals);
234         errdefer got_layout.deinit(scratch);
235         ifunc_layout = if (collect_ifunc)
236             try ifunc_collector.finish(scratch, &output_sections)
237         else
238             elf.ifunc.IfuncLayout{};
239         errdefer ifunc_layout.deinit(scratch);
240     }
241     defer ifunc_collector.deinit(scratch);
242     defer got_layout.deinit(scratch);
243     defer ifunc_layout.deinit(scratch);
244 
245     const live_output_count = layout.output.countLive(&output_sections);
246     if (live_output_count == 0) return error.NoAllocSections;
247     const program_header_count = try countProgramHeaders(&output_sections);
248 
249     {
250         const layout_phase = trace.product(.address_assignment);
251         defer layout_phase.end();
252         try layout.output.assign(&output_sections, options, program_header_count);
253     }
254     {
255         const manifest_phase = trace.product(.manifest_recording);
256         defer manifest_phase.end();
257         for (output_sections) |section| {
258             if (section.logicalSize() == 0) continue;
259             try manifest_builder.addSection(
260                 section.kind.name(),
261                 section.address,
262                 section.file_offset,
263                 section.logicalSize(),
264                 section.reserved_size,
265                 section.alignment,
266             );
267         }
268         if (manifest_builder.recordsContributions()) {
269             try addContributionRecords(&manifest_builder, objects.items, layouts[0..layout_count], &output_sections);
270         }
271     }
272 
273     var symbol_addresses = try SymbolAddressCache.init(scratch, objects.items, layouts[0..layout_count], &output_sections);
274     defer symbol_addresses.deinit(scratch);
275 
276     try elf.ifunc.finalize(objects.items, layouts[0..layout_count], &output_sections, &globals, &symbol_addresses, ifunc_layout);
277 
278     const entry_address = resolve_entry: {
279         const symbol_phase = trace.product(.symbol_database);
280         defer symbol_phase.end();
281         break :resolve_entry try resolveEntry(objects.items, layouts, &output_sections, &globals, &symbol_addresses, options.entry_symbol);
282     };
283 
284     var linked_image = write_image: {
285         const write_phase = trace.product(.output_writing);
286         defer write_phase.end();
287         break :write_image try image.write(
288             scratch,
289             allocator,
290             objects.items,
291             layouts,
292             &output_sections,
293             &globals,
294             global_symbol_refs.items,
295             &symbol_addresses,
296             got_layout,
297             options,
298             entry_address,
299             live_output_count,
300             program_header_count,
301         );
302     };
303     errdefer linked_image.deinit(allocator);
304     const linked_bytes = linked_image.bytes;
305 
306     try elf.ifunc.write(linked_bytes, &output_sections, ifunc_layout);
307     {
308         const materialize_phase = trace.product(.payload_materialization);
309         defer materialize_phase.end();
310         try relocation_namespace.materialize(scratch, objects.items, layouts, &output_sections, &globals, &symbol_addresses, got_layout, linked_bytes, options);
311     }
312     if (@import("builtin").mode == .debug) elf.payload.verifyMergePieceBytes(linked_bytes, objects.items, layouts, &output_sections);
313     try finishBuildIdNote(linked_bytes, &output_sections, options);
314 
315     var manifest = finish_manifest: {
316         const manifest_phase = trace.product(.manifest_recording);
317         defer manifest_phase.end();
318         if (manifest_builder.recordsContributions()) {
319             try addExternalTargetRecords(
320                 &manifest_builder,
321                 scratch,
322                 objects.items,
323                 layouts[0..layout_count],
324                 &output_sections,
325                 &globals,
326                 global_symbol_refs.items,
327                 &symbol_addresses,
328             );
329             try addGotEntryRecords(&manifest_builder, objects.items, &output_sections, &globals, got_layout);
330             try addMergePieceRecords(&manifest_builder, objects.items, layouts[0..layout_count], &output_sections);
331         }
332         break :finish_manifest try manifest_builder.finish();
333     };
334     errdefer manifest.deinit(allocator);
335 
336     return .{
337         .bytes = linked_bytes,
338         .storage = linked_image.storage,
339         .manifest = manifest,
340     };
341 }
342 
343 fn parseInputObject(
344     scratch: Allocator,
345     input: model.Input,
346     options: model.LinkOptions,
347 ) model.Error!ObjectFile {
348     return parseObjectWithOptions(scratch, input, .{ .strip_debug = options.strip_debug }) catch |err| switch (err) {
349         error.InvalidElfHeader => {
350             const input_format = root.detectObjectFormat(input.bytes) catch |detect_err| switch (detect_err) {
351                 error.UnsupportedFormat => return error.UnsupportedFormat,
352                 else => return detect_err,
353             };
354             if (input_format == .elf) return err;
355             if (options.diagnostics) |diagnostics| {
356                 diagnostics.recordUnsupportedInputFormat(
357                     input.name,
358                     input_format,
359                     options.target.object_format,
360                 );
361             }
362             return error.UnsupportedFormat;
363         },
364         else => return err,
365     };
366 }
367 
368 const UndefinedRelocationFailure = struct {
369     found: bool = false,
370     object_index: usize = 0,
371     section_index: usize = 0,
372     relocation_index: usize = 0,
373     symbol_index: usize = 0,
374 
375     fn isFound(failure: UndefinedRelocationFailure) bool {
376         return failure.found;
377     }
378 
379     fn before(left: UndefinedRelocationFailure, right: UndefinedRelocationFailure) bool {
380         if (left.object_index != right.object_index) return left.object_index < right.object_index;
381         if (left.section_index != right.section_index) return left.section_index < right.section_index;
382         return left.relocation_index < right.relocation_index;
383     }
384 };
385 
386 const UnsupportedRelocationFailure = struct {
387     found: bool = false,
388     object_index: usize = 0,
389     section_index: usize = 0,
390     relocation_index: usize = 0,
391 
392     fn isFound(failure: UnsupportedRelocationFailure) bool {
393         return failure.found;
394     }
395 
396     fn before(left: UnsupportedRelocationFailure, right: UnsupportedRelocationFailure) bool {
397         if (left.object_index != right.object_index) return left.object_index < right.object_index;
398         if (left.section_index != right.section_index) return left.section_index < right.section_index;
399         return left.relocation_index < right.relocation_index;
400     }
401 };
402 
403 const UnsupportedRelocationFailures = parallel.FailureSlots(UnsupportedRelocationFailure);
404 
405 const UnsupportedRelocationContext = struct {
406     objects: []const ObjectFile,
407     failures: *UnsupportedRelocationFailures,
408 };
409 
410 fn requireSupportedRelocations(
411     scratch: Allocator,
412     objects: []const ObjectFile,
413     options: model.LinkOptions,
414 ) model.Error!void {
415     var total: usize = 0;
416     for (objects) |object| total += object.relocations.len;
417     const requested_workers = if (options.max_link_jobs != 0)
418         options.max_link_jobs
419     else
420         total / format.relocations_per_worker;
421     const workers = if (total >= format.parallel_relocation_threshold)
422         parallel.chooseWorkers(total, requested_workers)
423     else
424         1;
425 
426     if (workers <= 1) {
427         for (objects, 0..) |_, object_index| {
428             if (firstUnsupportedRelocation(objects, object_index)) |failure| {
429                 return failSupportedRelocations(objects, failure, options);
430             }
431         }
432         return;
433     }
434 
435     var failures = try UnsupportedRelocationFailures.init(scratch, workers, .{});
436     defer failures.deinit(scratch);
437 
438     var context = UnsupportedRelocationContext{
439         .objects = objects,
440         .failures = &failures,
441     };
442     parallel.forItems(objects.len, workers, &context, scanSupportedRelocationObject);
443     if (failures.earliest(UnsupportedRelocationFailure.isFound, UnsupportedRelocationFailure.before)) |failure| {
444         return failSupportedRelocations(objects, failure, options);
445     }
446 }
447 
448 fn failSupportedRelocations(
449     objects: []const ObjectFile,
450     failure: UnsupportedRelocationFailure,
451     options: model.LinkOptions,
452 ) model.Error {
453     const object = objects[failure.object_index];
454     const relocations = object.relocationsForSection(failure.section_index);
455     diagnostic.recordUnsupportedRelocation(options, object, failure.section_index, relocations[failure.relocation_index]);
456     return error.UnsupportedRelocation;
457 }
458 
459 fn scanSupportedRelocationObject(context: *UnsupportedRelocationContext, worker: usize, object_index: usize) void {
460     const failure = firstUnsupportedRelocation(context.objects, object_index) orelse return;
461     const current = context.failures.items[worker];
462     if (!current.found or UnsupportedRelocationFailure.before(failure, current)) {
463         context.failures.record(worker, failure);
464     }
465 }
466 
467 fn firstUnsupportedRelocation(objects: []const ObjectFile, object_index: usize) ?UnsupportedRelocationFailure {
468     const object = objects[object_index];
469     if (object.relocations.len == 0) return null;
470     for (object.sections, 0..) |_, section_index| {
471         if (!sectionRelocationsAffectOutput(object, section_index)) continue;
472         if (sectionDiscarded(object, section_index)) continue;
473         if (foldedSection(object, section_index) != null) continue;
474 
475         const relocations = object.relocationsForSection(section_index);
476         for (relocations, 0..) |entry, relocation_index| {
477             if (relocation_namespace.applicationSupported(entry.relocationType())) continue;
478             return .{
479                 .found = true,
480                 .object_index = object_index,
481                 .section_index = section_index,
482                 .relocation_index = relocation_index,
483             };
484         }
485     }
486     return null;
487 }
488 
489 fn requireEntrySymbol(
490     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
491     options: model.LinkOptions,
492 ) model.Error!void {
493     if (!globals.contains(options.entry_symbol)) return error.MissingEntrySymbol;
494 }
495 
496 const UndefinedRelocationFailures = parallel.FailureSlots(UndefinedRelocationFailure);
497 
498 const UndefinedRelocationContext = struct {
499     objects: []const ObjectFile,
500     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
501     failures: *UndefinedRelocationFailures,
502 };
503 
504 fn requireResolvedRelocationSymbols(
505     scratch: Allocator,
506     objects: []const ObjectFile,
507     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
508     options: model.LinkOptions,
509 ) model.Error!void {
510     const total = unresolvedRelocationScanTotal(objects);
511     const requested_workers = if (options.max_link_jobs != 0)
512         options.max_link_jobs
513     else
514         total / format.relocations_per_worker;
515     const workers = if (total >= format.parallel_relocation_threshold)
516         parallel.chooseWorkers(total, requested_workers)
517     else
518         1;
519 
520     if (workers <= 1) return requireResolvedRelocationSymbolsSerial(objects, globals, options);
521 
522     var failures = try UndefinedRelocationFailures.init(scratch, workers, .{});
523     defer failures.deinit(scratch);
524 
525     var context = UndefinedRelocationContext{
526         .objects = objects,
527         .globals = globals,
528         .failures = &failures,
529     };
530     parallel.forItems(objects.len, workers, &context, scanResolvedRelocationObject);
531     if (failures.earliest(UndefinedRelocationFailure.isFound, UndefinedRelocationFailure.before) != null) {
532         return requireResolvedRelocationSymbolsSerial(objects, globals, options);
533     }
534 }
535 
536 fn requireResolvedRelocationSymbolsSerial(
537     objects: []const ObjectFile,
538     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
539     options: model.LinkOptions,
540 ) model.Error!void {
541     var found_undefined = false;
542     for (objects) |object| {
543         if (!object.has_named_strong_undefined_symbols) continue;
544         if (object.relocations.len == 0) continue;
545         for (object.sections, 0..) |_, section_index| {
546             if (!sectionRelocationsAffectOutput(object, section_index)) continue;
547             if (sectionDiscarded(object, section_index)) continue;
548             if (foldedSection(object, section_index) != null) continue;
549 
550             const relocations = object.relocationsForSection(section_index);
551             for (relocations, 0..) |entry, relocation_index| {
552                 if (relocation_namespace.isNone(entry)) continue;
553                 if (entry.symbolIndex() >= object.symbols.len) return error.UndefinedSymbol;
554 
555                 const symbol = object.symbols[@intCast(entry.symbolIndex())];
556                 if (!symbol.isUndefined()) continue;
557                 if (globals.contains(symbol.name)) continue;
558                 if (symbol.isWeakUndefined()) continue;
559                 if (unresolvedSymbolHasSyntheticBoundary(objects, symbol.name)) continue;
560                 if (relocation_namespace.isRelaxedTlsRuntimeResolver(object, relocations, relocation_index)) continue;
561                 diagnostic.recordUndefinedSymbol(options, object, symbol);
562                 found_undefined = true;
563             }
564         }
565     }
566     if (found_undefined) return error.UndefinedSymbol;
567 }
568 
569 fn unresolvedRelocationScanTotal(objects: []const ObjectFile) usize {
570     var total: usize = 0;
571     for (objects) |object| {
572         if (!object.has_named_strong_undefined_symbols) continue;
573         total += object.relocations.len;
574     }
575     return total;
576 }
577 
578 fn scanResolvedRelocationObject(context: *UndefinedRelocationContext, worker: usize, object_index: usize) void {
579     const failure = firstUnresolvedRelocation(context.objects, context.globals, object_index) orelse return;
580     const current = context.failures.items[worker];
581     if (!current.found or UndefinedRelocationFailure.before(failure, current)) {
582         context.failures.record(worker, failure);
583     }
584 }
585 
586 fn firstUnresolvedRelocation(
587     objects: []const ObjectFile,
588     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
589     object_index: usize,
590 ) ?UndefinedRelocationFailure {
591     const object = objects[object_index];
592     if (!object.has_named_strong_undefined_symbols) return null;
593     if (object.relocations.len == 0) return null;
594     for (object.sections, 0..) |_, section_index| {
595         if (!sectionRelocationsAffectOutput(object, section_index)) continue;
596         if (sectionDiscarded(object, section_index)) continue;
597         if (foldedSection(object, section_index) != null) continue;
598 
599         const relocations = object.relocationsForSection(section_index);
600         for (relocations, 0..) |entry, relocation_index| {
601             if (relocation_namespace.isNone(entry)) continue;
602             if (entry.symbolIndex() >= object.symbols.len) return .{
603                 .found = true,
604                 .object_index = object_index,
605                 .section_index = section_index,
606                 .relocation_index = relocation_index,
607                 .symbol_index = object.symbols.len,
608             };
609 
610             const symbol_index: usize = @intCast(entry.symbolIndex());
611             const symbol = object.symbols[symbol_index];
612             if (!symbol.isUndefined()) continue;
613             if (globals.contains(symbol.name)) continue;
614             if (symbol.isWeakUndefined()) continue;
615             if (unresolvedSymbolHasSyntheticBoundary(objects, symbol.name)) continue;
616             if (relocation_namespace.isRelaxedTlsRuntimeResolver(object, relocations, relocation_index)) continue;
617             return .{
618                 .found = true,
619                 .object_index = object_index,
620                 .section_index = section_index,
621                 .relocation_index = relocation_index,
622                 .symbol_index = symbol_index,
623             };
624         }
625     }
626     return null;
627 }
628 
629 fn unresolvedSymbolHasSyntheticBoundary(objects: []const ObjectFile, symbol_name: []const u8) bool {
630     const boundary = syntheticBoundaryForSymbol(symbol_name) orelse return false;
631     if (boundary.optional) return true;
632     return hasSyntheticBoundarySection(objects, boundary.section_name);
633 }
634 
635 fn hasSyntheticBoundarySection(objects: []const ObjectFile, section_name: []const u8) bool {
636     for (objects) |object| {
637         for (object.sections, 0..) |section, section_index| {
638             if (!sectionIsAllocated(section)) continue;
639             if (section.size == 0) continue;
640             if (sectionDiscarded(object, section_index)) continue;
641             if (foldedSection(object, section_index) != null) continue;
642             if (boundarySectionNameMatches(.{ .section_name = section_name, .stop = false }, sectionNameOrEmpty(object, section_index))) return true;
643         }
644     }
645     return false;
646 }
647 
648 fn addContributionRecords(
649     manifest_builder: *incremental.Builder,
650     objects: []const ObjectFile,
651     layouts: []const ObjectLayout,
652     output_sections: []const OutputSection,
653 ) model.Error!void {
654     for (objects, 0..) |object, object_index| {
655         try addDiscardedSectionRecords(manifest_builder, object);
656 
657         const object_layout = layouts[object_index];
658         for (object.sections, 0..) |_, section_index| {
659             const contribution = contributionAt(object_layout.sections, section_index) orelse continue;
660             const output = output_sections[contribution.outputIndex()];
661             const name = try sectionName(object, section_index);
662             try manifest_builder.addContributionWithFileSize(
663                 object.name,
664                 object.input_index,
665                 .section,
666                 name,
667                 @intCast(section_index),
668                 output.kind.name(),
669                 output.address + contribution.offset,
670                 output.file_offset + contribution.offset,
671                 contribution.size,
672                 if (output.kind.isNoBits()) 0 else contribution.size,
673                 contribution.reserved_size,
674                 contribution.alignment,
675             );
676         }
677         if (object_layout.common_symbols.len != 0) {
678             for (object.symbols, 0..) |symbol, symbol_index| {
679                 const contribution = contributionAt(object_layout.common_symbols, symbol_index) orelse continue;
680                 const output = output_sections[contribution.outputIndex()];
681                 try manifest_builder.addContributionWithFileSize(
682                     object.name,
683                     object.input_index,
684                     .common_symbol,
685                     symbol.name,
686                     @intCast(symbol_index),
687                     output.kind.name(),
688                     output.address + contribution.offset,
689                     output.file_offset + contribution.offset,
690                     contribution.size,
691                     if (output.kind.isNoBits()) 0 else contribution.size,
692                     contribution.reserved_size,
693                     contribution.alignment,
694                 );
695             }
696         }
697     }
698 }
699 
700 fn addExternalTargetRecords(
701     manifest_builder: *incremental.Builder,
702     scratch: Allocator,
703     objects: []const ObjectFile,
704     layouts: []const ObjectLayout,
705     output_sections: []const OutputSection,
706     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
707     global_symbol_refs: []const SymbolRef,
708     symbol_addresses: *SymbolAddressCache,
709 ) model.Error!void {
710     for (global_symbol_refs) |current_ref| {
711         const object = objects[current_ref.object_index];
712         const symbol = object.symbols[current_ref.symbol_index];
713         if (section_state.symbolSectionDiscarded(object, symbol)) continue;
714         const resolved_address = addressing.relocationAddress(
715             .serial,
716             objects,
717             layouts,
718             output_sections,
719             globals,
720             symbol_addresses,
721             current_ref.object_index,
722             current_ref.symbol_index,
723         ) catch |err| switch (err) {
724             error.OutOfMemory => return error.OutOfMemory,
725             else => continue,
726         };
727         try manifest_builder.addExternalTarget(symbol.name, resolved_address, symbol.size);
728     }
729 
730     var recorded_boundaries: std.StringHashMapUnmanaged(void) = .{};
731     defer recorded_boundaries.deinit(scratch);
732     for (objects) |object| {
733         for (object.symbols) |symbol| {
734             if (!symbol.isUndefined()) continue;
735             if (symbol.name.len == 0) continue;
736             if (globals.contains(symbol.name)) continue;
737             const symbol_boundary = addressing.boundaryForSymbol(symbol.name) orelse continue;
738             const boundary_address = symbol_addresses.syntheticBoundaryAddress(symbol_boundary) orelse continue;
739             const gop = try recorded_boundaries.getOrPut(scratch, symbol.name);
740             if (gop.found_existing) continue;
741             try manifest_builder.addExternalTarget(symbol.name, boundary_address, 0);
742         }
743     }
744 }
745 
746 fn addGotEntryRecords(
747     manifest_builder: *incremental.Builder,
748     objects: []const ObjectFile,
749     output_sections: []const OutputSection,
750     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
751     got_layout: layout.GotLayout,
752 ) model.Error!void {
753     if (got_layout.entries.len == 0) return;
754     const got = output_sections[@backingInt(output_section.OutputSectionKind.got)];
755     for (got_layout.entries) |entry| {
756         if (entry.ref.object_index >= objects.len) continue;
757         const object = objects[entry.ref.object_index];
758         if (entry.ref.symbol_index >= object.symbols.len) continue;
759         const symbol = object.symbols[entry.ref.symbol_index];
760         const unified_name = unified: {
761             if (symbol.name.len == 0) break :unified "";
762             const global = globals.get(symbol.name) orelse break :unified "";
763             if (!global.ref.eql(entry.ref)) break :unified "";
764             break :unified symbol.name;
765         };
766         const ordinal = std.math.cast(u32, entry.ref.symbol_index) orelse continue;
767         try manifest_builder.addGotEntry(
768             object.input_index,
769             object.name,
770             ordinal,
771             unified_name,
772             got.address + entry.offset,
773         );
774     }
775 }
776 
777 fn addMergePieceRecords(
778     manifest_builder: *incremental.Builder,
779     objects: []const ObjectFile,
780     layouts: []const ObjectLayout,
781     output_sections: []const OutputSection,
782 ) model.Error!void {
783     for (objects, 0..) |object, object_index| {
784         const merge_sections = layouts[object_index].merge_sections;
785         for (merge_sections, 0..) |merge_section, section_index| {
786             if (!merge_section.isPresent()) continue;
787             const ordinal = std.math.cast(u32, section_index) orelse continue;
788             for (merge_section.pieces) |piece| {
789                 const output = output_sections[piece.contribution.outputIndex()];
790                 try manifest_builder.addMergePiece(
791                     object.input_index,
792                     object.name,
793                     ordinal,
794                     piece.input_offset,
795                     piece.size,
796                     output.address + piece.contribution.offset + piece.output_intra_offset,
797                 );
798             }
799         }
800     }
801 }
802 
803 fn addDiscardedSectionRecords(
804     manifest_builder: *incremental.Builder,
805     object: ObjectFile,
806 ) model.Error!void {
807     for (object.sections, 0..) |section, section_index| {
808         if ((section.flags & std.elf.SHF_ALLOC) == 0 or section.size == 0) continue;
809 
810         const reason: incremental.DiscardReason = if (foldedSection(object, section_index) != null)
811             .identical_code_folded
812         else if (sectionDiscarded(object, section_index))
813             .discarded
814         else
815             continue;
816 
817         try manifest_builder.addDiscardedContribution(
818             object.name,
819             object.input_index,
820             try sectionName(object, section_index),
821             @intCast(section_index),
822             reason,
823             section.size,
824             @max(section.alignment, 1),
825         );
826     }
827 }
828 
829 fn resolveEntry(
830     objects: []const ObjectFile,
831     layouts: []const ObjectLayout,
832     output_sections: []const OutputSection,
833     globals: *const std.StringHashMapUnmanaged(GlobalSymbol),
834     symbol_addresses: *SymbolAddressCache,
835     entry_symbol: []const u8,
836 ) model.Error!u64 {
837     const global = globals.get(entry_symbol) orelse return error.MissingEntrySymbol;
838     return symbolAddress(.serial, objects, layouts, output_sections, globals, symbol_addresses, global.ref.object_index, global.ref.symbol_index);
839 }