forked from crystal-lang/crystal
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support call stacks for MinGW-w64 builds
- Loading branch information
1 parent
5f72133
commit 16a3985
Showing
11 changed files
with
371 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
module Crystal | ||
# :nodoc: | ||
# | ||
# Portable Executable reader. | ||
# | ||
# Documentation: | ||
# - <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format> | ||
struct PE | ||
class Error < Exception | ||
end | ||
|
||
record SectionHeader, name : String, virtual_offset : UInt32, offset : UInt32, size : UInt32 | ||
|
||
record COFFSymbol, offset : UInt32, name : String | ||
|
||
getter original_image_base : UInt64 | ||
@section_headers : Slice(SectionHeader) | ||
@string_table_base : UInt32 | ||
getter coff_symbols = Hash(Int32, Array(COFFSymbol)).new | ||
|
||
def self.open(path : String | ::Path, &) | ||
File.open(path, "r") do |file| | ||
yield new(file) | ||
end | ||
end | ||
|
||
def initialize(@io : IO::FileDescriptor) | ||
dos_header = uninitialized LibC::IMAGE_DOS_HEADER | ||
io.read_fully(pointerof(dos_header).to_slice(1).to_unsafe_bytes) | ||
raise Error.new("Invalid DOS header") unless dos_header.e_magic == 0x5A4D # MZ | ||
|
||
io.seek(dos_header.e_lfanew) | ||
nt_header = uninitialized LibC::IMAGE_NT_HEADERS | ||
io.read_fully(pointerof(nt_header).to_slice(1).to_unsafe_bytes) | ||
raise Error.new("Invalid PE header") unless nt_header.signature == 0x00004550 # PE\0\0 | ||
|
||
@original_image_base = nt_header.optionalHeader.imageBase | ||
@string_table_base = nt_header.fileHeader.pointerToSymbolTable + nt_header.fileHeader.numberOfSymbols * sizeof(LibC::IMAGE_SYMBOL) | ||
|
||
section_count = nt_header.fileHeader.numberOfSections | ||
nt_section_headers = Pointer(LibC::IMAGE_SECTION_HEADER).malloc(section_count).to_slice(section_count) | ||
io.read_fully(nt_section_headers.to_unsafe_bytes) | ||
|
||
@section_headers = nt_section_headers.map do |nt_header| | ||
name_buf = nt_header.name.to_slice | ||
while name_buf.last?.try(&.zero?) | ||
name_buf = name_buf[0, name_buf.size - 1] | ||
end | ||
name = String.new(name_buf) | ||
|
||
if name.starts_with?('/') | ||
io.seek(@string_table_base + name[1..].to_i) | ||
name = io.gets('\0', chomp: true).not_nil! | ||
end | ||
|
||
SectionHeader.new(name: name, virtual_offset: nt_header.virtualAddress, offset: nt_header.pointerToRawData, size: nt_header.virtualSize) | ||
end | ||
|
||
io.seek(nt_header.fileHeader.pointerToSymbolTable) | ||
image_symbol_count = nt_header.fileHeader.numberOfSymbols | ||
image_symbols = Pointer(LibC::IMAGE_SYMBOL).malloc(image_symbol_count).to_slice(image_symbol_count) | ||
io.read_fully(image_symbols.to_unsafe_bytes) | ||
|
||
aux_count = 0 | ||
image_symbols.each_with_index do |sym, i| | ||
if aux_count == 0 | ||
aux_count = sym.numberOfAuxSymbols.to_i | ||
else | ||
aux_count &-= 1 | ||
end | ||
|
||
next unless aux_count == 0 | ||
next unless sym.type.bits_set?(0x20) # COFF function | ||
next unless sym.sectionNumber > 0 # one-based index | ||
next unless sym.storageClass.in?(LibC::IMAGE_SYM_CLASS_EXTERNAL, LibC::IMAGE_SYM_CLASS_STATIC) | ||
|
||
if sym.n.name.short == 0 | ||
io.seek(@string_table_base + sym.n.name.long) | ||
name = io.gets('\0', chomp: true).not_nil! | ||
else | ||
name = String.new(sym.n.shortName.to_slice).rstrip('\0') | ||
end | ||
|
||
section_coff_symbols = @coff_symbols.put_if_absent(sym.sectionNumber.to_i &- 1) { [] of COFFSymbol } | ||
section_coff_symbols << COFFSymbol.new(sym.value, name) | ||
end | ||
|
||
@coff_symbols.each_with_index do |(_, symbols), i| | ||
symbols.sort_by!(&.offset) | ||
symbols << COFFSymbol.new(@section_headers[i].size, "??") | ||
end | ||
end | ||
|
||
def read_section?(name : String, &) | ||
if sh = @section_headers.find(&.name.== name) | ||
@io.seek(sh.offset) do | ||
yield sh, @io | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.