# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
+import io
+import resource
+import struct
+import zlib
from ..common.py3compat import BytesIO
from ..common.exceptions import ELFError
from ..common.utils import struct_parse, elf_assert
-from ..construct import ConstructError
from .structs import ELFStructs
from .sections import (
Section, StringTableSection, SymbolTableSection,
def has_dwarf_info(self):
""" Check whether this file appears to have debugging information.
- We assume that if it has the debug_info section, it has all theother
- required sections as well.
+ We assume that if it has the .debug_info or .zdebug_info section, it
+ has all the other required sections as well.
"""
- return bool(self.get_section_by_name('.debug_info'))
+ return bool(self.get_section_by_name('.debug_info')) or \
+ bool(self.get_section_by_name('.zdebug_info'))
def get_dwarf_info(self, relocate_dwarf_sections=True):
""" Return a DWARFInfo object representing the debugging information in
# present.
# Sections that aren't found will be passed as None to DWARFInfo.
#
+
+ section_names = ('.debug_info', '.debug_abbrev', '.debug_str',
+ '.debug_line', '.debug_frame',
+ '.debug_loc', '.debug_ranges')
+
+ compressed = bool(self.get_section_by_name('.zdebug_info'))
+ if compressed:
+ section_names = tuple(map(lambda x: '.z' + x[1:], section_names))
+
+ debug_info_sec_name, debug_abbrev_sec_name, debug_str_sec_name, \
+ debug_line_sec_name, debug_frame_sec_name, debug_loc_sec_name, \
+ debug_ranges_sec_name = section_names
+
debug_sections = {}
- for secname in ('.debug_info', '.debug_abbrev', '.debug_str',
- '.debug_line', '.debug_frame',
- '.debug_loc', '.debug_ranges'):
+ for secname in section_names:
section = self.get_section_by_name(secname)
if section is None:
debug_sections[secname] = None
else:
- debug_sections[secname] = self._read_dwarf_section(
+ dwarf_section = self._read_dwarf_section(
section,
relocate_dwarf_sections)
+ if compressed:
+ dwarf_section = self._decompress_dwarf_section(dwarf_section)
+ debug_sections[secname] = dwarf_section
return DWARFInfo(
config=DwarfConfig(
little_endian=self.little_endian,
default_address_size=self.elfclass // 8,
machine_arch=self.get_machine_arch()),
- debug_info_sec=debug_sections['.debug_info'],
- debug_abbrev_sec=debug_sections['.debug_abbrev'],
- debug_frame_sec=debug_sections['.debug_frame'],
+ debug_info_sec=debug_sections[debug_info_sec_name],
+ debug_abbrev_sec=debug_sections[debug_abbrev_sec_name],
+ debug_frame_sec=debug_sections[debug_frame_sec_name],
# TODO(eliben): reading of eh_frame is not hooked up yet
eh_frame_sec=None,
- debug_str_sec=debug_sections['.debug_str'],
- debug_loc_sec=debug_sections['.debug_loc'],
- debug_ranges_sec=debug_sections['.debug_ranges'],
- debug_line_sec=debug_sections['.debug_line'])
+ debug_str_sec=debug_sections[debug_str_sec_name],
+ debug_loc_sec=debug_sections[debug_loc_sec_name],
+ debug_ranges_sec=debug_sections[debug_ranges_sec_name],
+ debug_line_sec=debug_sections[debug_line_sec_name])
+
def get_machine_arch(self):
""" Return the machine architecture, as detected from the ELF header.
name=section.name,
global_offset=section['sh_offset'],
size=section['sh_size'])
+
+ @staticmethod
+ def _decompress_dwarf_section(section):
+ """ Returns the uncompressed contents of the provided DWARF section.
+ """
+ # TODO: support other compression formats from readelf.c
+ assert section.size > 12, 'Unsupported compression format.'
+
+ section.stream.seek(0)
+ # According to readelf.c the content should contain "ZLIB"
+ # followed by the uncompressed section size - 8 bytes in
+ # big-endian order
+ compression_type = section.stream.read(4)
+ assert compression_type == b'ZLIB', \
+ 'Invalid compression type: %r' % (compression_type)
+
+ uncompressed_size = struct.unpack('>Q', section.stream.read(8))[0]
+
+ decompressor = zlib.decompressobj()
+ uncompressed_stream = BytesIO()
+ while True:
+ chunk = section.stream.read(resource.getpagesize())
+ if not chunk:
+ break
+ uncompressed_stream.write(decompressor.decompress(chunk))
+ uncompressed_stream.write(decompressor.flush())
+
+ uncompressed_stream.seek(0, io.SEEK_END)
+ size = uncompressed_stream.tell()
+ assert uncompressed_size == size, \
+ 'Wrong uncompressed size: expected %r, but got %r' % (
+ uncompressed_size, size,
+ )
+
+ return section._replace(stream=uncompressed_stream, size=size)
def _dump_debug_info(self):
""" Dump the debugging info section.
"""
- self._emitline('Contents of the .debug_info section:\n')
+ self._emitline('Contents of the %s section:\n' % self._dwarfinfo.debug_info_sec.name)
# Offset of the .debug_info section in the stream
section_offset = self._dwarfinfo.debug_info_sec.global_offset
""" Dump the (decoded) line programs from .debug_line
The programs are dumped in the order of the CUs they belong to.
"""
- self._emitline('Decoded dump of debug contents of section .debug_line:\n')
+ self._emitline('Decoded dump of debug contents of section %s:\n' % self._dwarfinfo.debug_line_sec.name)
for cu in self._dwarfinfo.iter_CUs():
lineprogram = self._dwarfinfo.line_program_for_CU(cu)
"""
if not self._dwarfinfo.has_CFI():
return
- self._emitline('Contents of the .debug_frame section:')
+ self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_frame_sec.name)
for entry in self._dwarfinfo.CFI_entries():
if isinstance(entry, CIE):
if not self._dwarfinfo.has_CFI():
return
- self._emitline('Contents of the .debug_frame section:')
+ self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_frame_sec.name)
for entry in self._dwarfinfo.CFI_entries():
if isinstance(entry, CIE):