2 # -------------------------------------------------------------------------
4 # Utility to dump PMD_INFO_STRING support from an object file
6 # -------------------------------------------------------------------------
9 from optparse
import OptionParser
14 # For running from development directory. It should take precedence over the
15 # installed pyelftools.
16 sys
.path
.insert(0, '.')
19 from elftools
import __version__
20 from elftools
.common
.exceptions
import ELFError
21 from elftools
.common
.py3compat
import (
22 ifilter
, byte2int
, bytes2str
, itervalues
, str2bytes
)
23 from elftools
.elf
.elffile
import ELFFile
24 from elftools
.elf
.dynamic
import DynamicSection
, DynamicSegment
25 from elftools
.elf
.enums
import ENUM_D_TAG
26 from elftools
.elf
.segments
import InterpSegment
27 from elftools
.elf
.sections
import SymbolTableSection
28 from elftools
.elf
.gnuversions
import (
29 GNUVerSymSection
, GNUVerDefSection
,
32 from elftools
.elf
.relocation
import RelocationSection
33 from elftools
.elf
.descriptions
import (
34 describe_ei_class
, describe_ei_data
, describe_ei_version
,
35 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
36 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
37 describe_sh_type
, describe_sh_flags
,
38 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
39 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
42 from elftools
.elf
.constants
import E_FLAGS
43 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
44 from elftools
.dwarf
.descriptions
import (
45 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
46 describe_CFI_instructions
, describe_CFI_register_rule
,
47 describe_CFI_CFA_rule
,
49 from elftools
.dwarf
.constants
import (
50 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
51 from elftools
.dwarf
.callframe
import CIE
, FDE
56 # ===========================================
61 Class for vendors. This is the top level class
62 for the devices belong to a specific vendor.
63 self.devices is the device dictionary
64 subdevices are in each device.
67 def __init__(self
, vendorStr
):
69 Class initializes with the raw line from pci.ids
70 Parsing takes place inside __init__
72 self
.ID
= vendorStr
.split()[0]
73 self
.name
= vendorStr
.replace("%s " % self
.ID
, "").rstrip()
76 def addDevice(self
, deviceStr
):
78 Adds a device to self.devices
79 takes the raw line from pci.ids
83 if devID
in self
.devices
:
86 self
.devices
[devID
] = Device(deviceStr
)
89 print self
.ID
, self
.name
90 for id, dev
in self
.devices
.items():
93 def find_device(self
, devid
):
94 # convert to a hex string and remove 0x
95 devid
= hex(devid
)[2:]
97 return self
.devices
[devid
]
99 return Device("%s Unknown Device" % devid
)
104 def __init__(self
, deviceStr
):
106 Class for each device.
107 Each vendor has its own devices dictionary.
109 s
= deviceStr
.strip()
110 self
.ID
= s
.split()[0]
111 self
.name
= s
.replace("%s " % self
.ID
, "")
115 print "\t%s\t%s" % (self
.ID
, self
.name
)
116 for subID
, subdev
in self
.subdevices
.items():
119 def addSubDevice(self
, subDeviceStr
):
121 Adds a subvendor, subdevice to device.
122 Uses raw line from pci.ids
124 s
= subDeviceStr
.strip()
128 subDeviceName
= s
.split(" ")[-1]
129 devID
= "%s:%s" % (subVendorID
, subDeviceID
)
130 self
.subdevices
[devID
] = SubDevice(
131 subVendorID
, subDeviceID
, subDeviceName
)
133 def find_subid(self
, subven
, subdev
):
134 subven
= hex(subven
)[2:]
135 subdev
= hex(subdev
)[2:]
136 devid
= "%s:%s" % (subven
, subdev
)
139 return self
.subdevices
[devid
]
141 if (subven
== "ffff" and subdev
== "ffff"):
142 return SubDevice("ffff", "ffff", "(All Subdevices)")
144 return SubDevice(subven
, subdev
, "(Unknown Subdevice)")
149 Class for subdevices.
152 def __init__(self
, vendor
, device
, name
):
154 Class initializes with vendorid, deviceid and name
156 self
.vendorID
= vendor
157 self
.deviceID
= device
161 print "\t\t%s\t%s\t%s" % (self
.vendorID
, self
.deviceID
, self
.name
)
166 Top class for all pci.ids entries.
167 All queries will be asked to this class.
168 PCIIds.vendors["0e11"].devices["0046"].\
169 subdevices["0e11:4091"].name = "Smart Array 6i"
172 def __init__(self
, filename
):
174 Prepares the directories.
175 Checks local data file.
176 Tries to load from local, if not found, downloads from web
182 self
.readLocal(filename
)
185 def reportVendors(self
):
186 """Reports the vendors
188 for vid
, v
in self
.vendors
.items():
191 def report(self
, vendor
=None):
193 Reports everything for all vendors or a specific vendor
194 PCIIds.report() reports everything
195 PCIIDs.report("0e11") reports only "Compaq Computer Corporation"
197 if vendor
is not None:
198 self
.vendors
[vendor
].report()
200 for vID
, v
in self
.vendors
.items():
203 def find_vendor(self
, vid
):
204 # convert vid to a hex string and remove the 0x
208 return self
.vendors
[vid
]
210 return Vendor("%s Unknown Vendor" % (vid
))
212 def findDate(self
, content
):
214 if l
.find("Date:") > -1:
215 return l
.split()[-2].replace("-", "")
219 if len(self
.contents
) < 1:
220 print "data/%s-pci.ids not found" % self
.date
224 for l
in self
.contents
:
227 elif len(l
.strip()) == 0:
230 if l
.find("\t\t") == 0:
231 self
.vendors
[vendorID
].devices
[
232 deviceID
].addSubDevice(l
)
233 elif l
.find("\t") == 0:
234 deviceID
= l
.strip().split()[0]
235 self
.vendors
[vendorID
].addDevice(l
)
237 vendorID
= l
.split()[0]
238 self
.vendors
[vendorID
] = Vendor(l
)
240 def readLocal(self
, filename
):
244 self
.contents
= open(filename
).readlines()
245 self
.date
= self
.findDate(self
.contents
)
249 Loads database from local. If there is no file,
250 it creates a new one from web
252 self
.date
= idsfile
[0].split("/")[1].split("-")[0]
256 # =======================================
258 def search_file(filename
, search_path
):
259 """ Given a search path, find file with requested name """
260 for path
in string
.split(search_path
, ":"):
261 candidate
= os
.path
.join(path
, filename
)
262 if os
.path
.exists(candidate
):
263 return os
.path
.abspath(candidate
)
267 class ReadElf(object):
268 """ display_* methods are used to emit output into the output stream
271 def __init__(self
, file, output
):
273 stream object with the ELF file to read
276 output stream to write to
278 self
.elffile
= ELFFile(file)
281 # Lazily initialized if a debug dump is requested
282 self
._dwarfinfo
= None
284 self
._versioninfo
= None
286 def _section_from_spec(self
, spec
):
287 """ Retrieve a section given a "spec" (either number or name).
288 Return None if no such section exists in the file.
292 if num
< self
.elffile
.num_sections():
293 return self
.elffile
.get_section(num
)
297 # Not a number. Must be a name then
298 return self
.elffile
.get_section_by_name(str2bytes(spec
))
300 def pretty_print_pmdinfo(self
, pmdinfo
):
303 for i
in pmdinfo
["pci_ids"]:
304 vendor
= pcidb
.find_vendor(i
[0])
305 device
= vendor
.find_device(i
[1])
306 subdev
= device
.find_subid(i
[2], i
[3])
307 print("%s (%s) : %s (%s) %s" %
308 (vendor
.name
, vendor
.ID
, device
.name
,
309 device
.ID
, subdev
.name
))
311 def parse_pmd_info_string(self
, mystring
):
315 optional_pmd_info
= [{'id': 'params', 'tag': 'PMD PARAMETERS'}]
317 i
= mystring
.index("=")
318 mystring
= mystring
[i
+ 2:]
319 pmdinfo
= json
.loads(mystring
)
322 print(json
.dumps(pmdinfo
))
325 print("PMD NAME: " + pmdinfo
["name"])
326 for i
in optional_pmd_info
:
328 print("%s: %s" % (i
['tag'], pmdinfo
[i
['id']]))
329 except KeyError as e
:
332 if (len(pmdinfo
["pci_ids"]) != 0):
333 print("PMD HW SUPPORT:")
334 if pcidb
is not None:
335 self
.pretty_print_pmdinfo(pmdinfo
)
337 print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE")
338 for i
in pmdinfo
["pci_ids"]:
339 print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" %
340 (i
[0], i
[1], i
[2], i
[3]))
344 def display_pmd_info_strings(self
, section_spec
):
345 """ Display a strings dump of a section. section_spec is either a
346 section number or a name.
348 section
= self
._section
_from
_spec
(section_spec
)
352 data
= section
.data()
355 while dataptr
< len(data
):
356 while (dataptr
< len(data
) and
357 not (32 <= byte2int(data
[dataptr
]) <= 127)):
360 if dataptr
>= len(data
):
364 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
367 mystring
= bytes2str(data
[dataptr
:endptr
])
368 rc
= mystring
.find("PMD_INFO_STRING")
370 self
.parse_pmd_info_string(mystring
)
374 def find_librte_eal(self
, section
):
375 for tag
in section
.iter_tags():
376 if tag
.entry
.d_tag
== 'DT_NEEDED':
377 if "librte_eal" in tag
.needed
:
381 def search_for_autoload_path(self
):
386 section
= self
._section
_from
_spec
(".dynamic")
388 eallib
= self
.find_librte_eal(section
)
389 if eallib
is not None:
390 ldlibpath
= os
.environ
.get('LD_LIBRARY_PATH')
391 if ldlibpath
is None:
393 dtr
= self
.get_dt_runpath(section
)
394 library
= search_file(eallib
,
395 dtr
+ ":" + ldlibpath
+
396 ":/usr/lib64:/lib64:/usr/lib:/lib")
399 if raw_output
is False:
400 print("Scanning for autoload path in %s" % library
)
401 scanfile
= open(library
, 'rb')
402 scanelf
= ReadElf(scanfile
, sys
.stdout
)
403 except AttributeError:
404 # Not a dynamic binary
410 section
= scanelf
._section
_from
_spec
(".rodata")
412 if scanfile
is not None:
416 data
= section
.data()
419 while dataptr
< len(data
):
420 while (dataptr
< len(data
) and
421 not (32 <= byte2int(data
[dataptr
]) <= 127)):
424 if dataptr
>= len(data
):
428 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
431 mystring
= bytes2str(data
[dataptr
:endptr
])
432 rc
= mystring
.find("DPDK_PLUGIN_PATH")
434 rc
= mystring
.find("=")
435 return (mystring
[rc
+ 1:], library
)
438 if scanfile
is not None:
442 def get_dt_runpath(self
, dynsec
):
443 for tag
in dynsec
.iter_tags():
444 if tag
.entry
.d_tag
== 'DT_RUNPATH':
448 def process_dt_needed_entries(self
):
449 """ Look to see if there are any DT_NEEDED entries in the binary
450 And process those if there are
454 ldlibpath
= os
.environ
.get('LD_LIBRARY_PATH')
455 if ldlibpath
is None:
458 dynsec
= self
._section
_from
_spec
(".dynamic")
460 runpath
= self
.get_dt_runpath(dynsec
)
461 except AttributeError:
462 # dynsec is None, just return
465 for tag
in dynsec
.iter_tags():
466 if tag
.entry
.d_tag
== 'DT_NEEDED':
467 rc
= tag
.needed
.find("librte_pmd")
469 library
= search_file(tag
.needed
,
470 runpath
+ ":" + ldlibpath
+
471 ":/usr/lib64:/lib64:/usr/lib:/lib")
472 if library
is not None:
473 if raw_output
is False:
474 print("Scanning %s for pmd information" % library
)
475 with
open(library
, 'rb') as file:
477 libelf
= ReadElf(file, sys
.stdout
)
478 except ELFError
as e
:
479 print("%s is no an ELF file" % library
)
481 libelf
.process_dt_needed_entries()
482 libelf
.display_pmd_info_strings(".rodata")
486 def scan_autoload_path(autoload_path
):
489 if os
.path
.exists(autoload_path
) is False:
493 dirs
= os
.listdir(autoload_path
)
495 # Couldn't read the directory, give up
499 dpath
= os
.path
.join(autoload_path
, d
)
500 if os
.path
.isdir(dpath
):
501 scan_autoload_path(dpath
)
502 if os
.path
.isfile(dpath
):
504 file = open(dpath
, 'rb')
505 readelf
= ReadElf(file, sys
.stdout
)
506 except ELFError
as e
:
507 # this is likely not an elf file, skip it
510 # No permission to read the file, skip it
513 if raw_output
is False:
514 print("Hw Support for library %s" % d
)
515 readelf
.display_pmd_info_strings(".rodata")
519 def scan_for_autoload_pmds(dpdk_path
):
521 search the specified application or path for a pmd autoload path
522 then scan said path for pmds and report hw support
526 if (os
.path
.isfile(dpdk_path
) is False):
527 if raw_output
is False:
528 print("Must specify a file name")
531 file = open(dpdk_path
, 'rb')
533 readelf
= ReadElf(file, sys
.stdout
)
534 except ElfError
as e
:
535 if raw_output
is False:
536 print("Unable to parse %s" % file)
539 (autoload_path
, scannedfile
) = readelf
.search_for_autoload_path()
540 if (autoload_path
is None or autoload_path
is ""):
541 if (raw_output
is False):
542 print("No autoload path configured in %s" % dpdk_path
)
544 if (raw_output
is False):
545 if (scannedfile
is None):
546 scannedfile
= dpdk_path
547 print("Found autoload path %s in %s" % (autoload_path
, scannedfile
))
550 if (raw_output
is False):
551 print("Discovered Autoload HW Support:")
552 scan_autoload_path(autoload_path
)
556 def main(stream
=None):
560 pcifile_default
= "./pci.ids" # for unknown OS's assume local file
561 if platform
.system() == 'Linux':
562 pcifile_default
= "/usr/share/hwdata/pci.ids"
563 elif platform
.system() == 'FreeBSD':
564 pcifile_default
= "/usr/local/share/pciids/pci.ids"
565 if not os
.path
.exists(pcifile_default
):
566 pcifile_default
= "/usr/share/misc/pci_vendors"
568 optparser
= OptionParser(
569 usage
='usage: %prog [-hrtp] [-d <pci id file] <elf-file>',
570 description
="Dump pmd hardware support info",
571 add_help_option
=True)
572 optparser
.add_option('-r', '--raw',
573 action
='store_true', dest
='raw_output',
574 help='Dump raw json strings')
575 optparser
.add_option("-d", "--pcidb", dest
="pcifile",
576 help="specify a pci database "
577 "to get vendor names from",
578 default
=pcifile_default
, metavar
="FILE")
579 optparser
.add_option("-t", "--table", dest
="tblout",
580 help="output information on hw support as a hex table",
582 optparser
.add_option("-p", "--plugindir", dest
="pdir",
583 help="scan dpdk for autoload plugins",
586 options
, args
= optparser
.parse_args()
588 if options
.raw_output
:
592 pcidb
= PCIIds(options
.pcifile
)
594 print("Pci DB file not found")
598 options
.pcifile
= None
602 optparser
.print_usage()
605 if options
.pdir
is True:
606 exit(scan_for_autoload_pmds(args
[0]))
608 ldlibpath
= os
.environ
.get('LD_LIBRARY_PATH')
609 if (ldlibpath
is None):
612 if (os
.path
.exists(args
[0]) is True):
615 myelffile
= search_file(
616 args
[0], ldlibpath
+ ":/usr/lib64:/lib64:/usr/lib:/lib")
618 if (myelffile
is None):
619 print("File not found")
622 with
open(myelffile
, 'rb') as file:
624 readelf
= ReadElf(file, sys
.stdout
)
625 readelf
.process_dt_needed_entries()
626 readelf
.display_pmd_info_strings(".rodata")
629 except ELFError
as ex
:
630 sys
.stderr
.write('ELF error: %s\n' % ex
)
634 # -------------------------------------------------------------------------
635 if __name__
== '__main__':