1 # SPDX-License-Identifier: BSD-3-Clause
2 # Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
9 # Names up to this length are stored immediately in symbol table entries.
12 # Special "section numbers" changing the meaning of symbol table entry.
18 class CoffFileHeader(ctypes.LittleEndianStructure):
21 ("magic", ctypes.c_uint16),
22 ("section_count", ctypes.c_uint16),
23 ("timestamp", ctypes.c_uint32),
24 ("symbol_table_offset", ctypes.c_uint32),
25 ("symbol_count", ctypes.c_uint32),
26 ("optional_header_size", ctypes.c_uint16),
27 ("flags", ctypes.c_uint16),
31 class CoffName(ctypes.Union):
32 class Reference(ctypes.LittleEndianStructure):
35 ("zeroes", ctypes.c_uint32),
36 ("offset", ctypes.c_uint32),
39 Immediate = ctypes.c_char * 8
43 ("immediate", Immediate),
44 ("reference", Reference),
48 class CoffSection(ctypes.LittleEndianStructure):
52 ("physical_address", ctypes.c_uint32),
53 ("physical_address", ctypes.c_uint32),
54 ("size", ctypes.c_uint32),
55 ("data_offset", ctypes.c_uint32),
56 ("relocations_offset", ctypes.c_uint32),
57 ("line_numbers_offset", ctypes.c_uint32),
58 ("relocation_count", ctypes.c_uint16),
59 ("line_number_count", ctypes.c_uint16),
60 ("flags", ctypes.c_uint32),
64 class CoffSymbol(ctypes.LittleEndianStructure):
68 ("value", ctypes.c_uint32),
69 ("section_number", ctypes.c_int16),
70 ("type", ctypes.c_uint16),
71 ("storage_class", ctypes.c_uint8),
72 ("auxiliary_count", ctypes.c_uint8),
77 def __init__(self, image, symbol: CoffSymbol):
83 if self._coff.name.reference.zeroes:
84 return decode_asciiz(bytes(self._coff.name.immediate))
86 offset = self._coff.name.reference.offset
87 offset -= ctypes.sizeof(ctypes.c_uint32)
88 return self._image.get_string(offset)
90 def get_value(self, offset):
91 section_number = self._coff.section_number
93 if section_number == COFF_SN_UNDEFINED:
96 if section_number == COFF_SN_DEBUG:
99 if section_number == COFF_SN_ABSOLUTE:
100 return bytes(ctypes.c_uint32(self._coff.value))
102 section_data = self._image.get_section_data(section_number)
103 section_offset = self._coff.value + offset
104 return section_data[section_offset:]
108 def __init__(self, data):
109 header = CoffFileHeader.from_buffer_copy(data)
110 header_size = ctypes.sizeof(header) + header.optional_header_size
112 sections_desc = CoffSection * header.section_count
113 sections = sections_desc.from_buffer_copy(data, header_size)
115 symbols_desc = CoffSymbol * header.symbol_count
116 symbols = symbols_desc.from_buffer_copy(data, header.symbol_table_offset)
118 strings_offset = header.symbol_table_offset + ctypes.sizeof(symbols)
119 strings = Image._parse_strings(data[strings_offset:])
122 self._header = header
123 self._sections = sections
124 self._symbols = symbols
125 self._strings = strings
128 def _parse_strings(data):
129 full_size = ctypes.c_uint32.from_buffer_copy(data)
130 header_size = ctypes.sizeof(full_size)
131 return data[header_size : full_size.value]
136 while i < self._header.symbol_count:
137 symbol = self._symbols[i]
138 yield Symbol(self, symbol)
139 i += symbol.auxiliary_count + 1
141 def get_section_data(self, number):
142 # section numbers are 1-based
143 section = self._sections[number - 1]
144 base = section.data_offset
145 return self._data[base : base + section.size]
147 def get_string(self, offset):
148 return decode_asciiz(self._strings[offset:])
151 def decode_asciiz(data):
152 index = data.find(b'\x00')
153 end = index if index >= 0 else len(data)
154 return data[:end].decode()