1*5031436fSDmitry Kozlyuk# SPDX-License-Identifier: BSD-3-Clause 2*5031436fSDmitry Kozlyuk# Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com> 3*5031436fSDmitry Kozlyuk 4*5031436fSDmitry Kozlyukimport ctypes 5*5031436fSDmitry Kozlyuk 6*5031436fSDmitry Kozlyuk# x86_64 little-endian 7*5031436fSDmitry KozlyukCOFF_MAGIC = 0x8664 8*5031436fSDmitry Kozlyuk 9*5031436fSDmitry Kozlyuk# Names up to this length are stored immediately in symbol table entries. 10*5031436fSDmitry KozlyukCOFF_NAMELEN = 8 11*5031436fSDmitry Kozlyuk 12*5031436fSDmitry Kozlyuk# Special "section numbers" changing the meaning of symbol table entry. 13*5031436fSDmitry KozlyukCOFF_SN_UNDEFINED = 0 14*5031436fSDmitry KozlyukCOFF_SN_ABSOLUTE = -1 15*5031436fSDmitry KozlyukCOFF_SN_DEBUG = -2 16*5031436fSDmitry Kozlyuk 17*5031436fSDmitry Kozlyuk 18*5031436fSDmitry Kozlyukclass CoffFileHeader(ctypes.LittleEndianStructure): 19*5031436fSDmitry Kozlyuk _pack_ = True 20*5031436fSDmitry Kozlyuk _fields_ = [ 21*5031436fSDmitry Kozlyuk ("magic", ctypes.c_uint16), 22*5031436fSDmitry Kozlyuk ("section_count", ctypes.c_uint16), 23*5031436fSDmitry Kozlyuk ("timestamp", ctypes.c_uint32), 24*5031436fSDmitry Kozlyuk ("symbol_table_offset", ctypes.c_uint32), 25*5031436fSDmitry Kozlyuk ("symbol_count", ctypes.c_uint32), 26*5031436fSDmitry Kozlyuk ("optional_header_size", ctypes.c_uint16), 27*5031436fSDmitry Kozlyuk ("flags", ctypes.c_uint16), 28*5031436fSDmitry Kozlyuk ] 29*5031436fSDmitry Kozlyuk 30*5031436fSDmitry Kozlyuk 31*5031436fSDmitry Kozlyukclass CoffName(ctypes.Union): 32*5031436fSDmitry Kozlyuk class Reference(ctypes.LittleEndianStructure): 33*5031436fSDmitry Kozlyuk _pack_ = True 34*5031436fSDmitry Kozlyuk _fields_ = [ 35*5031436fSDmitry Kozlyuk ("zeroes", ctypes.c_uint32), 36*5031436fSDmitry Kozlyuk ("offset", ctypes.c_uint32), 37*5031436fSDmitry Kozlyuk ] 38*5031436fSDmitry Kozlyuk 39*5031436fSDmitry Kozlyuk Immediate = ctypes.c_char * 8 40*5031436fSDmitry Kozlyuk 41*5031436fSDmitry Kozlyuk _pack_ = True 42*5031436fSDmitry Kozlyuk _fields_ = [ 43*5031436fSDmitry Kozlyuk ("immediate", Immediate), 44*5031436fSDmitry Kozlyuk ("reference", Reference), 45*5031436fSDmitry Kozlyuk ] 46*5031436fSDmitry Kozlyuk 47*5031436fSDmitry Kozlyuk 48*5031436fSDmitry Kozlyukclass CoffSection(ctypes.LittleEndianStructure): 49*5031436fSDmitry Kozlyuk _pack_ = True 50*5031436fSDmitry Kozlyuk _fields_ = [ 51*5031436fSDmitry Kozlyuk ("name", CoffName), 52*5031436fSDmitry Kozlyuk ("physical_address", ctypes.c_uint32), 53*5031436fSDmitry Kozlyuk ("physical_address", ctypes.c_uint32), 54*5031436fSDmitry Kozlyuk ("size", ctypes.c_uint32), 55*5031436fSDmitry Kozlyuk ("data_offset", ctypes.c_uint32), 56*5031436fSDmitry Kozlyuk ("relocations_offset", ctypes.c_uint32), 57*5031436fSDmitry Kozlyuk ("line_numbers_offset", ctypes.c_uint32), 58*5031436fSDmitry Kozlyuk ("relocation_count", ctypes.c_uint16), 59*5031436fSDmitry Kozlyuk ("line_number_count", ctypes.c_uint16), 60*5031436fSDmitry Kozlyuk ("flags", ctypes.c_uint32), 61*5031436fSDmitry Kozlyuk ] 62*5031436fSDmitry Kozlyuk 63*5031436fSDmitry Kozlyuk 64*5031436fSDmitry Kozlyukclass CoffSymbol(ctypes.LittleEndianStructure): 65*5031436fSDmitry Kozlyuk _pack_ = True 66*5031436fSDmitry Kozlyuk _fields_ = [ 67*5031436fSDmitry Kozlyuk ("name", CoffName), 68*5031436fSDmitry Kozlyuk ("value", ctypes.c_uint32), 69*5031436fSDmitry Kozlyuk ("section_number", ctypes.c_int16), 70*5031436fSDmitry Kozlyuk ("type", ctypes.c_uint16), 71*5031436fSDmitry Kozlyuk ("storage_class", ctypes.c_uint8), 72*5031436fSDmitry Kozlyuk ("auxiliary_count", ctypes.c_uint8), 73*5031436fSDmitry Kozlyuk ] 74*5031436fSDmitry Kozlyuk 75*5031436fSDmitry Kozlyuk 76*5031436fSDmitry Kozlyukclass Symbol: 77*5031436fSDmitry Kozlyuk def __init__(self, image, symbol: CoffSymbol): 78*5031436fSDmitry Kozlyuk self._image = image 79*5031436fSDmitry Kozlyuk self._coff = symbol 80*5031436fSDmitry Kozlyuk 81*5031436fSDmitry Kozlyuk @property 82*5031436fSDmitry Kozlyuk def name(self): 83*5031436fSDmitry Kozlyuk if self._coff.name.reference.zeroes: 84*5031436fSDmitry Kozlyuk return decode_asciiz(bytes(self._coff.name.immediate)) 85*5031436fSDmitry Kozlyuk 86*5031436fSDmitry Kozlyuk offset = self._coff.name.reference.offset 87*5031436fSDmitry Kozlyuk offset -= ctypes.sizeof(ctypes.c_uint32) 88*5031436fSDmitry Kozlyuk return self._image.get_string(offset) 89*5031436fSDmitry Kozlyuk 90*5031436fSDmitry Kozlyuk def get_value(self, offset): 91*5031436fSDmitry Kozlyuk section_number = self._coff.section_number 92*5031436fSDmitry Kozlyuk 93*5031436fSDmitry Kozlyuk if section_number == COFF_SN_UNDEFINED: 94*5031436fSDmitry Kozlyuk return None 95*5031436fSDmitry Kozlyuk 96*5031436fSDmitry Kozlyuk if section_number == COFF_SN_DEBUG: 97*5031436fSDmitry Kozlyuk return None 98*5031436fSDmitry Kozlyuk 99*5031436fSDmitry Kozlyuk if section_number == COFF_SN_ABSOLUTE: 100*5031436fSDmitry Kozlyuk return bytes(ctypes.c_uint32(self._coff.value)) 101*5031436fSDmitry Kozlyuk 102*5031436fSDmitry Kozlyuk section_data = self._image.get_section_data(section_number) 103*5031436fSDmitry Kozlyuk section_offset = self._coff.value + offset 104*5031436fSDmitry Kozlyuk return section_data[section_offset:] 105*5031436fSDmitry Kozlyuk 106*5031436fSDmitry Kozlyuk 107*5031436fSDmitry Kozlyukclass Image: 108*5031436fSDmitry Kozlyuk def __init__(self, data): 109*5031436fSDmitry Kozlyuk header = CoffFileHeader.from_buffer_copy(data) 110*5031436fSDmitry Kozlyuk header_size = ctypes.sizeof(header) + header.optional_header_size 111*5031436fSDmitry Kozlyuk 112*5031436fSDmitry Kozlyuk sections_desc = CoffSection * header.section_count 113*5031436fSDmitry Kozlyuk sections = sections_desc.from_buffer_copy(data, header_size) 114*5031436fSDmitry Kozlyuk 115*5031436fSDmitry Kozlyuk symbols_desc = CoffSymbol * header.symbol_count 116*5031436fSDmitry Kozlyuk symbols = symbols_desc.from_buffer_copy(data, header.symbol_table_offset) 117*5031436fSDmitry Kozlyuk 118*5031436fSDmitry Kozlyuk strings_offset = header.symbol_table_offset + ctypes.sizeof(symbols) 119*5031436fSDmitry Kozlyuk strings = Image._parse_strings(data[strings_offset:]) 120*5031436fSDmitry Kozlyuk 121*5031436fSDmitry Kozlyuk self._data = data 122*5031436fSDmitry Kozlyuk self._header = header 123*5031436fSDmitry Kozlyuk self._sections = sections 124*5031436fSDmitry Kozlyuk self._symbols = symbols 125*5031436fSDmitry Kozlyuk self._strings = strings 126*5031436fSDmitry Kozlyuk 127*5031436fSDmitry Kozlyuk @staticmethod 128*5031436fSDmitry Kozlyuk def _parse_strings(data): 129*5031436fSDmitry Kozlyuk full_size = ctypes.c_uint32.from_buffer_copy(data) 130*5031436fSDmitry Kozlyuk header_size = ctypes.sizeof(full_size) 131*5031436fSDmitry Kozlyuk return data[header_size : full_size.value] 132*5031436fSDmitry Kozlyuk 133*5031436fSDmitry Kozlyuk @property 134*5031436fSDmitry Kozlyuk def symbols(self): 135*5031436fSDmitry Kozlyuk i = 0 136*5031436fSDmitry Kozlyuk while i < self._header.symbol_count: 137*5031436fSDmitry Kozlyuk symbol = self._symbols[i] 138*5031436fSDmitry Kozlyuk yield Symbol(self, symbol) 139*5031436fSDmitry Kozlyuk i += symbol.auxiliary_count + 1 140*5031436fSDmitry Kozlyuk 141*5031436fSDmitry Kozlyuk def get_section_data(self, number): 142*5031436fSDmitry Kozlyuk # section numbers are 1-based 143*5031436fSDmitry Kozlyuk section = self._sections[number - 1] 144*5031436fSDmitry Kozlyuk base = section.data_offset 145*5031436fSDmitry Kozlyuk return self._data[base : base + section.size] 146*5031436fSDmitry Kozlyuk 147*5031436fSDmitry Kozlyuk def get_string(self, offset): 148*5031436fSDmitry Kozlyuk return decode_asciiz(self._strings[offset:]) 149*5031436fSDmitry Kozlyuk 150*5031436fSDmitry Kozlyuk 151*5031436fSDmitry Kozlyukdef decode_asciiz(data): 152*5031436fSDmitry Kozlyuk index = data.find(b'\x00') 153*5031436fSDmitry Kozlyuk end = index if index >= 0 else len(data) 154*5031436fSDmitry Kozlyuk return data[:end].decode() 155