dpdk/buildtools/coff.py
<<
>>
Prefs
   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