new file mode 100644
@@ -0,0 +1,194 @@
+import ctypes
+
+
+class ElfIdent(ctypes.Structure):
+ _pack_ = True
+ _fields_ = [
+ ("magic", ctypes.c_char * 4),
+ ("class_", ctypes.c_uint8),
+ ("data", ctypes.c_uint8),
+ ("version", ctypes.c_uint8),
+ ("os_abi", ctypes.c_uint8),
+ ("abi_version", ctypes.c_uint8),
+ ("pad", ctypes.c_uint8 * 7),
+ ]
+
+ @property
+ def is_magic_ok(self):
+ return self.magic.value == b"\x7fELF"
+
+ @property
+ def is_32bit(self):
+ return self.class_ == 1
+
+ @property
+ def is_big_endian(self):
+ return self.data == 2
+
+ def define_structures(self):
+ base_type = ctypes.LittleEndianStructure
+ if self.is_big_endian:
+ base = ctypes.BigEndianStructure
+
+ size_type = ctypes.c_uint64
+ if self.is_32bit:
+ size_type = ctypes.c_uint32
+
+ class FileHeader(base_type):
+ _pack_ = True
+ _fields_ = [
+ ("e_ident", ElfIdent),
+ ("e_type", ctypes.c_uint16),
+ ("e_machine", ctypes.c_uint16),
+ ("e_version", ctypes.c_uint32),
+ ("e_entry", size_type),
+ ("e_phoff", size_type),
+ ("e_shoff", size_type),
+ ("e_flags", ctypes.c_uint32),
+ ("e_ehsize", ctypes.c_uint16),
+ ("e_phentsize", ctypes.c_uint16),
+ ("e_phnum", ctypes.c_uint16),
+ ("e_shentsize", ctypes.c_uint16),
+ ("e_shnum", ctypes.c_uint16),
+ ("e_shstrndx", ctypes.c_uint16),
+ ]
+
+ class SectionHeader(base_type):
+ _pack_ = True
+ _fields_ = [
+ ("sh_name", ctypes.c_uint32),
+ ("sh_type", ctypes.c_uint32),
+ ("sh_flags", size_type),
+ ("sh_addr", size_type),
+ ("sh_offset", size_type),
+ ("sh_size", size_type),
+ ("sh_link", ctypes.c_uint32),
+ ("sh_info", ctypes.c_uint32),
+ ("sh_addralign", size_type),
+ ("sh_entsize", size_type),
+ ]
+
+ class Symbol32(base_type):
+ _pack_ = True
+ _fields_ = [
+ ("st_name", ctypes.c_uint32),
+ ("st_value", ctypes.c_uint32),
+ ("st_size", ctypes.c_uint32),
+ ("st_info", ctypes.c_uint8),
+ ("st_other", ctypes.c_uint8),
+ ("st_shndx", ctypes.c_uint16),
+ ]
+
+ class Symbol64(base_type):
+ _pack_ = True
+ _fields_ = [
+ ("st_name", ctypes.c_uint32),
+ ("st_info", ctypes.c_uint8),
+ ("st_other", ctypes.c_uint8),
+ ("st_shndx", ctypes.c_uint16),
+ ("st_value", ctypes.c_uint64),
+ ("st_size", ctypes.c_uint64),
+ ]
+
+ Symbol = Symbol32 if self.is_32bit else Symbol64
+
+ return FileHeader, SectionHeader, Symbol
+
+
+class Symbol:
+ def __init__(self, image, elf):
+ self._image = image
+ self._elf = elf
+
+
+ @property
+ def address(self):
+ base = self._image._sections[self._elf.st_shndx].sh_offset
+ offset = base + self._elf.st_value
+ memory = ctypes.c_char.from_buffer(self._image._data, offset)
+ return ctypes.addressof(memory)
+
+
+class Image:
+ def __init__(self, data):
+ SHN_UNDEF = 0x0000
+ SHN_XINDEX = 0xFFFF
+
+ ident = ElfIdent.from_buffer(data)
+ ElfFileHeader, ElfSectionHeader, ElfSymbol = ident.define_structures()
+
+ header = ElfFileHeader.from_buffer(data)
+
+ if header.e_shnum == SHN_UNDEF:
+ section = ElfSectionHeader.from_buffer(data, header.e_shoff)
+ sections_num = section.sh_size
+ else:
+ sections_num = header.e_shnum
+ sections_desc = ElfSectionHeader * sections_num
+ sections = sections_desc.from_buffer(data, header.e_shoff)
+
+ if header.e_shstrndx == SHN_XINDEX:
+ strings_index = sections[0].sh_link
+ else:
+ strings_index = header.e_shstrndx
+
+ symtab, strtab = Image._find_symbol_table(data, sections, ElfSymbol)
+
+ self._data = data
+ self._header = header
+ self._sections = sections
+ self._strings = sections[strings_index]
+ self._symtab = symtab
+ self._strtab = strtab
+
+
+ @staticmethod
+ def _find_symbol_table(data, sections, symbol_type):
+ SHT_SYMTAB = 2
+ SHT_SYMTAB_SHNDX = 18
+
+ for section in sections:
+ if section.sh_type == SHT_SYMTAB:
+ symbol_count = section.sh_size // ctypes.sizeof(symbol_type)
+ symtab_desc = symbol_type * symbol_count
+ symtab_offset = section.sh_offset
+ symtab = symtab_desc.from_buffer(data, symtab_offset)
+
+ strtab = sections[section.sh_link].sh_offset
+
+ return symtab, strtab
+
+ # TODO: SHT_SYMTAB_SHNDX?
+
+ raise Exception('no symbol table')
+
+
+ @property
+ def is_big_endian(self):
+ return self._header.e_ident.is_big_endian
+
+
+ def find_symbol(self, name: str, start: Symbol = None):
+ name_size = len(name)
+ name_desc = ctypes.c_char * name_size
+
+ i = 0
+ if start:
+ table_address = ctypes.addressof(self._symtab)
+ start_address = ctypes.addressof(start._elf)
+ i = (start_address - table_address) // ctypes.sizeof(start._elf) + 1
+
+ while i < len(self._symtab):
+ symbol = self._symtab[i]
+
+ name_offset = self._strtab + symbol.st_name
+ if name_offset + name_size > len(self._data):
+ break
+
+ symbol_name = name_desc.from_buffer(self._data, name_offset).value
+ if symbol_name == name.encode():
+ return Symbol(self, symbol)
+
+ i += 1
+
+ return None
new file mode 100755
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+
+import argparse
+import ctypes
+import elf
+import json
+import mmap
+import os
+import sys
+import tempfile
+
+
+def define_rte_pci_id(is_big_endian):
+ base_type = ctypes.LittleEndianStructure
+ if is_big_endian:
+ base_type = ctypes.BigEndianStructure
+
+ class rte_pci_id(base_type):
+ _pack_ = True
+ _fields_ = [
+ ("class_id", ctypes.c_uint32),
+ ("vendor_id", ctypes.c_uint16),
+ ("device_id", ctypes.c_uint16),
+ ("subsystem_vendor_id", ctypes.c_uint16),
+ ("subsystem_device_id", ctypes.c_uint16),
+ ]
+
+ return rte_pci_id
+
+
+class Driver:
+ OPTIONS = [
+ ("params", "_param_string_export"),
+ ("kmod", "_kmod_dep_export"),
+ ]
+
+ def __init__(self, name, options):
+ self.name = name
+ for key, value in options.items():
+ setattr(self, key, value)
+ self.pci_ids = []
+
+ @classmethod
+ def load(cls, image, symbol):
+ name = ctypes.string_at(symbol.address).decode()
+
+ options = {}
+ for key, suffix in cls.OPTIONS:
+ option_symbol = image.find_symbol(f"__{name}{suffix}")
+ if option_symbol:
+ value = ctypes.string_at(option_symbol.address).decode()
+ options[key] = value
+
+ driver = cls(name, options)
+
+ pci_table_name_symbol = image.find_symbol(f"__{name}_pci_tbl_export")
+ if not pci_table_name_symbol:
+ return driver
+
+ pci_table_name = ctypes.string_at(pci_table_name_symbol.address).decode()
+
+ pci_table_symbol = image.find_symbol(pci_table_name)
+ if not pci_table_symbol:
+ raise Exception('PCI table declared but not defined')
+
+ rte_pci_id = define_rte_pci_id(image.is_big_endian)
+
+ pci_id = rte_pci_id.from_address(pci_table_symbol.address)
+ while pci_id.device_id:
+ driver.pci_ids.append(
+ [
+ pci_id.vendor_id,
+ pci_id.device_id,
+ pci_id.subsystem_vendor_id,
+ pci_id.subsystem_device_id,
+ ]
+ )
+ pci_id = rte_pci_id.from_address(
+ ctypes.addressof(pci_id) + ctypes.sizeof(pci_id)
+ )
+
+ return driver
+
+ def dump(self, file):
+ dumped = json.dumps(self.__dict__)
+ escaped = dumped.replace('"', '\\"')
+ print(
+ f"const char {self.name}_pmd_info[] __attribute__((used)) = "
+ f'"PMD_INFO_STRING= {escaped}";',
+ file=file,
+ )
+
+
+def get_symbols_by_prefix(image, name):
+ symbol = image.find_symbol(name)
+ while symbol:
+ yield symbol
+ symbol = image.find_symbol(name, symbol)
+
+
+def load_drivers(image):
+ drivers = []
+ for symbol in get_symbols_by_prefix(image, "this_pmd_name"):
+ drivers.append(Driver.load(image, symbol))
+ return drivers
+
+
+def dump_drivers(drivers, file):
+ for driver in drivers:
+ driver.dump(file)
+
+
+def parse_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("input", help="input object file path")
+ parser.add_argument("output", help="output C file path")
+ return parser.parse_args()
+
+
+def map_input(path):
+ if path == '-':
+ fd, name = tempfile.mkstemp()
+ else:
+ fd = os.open(path, os.O_RDONLY)
+ return mmap.mmap(fd, 0, access=mmap.ACCESS_COPY)
+
+
+def open_output(path):
+ if path == '-':
+ return sys.stdout
+ return open(path, 'w')
+
+
+def main():
+ args = parse_args()
+ memory = map_input(args.input)
+ image = elf.Image(memory)
+ drivers = load_drivers(image)
+ output = open_output(args.output)
+ dump_drivers(drivers, output)
+
+
+if __name__ == "__main__":
+ main()