[diffoscope] 01/01: comparators.elf: Tidy/reformat module
Chris Lamb
chris at chris-lamb.co.uk
Thu Dec 29 11:09:46 CET 2016
This is an automated email from the git hooks/post-receive script.
lamby pushed a commit to branch master
in repository diffoscope.
commit bdad340190259526dbeffebc60aa027c4a344d02
Author: Chris Lamb <lamby at debian.org>
Date: Thu Dec 29 09:47:39 2016 +0000
comparators.elf: Tidy/reformat module
---
diffoscope/comparators/elf.py | 251 +++++++++++++++++++++++++++++-------------
1 file changed, 174 insertions(+), 77 deletions(-)
diff --git a/diffoscope/comparators/elf.py b/diffoscope/comparators/elf.py
index 2570d65..a3a65b7 100644
--- a/diffoscope/comparators/elf.py
+++ b/diffoscope/comparators/elf.py
@@ -17,8 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with diffoscope. If not, see <https://www.gnu.org/licenses/>.
+import os
import re
-import os.path
import subprocess
import collections
@@ -31,9 +31,26 @@ from diffoscope.difference import Difference
from .deb import DebFile, get_build_id_map
from .utils.file import File
from .utils.command import Command
-from .utils.container import Container
+from .utils.container import Container
from .utils.libarchive import list_libarchive
+DEBUG_SECTION_GROUPS = (
+ 'rawline',
+ 'info',
+ 'abbrev',
+ 'pubnames',
+ 'aranges',
+ 'macro',
+ 'frames',
+ 'loc',
+ 'ranges',
+ 'pubtypes',
+ 'trace_info',
+ 'trace_abbrev',
+ 'trace_aranges',
+ 'gdb_index',
+)
+
class Readelf(Command):
def __init__(self, *args, **kwargs):
@@ -128,8 +145,7 @@ class ReadelfDebugDump(Readelf):
debug_section_group = cls.__name__[len('ReadelfDebugDump_'):]
if debug_section_group:
return ReadelfDebugDump(debug_section_group, *args, **kwargs)
- else:
- return super(Readelf, cls).__new__(cls)
+ return super(Readelf, cls).__new__(cls)
def __init__(self, debug_section_group, *args, **kwargs):
self._debug_section_group = debug_section_group
@@ -139,26 +155,26 @@ class ReadelfDebugDump(Readelf):
return ['--debug-dump=%s' % self._debug_section_group]
-DEBUG_SECTION_GROUPS = ['rawline', 'info', 'abbrev', 'pubnames', 'aranges',
- 'macro', 'frames', 'loc', 'ranges', 'pubtypes',
- 'trace_info', 'trace_abbrev', 'trace_aranges',
- 'gdb_index']
-
-
-READELF_DEBUG_DUMP_COMMANDS = \
- [ type('ReadelfDebugDump_%s' % section_group, (ReadelfDebugDump,), {})
- for section_group in DEBUG_SECTION_GROUPS ]
+READELF_DEBUG_DUMP_COMMANDS = [
+ type('ReadelfDebugDump_{}'.format(x), (ReadelfDebugDump,), {})
+ for x in DEBUG_SECTION_GROUPS
+]
class ReadElfSection(Readelf):
@staticmethod
def base_options():
if not hasattr(ReadElfSection, '_base_options'):
- options = []
- help_output = subprocess.check_output(['readelf', '--help'], shell=False, stderr=subprocess.DEVNULL).decode('us-ascii', errors='replace')
- if '--decompress' in help_output:
- options.append('--decompress')
- ReadElfSection._base_options = options
+ output = subprocess.check_output(
+ ['readelf', '--help'],
+ shell=False,
+ stderr=subprocess.DEVNULL,
+ ).decode('us-ascii', errors='replace')
+
+ ReadElfSection._base_options = []
+ for x in ('--decompress',):
+ if x in output:
+ ReadElfSection._base_options.append(x)
return ReadElfSection._base_options
def __init__(self, path, section_name, *args, **kwargs):
@@ -189,8 +205,12 @@ class ObjdumpSection(Command):
@tool_required('objdump')
def cmdline(self):
- return ['objdump'] + self.objdump_options() + \
- ['--section='+self._section_name, self.path]
+ return [
+ 'objdump',
+ ] + self.objdump_options() + [
+ '--section={}'.format(self._section_name),
+ self.path,
+ ]
def filter(self, line):
# Remove the filename from the output
@@ -198,6 +218,7 @@ class ObjdumpSection(Command):
return b''
if line.startswith(b'In archive'):
return b''
+
return line
class ObjdumpDisassembleSection(ObjdumpSection):
@@ -215,25 +236,28 @@ class ObjdumpDisassembleSection(ObjdumpSection):
return ObjdumpDisassembleSection.RE_SYMBOL_COMMENT.sub(r'\1', line)
-READELF_COMMANDS = [ReadelfFileHeader,
- ReadelfProgramHeader,
- ReadelfSections,
- ReadelfSymbols,
- ReadelfRelocs,
- ReadelfDynamic,
- ReadelfNotes,
- RedaelfVersionInfo,
- ]
-
+READELF_COMMANDS = (
+ ReadelfFileHeader,
+ ReadelfProgramHeader,
+ ReadelfSections,
+ ReadelfSymbols,
+ ReadelfRelocs,
+ ReadelfDynamic,
+ ReadelfNotes,
+ RedaelfVersionInfo,
+)
def _compare_elf_data(path1, path2):
- return [Difference.from_command(cmd, path1, path2) for cmd in READELF_COMMANDS + READELF_DEBUG_DUMP_COMMANDS]
+ return [
+ Difference.from_command(x, path1, path2)
+ for x in list(READELF_COMMANDS) + READELF_DEBUG_DUMP_COMMANDS
+ ]
def _should_skip_section(name, type):
- for cmd in READELF_COMMANDS:
- if cmd.should_skip_section(name, type):
- logger.debug('skipping section %s, covered by %s', name, cmd)
+ for x in READELF_COMMANDS:
+ if x.should_skip_section(name, type):
+ logger.debug("Skipping section %s, covered by %s", name, x)
return True
if name.startswith('.debug') or name.startswith('.zdebug'):
# section .debug_str looks much nicer with `readelf --string-dump`
@@ -288,88 +312,124 @@ class ElfSection(File):
return False
def compare(self, other, source=None):
- return Difference.from_command(ReadElfSection,
- self.path, other.path,
- command_args=[self._name])
+ return Difference.from_command(
+ ReadElfSection,
+ self.path,
+ other.path,
+ command_args=[self._name],
+ )
class ElfCodeSection(ElfSection):
def compare(self, other, source=None):
- return Difference.from_command(ObjdumpDisassembleSection,
- self.path, other.path,
- command_args=[self._name])
+ return Difference.from_command(
+ ObjdumpDisassembleSection,
+ self.path,
+ other.path,
+ command_args=[self._name],
+ )
class ElfStringSection(ElfSection):
def compare(self, other, source=None):
- return Difference.from_command(ReadelfStringSection,
- self.path, other.path,
- command_args=[self._name])
+ return Difference.from_command(
+ ReadelfStringSection,
+ self.path,
+ other.path,
+ command_args=[self._name],
+ )
@tool_required('readelf')
def get_build_id(path):
try:
- output = subprocess.check_output(['readelf', '--notes', path], stderr=subprocess.DEVNULL)
+ output = subprocess.check_output(
+ ['readelf', '--notes', path],
+ stderr=subprocess.DEVNULL,
+ )
except subprocess.CalledProcessError as e:
- logger.debug('Unable to get Build Id for %s: %s', path, e)
+ logger.debug("Unable to get Build ID for %s: %s", path, e)
return None
+
m = re.search(r'^\s+Build ID: ([0-9a-f]+)$', output.decode('utf-8'), flags=re.MULTILINE)
if not m:
return None
+
return m.group(1)
@tool_required('readelf')
def get_debug_link(path):
try:
- output = subprocess.check_output(['readelf', '--string-dump=.gnu_debuglink', path], stderr=subprocess.DEVNULL)
+ output = subprocess.check_output(
+ ['readelf', '--string-dump=.gnu_debuglink', path],
+ stderr=subprocess.DEVNULL,
+ )
except subprocess.CalledProcessError as e:
- logger.debug('Unable to get Build Id for %s: %s', path, e)
+ logger.debug("Unable to get Build Id for %s: %s", path, e)
return None
+
m = re.search(r'^\s+\[\s+0\]\s+(\S+)$', output.decode('utf-8', errors='replace'), flags=re.MULTILINE)
if not m:
return None
+
return m.group(1)
class ElfContainer(Container):
- SECTION_FLAG_MAPPING = {'X': ElfCodeSection, 'S': ElfStringSection, '_': ElfSection}
+ SECTION_FLAG_MAPPING = {
+ 'X': ElfCodeSection,
+ 'S': ElfStringSection,
+ '_': ElfSection,
+ }
@tool_required('readelf')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- logger.debug('creating ElfContainer for file %s', self.source.path)
+ logger.debug("Creating ElfContainer for %s", self.source.path)
+
cmd = ['readelf', '--wide', '--section-headers', self.source.path]
output = subprocess.check_output(cmd, shell=False, stderr=subprocess.DEVNULL)
has_debug_symbols = False
+
try:
output = output.decode('utf-8').split('\n')
if output[1].startswith('File:'):
output = output[2:]
output = output[5:]
- self._sections = collections.OrderedDict()
# Entires of readelf --section-headers have the following columns:
# [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+ self._sections = collections.OrderedDict()
for line in output:
if line.startswith('Key to Flags'):
break
+
# Strip number column because there may be spaces in the brakets
line = line.split(']', 1)[1].split()
name, type, flags = line[0], line[1], line[6] + '_'
+
if name.startswith('.debug') or name.startswith('.zdebug'):
has_debug_symbols = True
+
if _should_skip_section(name, type):
continue
+
# Use first match, with last option being '_' as fallback
- elf_class = [ElfContainer.SECTION_FLAG_MAPPING[flag] for flag in flags if \
- flag in ElfContainer.SECTION_FLAG_MAPPING][0]
+ elf_class = [
+ ElfContainer.SECTION_FLAG_MAPPING[x]
+ for x in flags if x in ElfContainer.SECTION_FLAG_MAPPING
+ ][0]
+
+ logger.debug("Adding section %s (%s) as %s", name, type, elf_class)
self._sections[name] = elf_class(self, name)
- logger.debug('adding section %s (%s) as %s', name, type, elf_class)
+
except Exception as e:
command = ' '.join(cmd)
- logger.debug('OutputParsingError in %s from `%s` output - %s:%s',
- self.__class__.__name__, command, e.__class__.__name__, e)
+ logger.debug(
+ "OutputParsingError in %s from `%s` output - %s:%s",
+ self.__class__.__name__, command, e.__class__.__name__, e,
+ )
raise OutputParsingError(command, self)
+
if not has_debug_symbols:
self._install_debug_symbols()
@@ -380,51 +440,84 @@ class ElfContainer(Container):
deb = self.source.container.source.container.source.container.source
except AttributeError:
return
+
# It needs to be a .deb and we need access a to a -dbgsym package in
# the same .changes, directory or archive
if not isinstance(deb, DebFile) or not deb.container:
return
- # Retrieve the Build Id for the ELF file we are exhamining
+
+ # Retrieve the Build ID for the ELF file we are examining
build_id = get_build_id(self.source.path)
debuglink = get_debug_link(self.source.path)
if not build_id or not debuglink:
return
- logger.debug('Looking for a dbgsym package for Build Id %s (debuglink: %s)', build_id, debuglink)
+
+ logger.debug(
+ "Looking for a dbgsym package for Build Id %s (debuglink: %s)",
+ build_id,
+ debuglink,
+ )
+
# Build a map of Build-Ids if it doesn't exist yet
if not hasattr(deb.container, 'dbgsym_build_id_map'):
deb.container.dbgsym_build_id_map = get_build_id_map(deb.container)
+
if not build_id in deb.container.dbgsym_build_id_map:
logger.debug('Unable to find a matching debug package for Build Id %s', build_id)
return
+
dbgsym_package = deb.container.dbgsym_build_id_map[build_id]
- debug_file_path = './usr/lib/debug/.build-id/{0}/{1}.debug'.format(build_id[0:2], build_id[2:])
+ debug_file_path = './usr/lib/debug/.build-id/{0}/{1}.debug'.format(
+ build_id[:2],
+ build_id[2:],
+ )
debug_file = dbgsym_package.as_container.data_tar.as_container.lookup_file(debug_file_path)
if not debug_file:
logger.debug('Unable to find the matching debug file %s in %s', debug_file_path, dbgsym_package)
return
- # Create a .debug directory and link the debug symbols there with the right name
- dest_path = os.path.join(os.path.dirname(self.source.path), '.debug', os.path.basename(debuglink))
+
+ # Create a .debug directory and link the debug symbols there with the
+ # right name
+ dest_path = os.path.join(
+ os.path.dirname(self.source.path),
+ '.debug',
+ os.path.basename(debuglink),
+ )
os.mkdir(os.path.dirname(dest_path))
- # If #812089 was fixed, we would just do:
- #os.link(debug_file.path, dest_path)
- # But for now, we need to do more complicated things…
- # 1. Use objcopy to create a file with only the original .gnu_debuglink section
- # as we will have to update it to get the CRC right.
- debuglink_path = get_named_temporary_file(prefix='{}.debuglink.'.format(self.source.path)).name
- subprocess.check_call(['objcopy', '--only-section=.gnu_debuglink', self.source.path, debuglink_path], shell=False, stderr=subprocess.DEVNULL)
- # 2. Monkey-patch the ElfSection object created for the .gnu_debuglink to
- # change the path to point to this new file
+
+ def objcopy(*args):
+ subprocess.check_call(
+ ('objcopy',) + args,
+ shell=False,
+ stderr=subprocess.DEVNULL,
+ )
+
+ # If #812089 was fixed, we would just do os.link(debug_file.path,
+ # dest_path) but for now, we need to do more complicated things…
+ # 1. Use objcopy to create a file with only the original .gnu_debuglink
+ # section as we will have to update it to get the CRC right.
+ debuglink_path = get_named_temporary_file(
+ prefix='{}.debuglink.'.format(self.source.path),
+ ).name
+
+ objcopy('--only-section=.gnu_debuglink', self.source.path, debuglink_path)
+
+ # 2. Monkey-patch the ElfSection object created for the .gnu_debuglink
+ # to change the path to point to this new file
section = self._sections['.gnu_debuglink']
class MonkeyPatchedElfSection(section.__class__):
@property
def path(self):
return debuglink_path
section.__class__ = MonkeyPatchedElfSection
+
# 3. Create a file with the debug symbols in uncompressed form
- subprocess.check_call(['objcopy', '--decompress-debug-sections', debug_file.path, dest_path], shell=False, stderr=subprocess.DEVNULL)
+ objcopy('--decompress-debug-sections', debug_file.path, dest_path)
+
# 4. Update the .gnu_debuglink to this new file so we get the CRC right
- subprocess.check_call(['objcopy', '--remove-section=.gnu_debuglink', self.source.path], shell=False, stderr=subprocess.DEVNULL)
- subprocess.check_call(['objcopy', '--add-gnu-debuglink=%s' % dest_path, self.source.path], shell=False, stderr=subprocess.DEVNULL)
+ objcopy('--remove-section=.gnu_debuglink', self.source.path)
+ objcopy('--add-gnu-debuglink={}'.format(dest_path), self.source.path)
+
logger.debug('Installed debug symbols at %s', dest_path)
def get_member_names(self):
@@ -451,12 +544,16 @@ class StaticLibFile(File):
@staticmethod
def recognizes(file):
- return StaticLibFile.RE_FILE_TYPE.search(file.magic_file_type) and StaticLibFile.RE_FILE_EXTENSION.search(file.name)
+ return StaticLibFile.RE_FILE_TYPE.search(file.magic_file_type) and \
+ StaticLibFile.RE_FILE_EXTENSION.search(file.name)
def compare_details(self, other, source=None):
- differences = []
- differences.append(Difference.from_text_readers(list_libarchive(self.path),
- list_libarchive(other.path),
- self.path, other.path, source="file list"))
+ differences = [Difference.from_text_readers(
+ list_libarchive(self.path),
+ list_libarchive(other.path),
+ self.path,
+ other.path,
+ source="file list",
+ )]
differences.extend(_compare_elf_data(self.path, other.path))
return differences
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/diffoscope.git
More information about the diffoscope
mailing list