lib/tldr/src/formats/dispatch.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const root = @import("../root.zig");
3
4 const coff = @import("coff.zig");
5 const elf = @import("elf/root.zig");
6 const macho = @import("macho.zig");
7
8 const elf_object = elf.object;
9
10 pub fn parseObject(
11 allocator: std.mem.Allocator,
12 input: root.Input,
13 ) root.Error!root.Object {
14 return switch (try detectObjectFormat(input.bytes)) {
15 .elf => try elf.parseObjectMetadata(allocator, input),
16 .coff => try coff.parseObjectMetadata(allocator, input.bytes),
17 .macho => try macho.parseObjectMetadata(allocator, input.bytes),
18 .wasm => error.UnsupportedFormat,
19 };
20 }
21
22 pub fn detectObjectFormat(bytes: []const u8) root.Error!root.model.ObjectFormat {
23 if (bytes.len >= 4 and std.mem.eql(u8, bytes[0..4], std.elf.MAGIC)) return .elf;
24 if (isMachO(bytes)) return .macho;
25 if (coff.isObject(bytes)) return .coff;
26 return error.UnsupportedFormat;
27 }
28
29 pub fn validateInputFormats(inputs: []const root.Input, options: root.LinkOptions) root.Error!void {
30 for (inputs) |input| {
31 if (root.archive.isArchive(input.bytes)) continue;
32 const input_format = try detectObjectFormat(input.bytes);
33 if (input_format == options.target.object_format) continue;
34 if (options.diagnostics) |diagnostics| {
35 diagnostics.recordUnsupportedInputFormat(
36 input.name,
37 input_format,
38 options.target.object_format,
39 );
40 }
41 return error.UnsupportedFormat;
42 }
43 }
44
45 fn isMachO(bytes: []const u8) bool {
46 if (bytes.len < 4) return false;
47 return switch (std.mem.readInt(u32, bytes[0..4], .little)) {
48 std.macho.MH_MAGIC,
49 std.macho.MH_CIGAM,
50 std.macho.MH_MAGIC_64,
51 std.macho.MH_CIGAM_64,
52 std.macho.FAT_MAGIC,
53 std.macho.FAT_CIGAM,
54 std.macho.FAT_MAGIC_64,
55 std.macho.FAT_CIGAM_64,
56 => true,
57 else => false,
58 };
59 }
60
61 test "object format detection recognizes supported object containers" {
62 var coff_header = @as([@sizeOf(std.coff.Header)]u8, @splat(0));
63 std.mem.writeInt(u16, coff_header[0..2], @backingInt(std.coff.IMAGE.FILE.MACHINE.AMD64), .little);
64
65 try std.testing.expectEqual(root.model.ObjectFormat.elf, try detectObjectFormat(std.elf.MAGIC ++ "rest"));
66 try std.testing.expectEqual(root.model.ObjectFormat.coff, try detectObjectFormat(&coff_header));
67 try std.testing.expectError(error.UnsupportedFormat, detectObjectFormat("not an object"));
68 }
69
70 test "object format detection recognizes Mach-O container magics" {
71 const macho_magics = [_]u32{
72 std.macho.MH_MAGIC,
73 std.macho.MH_CIGAM,
74 std.macho.MH_MAGIC_64,
75 std.macho.MH_CIGAM_64,
76 std.macho.FAT_MAGIC,
77 std.macho.FAT_CIGAM,
78 std.macho.FAT_MAGIC_64,
79 std.macho.FAT_CIGAM_64,
80 };
81
82 for (macho_magics) |magic| {
83 var header: [4]u8 = undefined;
84 std.mem.writeInt(u32, &header, magic, .little);
85 try std.testing.expectEqual(root.model.ObjectFormat.macho, try detectObjectFormat(&header));
86 }
87 }
88
89 test "object parser dispatches ELF metadata" {
90 const allocator = std.testing.allocator;
91 const text = [_]u8{0xc3};
92 const text_index: u16 = 1;
93 const bytes = try elf_object.build(allocator, .{
94 .sections = &.{elf_object.Section.progbits(".text", &text, std.elf.SHF_EXECINSTR, 16)},
95 .symbols = &.{
96 elf_object.Symbol.section(text_index),
97 elf_object.Symbol.function("_start", text_index, 0, text.len),
98 },
99 });
100 defer allocator.free(bytes);
101
102 var parsed = try parseObject(allocator, .{ .name = "start.o", .bytes = bytes });
103 defer parsed.deinit(allocator);
104
105 try std.testing.expectEqual(root.model.ObjectFormat.elf, parsed.target.object_format);
106 try std.testing.expect(parsed.sections.len > text_index);
107 try std.testing.expectEqualStrings(".text", parsed.sections[text_index].name);
108 try std.testing.expectEqual(@as(u64, 16), parsed.sections[text_index].alignment);
109 for (parsed.symbols) |symbol| {
110 if (std.mem.eql(u8, symbol.name, "_start")) {
111 try std.testing.expect(symbol.external);
112 try std.testing.expect(!symbol.undefined);
113 return;
114 }
115 }
116 return error.MissingEntrySymbol;
117 }