1#!/usr/bin/env python3 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright (c) 2016 Neil Horman <nhorman@tuxdriver.com> 4# Copyright (c) 2020 Dmitry Kozlyuk <dmitry.kozliuk@gmail.com> 5 6import argparse 7import ctypes 8import json 9import sys 10import tempfile 11 12try: 13 import elftools 14 from elftools.elf.elffile import ELFFile 15 from elftools.elf.sections import SymbolTableSection 16except ImportError: 17 pass 18 19import coff 20 21 22class ELFSymbol: 23 def __init__(self, image, symbol): 24 self._image = image 25 self._symbol = symbol 26 27 @property 28 def string_value(self): 29 size = self._symbol["st_size"] 30 value = self.get_value(0, size) 31 return coff.decode_asciiz(value) # not COFF-specific 32 33 def get_value(self, offset, size): 34 section = self._symbol["st_shndx"] 35 data = self._image.get_section(section).data() 36 base = self._symbol["st_value"] + offset 37 return data[base : base + size] 38 39 40class ELFImage: 41 def __init__(self, data): 42 version = tuple(int(c) for c in elftools.__version__.split(".")) 43 self._legacy_elftools = version < (0, 24) 44 45 self._image = ELFFile(data) 46 47 section = b".symtab" if self._legacy_elftools else ".symtab" 48 self._symtab = self._image.get_section_by_name(section) 49 if not isinstance(self._symtab, SymbolTableSection): 50 raise Exception(".symtab section is not a symbol table") 51 52 @property 53 def is_big_endian(self): 54 return not self._image.little_endian 55 56 def find_by_name(self, name): 57 symbol = self._get_symbol_by_name(name) 58 return ELFSymbol(self._image, symbol[0]) if symbol else None 59 60 def _get_symbol_by_name(self, name): 61 if not self._legacy_elftools: 62 return self._symtab.get_symbol_by_name(name) 63 name = name.encode("utf-8") 64 for symbol in self._symtab.iter_symbols(): 65 if symbol.name == name: 66 return [symbol] 67 return None 68 69 def find_by_prefix(self, prefix): 70 prefix = prefix.encode("utf-8") if self._legacy_elftools else prefix 71 for i in range(self._symtab.num_symbols()): 72 symbol = self._symtab.get_symbol(i) 73 if symbol.name.startswith(prefix): 74 yield ELFSymbol(self._image, symbol) 75 76 77class COFFSymbol: 78 def __init__(self, image, symbol): 79 self._image = image 80 self._symbol = symbol 81 82 def get_value(self, offset, size): 83 value = self._symbol.get_value(offset) 84 return value[:size] if value else value 85 86 @property 87 def string_value(self): 88 value = self._symbol.get_value(0) 89 return coff.decode_asciiz(value) if value else '' 90 91 92class COFFImage: 93 def __init__(self, data): 94 self._image = coff.Image(data) 95 96 @property 97 def is_big_endian(self): 98 return False 99 100 def find_by_prefix(self, prefix): 101 for symbol in self._image.symbols: 102 if symbol.name.startswith(prefix): 103 yield COFFSymbol(self._image, symbol) 104 105 def find_by_name(self, name): 106 for symbol in self._image.symbols: 107 if symbol.name == name: 108 return COFFSymbol(self._image, symbol) 109 return None 110 111 112def define_rte_pci_id(is_big_endian): 113 base_type = ctypes.LittleEndianStructure 114 if is_big_endian: 115 base_type = ctypes.BigEndianStructure 116 117 class rte_pci_id(base_type): 118 _pack_ = True 119 _fields_ = [ 120 ("class_id", ctypes.c_uint32), 121 ("vendor_id", ctypes.c_uint16), 122 ("device_id", ctypes.c_uint16), 123 ("subsystem_vendor_id", ctypes.c_uint16), 124 ("subsystem_device_id", ctypes.c_uint16), 125 ] 126 127 return rte_pci_id 128 129 130class Driver: 131 OPTIONS = [ 132 ("params", "_param_string_export"), 133 ("kmod", "_kmod_dep_export"), 134 ] 135 136 def __init__(self, name, options): 137 self.name = name 138 for key, value in options.items(): 139 setattr(self, key, value) 140 self.pci_ids = [] 141 142 @classmethod 143 def load(cls, image, symbol): 144 name = symbol.string_value 145 146 options = {} 147 for key, suffix in cls.OPTIONS: 148 option_symbol = image.find_by_name("__%s%s" % (name, suffix)) 149 if option_symbol: 150 value = option_symbol.string_value 151 options[key] = value 152 153 driver = cls(name, options) 154 155 pci_table_name_symbol = image.find_by_name("__%s_pci_tbl_export" % name) 156 if pci_table_name_symbol: 157 driver.pci_ids = cls._load_pci_ids(image, pci_table_name_symbol) 158 159 return driver 160 161 @staticmethod 162 def _load_pci_ids(image, table_name_symbol): 163 table_name = table_name_symbol.string_value 164 table_symbol = image.find_by_name(table_name) 165 if not table_symbol: 166 raise Exception("PCI table declared but not defined: %d" % table_name) 167 168 rte_pci_id = define_rte_pci_id(image.is_big_endian) 169 170 result = [] 171 while True: 172 size = ctypes.sizeof(rte_pci_id) 173 offset = size * len(result) 174 data = table_symbol.get_value(offset, size) 175 if not data: 176 break 177 pci_id = rte_pci_id.from_buffer_copy(data) 178 if not pci_id.device_id: 179 break 180 result.append( 181 [ 182 pci_id.vendor_id, 183 pci_id.device_id, 184 pci_id.subsystem_vendor_id, 185 pci_id.subsystem_device_id, 186 ] 187 ) 188 return result 189 190 def dump(self, file): 191 dumped = json.dumps(self.__dict__) 192 escaped = dumped.replace('"', '\\"') 193 print( 194 'const char %s_pmd_info[] __attribute__((used)) = "PMD_INFO_STRING= %s";' 195 % (self.name, escaped), 196 file=file, 197 ) 198 199 200def load_drivers(image): 201 drivers = [] 202 for symbol in image.find_by_prefix("this_pmd_name"): 203 drivers.append(Driver.load(image, symbol)) 204 return drivers 205 206 207def dump_drivers(drivers, file): 208 # Keep legacy order of definitions. 209 for driver in reversed(drivers): 210 driver.dump(file) 211 212 213def parse_args(): 214 parser = argparse.ArgumentParser() 215 parser.add_argument("format", help="object file format, 'elf' or 'coff'") 216 parser.add_argument( 217 "input", nargs='+', help="input object file path or '-' for stdin" 218 ) 219 parser.add_argument("output", help="output C file path or '-' for stdout") 220 return parser.parse_args() 221 222 223def open_input(path): 224 if path == "-": 225 temp = tempfile.TemporaryFile() 226 temp.write(sys.stdin.buffer.read()) 227 return temp 228 return open(path, "rb") 229 230 231def read_input(path): 232 if path == "-": 233 return sys.stdin.buffer.read() 234 with open(path, "rb") as file: 235 return file.read() 236 237 238def load_image(fmt, path): 239 if fmt == "elf": 240 return ELFImage(open_input(path)) 241 if fmt == "coff": 242 return COFFImage(read_input(path)) 243 raise Exception("unsupported object file format") 244 245 246def open_output(path): 247 if path == "-": 248 return sys.stdout 249 return open(path, "w") 250 251 252def write_header(output): 253 output.write( 254 "static __attribute__((unused)) const char *generator = \"%s\";\n" % sys.argv[0] 255 ) 256 257 258def main(): 259 args = parse_args() 260 if args.input.count('-') > 1: 261 raise Exception("'-' input cannot be used multiple times") 262 if args.format == "elf" and "ELFFile" not in globals(): 263 raise Exception("elftools module not found") 264 265 output = open_output(args.output) 266 write_header(output) 267 for path in args.input: 268 image = load_image(args.format, path) 269 drivers = load_drivers(image) 270 dump_drivers(drivers, output) 271 272 273if __name__ == "__main__": 274 main() 275