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 }