| ... | @@ -535,15 +535,21 @@ const coff = struct { | ... | @@ -535,15 +535,21 @@ const coff = struct { |
| 535 | opts.imports or | 535 | opts.imports or |
| 536 | opts.tls; | 536 | opts.tls; |
| 537 | | 537 | |
| 538 | const data_dirs, const magic = if (header.size_of_optional_header > 0) optional_header: { | 538 | const ImageInfo = struct { |
| | 539 | data_dirs: []const std.coff.ImageDataDirectory, |
| | 540 | magic: std.coff.OptionalHeader.Magic, |
| | 541 | image_base: u64, |
| | 542 | }; |
| | 543 | |
| | 544 | const image_info: ?ImageInfo = if (header.size_of_optional_header > 0) image_info: { |
| 539 | if (!opts.file_headers and !needs_data_dirs) { | 545 | if (!opts.file_headers and !needs_data_dirs) { |
| 540 | try fr.seekBy(header.size_of_optional_header); | 546 | try fr.seekBy(header.size_of_optional_header); |
| 541 | break :optional_header .{ &.{}, null }; | 547 | break :image_info null; |
| 542 | } | 548 | } |
| 543 | | 549 | |
| 544 | if (opts.file_headers) try w.writeAll("COFF Optional Header:\n"); | 550 | if (opts.file_headers) try w.writeAll("COFF Optional Header:\n"); |
| 545 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); | 551 | const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little)); |
| 546 | const num_directory_entries = switch (magic) { | 552 | const num_directory_entries, const image_base = switch (magic) { |
| 547 | inline .PE32, .@"PE32+" => |v| num_data_dirs: { | 553 | inline .PE32, .@"PE32+" => |v| num_data_dirs: { |
| 548 | const OptionalHeader = if (v == .PE32) | 554 | const OptionalHeader = if (v == .PE32) |
| 549 | std.coff.OptionalHeader.PE32 | 555 | std.coff.OptionalHeader.PE32 |
| ... | @@ -593,7 +599,10 @@ const coff = struct { | ... | @@ -593,7 +599,10 @@ const coff = struct { |
| 593 | try w.writeByte('\n'); | 599 | try w.writeByte('\n'); |
| 594 | } | 600 | } |
| 595 | | 601 | |
| 596 | break :num_data_dirs optional_header.number_of_rva_and_sizes; | 602 | break :num_data_dirs .{ |
| | 603 | optional_header.number_of_rva_and_sizes, |
| | 604 | optional_header.image_base, |
| | 605 | }; |
| 597 | }, | 606 | }, |
| 598 | else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}), | 607 | else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}), |
| 599 | }; | 608 | }; |
| ... | @@ -614,10 +623,14 @@ const coff = struct { | ... | @@ -614,10 +623,14 @@ const coff = struct { |
| 614 | } | 623 | } |
| 615 | if (opts.file_headers) try w.writeByte('\n'); | 624 | if (opts.file_headers) try w.writeByte('\n'); |
| 616 | | 625 | |
| 617 | break :optional_header .{ known_dirs[0..@min(known_dirs.len, num_directory_entries)], magic }; | 626 | break :image_info .{ |
| | 627 | .data_dirs = known_dirs[0..@min(known_dirs.len, num_directory_entries)], |
| | 628 | .magic = magic, |
| | 629 | .image_base = image_base, |
| | 630 | }; |
| 618 | } else if (is_image) { | 631 | } else if (is_image) { |
| 619 | return failParse(opts, "image did not contain an optional header", .{}); | 632 | return failParse(opts, "image did not contain an optional header", .{}); |
| 620 | } else .{ &.{}, null }; | 633 | } else null; |
| 621 | | 634 | |
| 622 | // Section names in images don't use the string table, as they must fit inline in the header | 635 | // Section names in images don't use the string table, as they must fit inline in the header |
| 623 | const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0; | 636 | const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0; |
| ... | @@ -1013,7 +1026,7 @@ const coff = struct { | ... | @@ -1013,7 +1026,7 @@ const coff = struct { |
| 1013 | defer gpa.free(rva_index); | 1026 | defer gpa.free(rva_index); |
| 1014 | | 1027 | |
| 1015 | if (opts.exports) { | 1028 | if (opts.exports) { |
| 1016 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .EXPORT)) |section_index| { | 1029 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, image_info.?.data_dirs, .EXPORT)) |section_index| { |
| 1017 | const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err| | 1030 | const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err| |
| 1018 | return failParse(opts, "unable to read export directory: {t}", .{err}); | 1031 | return failParse(opts, "unable to read export directory: {t}", .{err}); |
| 1019 | | 1032 | |
| ... | @@ -1056,7 +1069,7 @@ const coff = struct { | ... | @@ -1056,7 +1069,7 @@ const coff = struct { |
| 1056 | | 1069 | |
| 1057 | // All the variable length fields should be contained within this directory. | 1070 | // All the variable length fields should be contained within this directory. |
| 1058 | // Read it entirely to avoid needing to seek per-name when iterating. | 1071 | // Read it entirely to avoid needing to seek per-name when iterating. |
| 1059 | const dir = data_dirs[@intFromEnum(DIRECTORY_ENTRY.EXPORT)]; | 1072 | const dir = image_info.?.data_dirs[@intFromEnum(DIRECTORY_ENTRY.EXPORT)]; |
| 1060 | const dir_end_rva = dir.virtual_address + dir.size; | 1073 | const dir_end_rva = dir.virtual_address + dir.size; |
| 1061 | const dir_loc = fr.logicalPos(); | 1074 | const dir_loc = fr.logicalPos(); |
| 1062 | const dir_slice = try r.readAlloc(gpa, dir.size); | 1075 | const dir_slice = try r.readAlloc(gpa, dir.size); |
| ... | @@ -1097,7 +1110,15 @@ const coff = struct { | ... | @@ -1097,7 +1110,15 @@ const coff = struct { |
| 1097 | } | 1110 | } |
| 1098 | | 1111 | |
| 1099 | if (opts.imports) { | 1112 | if (opts.imports) { |
| 1100 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .IMPORT)) |_| { | 1113 | if (try seekToDataDirectory( |
| | 1114 | opts, |
| | 1115 | fr, |
| | 1116 | w, |
| | 1117 | rva_index, |
| | 1118 | sections.items, |
| | 1119 | image_info.?.data_dirs, |
| | 1120 | .IMPORT, |
| | 1121 | )) |_| { |
| 1101 | const Entry = std.coff.ImportDirectoryEntry; | 1122 | const Entry = std.coff.ImportDirectoryEntry; |
| 1102 | var directory_entries: std.ArrayList(Entry) = .empty; | 1123 | var directory_entries: std.ArrayList(Entry) = .empty; |
| 1103 | defer directory_entries.deinit(gpa); | 1124 | defer directory_entries.deinit(gpa); |
| ... | @@ -1114,14 +1135,20 @@ const coff = struct { | ... | @@ -1114,14 +1135,20 @@ const coff = struct { |
| 1114 | } | 1135 | } |
| 1115 | | 1136 | |
| 1116 | for (directory_entries.items) |entry| { | 1137 | for (directory_entries.items) |entry| { |
| 1117 | const name_section = sectionContainingRva(rva_index, sections.items, entry.name_rva) orelse | 1138 | const name_section = sectionContainingRva( |
| | 1139 | rva_index, |
| | 1140 | sections.items, |
| | 1141 | entry.name_rva, |
| | 1142 | ) orelse |
| 1118 | return failParse( | 1143 | return failParse( |
| 1119 | opts, | 1144 | opts, |
| 1120 | "import directory entry name rva 0x{x} was not found in any section", | 1145 | "import directory entry name rva 0x{x} was not found in any section", |
| 1121 | .{entry.name_rva}, | 1146 | .{entry.name_rva}, |
| 1122 | ); | 1147 | ); |
| 1123 | | 1148 | |
| 1124 | const name_loc = sections.items[name_section].rvaFileOffset(entry.name_rva) catch unreachable; | 1149 | const name_loc = sections.items[name_section].rvaFileOffset( |
| | 1150 | entry.name_rva, |
| | 1151 | ) catch unreachable; |
| 1125 | fr.seekTo(name_loc) catch |err| | 1152 | fr.seekTo(name_loc) catch |err| |
| 1126 | return failParse( | 1153 | return failParse( |
| 1127 | opts, | 1154 | opts, |
| ... | @@ -1161,7 +1188,7 @@ const coff = struct { | ... | @@ -1161,7 +1188,7 @@ const coff = struct { |
| 1161 | .{ ilt_loc, err }, | 1188 | .{ ilt_loc, err }, |
| 1162 | ); | 1189 | ); |
| 1163 | | 1190 | |
| 1164 | switch (magic.?) { | 1191 | switch (image_info.?.magic) { |
| 1165 | _ => try w.writeAll("(unknown magic)"), | 1192 | _ => try w.writeAll("(unknown magic)"), |
| 1166 | inline else => |m| { | 1193 | inline else => |m| { |
| 1167 | const TableEntry = std.coff.ImportLookupTableEntry(m); | 1194 | const TableEntry = std.coff.ImportLookupTableEntry(m); |
| ... | @@ -1230,8 +1257,79 @@ const coff = struct { | ... | @@ -1230,8 +1257,79 @@ const coff = struct { |
| 1230 | } | 1257 | } |
| 1231 | | 1258 | |
| 1232 | if (opts.tls) { | 1259 | if (opts.tls) { |
| 1233 | if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .TLS)) |_| { | 1260 | if (try seekToDataDirectory( |
| 1234 | // TODO | 1261 | opts, |
| | 1262 | fr, |
| | 1263 | w, |
| | 1264 | rva_index, |
| | 1265 | sections.items, |
| | 1266 | image_info.?.data_dirs, |
| | 1267 | .TLS, |
| | 1268 | )) |_| { |
| | 1269 | switch (image_info.?.magic) { |
| | 1270 | _ => try w.writeAll("(unknown magic)"), |
| | 1271 | inline else => |m| { |
| | 1272 | const TlsDirectoryEntry = std.coff.TlsDirectoryEntry(m); |
| | 1273 | const tls_entry = r.takeStruct(TlsDirectoryEntry, .little) catch |err| |
| | 1274 | return failParse(opts, "unable to read tls directory: {t}", .{err}); |
| | 1275 | |
| | 1276 | try w.writeAll("TLS Directory:\n"); |
| | 1277 | try dumpHeader(w, TlsDirectoryEntry, &tls_entry, struct {}); |
| | 1278 | |
| | 1279 | try w.writeAll(" | "); |
| | 1280 | if (tls_entry.characteristics.alignment == .NONE) { |
| | 1281 | try w.writeAll("Alignment not specified"); |
| | 1282 | } else { |
| | 1283 | try w.print( |
| | 1284 | "Alignment: {d}", |
| | 1285 | .{tls_entry.characteristics.alignment.toByteUnits().?}, |
| | 1286 | ); |
| | 1287 | } |
| | 1288 | |
| | 1289 | try w.writeAll( |
| | 1290 | \\ |
| | 1291 | \\ |
| | 1292 | \\TLS Callbacks: |
| | 1293 | \\ Address |
| | 1294 | \\ |
| | 1295 | ); |
| | 1296 | |
| | 1297 | const callbacks_rva: u32 = @intCast(tls_entry.callbacks_va - image_info.?.image_base); |
| | 1298 | const section_index = sectionContainingRva( |
| | 1299 | rva_index, |
| | 1300 | sections.items, |
| | 1301 | callbacks_rva, |
| | 1302 | ) orelse |
| | 1303 | return failParse( |
| | 1304 | opts, |
| | 1305 | "tls callbacks rva 0x{x} was not found in any section", |
| | 1306 | .{callbacks_rva}, |
| | 1307 | ); |
| | 1308 | |
| | 1309 | const callbacks_loc = sections.items[section_index] |
| | 1310 | .rvaFileOffset(callbacks_rva) catch unreachable; |
| | 1311 | |
| | 1312 | fr.seekTo(callbacks_loc) catch |err| |
| | 1313 | return failParse( |
| | 1314 | opts, |
| | 1315 | "unable to seek to tls callbacks array at offset 0x{x}: {t}", |
| | 1316 | .{ callbacks_loc, err }, |
| | 1317 | ); |
| | 1318 | |
| | 1319 | while (true) { |
| | 1320 | const callback_va = r.takeInt(@FieldType(TlsDirectoryEntry, "callbacks_va"), .little) catch |err| |
| | 1321 | return failParse( |
| | 1322 | opts, |
| | 1323 | "unable to read tls callbacks array: {t}", |
| | 1324 | .{err}, |
| | 1325 | ); |
| | 1326 | |
| | 1327 | try w.print("{x: >16} \n", .{callback_va}); |
| | 1328 | if (callback_va == 0) break; |
| | 1329 | } |
| | 1330 | try w.writeByte('\n'); |
| | 1331 | }, |
| | 1332 | } |
| 1235 | } | 1333 | } |
| 1236 | } | 1334 | } |
| 1237 | } | 1335 | } |
| ... | @@ -1245,8 +1343,10 @@ const coff = struct { | ... | @@ -1245,8 +1343,10 @@ const coff = struct { |
| 1245 | data_dirs: []const std.coff.ImageDataDirectory, | 1343 | data_dirs: []const std.coff.ImageDataDirectory, |
| 1246 | entry: DIRECTORY_ENTRY, | 1344 | entry: DIRECTORY_ENTRY, |
| 1247 | ) !?u16 { | 1345 | ) !?u16 { |
| 1248 | if (@intFromEnum(entry) < data_dirs.len) { | 1346 | if (@intFromEnum(entry) < data_dirs.len) blk: { |
| 1249 | const rva = data_dirs[@intFromEnum(entry)].virtual_address; | 1347 | const rva = data_dirs[@intFromEnum(entry)].virtual_address; |
| | 1348 | if (rva == 0) break :blk; |
| | 1349 | |
| 1250 | const section_index = sectionContainingRva(rva_index, sections, rva) orelse | 1350 | const section_index = sectionContainingRva(rva_index, sections, rva) orelse |
| 1251 | return failParse( | 1351 | return failParse( |
| 1252 | opts, | 1352 | opts, |