5031436f45
Common Object File Format (COFF) is used on Windows in place of ELF. Add COFF parser to pmdinfogen. Also add an argument to specify input file format, which is selected at configure time based on the target. Signed-off-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
155 lines
4.4 KiB
Python
155 lines
4.4 KiB
Python
# SPDX-License-Identifier: BSD-3-Clause
|
|
# Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
|
|
|
|
import ctypes
|
|
|
|
# x86_64 little-endian
|
|
COFF_MAGIC = 0x8664
|
|
|
|
# Names up to this length are stored immediately in symbol table entries.
|
|
COFF_NAMELEN = 8
|
|
|
|
# Special "section numbers" changing the meaning of symbol table entry.
|
|
COFF_SN_UNDEFINED = 0
|
|
COFF_SN_ABSOLUTE = -1
|
|
COFF_SN_DEBUG = -2
|
|
|
|
|
|
class CoffFileHeader(ctypes.LittleEndianStructure):
|
|
_pack_ = True
|
|
_fields_ = [
|
|
("magic", ctypes.c_uint16),
|
|
("section_count", ctypes.c_uint16),
|
|
("timestamp", ctypes.c_uint32),
|
|
("symbol_table_offset", ctypes.c_uint32),
|
|
("symbol_count", ctypes.c_uint32),
|
|
("optional_header_size", ctypes.c_uint16),
|
|
("flags", ctypes.c_uint16),
|
|
]
|
|
|
|
|
|
class CoffName(ctypes.Union):
|
|
class Reference(ctypes.LittleEndianStructure):
|
|
_pack_ = True
|
|
_fields_ = [
|
|
("zeroes", ctypes.c_uint32),
|
|
("offset", ctypes.c_uint32),
|
|
]
|
|
|
|
Immediate = ctypes.c_char * 8
|
|
|
|
_pack_ = True
|
|
_fields_ = [
|
|
("immediate", Immediate),
|
|
("reference", Reference),
|
|
]
|
|
|
|
|
|
class CoffSection(ctypes.LittleEndianStructure):
|
|
_pack_ = True
|
|
_fields_ = [
|
|
("name", CoffName),
|
|
("physical_address", ctypes.c_uint32),
|
|
("physical_address", ctypes.c_uint32),
|
|
("size", ctypes.c_uint32),
|
|
("data_offset", ctypes.c_uint32),
|
|
("relocations_offset", ctypes.c_uint32),
|
|
("line_numbers_offset", ctypes.c_uint32),
|
|
("relocation_count", ctypes.c_uint16),
|
|
("line_number_count", ctypes.c_uint16),
|
|
("flags", ctypes.c_uint32),
|
|
]
|
|
|
|
|
|
class CoffSymbol(ctypes.LittleEndianStructure):
|
|
_pack_ = True
|
|
_fields_ = [
|
|
("name", CoffName),
|
|
("value", ctypes.c_uint32),
|
|
("section_number", ctypes.c_int16),
|
|
("type", ctypes.c_uint16),
|
|
("storage_class", ctypes.c_uint8),
|
|
("auxiliary_count", ctypes.c_uint8),
|
|
]
|
|
|
|
|
|
class Symbol:
|
|
def __init__(self, image, symbol: CoffSymbol):
|
|
self._image = image
|
|
self._coff = symbol
|
|
|
|
@property
|
|
def name(self):
|
|
if self._coff.name.reference.zeroes:
|
|
return decode_asciiz(bytes(self._coff.name.immediate))
|
|
|
|
offset = self._coff.name.reference.offset
|
|
offset -= ctypes.sizeof(ctypes.c_uint32)
|
|
return self._image.get_string(offset)
|
|
|
|
def get_value(self, offset):
|
|
section_number = self._coff.section_number
|
|
|
|
if section_number == COFF_SN_UNDEFINED:
|
|
return None
|
|
|
|
if section_number == COFF_SN_DEBUG:
|
|
return None
|
|
|
|
if section_number == COFF_SN_ABSOLUTE:
|
|
return bytes(ctypes.c_uint32(self._coff.value))
|
|
|
|
section_data = self._image.get_section_data(section_number)
|
|
section_offset = self._coff.value + offset
|
|
return section_data[section_offset:]
|
|
|
|
|
|
class Image:
|
|
def __init__(self, data):
|
|
header = CoffFileHeader.from_buffer_copy(data)
|
|
header_size = ctypes.sizeof(header) + header.optional_header_size
|
|
|
|
sections_desc = CoffSection * header.section_count
|
|
sections = sections_desc.from_buffer_copy(data, header_size)
|
|
|
|
symbols_desc = CoffSymbol * header.symbol_count
|
|
symbols = symbols_desc.from_buffer_copy(data, header.symbol_table_offset)
|
|
|
|
strings_offset = header.symbol_table_offset + ctypes.sizeof(symbols)
|
|
strings = Image._parse_strings(data[strings_offset:])
|
|
|
|
self._data = data
|
|
self._header = header
|
|
self._sections = sections
|
|
self._symbols = symbols
|
|
self._strings = strings
|
|
|
|
@staticmethod
|
|
def _parse_strings(data):
|
|
full_size = ctypes.c_uint32.from_buffer_copy(data)
|
|
header_size = ctypes.sizeof(full_size)
|
|
return data[header_size : full_size.value]
|
|
|
|
@property
|
|
def symbols(self):
|
|
i = 0
|
|
while i < self._header.symbol_count:
|
|
symbol = self._symbols[i]
|
|
yield Symbol(self, symbol)
|
|
i += symbol.auxiliary_count + 1
|
|
|
|
def get_section_data(self, number):
|
|
# section numbers are 1-based
|
|
section = self._sections[number - 1]
|
|
base = section.data_offset
|
|
return self._data[base : base + section.size]
|
|
|
|
def get_string(self, offset):
|
|
return decode_asciiz(self._strings[offset:])
|
|
|
|
|
|
def decode_asciiz(data):
|
|
index = data.find(b'\x00')
|
|
end = index if index >= 0 else len(data)
|
|
return data[:end].decode()
|