xref: /dpdk/buildtools/coff.py (revision 5031436f459e197da1fec087564026ec29d94227)
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
3
4import ctypes
5
6# x86_64 little-endian
7COFF_MAGIC = 0x8664
8
9# Names up to this length are stored immediately in symbol table entries.
10COFF_NAMELEN = 8
11
12# Special "section numbers" changing the meaning of symbol table entry.
13COFF_SN_UNDEFINED = 0
14COFF_SN_ABSOLUTE = -1
15COFF_SN_DEBUG = -2
16
17
18class CoffFileHeader(ctypes.LittleEndianStructure):
19    _pack_ = True
20    _fields_ = [
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),
28    ]
29
30
31class CoffName(ctypes.Union):
32    class Reference(ctypes.LittleEndianStructure):
33        _pack_ = True
34        _fields_ = [
35            ("zeroes", ctypes.c_uint32),
36            ("offset", ctypes.c_uint32),
37        ]
38
39    Immediate = ctypes.c_char * 8
40
41    _pack_ = True
42    _fields_ = [
43        ("immediate", Immediate),
44        ("reference", Reference),
45    ]
46
47
48class CoffSection(ctypes.LittleEndianStructure):
49    _pack_ = True
50    _fields_ = [
51        ("name", CoffName),
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),
61    ]
62
63
64class CoffSymbol(ctypes.LittleEndianStructure):
65    _pack_ = True
66    _fields_ = [
67        ("name", CoffName),
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),
73    ]
74
75
76class Symbol:
77    def __init__(self, image, symbol: CoffSymbol):
78        self._image = image
79        self._coff = symbol
80
81    @property
82    def name(self):
83        if self._coff.name.reference.zeroes:
84            return decode_asciiz(bytes(self._coff.name.immediate))
85
86        offset = self._coff.name.reference.offset
87        offset -= ctypes.sizeof(ctypes.c_uint32)
88        return self._image.get_string(offset)
89
90    def get_value(self, offset):
91        section_number = self._coff.section_number
92
93        if section_number == COFF_SN_UNDEFINED:
94            return None
95
96        if section_number == COFF_SN_DEBUG:
97            return None
98
99        if section_number == COFF_SN_ABSOLUTE:
100            return bytes(ctypes.c_uint32(self._coff.value))
101
102        section_data = self._image.get_section_data(section_number)
103        section_offset = self._coff.value + offset
104        return section_data[section_offset:]
105
106
107class Image:
108    def __init__(self, data):
109        header = CoffFileHeader.from_buffer_copy(data)
110        header_size = ctypes.sizeof(header) + header.optional_header_size
111
112        sections_desc = CoffSection * header.section_count
113        sections = sections_desc.from_buffer_copy(data, header_size)
114
115        symbols_desc = CoffSymbol * header.symbol_count
116        symbols = symbols_desc.from_buffer_copy(data, header.symbol_table_offset)
117
118        strings_offset = header.symbol_table_offset + ctypes.sizeof(symbols)
119        strings = Image._parse_strings(data[strings_offset:])
120
121        self._data = data
122        self._header = header
123        self._sections = sections
124        self._symbols = symbols
125        self._strings = strings
126
127    @staticmethod
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]
132
133    @property
134    def symbols(self):
135        i = 0
136        while i < self._header.symbol_count:
137            symbol = self._symbols[i]
138            yield Symbol(self, symbol)
139            i += symbol.auxiliary_count + 1
140
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]
146
147    def get_string(self, offset):
148        return decode_asciiz(self._strings[offset:])
149
150
151def decode_asciiz(data):
152    index = data.find(b'\x00')
153    end = index if index >= 0 else len(data)
154    return data[:end].decode()
155