2 # -------------------------------------------------------------------------
5 # Utility to dump PMD_INFO_STRING support from an object file
7 # -------------------------------------------------------------------------
10 from optparse import OptionParser
15 # For running from development directory. It should take precedence over the
16 # installed pyelftools.
17 sys.path.insert(0, '.')
20 from elftools import __version__
21 from elftools.common.exceptions import ELFError
22 from elftools.common.py3compat import (
23 ifilter, byte2int, bytes2str, itervalues, str2bytes)
24 from elftools.elf.elffile import ELFFile
25 from elftools.elf.dynamic import DynamicSection, DynamicSegment
26 from elftools.elf.enums import ENUM_D_TAG
27 from elftools.elf.segments import InterpSegment
28 from elftools.elf.sections import SymbolTableSection
29 from elftools.elf.gnuversions import (
30 GNUVerSymSection, GNUVerDefSection,
33 from elftools.elf.relocation import RelocationSection
34 from elftools.elf.descriptions import (
35 describe_ei_class, describe_ei_data, describe_ei_version,
36 describe_ei_osabi, describe_e_type, describe_e_machine,
37 describe_e_version_numeric, describe_p_type, describe_p_flags,
38 describe_sh_type, describe_sh_flags,
39 describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
40 describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
43 from elftools.elf.constants import E_FLAGS
44 from elftools.dwarf.dwarfinfo import DWARFInfo
45 from elftools.dwarf.descriptions import (
46 describe_reg_name, describe_attr_value, set_global_machine_arch,
47 describe_CFI_instructions, describe_CFI_register_rule,
48 describe_CFI_CFA_rule,
50 from elftools.dwarf.constants import (
51 DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
52 from elftools.dwarf.callframe import CIE, FDE
57 # ===========================================
62 Class for vendors. This is the top level class
63 for the devices belong to a specific vendor.
64 self.devices is the device dictionary
65 subdevices are in each device.
68 def __init__(self, vendorStr):
70 Class initializes with the raw line from pci.ids
71 Parsing takes place inside __init__
73 self.ID = vendorStr.split()[0]
74 self.name = vendorStr.replace("%s " % self.ID, "").rstrip()
77 def addDevice(self, deviceStr):
79 Adds a device to self.devices
80 takes the raw line from pci.ids
84 if devID in self.devices:
87 self.devices[devID] = Device(deviceStr)
90 print self.ID, self.name
91 for id, dev in self.devices.items():
94 def find_device(self, devid):
95 # convert to a hex string and remove 0x
96 devid = hex(devid)[2:]
98 return self.devices[devid]
100 return Device("%s Unknown Device" % devid)
105 def __init__(self, deviceStr):
107 Class for each device.
108 Each vendor has its own devices dictionary.
110 s = deviceStr.strip()
111 self.ID = s.split()[0]
112 self.name = s.replace("%s " % self.ID, "")
116 print "\t%s\t%s" % (self.ID, self.name)
117 for subID, subdev in self.subdevices.items():
120 def addSubDevice(self, subDeviceStr):
122 Adds a subvendor, subdevice to device.
123 Uses raw line from pci.ids
125 s = subDeviceStr.strip()
129 subDeviceName = s.split(" ")[-1]
130 devID = "%s:%s" % (subVendorID, subDeviceID)
131 self.subdevices[devID] = SubDevice(
132 subVendorID, subDeviceID, subDeviceName)
134 def find_subid(self, subven, subdev):
135 subven = hex(subven)[2:]
136 subdev = hex(subdev)[2:]
137 devid = "%s:%s" % (subven, subdev)
140 return self.subdevices[devid]
142 if (subven == "ffff" and subdev == "ffff"):
143 return SubDevice("ffff", "ffff", "(All Subdevices)")
145 return SubDevice(subven, subdev, "(Unknown Subdevice)")
150 Class for subdevices.
153 def __init__(self, vendor, device, name):
155 Class initializes with vendorid, deviceid and name
157 self.vendorID = vendor
158 self.deviceID = device
162 print "\t\t%s\t%s\t%s" % (self.vendorID, self.deviceID, self.name)
167 Top class for all pci.ids entries.
168 All queries will be asked to this class.
169 PCIIds.vendors["0e11"].devices["0046"].\
170 subdevices["0e11:4091"].name = "Smart Array 6i"
173 def __init__(self, filename):
175 Prepares the directories.
176 Checks local data file.
177 Tries to load from local, if not found, downloads from web
183 self.readLocal(filename)
186 def reportVendors(self):
187 """Reports the vendors
189 for vid, v in self.vendors.items():
192 def report(self, vendor=None):
194 Reports everything for all vendors or a specific vendor
195 PCIIds.report() reports everything
196 PCIIDs.report("0e11") reports only "Compaq Computer Corporation"
198 if vendor is not None:
199 self.vendors[vendor].report()
201 for vID, v in self.vendors.items():
204 def find_vendor(self, vid):
205 # convert vid to a hex string and remove the 0x
209 return self.vendors[vid]
211 return Vendor("%s Unknown Vendor" % (vid))
213 def findDate(self, content):
215 if l.find("Date:") > -1:
216 return l.split()[-2].replace("-", "")
220 if len(self.contents) < 1:
221 print "data/%s-pci.ids not found" % self.date
225 for l in self.contents:
228 elif len(l.strip()) == 0:
231 if l.find("\t\t") == 0:
232 self.vendors[vendorID].devices[
233 deviceID].addSubDevice(l)
234 elif l.find("\t") == 0:
235 deviceID = l.strip().split()[0]
236 self.vendors[vendorID].addDevice(l)
238 vendorID = l.split()[0]
239 self.vendors[vendorID] = Vendor(l)
241 def readLocal(self, filename):
245 self.contents = open(filename).readlines()
246 self.date = self.findDate(self.contents)
250 Loads database from local. If there is no file,
251 it creates a new one from web
253 self.date = idsfile[0].split("/")[1].split("-")[0]
257 # =======================================
259 def search_file(filename, search_path):
260 """ Given a search path, find file with requested name """
261 for path in string.split(search_path, ":"):
262 candidate = os.path.join(path, filename)
263 if os.path.exists(candidate):
264 return os.path.abspath(candidate)
268 class ReadElf(object):
269 """ display_* methods are used to emit output into the output stream
272 def __init__(self, file, output):
274 stream object with the ELF file to read
277 output stream to write to
279 self.elffile = ELFFile(file)
282 # Lazily initialized if a debug dump is requested
283 self._dwarfinfo = None
285 self._versioninfo = None
287 def _section_from_spec(self, spec):
288 """ Retrieve a section given a "spec" (either number or name).
289 Return None if no such section exists in the file.
293 if num < self.elffile.num_sections():
294 return self.elffile.get_section(num)
298 # Not a number. Must be a name then
299 return self.elffile.get_section_by_name(str2bytes(spec))
301 def pretty_print_pmdinfo(self, pmdinfo):
304 for i in pmdinfo["pci_ids"]:
305 vendor = pcidb.find_vendor(i[0])
306 device = vendor.find_device(i[1])
307 subdev = device.find_subid(i[2], i[3])
308 print("%s (%s) : %s (%s) %s" %
309 (vendor.name, vendor.ID, device.name,
310 device.ID, subdev.name))
312 def parse_pmd_info_string(self, mystring):
316 optional_pmd_info = [{'id': 'params', 'tag': 'PMD PARAMETERS'}]
318 i = mystring.index("=")
319 mystring = mystring[i + 2:]
320 pmdinfo = json.loads(mystring)
326 print("PMD NAME: " + pmdinfo["name"])
327 for i in optional_pmd_info:
329 print("%s: %s" % (i['tag'], pmdinfo[i['id']]))
330 except KeyError as e:
333 if (len(pmdinfo["pci_ids"]) != 0):
334 print("PMD HW SUPPORT:")
335 if pcidb is not None:
336 self.pretty_print_pmdinfo(pmdinfo)
338 print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE")
339 for i in pmdinfo["pci_ids"]:
340 print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" %
341 (i[0], i[1], i[2], i[3]))
345 def display_pmd_info_strings(self, section_spec):
346 """ Display a strings dump of a section. section_spec is either a
347 section number or a name.
349 section = self._section_from_spec(section_spec)
353 data = section.data()
356 while dataptr < len(data):
357 while (dataptr < len(data) and
358 not (32 <= byte2int(data[dataptr]) <= 127)):
361 if dataptr >= len(data):
365 while endptr < len(data) and byte2int(data[endptr]) != 0:
368 mystring = bytes2str(data[dataptr:endptr])
369 rc = mystring.find("PMD_INFO_STRING")
371 self.parse_pmd_info_string(mystring)
375 def find_librte_eal(self, section):
376 for tag in section.iter_tags():
377 if tag.entry.d_tag == 'DT_NEEDED':
378 if "librte_eal" in tag.needed:
382 def search_for_autoload_path(self):
387 section = self._section_from_spec(".dynamic")
389 eallib = self.find_librte_eal(section)
390 if eallib is not None:
391 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
392 if ldlibpath is None:
394 dtr = self.get_dt_runpath(section)
395 library = search_file(eallib,
396 dtr + ":" + ldlibpath +
397 ":/usr/lib64:/lib64:/usr/lib:/lib")
400 if raw_output is False:
401 print("Scanning for autoload path in %s" % library)
402 scanfile = open(library, 'rb')
403 scanelf = ReadElf(scanfile, sys.stdout)
404 except AttributeError:
405 # Not a dynamic binary
411 section = scanelf._section_from_spec(".rodata")
413 if scanfile is not None:
417 data = section.data()
420 while dataptr < len(data):
421 while (dataptr < len(data) and
422 not (32 <= byte2int(data[dataptr]) <= 127)):
425 if dataptr >= len(data):
429 while endptr < len(data) and byte2int(data[endptr]) != 0:
432 mystring = bytes2str(data[dataptr:endptr])
433 rc = mystring.find("DPDK_PLUGIN_PATH")
435 rc = mystring.find("=")
436 return (mystring[rc + 1:], library)
439 if scanfile is not None:
443 def get_dt_runpath(self, dynsec):
444 for tag in dynsec.iter_tags():
445 if tag.entry.d_tag == 'DT_RUNPATH':
449 def process_dt_needed_entries(self):
450 """ Look to see if there are any DT_NEEDED entries in the binary
451 And process those if there are
455 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
456 if ldlibpath is None:
459 dynsec = self._section_from_spec(".dynamic")
461 runpath = self.get_dt_runpath(dynsec)
462 except AttributeError:
463 # dynsec is None, just return
466 for tag in dynsec.iter_tags():
467 if tag.entry.d_tag == 'DT_NEEDED':
468 rc = tag.needed.find("librte_pmd")
470 library = search_file(tag.needed,
471 runpath + ":" + ldlibpath +
472 ":/usr/lib64:/lib64:/usr/lib:/lib")
473 if library is not None:
474 if raw_output is False:
475 print("Scanning %s for pmd information" % library)
476 with open(library, 'rb') as file:
478 libelf = ReadElf(file, sys.stdout)
479 except ELFError as e:
480 print("%s is no an ELF file" % library)
482 libelf.process_dt_needed_entries()
483 libelf.display_pmd_info_strings(".rodata")
487 def scan_autoload_path(autoload_path):
490 if os.path.exists(autoload_path) is False:
494 dirs = os.listdir(autoload_path)
496 # Couldn't read the directory, give up
500 dpath = os.path.join(autoload_path, d)
501 if os.path.isdir(dpath):
502 scan_autoload_path(dpath)
503 if os.path.isfile(dpath):
505 file = open(dpath, 'rb')
506 readelf = ReadElf(file, sys.stdout)
507 except ELFError as e:
508 # this is likely not an elf file, skip it
511 # No permission to read the file, skip it
514 if raw_output is False:
515 print("Hw Support for library %s" % d)
516 readelf.display_pmd_info_strings(".rodata")
520 def scan_for_autoload_pmds(dpdk_path):
522 search the specified application or path for a pmd autoload path
523 then scan said path for pmds and report hw support
527 if (os.path.isfile(dpdk_path) is False):
528 if raw_output is False:
529 print("Must specify a file name")
532 file = open(dpdk_path, 'rb')
534 readelf = ReadElf(file, sys.stdout)
535 except ElfError as e:
536 if raw_output is False:
537 print("Unable to parse %s" % file)
540 (autoload_path, scannedfile) = readelf.search_for_autoload_path()
541 if (autoload_path is None or autoload_path is ""):
542 if (raw_output is False):
543 print("No autoload path configured in %s" % dpdk_path)
545 if (raw_output is False):
546 if (scannedfile is None):
547 scannedfile = dpdk_path
548 print("Found autoload path %s in %s" % (autoload_path, scannedfile))
551 if (raw_output is False):
552 print("Discovered Autoload HW Support:")
553 scan_autoload_path(autoload_path)
557 def main(stream=None):
561 pcifile_default = "./pci.ids" # for unknown OS's assume local file
562 if platform.system() == 'Linux':
563 pcifile_default = "/usr/share/hwdata/pci.ids"
564 elif platform.system() == 'FreeBSD':
565 pcifile_default = "/usr/local/share/pciids/pci.ids"
566 if not os.path.exists(pcifile_default):
567 pcifile_default = "/usr/share/misc/pci_vendors"
569 optparser = OptionParser(
570 usage='usage: %prog [-hrtp] [-d <pci id file] <elf-file>',
571 description="Dump pmd hardware support info",
572 add_help_option=True,
574 optparser.add_option('-r', '--raw',
575 action='store_true', dest='raw_output',
576 help='Dump raw json strings')
577 optparser.add_option("-d", "--pcidb", dest="pcifile",
578 help="specify a pci database "
579 "to get vendor names from",
580 default=pcifile_default, metavar="FILE")
581 optparser.add_option("-t", "--table", dest="tblout",
582 help="output information on hw support as a hex table",
584 optparser.add_option("-p", "--plugindir", dest="pdir",
585 help="scan dpdk for autoload plugins",
588 options, args = optparser.parse_args()
590 if options.raw_output:
594 pcidb = PCIIds(options.pcifile)
596 print("Pci DB file not found")
600 options.pcifile = None
604 optparser.print_usage()
607 if options.pdir is True:
608 exit(scan_for_autoload_pmds(args[0]))
610 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
611 if (ldlibpath is None):
614 if (os.path.exists(args[0]) is True):
617 myelffile = search_file(
618 args[0], ldlibpath + ":/usr/lib64:/lib64:/usr/lib:/lib")
620 if (myelffile is None):
621 print("File not found")
624 with open(myelffile, 'rb') as file:
626 readelf = ReadElf(file, sys.stdout)
627 readelf.process_dt_needed_entries()
628 readelf.display_pmd_info_strings(".rodata")
631 except ELFError as ex:
632 sys.stderr.write('ELF error: %s\n' % ex)
636 # -------------------------------------------------------------------------
637 if __name__ == '__main__':