lib/tldr/src/link.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const model = @import("model.zig");
3 const object = @import("object.zig");
4 const trace = @import("trace.zig");
5 const formats = @import("formats/root.zig");
6 const incremental = @import("incremental/root.zig");
7
8 const Input = model.Input;
9 const LinkOptions = model.LinkOptions;
10 const Error = model.Error;
11 const Diagnostics = model.Diagnostics;
12 const LinkedImage = object.LinkedImage;
13
14 pub fn link(
15 allocator: std.mem.Allocator,
16 inputs: []const Input,
17 options: LinkOptions,
18 ) Error!LinkedImage {
19 const phase = trace.scope("link");
20 defer phase.end();
21 if (options.diagnostics) |diagnostics| diagnostics.clear();
22 try model.validateOptions(options);
23 return switch (options.target.object_format) {
24 .elf => try formats.elf.linkExecutable(allocator, inputs, options),
25 .coff => {
26 {
27 const input_phase = trace.product(.input_discovery);
28 defer input_phase.end();
29 try formats.validateInputFormats(inputs, options);
30 }
31 return try formats.coff.linkExecutable(allocator, inputs, options);
32 },
33 .macho => {
34 {
35 const input_phase = trace.product(.input_discovery);
36 defer input_phase.end();
37 try formats.validateInputFormats(inputs, options);
38 }
39 return try formats.macho.linkExecutable(allocator, inputs, options);
40 },
41 .wasm => error.UnsupportedFormat,
42 };
43 }
44
45 pub fn applyIncrementalMetadataPatch(
46 image: []u8,
47 candidate_image: []const u8,
48 options: LinkOptions,
49 ) Error!void {
50 return switch (options.target.object_format) {
51 .elf => try formats.elf.patchIncrementalMetadata(image, candidate_image),
52 .coff, .macho => {},
53 .wasm => error.UnsupportedFormat,
54 };
55 }
56
57 pub fn finishDirectIncrementalMetadataPatch(
58 image: []u8,
59 options: LinkOptions,
60 ) Error!void {
61 return switch (options.target.object_format) {
62 .elf => try formats.elf.finishIncrementalMetadata(image, options),
63 .coff, .macho => {},
64 .wasm => error.UnsupportedFormat,
65 };
66 }
67
68 pub const MetadataPatchRange = struct {
69 offset: usize,
70 len: usize,
71 };
72
73 pub fn incrementalBuildIdNoteRange(
74 image: []const u8,
75 options: LinkOptions,
76 ) Error!?MetadataPatchRange {
77 return switch (options.target.object_format) {
78 .elf => {
79 const range = (try formats.elf.buildIdNoteRange(image)) orelse return null;
80 return .{ .offset = range.offset, .len = range.len };
81 },
82 .coff, .macho => null,
83 .wasm => error.UnsupportedFormat,
84 };
85 }
86
87 pub fn incrementalMetadataPatchRange(
88 image: []const u8,
89 options: LinkOptions,
90 ) Error!?MetadataPatchRange {
91 return switch (options.target.object_format) {
92 .elf => {
93 const range = try formats.elf.patchIncrementalMetadataRange(image);
94 return .{ .offset = range.offset, .len = range.len };
95 },
96 .coff, .macho => null,
97 .wasm => error.UnsupportedFormat,
98 };
99 }
100
101 pub fn directIncrementalEvidenceAlloc(
102 allocator: std.mem.Allocator,
103 manifest: incremental.Manifest,
104 inputs: []const Input,
105 input_changes: incremental.InputChanges,
106 options: LinkOptions,
107 ) Error!incremental.DirectRelinkEvidence {
108 return switch (options.target.object_format) {
109 .elf => try formats.elf.relink.directEvidenceAlloc(allocator, manifest, inputs, input_changes, options),
110 .coff, .macho, .wasm => .{},
111 };
112 }
113
114 test "link rejects non-target object formats before ELF linking" {
115 var coff_header = @as([@sizeOf(std.coff.Header)]u8, @splat(0));
116 std.mem.writeInt(u16, coff_header[0..2], @backingInt(std.coff.IMAGE.FILE.MACHINE.AMD64), .little);
117
118 var diagnostics: Diagnostics = .{};
119 try std.testing.expectError(
120 error.UnsupportedFormat,
121 link(
122 std.testing.allocator,
123 &.{.{ .name = "main.obj", .bytes = &coff_header }},
124 .{ .diagnostics = &diagnostics },
125 ),
126 );
127
128 const failure = diagnostics.linkFailure(error.UnsupportedFormat);
129 const unsupported = switch (failure) {
130 .unsupported_input_format => |unsupported| unsupported,
131 else => return error.ExpectedUnsupportedInputFormatDiagnostic,
132 };
133 try std.testing.expectEqual(model.ObjectFormat.coff, unsupported.input_format);
134 try std.testing.expectEqual(model.ObjectFormat.elf, unsupported.target_format);
135 try std.testing.expectEqualStrings("main.obj", unsupported.input_name);
136 }