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