buildtools: support COFF in pmdinfogen

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>
This commit is contained in:
Dmitry Kozlyuk 2021-01-08 05:47:20 +03:00 committed by Thomas Monjalon
parent dd2ea14772
commit 5031436f45
4 changed files with 249 additions and 30 deletions

View File

@ -134,6 +134,7 @@ F: lib/*/*.map
Driver information Driver information
M: Neil Horman <nhorman@tuxdriver.com> M: Neil Horman <nhorman@tuxdriver.com>
M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com> M: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
F: buildtools/coff.py
F: buildtools/pmdinfogen.py F: buildtools/pmdinfogen.py
F: usertools/dpdk-pmdinfo.py F: usertools/dpdk-pmdinfo.py
F: doc/guides/tools/pmdinfo.rst F: doc/guides/tools/pmdinfo.rst

154
buildtools/coff.py Normal file
View File

@ -0,0 +1,154 @@
# 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()

View File

@ -17,7 +17,14 @@ else
endif endif
map_to_win_cmd = py3 + files('map_to_win.py') map_to_win_cmd = py3 + files('map_to_win.py')
sphinx_wrapper = py3 + files('call-sphinx-build.py') sphinx_wrapper = py3 + files('call-sphinx-build.py')
# select object file format
pmdinfogen = py3 + files('pmdinfogen.py') pmdinfogen = py3 + files('pmdinfogen.py')
if host_machine.system() == 'windows'
pmdinfogen += 'coff'
else
pmdinfogen += 'elf'
endif
# TODO: starting from Meson 0.51.0 use # TODO: starting from Meson 0.51.0 use
# python3 = import('python').find_installation('python', # python3 = import('python').find_installation('python',

View File

@ -9,8 +9,13 @@
import sys import sys
import tempfile import tempfile
try:
from elftools.elf.elffile import ELFFile from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection from elftools.elf.sections import SymbolTableSection
except ImportError:
pass
import coff
class ELFSymbol: class ELFSymbol:
@ -18,21 +23,18 @@ def __init__(self, image, symbol):
self._image = image self._image = image
self._symbol = symbol self._symbol = symbol
@property
def size(self):
return self._symbol["st_size"]
@property
def value(self):
data = self._image.get_section_data(self._symbol["st_shndx"])
base = self._symbol["st_value"]
return data[base:base + self.size]
@property @property
def string_value(self): def string_value(self):
value = self.value size = self._symbol["st_size"]
value = self.get_value(0, size)
return value[:-1].decode() if value else "" return value[:-1].decode() if value else ""
def get_value(self, offset, size):
section = self._symbol["st_shndx"]
data = self._image.get_section(section).data()
base = self._symbol["st_value"] + offset
return data[base : base + size]
class ELFImage: class ELFImage:
def __init__(self, data): def __init__(self, data):
@ -45,18 +47,50 @@ def __init__(self, data):
def is_big_endian(self): def is_big_endian(self):
return not self._image.little_endian return not self._image.little_endian
def get_section_data(self, name):
return self._image.get_section(name).data()
def find_by_name(self, name): def find_by_name(self, name):
symbol = self._symtab.get_symbol_by_name(name) symbol = self._symtab.get_symbol_by_name(name)
return ELFSymbol(self, symbol[0]) if symbol else None return ELFSymbol(self._image, symbol[0]) if symbol else None
def find_by_prefix(self, prefix): def find_by_prefix(self, prefix):
for i in range(self._symtab.num_symbols()): for i in range(self._symtab.num_symbols()):
symbol = self._symtab.get_symbol(i) symbol = self._symtab.get_symbol(i)
if symbol.name.startswith(prefix): if symbol.name.startswith(prefix):
yield ELFSymbol(self, symbol) yield ELFSymbol(self._image, symbol)
class COFFSymbol:
def __init__(self, image, symbol):
self._image = image
self._symbol = symbol
def get_value(self, offset, size):
value = self._symbol.get_value(offset)
return value[:size] if value else value
@property
def string_value(self):
value = self._symbol.get_value(0)
return coff.decode_asciiz(value) if value else ''
class COFFImage:
def __init__(self, data):
self._image = coff.Image(data)
@property
def is_big_endian(self):
return False
def find_by_prefix(self, prefix):
for symbol in self._image.symbols:
if symbol.name.startswith(prefix):
yield COFFSymbol(self._image, symbol)
def find_by_name(self, name):
for symbol in self._image.symbols:
if symbol.name == name:
return COFFSymbol(self._image, symbol)
return None
def define_rte_pci_id(is_big_endian): def define_rte_pci_id(is_big_endian):
@ -117,19 +151,24 @@ def _load_pci_ids(image, table_name_symbol):
rte_pci_id = define_rte_pci_id(image.is_big_endian) rte_pci_id = define_rte_pci_id(image.is_big_endian)
pci_id_size = ctypes.sizeof(rte_pci_id)
pci_ids_desc = rte_pci_id * (table_symbol.size // pci_id_size)
pci_ids = pci_ids_desc.from_buffer_copy(table_symbol.value)
result = [] result = []
for pci_id in pci_ids: while True:
size = ctypes.sizeof(rte_pci_id)
offset = size * len(result)
data = table_symbol.get_value(offset, size)
if not data:
break
pci_id = rte_pci_id.from_buffer_copy(data)
if not pci_id.device_id: if not pci_id.device_id:
break break
result.append([ result.append(
[
pci_id.vendor_id, pci_id.vendor_id,
pci_id.device_id, pci_id.device_id,
pci_id.subsystem_vendor_id, pci_id.subsystem_vendor_id,
pci_id.subsystem_device_id, pci_id.subsystem_device_id,
]) ]
)
return result return result
def dump(self, file): def dump(self, file):
@ -157,6 +196,7 @@ def dump_drivers(drivers, file):
def parse_args(): def parse_args():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("format", help="object file format, 'elf' or 'coff'")
parser.add_argument("input", help="input object file path or '-' for stdin") parser.add_argument("input", help="input object file path or '-' for stdin")
parser.add_argument("output", help="output C file path or '-' for stdout") parser.add_argument("output", help="output C file path or '-' for stdout")
return parser.parse_args() return parser.parse_args()
@ -170,6 +210,21 @@ def open_input(path):
return open(path, "rb") return open(path, "rb")
def read_input(path):
if path == "-":
return sys.stdin.buffer.read()
with open(path, "rb") as file:
return file.read()
def load_image(fmt, path):
if fmt == "elf":
return ELFImage(open_input(path))
if fmt == "coff":
return COFFImage(read_input(path))
raise Exception("unsupported object file format")
def open_output(path): def open_output(path):
if path == "-": if path == "-":
return sys.stdout return sys.stdout
@ -178,8 +233,10 @@ def open_output(path):
def main(): def main():
args = parse_args() args = parse_args()
infile = open_input(args.input) if args.format == "elf" and "ELFFile" not in globals():
image = ELFImage(infile) raise Exception("elftools module not found")
image = load_image(args.format, args.input)
drivers = load_drivers(image) drivers = load_drivers(image)
output = open_output(args.output) output = open_output(args.output)
dump_drivers(drivers, output) dump_drivers(drivers, output)