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 }