aboutsummaryrefslogtreecommitdiff
path: root/lib/std/coff.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-09-26 01:54:45 -0400
committerGitHub <noreply@github.com>2019-09-26 01:54:45 -0400
commit68bb3945708c43109c48bda3664176307d45b62c (patch)
treeafb9731e10cef9d192560b52cd9ae2cf179775c4 /lib/std/coff.zig
parent6128bc728d1e1024a178c16c2149f5b1a167a013 (diff)
parent4637e8f9699af9c3c6cf4df50ef5bb67c7a318a4 (diff)
downloadzig-68bb3945708c43109c48bda3664176307d45b62c.tar.gz
zig-68bb3945708c43109c48bda3664176307d45b62c.zip
Merge pull request #3315 from ziglang/mv-std-lib
Move std/ to lib/std/
Diffstat (limited to 'lib/std/coff.zig')
-rw-r--r--lib/std/coff.zig277
1 files changed, 277 insertions, 0 deletions
diff --git a/lib/std/coff.zig b/lib/std/coff.zig
new file mode 100644
index 0000000000..3890151d09
--- /dev/null
+++ b/lib/std/coff.zig
@@ -0,0 +1,277 @@
+const builtin = @import("builtin");
+const std = @import("std.zig");
+const io = std.io;
+const mem = std.mem;
+const os = std.os;
+const File = std.fs.File;
+
+const ArrayList = std.ArrayList;
+
+// CoffHeader.machine values
+// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
+const IMAGE_FILE_MACHINE_I386 = 0x014c;
+const IMAGE_FILE_MACHINE_IA64 = 0x0200;
+const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
+
+// OptionalHeader.magic values
+// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
+const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
+const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
+
+const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
+const IMAGE_DEBUG_TYPE_CODEVIEW = 2;
+const DEBUG_DIRECTORY = 6;
+
+pub const CoffError = error{
+ InvalidPEMagic,
+ InvalidPEHeader,
+ InvalidMachine,
+ MissingCoffSection,
+};
+
+// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+pub const Coff = struct {
+ in_file: File,
+ allocator: *mem.Allocator,
+
+ coff_header: CoffHeader,
+ pe_header: OptionalHeader,
+ sections: ArrayList(Section),
+
+ guid: [16]u8,
+ age: u32,
+
+ pub fn init(allocator: *mem.Allocator, in_file: File) Coff {
+ return Coff{
+ .in_file = in_file,
+ .allocator = allocator,
+ .coff_header = undefined,
+ .pe_header = undefined,
+ .sections = ArrayList(Section).init(allocator),
+ .guid = undefined,
+ .age = undefined,
+ };
+ }
+
+ pub fn loadHeader(self: *Coff) !void {
+ const pe_pointer_offset = 0x3C;
+
+ var file_stream = self.in_file.inStream();
+ const in = &file_stream.stream;
+
+ var magic: [2]u8 = undefined;
+ try in.readNoEof(magic[0..]);
+ if (!mem.eql(u8, magic, "MZ"))
+ return error.InvalidPEMagic;
+
+ // Seek to PE File Header (coff header)
+ try self.in_file.seekTo(pe_pointer_offset);
+ const pe_magic_offset = try in.readIntLittle(u32);
+ try self.in_file.seekTo(pe_magic_offset);
+
+ var pe_header_magic: [4]u8 = undefined;
+ try in.readNoEof(pe_header_magic[0..]);
+ if (!mem.eql(u8, pe_header_magic, [_]u8{ 'P', 'E', 0, 0 }))
+ return error.InvalidPEHeader;
+
+ self.coff_header = CoffHeader{
+ .machine = try in.readIntLittle(u16),
+ .number_of_sections = try in.readIntLittle(u16),
+ .timedate_stamp = try in.readIntLittle(u32),
+ .pointer_to_symbol_table = try in.readIntLittle(u32),
+ .number_of_symbols = try in.readIntLittle(u32),
+ .size_of_optional_header = try in.readIntLittle(u16),
+ .characteristics = try in.readIntLittle(u16),
+ };
+
+ switch (self.coff_header.machine) {
+ IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_IA64 => {},
+ else => return error.InvalidMachine,
+ }
+
+ try self.loadOptionalHeader(&file_stream);
+ }
+
+ fn loadOptionalHeader(self: *Coff, file_stream: *File.InStream) !void {
+ const in = &file_stream.stream;
+ self.pe_header.magic = try in.readIntLittle(u16);
+ // For now we're only interested in finding the reference to the .pdb,
+ // so we'll skip most of this header, which size is different in 32
+ // 64 bits by the way.
+ var skip_size: u16 = undefined;
+ if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32);
+ } else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64);
+ } else
+ return error.InvalidPEMagic;
+
+ try self.in_file.seekBy(skip_size);
+
+ const number_of_rva_and_sizes = try in.readIntLittle(u32);
+ if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
+ return error.InvalidPEHeader;
+
+ for (self.pe_header.data_directory) |*data_dir| {
+ data_dir.* = OptionalHeader.DataDirectory{
+ .virtual_address = try in.readIntLittle(u32),
+ .size = try in.readIntLittle(u32),
+ };
+ }
+ }
+
+ pub fn getPdbPath(self: *Coff, buffer: []u8) !usize {
+ try self.loadSections();
+
+ const header = blk: {
+ if (self.getSection(".buildid")) |section| {
+ break :blk section.header;
+ } else if (self.getSection(".rdata")) |section| {
+ break :blk section.header;
+ } else {
+ return error.MissingCoffSection;
+ }
+ };
+
+ const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY];
+ const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data;
+
+ var file_stream = self.in_file.inStream();
+ const in = &file_stream.stream;
+ try self.in_file.seekTo(file_offset);
+
+ // Find the correct DebugDirectoryEntry, and where its data is stored.
+ // It can be in any section.
+ const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry);
+ var i: u32 = 0;
+ blk: while (i < debug_dir_entry_count) : (i += 1) {
+ const debug_dir_entry = try in.readStruct(DebugDirectoryEntry);
+ if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) {
+ for (self.sections.toSlice()) |*section| {
+ const section_start = section.header.virtual_address;
+ const section_size = section.header.misc.virtual_size;
+ const rva = debug_dir_entry.address_of_raw_data;
+ const offset = rva - section_start;
+ if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) {
+ try self.in_file.seekTo(section.header.pointer_to_raw_data + offset);
+ break :blk;
+ }
+ }
+ }
+ }
+
+ var cv_signature: [4]u8 = undefined; // CodeView signature
+ try in.readNoEof(cv_signature[0..]);
+ // 'RSDS' indicates PDB70 format, used by lld.
+ if (!mem.eql(u8, cv_signature, "RSDS"))
+ return error.InvalidPEMagic;
+ try in.readNoEof(self.guid[0..]);
+ self.age = try in.readIntLittle(u32);
+
+ // Finally read the null-terminated string.
+ var byte = try in.readByte();
+ i = 0;
+ while (byte != 0 and i < buffer.len) : (i += 1) {
+ buffer[i] = byte;
+ byte = try in.readByte();
+ }
+
+ if (byte != 0 and i == buffer.len)
+ return error.NameTooLong;
+
+ return i;
+ }
+
+ pub fn loadSections(self: *Coff) !void {
+ if (self.sections.len == self.coff_header.number_of_sections)
+ return;
+
+ try self.sections.ensureCapacity(self.coff_header.number_of_sections);
+
+ var file_stream = self.in_file.inStream();
+ const in = &file_stream.stream;
+
+ var name: [8]u8 = undefined;
+
+ var i: u16 = 0;
+ while (i < self.coff_header.number_of_sections) : (i += 1) {
+ try in.readNoEof(name[0..]);
+ try self.sections.append(Section{
+ .header = SectionHeader{
+ .name = name,
+ .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) },
+ .virtual_address = try in.readIntLittle(u32),
+ .size_of_raw_data = try in.readIntLittle(u32),
+ .pointer_to_raw_data = try in.readIntLittle(u32),
+ .pointer_to_relocations = try in.readIntLittle(u32),
+ .pointer_to_line_numbers = try in.readIntLittle(u32),
+ .number_of_relocations = try in.readIntLittle(u16),
+ .number_of_line_numbers = try in.readIntLittle(u16),
+ .characteristics = try in.readIntLittle(u32),
+ },
+ });
+ }
+ }
+
+ pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section {
+ for (self.sections.toSlice()) |*sec| {
+ if (mem.eql(u8, sec.header.name[0..name.len], name)) {
+ return sec;
+ }
+ }
+ return null;
+ }
+};
+
+const CoffHeader = struct {
+ machine: u16,
+ number_of_sections: u16,
+ timedate_stamp: u32,
+ pointer_to_symbol_table: u32,
+ number_of_symbols: u32,
+ size_of_optional_header: u16,
+ characteristics: u16,
+};
+
+const OptionalHeader = struct {
+ const DataDirectory = struct {
+ virtual_address: u32,
+ size: u32,
+ };
+
+ magic: u16,
+ data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory,
+};
+
+const DebugDirectoryEntry = packed struct {
+ characteristiccs: u32,
+ time_date_stamp: u32,
+ major_version: u16,
+ minor_version: u16,
+ @"type": u32,
+ size_of_data: u32,
+ address_of_raw_data: u32,
+ pointer_to_raw_data: u32,
+};
+
+pub const Section = struct {
+ header: SectionHeader,
+};
+
+const SectionHeader = struct {
+ const Misc = union {
+ physical_address: u32,
+ virtual_size: u32,
+ };
+
+ name: [8]u8,
+ misc: Misc,
+ virtual_address: u32,
+ size_of_raw_data: u32,
+ pointer_to_raw_data: u32,
+ pointer_to_relocations: u32,
+ pointer_to_line_numbers: u32,
+ number_of_relocations: u16,
+ number_of_line_numbers: u16,
+ characteristics: u32,
+};