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 }