Merge remote-tracking branch 'upstream/master' into bbb
Conflicts:
elftools/elf/dynamic.py
Resolved by using upstream's verbatim copy because vapier's change is
now in upstream.
Change-Id: I6c597d4ef0a876d26736f96360cff95c072e8005
diff --git a/.gitignore b/.gitignore
index 0d20b64..603cba7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,11 @@
*.pyc
+.coverage
+.tox
+htmlcov
+tags
+build
+dist
+MANIFEST
+*.sublime-workspace
+
+
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index 4e7e920..0000000
--- a/.hgignore
+++ /dev/null
@@ -1,13 +0,0 @@
-syntax: glob
-
-*.pyc
-.coverage
-.tox
-htmlcov
-tags
-build
-dist
-MANIFEST
-*.sublime-workspace
-
-
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c2eaf23
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: python
+python:
+ - "2.7"
+ - "3.2"
+ - "3.3"
+ - "3.4"
+script: python test/all_tests.py
+
diff --git a/CHANGES b/CHANGES
index 54e772c..926dbe0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,7 +1,20 @@
Changelog
=========
-+ Version 0.22 (17.04.2013)
++ Version 0.22 (30.03.2014)
+
+ - pyelftools repository moved to https://github.com/eliben/pyelftools
+ - Support for version sections - contributed by Yann Rouillard.
+ - Better ARM support (including AArch64) - contributed by Dobromir Stefanov.
+ - Added some initial support for parsing Solaris OpenCSW ELF files
+ (contributed by Yann Rouillard).
+ - Added some initial support for DWARF4 (as generated by gcc 4.8)
+ and DWARF generated by recent versions of Clang (3.3).
+ - Added the get_full_path utility method to DIEs that have an associated
+ file name / path (based on pull request #16 by Shaheed Haque).
+ - Set up Travis CI integration.
+
++ Version 0.21 (17.04.2013)
- Added new example: dwarf_decode_address - decode function name and
file & line information from an address.
@@ -22,4 +35,3 @@
on Windows.
+ Version 0.10 - Initial public release (06.01.2012)
-
diff --git a/README.rst b/README.rst
index 264b898..d668f3b 100644
--- a/README.rst
+++ b/README.rst
@@ -3,7 +3,7 @@
**pyelftools** is a pure-Python library for parsing and analyzing ELF files
and DWARF debugging information. See the
-`User's guide <https://bitbucket.org/eliben/pyelftools/wiki/Userguide>`_
+`User's guide <https://github.com/eliben/pyelftools/wiki/User's-guide>`_
for more details.
Pre-requisites
@@ -12,7 +12,7 @@
As a user of **pyelftools**, one only needs Python to run. It works with
Python versions 2.6, 2.7 and 3.x (x >= 2). For hacking on **pyelftools** the
requirements are a bit more strict, please see the
-`hacking guide <https://bitbucket.org/eliben/pyelftools/wiki/Hacking>`_.
+`hacking guide <https://github.com/eliben/pyelftools/wiki/Hacking-guide>`_.
Installing
----------
@@ -23,21 +23,22 @@
Alternatively, you can download the source distribution for the most recent and
historic versions from the *Downloads* tab on the `pyelftools project page
-<https://bitbucket.org/eliben/pyelftools>`_ (by going to *Tags*). Then, you can
+<https://github.com/eliben/pyelftools>`_ (by going to *Tags*). Then, you can
install from source, as usual::
> python setup.py install
Since **pyelftools** is a work in progress, it's recommended to have the most
-recent version of the code. This can be done by downloading the ``tip`` tag
-("trunk") from *Downloads* or just cloning the Mercurial repository.
+recent version of the code. This can be done by downloading the `master zip
+file <https://github.com/eliben/pyelftools/archive/master.zip>`_ or just
+cloning the Git repository.
How to use it?
--------------
**pyelftools** is a regular Python library: you import and invoke it from your
own code. For a detailed usage guide and links to examples, please consult the
-`user's guide <https://bitbucket.org/eliben/pyelftools/wiki/Userguide>`_.
+`user's guide <https://github.com/eliben/pyelftools/wiki/User's-guide>`_.
License
-------
@@ -45,6 +46,12 @@
**pyelftools** is open source software. Its code is in the public domain. See
the ``LICENSE`` file for more details.
+CI Status
+---------
+**pyelftools** has automatic testing enabled through the convenient
+`Travis CI project <https://travis-ci.org>`_. Here is the latest build status:
-
+.. image:: https://travis-ci.org/eliben/pyelftools.png?branch=master
+ :align: center
+ :target: https://travis-ci.org/eliben/pyelftools
diff --git a/TODO b/TODO
index bc6ffdc..9028044 100644
--- a/TODO
+++ b/TODO
@@ -4,7 +4,15 @@
* Update elftools/__init__.py
* Update setup.py
* Update CHANGES
-* Tag in hg
+* Tag in git (v0.xx)
+
+construct
+---------
+
+The construct seems to be maintained again - they also backported my Python 3
+fixes. Theoretically, I can remove construct from pyelftools and use it as a
+dependency instead. I don't really have time to play with this now, but may
+do so in the future.
Distribution
------------
@@ -14,16 +22,11 @@
Preparing a new release
-----------------------
-* Run readelf tests with up-to-date readelf (from the new binutils)
-* Run all tests with Python 2.7 before packaging, on Linux
+* Run 'tox' tests (with '-r' to create new venvs)
* Make sure new version was updated everywhere appropriate
-* Packaging done on Linux
* Run ``python setup.py build sdist`` (no 'upload' yet)
* Untar the created ``dist/pyelftools-x.y.tar.gz`` and make sure
everything looks ok
-* Runt 'tox' tests (with '-r' to create new venvs)
* Now build with upload to send it to PyPi
* Test with pip install from some new virtualenv
-* If everything is OK, upload the distribution to BitBucket as well
* The older download can be kept alive for a couple of days
-
diff --git a/elftools/__init__.py b/elftools/__init__.py
index 90be2eb..8a3e56c 100644
--- a/elftools/__init__.py
+++ b/elftools/__init__.py
@@ -4,5 +4,4 @@
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
-__version__ = '0.21'
-
+__version__ = '0.22'
diff --git a/elftools/common/utils.py b/elftools/common/utils.py
index 7bd1d5b..a2c9edb 100644
--- a/elftools/common/utils.py
+++ b/elftools/common/utils.py
@@ -31,8 +31,8 @@
stream.seek(stream_pos)
return struct.parse_stream(stream)
except ConstructError as e:
- raise ELFParseError(e.message)
-
+ raise ELFParseError(str(e))
+
def parse_cstring_from_stream(stream, stream_pos=None):
""" Parse a C-string from the given stream. The string is returned without
@@ -78,11 +78,10 @@
@contextmanager
def preserve_stream_pos(stream):
""" Usage:
-
- # stream has some position FOO (return value of stream.tell())
- with preserve_stream_pos(stream):
- # do stuff that manipulates the stream
- # stream still has position FOO
+ # stream has some position FOO (return value of stream.tell())
+ with preserve_stream_pos(stream):
+ # do stuff that manipulates the stream
+ # stream still has position FOO
"""
saved_pos = stream.tell()
yield
diff --git a/elftools/construct/README b/elftools/construct/README
index 0d76f7f..7f9e141 100644
--- a/elftools/construct/README
+++ b/elftools/construct/README
@@ -2,6 +2,9 @@
data. This is my fork of construct 2, with some modifications for Python 3
and bug fixes. The construct website is http://construct.readthedocs.org
+pyelftools carries construct around because construct has been abandoned for
+a long time and didn't get bugfixes; it also didn't work with Python 3.
+
LICENSE is the original license.
diff --git a/elftools/dwarf/callframe.py b/elftools/dwarf/callframe.py
index 5b35af6..264adb8 100644
--- a/elftools/dwarf/callframe.py
+++ b/elftools/dwarf/callframe.py
@@ -26,13 +26,13 @@
Eventually, each entry gets its own structs based on the initial
length field it starts with. The address_size, however, is taken
from base_structs. This appears to be a limitation of the DWARFv3
- standard, fixed in v4 (where an address_size field exists for each
- CFI. A discussion I had on dwarf-discuss confirms this.
- Currently for base_structs I simply use the elfclass of the
- containing file, but more sophisticated methods are used by
- libdwarf and others, such as guessing which CU contains which FDEs
- (based on their address ranges) and taking the address_size from
- those CUs.
+ standard, fixed in v4.
+ A discussion I had on dwarf-discuss confirms this.
+ So for DWARFv4 we'll take the address size from the CIE header,
+ but for earlier versions will use the elfclass of the containing
+ file; more sophisticated methods are used by libdwarf and others,
+ such as guessing which CU contains which FDEs (based on their
+ address ranges) and taking the address_size from those CUs.
"""
def __init__(self, stream, size, base_structs):
self.stream = stream
@@ -99,6 +99,14 @@
header = struct_parse(
header_struct, self.stream, offset)
+ # If this is DWARF version 4 or later, we can have a more precise
+ # address size, read from the CIE header.
+ if entry_structs.dwarf_version >= 4:
+ entry_structs = DWARFStructs(
+ little_endian=entry_structs.little_endian,
+ dwarf_format=entry_structs.dwarf_format,
+ address_size=header.address_size)
+
# For convenience, compute the end offset for this entry
end_offset = (
offset + header.length +
diff --git a/elftools/dwarf/descriptions.py b/elftools/dwarf/descriptions.py
index 987e0d0..ca00cd1 100644
--- a/elftools/dwarf/descriptions.py
+++ b/elftools/dwarf/descriptions.py
@@ -82,7 +82,7 @@
'DW_CFA_advance_loc4', 'DW_CFA_advance_loc'):
_assert_FDE_instruction(instr)
factored_offset = instr.args[0] * cie['code_alignment_factor']
- s += ' %s: %s to %08x\n' % (
+ s += ' %s: %s to %016x\n' % (
name, factored_offset, factored_offset + pc)
pc += factored_offset
elif name in ( 'DW_CFA_remember_state', 'DW_CFA_restore_state',
@@ -197,6 +197,12 @@
"""
return '1' if attr.value else '0'
+def _describe_attr_present(attr, die, section_offset):
+ """ Some forms may simply mean that an attribute is present,
+ without providing any value.
+ """
+ return '1'
+
def _describe_attr_block(attr, die, section_offset):
s = '%s byte block: ' % len(attr.value)
s += ' '.join('%x' % item for item in attr.value) + ' '
@@ -227,6 +233,9 @@
DW_FORM_block2=_describe_attr_block,
DW_FORM_block4=_describe_attr_block,
DW_FORM_block=_describe_attr_block,
+ DW_FORM_flag_present=_describe_attr_present,
+ DW_FORM_exprloc=_describe_attr_block,
+ DW_FORM_ref_sig8=_describe_attr_ref,
)
diff --git a/elftools/dwarf/die.py b/elftools/dwarf/die.py
index cb5d050..86830fc 100644
--- a/elftools/dwarf/die.py
+++ b/elftools/dwarf/die.py
@@ -7,9 +7,12 @@
# This code is in the public domain
#-------------------------------------------------------------------------------
from collections import namedtuple
+import os
-from ..common.py3compat import OrderedDict
+from ..common.exceptions import DWARFError
+from ..common.py3compat import OrderedDict, bytes2str, iteritems
from ..common.utils import struct_parse, preserve_stream_pos
+from .enums import DW_FORM_raw2name
# AttributeValue - describes an attribute value in the DIE:
@@ -99,6 +102,20 @@
"""
return self._parent
+ def get_full_path(self):
+ """ Return the full path filename for the DIE.
+
+ The filename is the join of 'DW_AT_comp_dir' and 'DW_AT_name',
+ either of which may be missing in practice. Note that its value is
+ usually a string taken from the .debug_string section and the
+ returned value will be a string.
+ """
+ comp_dir_attr = self.attributes.get('DW_AT_comp_dir', None)
+ comp_dir = bytes2str(comp_dir_attr.value) if comp_dir_attr else ''
+ fname_attr = self.attributes.get('DW_AT_name', None)
+ fname = bytes2str(fname_attr.value) if fname_attr else ''
+ return os.path.join(comp_dir, fname)
+
def iter_children(self):
""" Yield all children of this DIE
"""
@@ -128,7 +145,7 @@
def __repr__(self):
s = 'DIE %s, size=%s, has_chidren=%s\n' % (
self.tag, self.size, self.has_children)
- for attrname, attrval in self.attributes.iteritems():
+ for attrname, attrval in iteritems(self.attributes):
s += ' |%-18s: %s\n' % (attrname, attrval)
return s
@@ -187,9 +204,15 @@
elif form == 'DW_FORM_flag':
value = not raw_value == 0
elif form == 'DW_FORM_indirect':
- form = raw_value
+ try:
+ form = DW_FORM_raw2name[raw_value]
+ except KeyError as err:
+ raise DWARFError(
+ 'Found DW_FORM_indirect with unknown raw_value=' +
+ str(raw_value))
+
raw_value = struct_parse(
- structs.Dwarf_dw_form[form], self.stream)
+ self.cu.structs.Dwarf_dw_form[form], self.stream)
# Let's hope this doesn't get too deep :-)
return self._translate_attr_value(form, raw_value)
else:
diff --git a/elftools/dwarf/dwarfinfo.py b/elftools/dwarf/dwarfinfo.py
index 9aa2f52..1995fc8 100644
--- a/elftools/dwarf/dwarfinfo.py
+++ b/elftools/dwarf/dwarfinfo.py
@@ -21,7 +21,7 @@
# Describes a debug section
-#
+#
# stream: a stream object containing the data of this section
# name: section name in the container file
# global_offset: the global offset of the section in its container file
@@ -30,7 +30,7 @@
# 'name' and 'global_offset' are for descriptional purposes only and
# aren't strictly required for the DWARF parsing to work.
#
-DebugSectionDescriptor = namedtuple('DebugSectionDescriptor',
+DebugSectionDescriptor = namedtuple('DebugSectionDescriptor',
'stream name global_offset size')
@@ -51,7 +51,7 @@
class DWARFInfo(object):
- """ Acts also as a "context" to other major objects, bridging between
+ """ Acts also as a "context" to other major objects, bridging between
various parts of the debug infromation.
"""
def __init__(self,
@@ -59,6 +59,7 @@
debug_info_sec,
debug_abbrev_sec,
debug_frame_sec,
+ eh_frame_sec,
debug_str_sec,
debug_loc_sec,
debug_ranges_sec,
@@ -68,19 +69,20 @@
debug_*_sec:
DebugSectionDescriptor for a section. Pass None for sections
- that don't exist. These arguments are best given with
+ that don't exist. These arguments are best given with
keyword syntax.
"""
self.config = config
self.debug_info_sec = debug_info_sec
self.debug_abbrev_sec = debug_abbrev_sec
self.debug_frame_sec = debug_frame_sec
+ self.eh_frame_sec = eh_frame_sec
self.debug_str_sec = debug_str_sec
self.debug_loc_sec = debug_loc_sec
self.debug_ranges_sec = debug_ranges_sec
self.debug_line_sec = debug_line_sec
- # This is the DWARFStructs the context uses, so it doesn't depend on
+ # This is the DWARFStructs the context uses, so it doesn't depend on
# DWARF format and address_size (these are determined per CU) - set them
# to default values.
self.structs = DWARFStructs(
@@ -119,7 +121,7 @@
return self._abbrevtable_cache[offset]
def get_string_from_table(self, offset):
- """ Obtain a string from the string table section, given an offset
+ """ Obtain a string from the string table section, given an offset
relative to the section.
"""
return parse_cstring_from_stream(self.debug_str_sec.stream, offset)
@@ -139,12 +141,12 @@
return None
def has_CFI(self):
- """ Does this dwarf info has a CFI section?
+ """ Does this dwarf info have a dwarf_frame CFI section?
"""
return self.debug_frame_sec is not None
def CFI_entries(self):
- """ Get a list of CFI entries from the .debug_frame section.
+ """ Get a list of dwarf_frame CFI entries from the .debug_frame section.
"""
cfi = CallFrameInfo(
stream=self.debug_frame_sec.stream,
@@ -152,17 +154,37 @@
base_structs=self.structs)
return cfi.get_entries()
+ def has_EH_CFI(self):
+ """ Does this dwarf info have a eh_frame CFI section?
+ """
+ return self.eh_frame_sec is not None
+
+ def EH_CFI_entries(self):
+ """ Get a list of eh_frame CFI entries from the .eh_frame section.
+ """
+ cfi = CallFrameInfo(
+ stream=self.eh_frame_sec.stream,
+ size=self.eh_frame_sec.size,
+ base_structs=self.structs)
+ return cfi.get_entries()
+
def location_lists(self):
""" Get a LocationLists object representing the .debug_loc section of
the DWARF data, or None if this section doesn't exist.
"""
- return LocationLists(self.debug_loc_sec.stream, self.structs)
+ if self.debug_loc_sec:
+ return LocationLists(self.debug_loc_sec.stream, self.structs)
+ else:
+ return None
def range_lists(self):
""" Get a RangeLists object representing the .debug_ranges section of
the DWARF data, or None if this section doesn't exist.
"""
- return RangeLists(self.debug_ranges_sec.stream, self.structs)
+ if self.debug_ranges_sec:
+ return RangeLists(self.debug_ranges_sec.stream, self.structs)
+ else:
+ return None
#------ PRIVATE ------#
@@ -175,18 +197,18 @@
# Compute the offset of the next CU in the section. The unit_length
# field of the CU header contains its size not including the length
# field itself.
- offset = ( offset +
- cu['unit_length'] +
+ offset = ( offset +
+ cu['unit_length'] +
cu.structs.initial_length_field_size())
yield cu
-
+
def _parse_CU_at_offset(self, offset):
""" Parse and return a CU at the given offset in the debug_info stream.
"""
# Section 7.4 (32-bit and 64-bit DWARF Formats) of the DWARF spec v3
- # states that the first 32-bit word of the CU header determines
+ # states that the first 32-bit word of the CU header determines
# whether the CU is represented with 32-bit or 64-bit DWARF format.
- #
+ #
# So we peek at the first word in the CU header to determine its
# dwarf format. Based on it, we then create a new DWARFStructs
# instance suitable for this CU and use it to parse the rest.
@@ -205,15 +227,15 @@
little_endian=self.config.little_endian,
dwarf_format=dwarf_format,
address_size=4)
-
+
cu_header = struct_parse(
cu_structs.Dwarf_CU_header, self.debug_info_sec.stream, offset)
if cu_header['address_size'] == 8:
cu_structs = DWARFStructs(
little_endian=self.config.little_endian,
dwarf_format=dwarf_format,
- address_size=8)
-
+ address_size=8)
+
cu_die_offset = self.debug_info_sec.stream.tell()
dwarf_assert(
self._is_supported_version(cu_header['version']),
@@ -224,11 +246,11 @@
structs=cu_structs,
cu_offset=offset,
cu_die_offset=cu_die_offset)
-
+
def _is_supported_version(self, version):
""" DWARF version supported by this parser
"""
- return 2 <= version <= 3
+ return 2 <= version <= 4
def _parse_line_program_at_offset(self, debug_line_offset, structs):
""" Given an offset to the .debug_line section, parse the line program
diff --git a/elftools/dwarf/enums.py b/elftools/dwarf/enums.py
index 1338725..d576dff 100644
--- a/elftools/dwarf/enums.py
+++ b/elftools/dwarf/enums.py
@@ -7,6 +7,7 @@
# This code is in the public domain
#-------------------------------------------------------------------------------
from ..construct import Pass
+from ..common.py3compat import iteritems
ENUM_DW_TAG = dict(
@@ -52,10 +53,12 @@
DW_TAG_namelist_items = 0x2c,
DW_TAG_packed_type = 0x2d,
DW_TAG_subprogram = 0x2e,
- DW_TAG_template_type_parameter = 0x2f,
+
+ # The DWARF standard defines these as _parameter, not _param, but we
+ # maintain compatibility with readelf.
DW_TAG_template_type_param = 0x2f,
- DW_TAG_template_value_parameter = 0x30,
DW_TAG_template_value_param = 0x30,
+
DW_TAG_thrown_type = 0x31,
DW_TAG_try_block = 0x32,
DW_TAG_variant_part = 0x33,
@@ -185,7 +188,9 @@
DW_AT_main_subprogram = 0x6a,
DW_AT_data_bit_offset = 0x6b,
DW_AT_const_expr = 0x6c,
-
+ DW_AT_enum_class = 0x6d,
+ DW_AT_linkage_name = 0x6e,
+
DW_AT_MIPS_fde = 0x2001,
DW_AT_MIPS_loop_begin = 0x2002,
DW_AT_MIPS_tail_loop_begin = 0x2003,
@@ -197,8 +202,46 @@
DW_AT_MIPS_abstract_name = 0x2009,
DW_AT_MIPS_clone_origin = 0x200a,
DW_AT_MIPS_has_inlines = 0x200b,
+ DW_AT_MIPS_stride_byte = 0x200c,
+ DW_AT_MIPS_stride_elem = 0x200d,
+ DW_AT_MIPS_ptr_dopetype = 0x200e,
+ DW_AT_MIPS_allocatable_dopetype = 0x200f,
+ DW_AT_MIPS_assumed_shape_dopetype = 0x2010,
+ DW_AT_MIPS_assumed_size = 0x2011,
- _default_ = Pass,
+ DW_AT_sf_names = 0x2101,
+ DW_AT_src_info = 0x2102,
+ DW_AT_mac_info = 0x2103,
+ DW_AT_src_coords = 0x2104,
+ DW_AT_body_begin = 0x2105,
+ DW_AT_body_end = 0x2106,
+ DW_AT_GNU_vector = 0x2107,
+ DW_AT_GNU_template_name = 0x2110,
+
+ DW_AT_GNU_call_site_value = 0x2111,
+ DW_AT_GNU_call_site_data_value = 0x2112,
+ DW_AT_GNU_call_site_target = 0x2113,
+ DW_AT_GNU_call_site_target_clobbered = 0x2114,
+ DW_AT_GNU_tail_call = 0x2115,
+ DW_AT_GNU_all_tail_call_sites = 0x2116,
+ DW_AT_GNU_all_call_sites = 0x2117,
+ DW_AT_GNU_all_source_call_sites = 0x2118,
+
+ DW_AT_APPLE_optimized = 0x3fe1,
+ DW_AT_APPLE_flags = 0x3fe2,
+ DW_AT_APPLE_isa = 0x3fe3,
+ DW_AT_APPLE_block = 0x3fe4,
+ DW_AT_APPLE_major_runtime_vers = 0x3fe5,
+ DW_AT_APPLE_runtime_class = 0x3fe6,
+ DW_AT_APPLE_omit_frame_ptr = 0x3fe7,
+ DW_AT_APPLE_property_name = 0x3fe8,
+ DW_AT_APPLE_property_getter = 0x3fe9,
+ DW_AT_APPLE_property_setter = 0x3fea,
+ DW_AT_APPLE_property_attribute = 0x3feb,
+ DW_AT_APPLE_objc_complete_type = 0x3fec,
+ DW_AT_APPLE_property = 0x3fed,
+
+ _default_ = Pass,
)
@@ -225,7 +268,16 @@
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16,
+ DW_FORM_sec_offset = 0x17,
+ DW_FORM_exprloc = 0x18,
+ DW_FORM_flag_present = 0x19,
+ DW_FORM_ref_sig8 = 0x20,
+ DW_FORM_GNU_strp_alt = 0x1f21,
+ DW_FORM_GNU_ref_alt = 0x1f20,
_default_ = Pass,
)
+# Inverse mapping for ENUM_DW_FORM
+DW_FORM_raw2name = dict((v, k) for k, v in iteritems(ENUM_DW_FORM))
+
diff --git a/elftools/dwarf/lineprogram.py b/elftools/dwarf/lineprogram.py
index ee5193e..810e603 100644
--- a/elftools/dwarf/lineprogram.py
+++ b/elftools/dwarf/lineprogram.py
@@ -215,10 +215,10 @@
add_entry_old_state(opcode, [operand])
elif opcode == DW_LNS_negate_stmt:
state.is_stmt = not state.is_stmt
- add_entry_old_state(opcode, [operand])
+ add_entry_old_state(opcode, [])
elif opcode == DW_LNS_set_basic_block:
state.basic_block = True
- add_entry_old_state(opcode, [operand])
+ add_entry_old_state(opcode, [])
elif opcode == DW_LNS_const_add_pc:
adjusted_opcode = 255 - self['opcode_base']
address_addend = ((adjusted_opcode // self['line_range']) *
diff --git a/elftools/dwarf/structs.py b/elftools/dwarf/structs.py
index cfb2515..39e4815 100644
--- a/elftools/dwarf/structs.py
+++ b/elftools/dwarf/structs.py
@@ -11,7 +11,7 @@
UBInt8, UBInt16, UBInt32, UBInt64, ULInt8, ULInt16, ULInt32, ULInt64,
SBInt8, SBInt16, SBInt32, SBInt64, SLInt8, SLInt16, SLInt32, SLInt64,
Adapter, Struct, ConstructError, If, RepeatUntil, Field, Rename, Enum,
- Array, PrefixedArray, CString, Embed,
+ Array, PrefixedArray, CString, Embed, StaticField
)
from ..common.construct_utils import RepeatUntilExcluding
@@ -19,39 +19,39 @@
class DWARFStructs(object):
- """ Exposes Construct structs suitable for parsing information from DWARF
+ """ Exposes Construct structs suitable for parsing information from DWARF
sections. Each compile unit in DWARF info can have its own structs
- object. Keep in mind that these structs have to be given a name (by
+ object. Keep in mind that these structs have to be given a name (by
calling them with a name) before being used for parsing (like other
Construct structs). Those that should be used without a name are marked
by (+).
-
+
Accessible attributes (mostly as described in chapter 7 of the DWARF
spec v3):
-
+
Dwarf_[u]int{8,16,32,64):
Data chunks of the common sizes
-
+
Dwarf_offset:
32-bit or 64-bit word, depending on dwarf_format
-
+
Dwarf_target_addr:
32-bit or 64-bit word, depending on address size
-
+
Dwarf_initial_length:
"Initial length field" encoding
section 7.4
-
+
Dwarf_{u,s}leb128:
ULEB128 and SLEB128 variable-length encoding
-
+
Dwarf_CU_header (+):
Compilation unit header
-
+
Dwarf_abbrev_declaration (+):
Abbreviation table declaration - doesn't include the initial
code, only the contents.
-
+
Dwarf_dw_form (+):
A dictionary mapping 'DW_FORM_*' keys into construct Structs
that parse such forms. These Structs have already been given
@@ -62,7 +62,7 @@
Dwarf_lineprog_file_entry (+):
A single file entry in a line program header or instruction
-
+
Dwarf_CIE_header (+):
A call-frame CIE
@@ -71,22 +71,27 @@
See also the documentation of public methods.
"""
- def __init__(self, little_endian, dwarf_format, address_size):
- """ little_endian:
+ def __init__(self,
+ little_endian, dwarf_format, address_size, dwarf_version=2):
+ """ dwarf_version:
+ Numeric DWARF version
+
+ little_endian:
True if the file is little endian, False if big
-
+
dwarf_format:
DWARF Format: 32 or 64-bit (see spec section 7.4)
-
+
address_size:
- Target machine address size, in bytes (4 or 8). (See spec
+ Target machine address size, in bytes (4 or 8). (See spec
section 7.5.1)
"""
assert dwarf_format == 32 or dwarf_format == 64
assert address_size == 8 or address_size == 4
self.little_endian = little_endian
- self.dwarf_format = dwarf_format
+ self.dwarf_format = dwarf_format
self.address_size = address_size
+ self.dwarf_version = dwarf_version
self._create_structs()
def initial_length_field_size(self):
@@ -131,7 +136,7 @@
def _create_initial_length(self):
def _InitialLength(name):
# Adapts a Struct that parses forward a full initial length field.
- # Only if the first word is the continuation value, the second
+ # Only if the first word is the continuation value, the second
# word is parsed from the stream.
#
return _InitialLengthAdapter(
@@ -152,13 +157,13 @@
self.Dwarf_uint16('version'),
self.Dwarf_offset('debug_abbrev_offset'),
self.Dwarf_uint8('address_size'))
-
+
def _create_abbrev_declaration(self):
self.Dwarf_abbrev_declaration = Struct('Dwarf_abbrev_entry',
Enum(self.Dwarf_uleb128('tag'), **ENUM_DW_TAG),
Enum(self.Dwarf_uint8('children_flag'), **ENUM_DW_CHILDREN),
RepeatUntilExcluding(
- lambda obj, ctx:
+ lambda obj, ctx:
obj.name == 'DW_AT_null' and obj.form == 'DW_FORM_null',
Struct('attr_spec',
Enum(self.Dwarf_uleb128('name'), **ENUM_DW_AT),
@@ -167,12 +172,12 @@
def _create_dw_form(self):
self.Dwarf_dw_form = dict(
DW_FORM_addr=self.Dwarf_target_addr(''),
-
+
DW_FORM_block1=self._make_block_struct(self.Dwarf_uint8),
DW_FORM_block2=self._make_block_struct(self.Dwarf_uint16),
DW_FORM_block4=self._make_block_struct(self.Dwarf_uint32),
DW_FORM_block=self._make_block_struct(self.Dwarf_uleb128),
-
+
# All DW_FORM_data<n> forms are assumed to be unsigned
DW_FORM_data1=self.Dwarf_uint8(''),
DW_FORM_data2=self.Dwarf_uint16(''),
@@ -180,19 +185,29 @@
DW_FORM_data8=self.Dwarf_uint64(''),
DW_FORM_sdata=self.Dwarf_sleb128(''),
DW_FORM_udata=self.Dwarf_uleb128(''),
-
+
DW_FORM_string=CString(''),
DW_FORM_strp=self.Dwarf_offset(''),
DW_FORM_flag=self.Dwarf_uint8(''),
-
+
DW_FORM_ref1=self.Dwarf_uint8(''),
DW_FORM_ref2=self.Dwarf_uint16(''),
DW_FORM_ref4=self.Dwarf_uint32(''),
DW_FORM_ref8=self.Dwarf_uint64(''),
DW_FORM_ref_udata=self.Dwarf_uleb128(''),
DW_FORM_ref_addr=self.Dwarf_offset(''),
-
+
DW_FORM_indirect=self.Dwarf_uleb128(''),
+
+ # New forms in DWARFv4
+ DW_FORM_flag_present = StaticField('', 0),
+ DW_FORM_sec_offset = self.Dwarf_offset(''),
+ DW_FORM_exprloc = self._make_block_struct(self.Dwarf_uleb128),
+ DW_FORM_ref_sig8 = self.Dwarf_offset(''),
+
+ DW_FORM_GNU_strp_alt=self.Dwarf_offset(''),
+ DW_FORM_GNU_ref_alt=self.Dwarf_offset(''),
+ DW_AT_GNU_all_call_sites=self.Dwarf_uleb128(''),
)
def _create_lineprog_header(self):
@@ -215,7 +230,7 @@
self.Dwarf_int8('line_base'),
self.Dwarf_uint8('line_range'),
self.Dwarf_uint8('opcode_base'),
- Array(lambda ctx: ctx['opcode_base'] - 1,
+ Array(lambda ctx: ctx['opcode_base'] - 1,
self.Dwarf_uint8('standard_opcode_lengths')),
RepeatUntilExcluding(
lambda obj, ctx: obj == b'',
@@ -226,14 +241,27 @@
)
def _create_callframe_entry_headers(self):
- self.Dwarf_CIE_header = Struct('Dwarf_CIE_header',
- self.Dwarf_initial_length('length'),
- self.Dwarf_offset('CIE_id'),
- self.Dwarf_uint8('version'),
- CString('augmentation'),
- self.Dwarf_uleb128('code_alignment_factor'),
- self.Dwarf_sleb128('data_alignment_factor'),
- self.Dwarf_uleb128('return_address_register'))
+ # The CIE header was modified in DWARFv4.
+ if self.dwarf_version == 4:
+ self.Dwarf_CIE_header = Struct('Dwarf_CIE_header',
+ self.Dwarf_initial_length('length'),
+ self.Dwarf_offset('CIE_id'),
+ self.Dwarf_uint8('version'),
+ CString('augmentation'),
+ self.Dwarf_uint8('address_size'),
+ self.Dwarf_uint8('segment_size'),
+ self.Dwarf_uleb128('code_alignment_factor'),
+ self.Dwarf_sleb128('data_alignment_factor'),
+ self.Dwarf_uleb128('return_address_register'))
+ else:
+ self.Dwarf_CIE_header = Struct('Dwarf_CIE_header',
+ self.Dwarf_initial_length('length'),
+ self.Dwarf_offset('CIE_id'),
+ self.Dwarf_uint8('version'),
+ CString('augmentation'),
+ self.Dwarf_uleb128('code_alignment_factor'),
+ self.Dwarf_sleb128('data_alignment_factor'),
+ self.Dwarf_uleb128('return_address_register'))
self.Dwarf_FDE_header = Struct('Dwarf_FDE_header',
self.Dwarf_initial_length('length'),
@@ -242,7 +270,7 @@
self.Dwarf_target_addr('address_range'))
def _make_block_struct(self, length_field):
- """ Create a struct for DW_FORM_block<size>
+ """ Create a struct for DW_FORM_block<size>
"""
return PrefixedArray(
subcon=self.Dwarf_uint8('elem'),
diff --git a/elftools/elf/constants.py b/elftools/elf/constants.py
index df75e16..b41f35a 100644
--- a/elftools/elf/constants.py
+++ b/elftools/elf/constants.py
@@ -6,6 +6,26 @@
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
+
+class E_FLAGS(object):
+ """ Flag values for the e_flags field of the ELF header
+ """
+ EF_ARM_EABIMASK=0xFF000000
+ EF_ARM_EABI_VER1=0x01000000
+ EF_ARM_EABI_VER2=0x02000000
+ EF_ARM_EABI_VER3=0x03000000
+ EF_ARM_EABI_VER4=0x04000000
+ EF_ARM_EABI_VER5=0x05000000
+ EF_ARM_GCCMASK=0x00400FFF
+ EF_ARM_HASENTRY=0x02
+ EF_ARM_SYMSARESORTED=0x04
+ EF_ARM_DYNSYMSUSESEGIDX=0x8
+ EF_ARM_MAPSYMSFIRST=0x10
+ EF_ARM_LE8=0x00400000
+ EF_ARM_BE8=0x00800000
+ EF_ARM_ABI_FLOAT_SOFT=0x00000200
+ EF_ARM_ABI_FLOAT_HARD=0x00000400
+
class SHN_INDICES(object):
""" Special section indices
"""
@@ -45,3 +65,25 @@
PF_MASKOS=0x00FF0000
PF_MASKPROC=0xFF000000
+
+# symbol info flags for entries
+# in the .SUNW_syminfo section
+class SUNW_SYMINFO_FLAGS(object):
+ """ Flags for the si_flags field of entries
+ in the .SUNW_syminfo section
+ """
+ SYMINFO_FLG_DIRECT=0x1
+ SYMINFO_FLG_FILTER=0x2
+ SYMINFO_FLG_COPY=0x4
+ SYMINFO_FLG_LAZYLOAD=0x8
+ SYMINFO_FLG_DIRECTBIND=0x10
+ SYMINFO_FLG_NOEXTDIRECT=0x20
+ SYMINFO_FLG_AUXILIARY=0x40
+ SYMINFO_FLG_INTERPOSE=0x80
+ SYMINFO_FLG_CAP=0x100
+ SYMINFO_FLG_DEFERRED=0x200
+
+class VER_FLAGS(object):
+ VER_FLG_BASE=0x1
+ VER_FLG_WEAK=0x2
+ VER_FLG_INFO=0x4
diff --git a/elftools/elf/descriptions.py b/elftools/elf/descriptions.py
index df81000..6d108c2 100644
--- a/elftools/elf/descriptions.py
+++ b/elftools/elf/descriptions.py
@@ -7,9 +7,9 @@
# This code is in the public domain
#-------------------------------------------------------------------------------
from .enums import (
- ENUM_D_TAG, ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
- )
-from .constants import P_FLAGS, SH_FLAGS
+ ENUM_D_TAG, ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64,
+ ENUM_RELOC_TYPE_ARM, ENUM_RELOC_TYPE_AARCH64)
+from .constants import P_FLAGS, SH_FLAGS, SUNW_SYMINFO_FLAGS, VER_FLAGS
from ..common.py3compat import iteritems
@@ -77,6 +77,10 @@
return _DESCR_RELOC_TYPE_i386.get(x, _unknown)
elif arch == 'x64':
return _DESCR_RELOC_TYPE_x64.get(x, _unknown)
+ elif arch == 'ARM':
+ return _DESCR_RELOC_TYPE_ARM.get(x, _unknown)
+ elif arch == 'AArch64':
+ return _DESCR_RELOC_TYPE_AARCH64.get(x, _unknown)
else:
return 'unrecognized: %-7x' % (x & 0xFFFFFFFF)
@@ -84,6 +88,28 @@
return _DESCR_D_TAG.get(x, _unknown)
+def describe_syminfo_flags(x):
+ return ''.join(_DESCR_SYMINFO_FLAGS[flag] for flag in (
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_CAP,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECT,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_FILTER,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_AUXILIARY,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECTBIND,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_COPY,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_LAZYLOAD,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_NOEXTDIRECT,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_INTERPOSE,
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DEFERRED) if x & flag)
+
+def describe_symbol_boundto(x):
+ return _DESCR_SYMINFO_BOUNDTO.get(x, '%3s' % x)
+
+def describe_ver_flags(x):
+ return ' | '.join(_DESCR_VER_FLAGS[flag] for flag in (
+ VER_FLAGS.VER_FLG_WEAK,
+ VER_FLAGS.VER_FLG_BASE,
+ VER_FLAGS.VER_FLG_INFO) if x & flag)
+
#-------------------------------------------------------------------------------
_unknown = '<unknown>'
@@ -143,6 +169,9 @@
EM_IA_64='Intel IA-64',
EM_X86_64='Advanced Micro Devices X86-64',
EM_AVR='Atmel AVR 8-bit microcontroller',
+ EM_ARM='ARM',
+ EM_AARCH64='AArch64',
+ EM_BLAFKIN='Analog Devices Blackfin',
RESERVED='RESERVED',
)
@@ -157,6 +186,11 @@
PT_GNU_EH_FRAME='GNU_EH_FRAME',
PT_GNU_STACK='GNU_STACK',
PT_GNU_RELRO='GNU_RELRO',
+ PT_ARM_ARCHEXT='ARM_ARCHEXT',
+ PT_ARM_EXIDX='ARM_EXIDX',
+ PT_ARM_UNWIND='ARM_UNWIND',
+ PT_AARCH64_ARCHEXT='AARCH64_ARCHEXT',
+ PT_AARCH64_UNWIND='AARCH64_UNWIND',
)
_DESCR_P_FLAGS = {
@@ -188,6 +222,10 @@
SHT_GNU_verneed='VERNEED',
SHT_GNU_versym='VERSYM',
SHT_GNU_LIBLIST='GNU_LIBLIST',
+ SHT_ARM_EXIDX='ARM_EXIDX',
+ SHT_ARM_PREEMPTMAP='ARM_PREEMPTMAP',
+ SHT_ARM_ATTRIBUTES='ARM_ATTRIBUTES',
+ SHT_ARM_DEBUGOVERLAY='ARM_DEBUGOVERLAY',
)
_DESCR_SH_FLAGS = {
@@ -227,7 +265,10 @@
STV_DEFAULT='DEFAULT',
STV_INTERNAL='INTERNAL',
STV_HIDDEN='HIDDEN',
- STD_PROTECTED='PROTECTED',
+ STV_PROTECTED='PROTECTED',
+ STV_EXPORTED='EXPORTED',
+ STV_SINGLETON='SINGLETON',
+ STV_ELIMINATE='ELIMINATE',
)
_DESCR_ST_SHNDX = dict(
@@ -236,11 +277,44 @@
SHN_COMMON='COM',
)
+_DESCR_SYMINFO_FLAGS = {
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECT: 'D',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECTBIND: 'B',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_COPY: 'C',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_LAZYLOAD: 'L',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_NOEXTDIRECT: 'N',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_AUXILIARY: 'A',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_FILTER: 'F',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_INTERPOSE: 'I',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_CAP: 'S',
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DEFERRED: 'P',
+}
+
+_DESCR_SYMINFO_BOUNDTO = dict(
+ SYMINFO_BT_SELF='<self>',
+ SYMINFO_BT_PARENT='<parent>',
+ SYMINFO_BT_NONE='',
+ SYMINFO_BT_EXTERN='<extern>',
+)
+
+_DESCR_VER_FLAGS = {
+ 0: '',
+ VER_FLAGS.VER_FLG_BASE: 'BASE',
+ VER_FLAGS.VER_FLG_WEAK: 'WEAK',
+ VER_FLAGS.VER_FLG_INFO: 'INFO',
+}
+
_DESCR_RELOC_TYPE_i386 = dict(
(v, k) for k, v in iteritems(ENUM_RELOC_TYPE_i386))
_DESCR_RELOC_TYPE_x64 = dict(
(v, k) for k, v in iteritems(ENUM_RELOC_TYPE_x64))
+_DESCR_RELOC_TYPE_ARM = dict(
+ (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_ARM))
+
+_DESCR_RELOC_TYPE_AARCH64 = dict(
+ (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_AARCH64))
+
_DESCR_D_TAG = dict(
(v, k) for k, v in iteritems(ENUM_D_TAG))
diff --git a/elftools/elf/dynamic.py b/elftools/elf/dynamic.py
index 824cb78..9f985c2 100644
--- a/elftools/elf/dynamic.py
+++ b/elftools/elf/dynamic.py
@@ -10,10 +10,9 @@
from .sections import Section
from .segments import Segment
+from ..common.exceptions import ELFError
from ..common.utils import struct_parse, parse_cstring_from_stream
-from .enums import ENUM_D_TAG
-
class _DynamicStringTable(object):
""" Bare string table based on values found via ELF dynamic tags and
@@ -40,13 +39,16 @@
value of DT_SONAME (fetched from the dynamic symbol table).
"""
_HANDLED_TAGS = frozenset(
- ['DT_NEEDED', 'DT_RPATH', 'DT_RUNPATH', 'DT_SONAME'])
+ ['DT_NEEDED', 'DT_RPATH', 'DT_RUNPATH', 'DT_SONAME',
+ 'DT_SUNW_FILTER'])
- def __init__(self, entry, dynstr):
+ def __init__(self, entry, stringtable):
+ if stringtable is None:
+ raise ELFError('Creating DynamicTag without string table')
self.entry = entry
- if entry.d_tag in self._HANDLED_TAGS and dynstr:
+ if entry.d_tag in self._HANDLED_TAGS:
setattr(self, entry.d_tag[3:].lower(),
- dynstr.get_string(self.entry.d_val))
+ stringtable.get_string(self.entry.d_val))
def __getitem__(self, name):
""" Implement dict-like access to entries
@@ -67,24 +69,25 @@
class Dynamic(object):
""" Shared functionality between dynamic sections and segments.
"""
- def __init__(self, stream, elffile, position):
+ def __init__(self, stream, elffile, stringtable, position):
self._stream = stream
self._elffile = elffile
self._elfstructs = elffile.structs
self._num_tags = -1
self._offset = position
self._tagsize = self._elfstructs.Elf_Dyn.sizeof()
- self.__string_table = None
- @property
- def _string_table(self):
+ # Do not access this directly yourself; use _get_stringtable() instead.
+ self._stringtable = stringtable
+
+ def _get_stringtable(self):
""" Return a string table for looking up dynamic tag related strings.
- This won't be a "full" string table object, but will at least support
- the get_string() function.
+ This won't be a "full" string table object, but will at least
+ support the get_string() function.
"""
- if self.__string_table:
- return self.__string_table
+ if self._stringtable:
+ return self._stringtable
# If the ELF has stripped its section table (which is unusual, but
# perfectly valid), we need to use the dynamic tags to locate the
@@ -96,19 +99,15 @@
# If we found a dynamic string table, locate the offset in the file
# by using the program headers.
if strtab:
- for segment in self._elffile.iter_segments():
- if (strtab >= segment['p_vaddr'] and
- strtab < segment['p_vaddr'] + segment['p_filesz']):
- self.__string_table = _DynamicStringTable(
- self._stream,
- segment['p_offset'] + (strtab - segment['p_vaddr']))
- return self.__string_table
+ table_offset = next(self._elffile.address_offsets(strtab), None)
+ if table_offset is not None:
+ self._stringtable = _DynamicStringTable(self._stream, table_offset)
+ return self._stringtable
# That didn't work for some reason. Let's use the section header
# even though this ELF is super weird.
- self.__string_table = self._elffile.get_section_by_name(b'.dynstr')
-
- return self.__string_table
+ self._stringtable = self._elffile.get_section_by_name(b'.dynstr')
+ return self._stringtable
def _iter_tags(self, type=None):
""" Yield all raw tags (limit to |type| if specified)
@@ -124,7 +123,7 @@
""" Yield all tags (limit to |type| if specified)
"""
for tag in self._iter_tags(type=type):
- yield DynamicTag(tag, self._string_table)
+ yield DynamicTag(tag, self._get_stringtable())
def _get_tag(self, n):
""" Get the raw tag at index #n from the file
@@ -138,7 +137,7 @@
def get_tag(self, n):
""" Get the tag at index #n from the file (DynamicTag object)
"""
- return DynamicTag(self._get_tag(n), self._string_table)
+ return DynamicTag(self._get_tag(n), self._get_stringtable())
def num_tags(self):
""" Number of dynamic tags in the file
@@ -158,12 +157,25 @@
"""
def __init__(self, header, name, stream, elffile):
Section.__init__(self, header, name, stream)
- Dynamic.__init__(self, stream, elffile, self['sh_offset'])
+ stringtable = elffile.get_section(header['sh_link'])
+ Dynamic.__init__(self, stream, elffile, stringtable, self['sh_offset'])
class DynamicSegment(Segment, Dynamic):
""" ELF dynamic table segment. Knows how to process the list of tags.
"""
def __init__(self, header, stream, elffile):
+ # The string table section to be used to resolve string names in
+ # the dynamic tag array is the one pointed at by the sh_link field
+ # of the dynamic section header.
+ # So we must look for the dynamic section contained in the dynamic
+ # segment, we do so by searching for the dynamic section whose content
+ # is located at the same offset as the dynamic segment
+ stringtable = None
+ for section in elffile.iter_sections():
+ if (isinstance(section, DynamicSection) and
+ section['sh_offset'] == header['p_offset']):
+ stringtable = elffile.get_section(section['sh_link'])
+ break
Segment.__init__(self, header, stream)
- Dynamic.__init__(self, stream, elffile, self['p_offset'])
+ Dynamic.__init__(self, stream, elffile, stringtable, self['p_offset'])
diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py
index 1d8d6de..15aa07e 100644
--- a/elftools/elf/elffile.py
+++ b/elftools/elf/elffile.py
@@ -12,11 +12,14 @@
from ..construct import ConstructError
from .structs import ELFStructs
from .sections import (
- Section, StringTableSection, SymbolTableSection, NullSection)
+ Section, StringTableSection, SymbolTableSection,
+ SUNWSyminfoTableSection, NullSection)
from .dynamic import DynamicSection, DynamicSegment
from .relocation import RelocationSection, RelocationHandler
+from .gnuversions import (
+ GNUVerNeedSection, GNUVerDefSection,
+ GNUVerSymSection)
from .segments import Segment, InterpSegment
-from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
from ..dwarf.dwarfinfo import DWARFInfo, DebugSectionDescriptor, DwarfConfig
@@ -105,6 +108,18 @@
for i in range(self.num_segments()):
yield self.get_segment(i)
+ def address_offsets(self, start, size=1):
+ """ Yield a file offset for each ELF segment containing a memory region.
+
+ A memory region is defined by the range [start...start+size). The
+ offset of the region is yielded.
+ """
+ end = start + size
+ for seg in self.iter_segments():
+ if (start >= seg['p_vaddr'] and
+ end <= seg['p_vaddr'] + seg['p_filesz']):
+ yield start - seg['p_vaddr'] + seg['p_offset']
+
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
@@ -125,24 +140,26 @@
#
debug_sections = {}
for secname in (b'.debug_info', b'.debug_abbrev', b'.debug_str',
- b'.debug_line', b'.debug_frame', b'.debug_loc',
- b'.debug_ranges'):
+ b'.debug_line', b'.debug_frame',
+ b'.debug_loc', b'.debug_ranges'):
section = self.get_section_by_name(secname)
if section is None:
debug_sections[secname] = None
else:
debug_sections[secname] = self._read_dwarf_section(
- section,
- relocate_dwarf_sections)
+ section,
+ relocate_dwarf_sections)
return DWARFInfo(
config=DwarfConfig(
little_endian=self.little_endian,
- default_address_size=self.elfclass / 8,
+ default_address_size=self.elfclass // 8,
machine_arch=self.get_machine_arch()),
debug_info_sec=debug_sections[b'.debug_info'],
debug_abbrev_sec=debug_sections[b'.debug_abbrev'],
debug_frame_sec=debug_sections[b'.debug_frame'],
+ # TODO(eliben): reading of eh_frame is not hooked up yet
+ eh_frame_sec=None,
debug_str_sec=debug_sections[b'.debug_str'],
debug_loc_sec=debug_sections[b'.debug_loc'],
debug_ranges_sec=debug_sections[b'.debug_ranges'],
@@ -158,6 +175,8 @@
return 'x86'
elif self['e_machine'] == 'EM_ARM':
return 'ARM'
+ elif self['e_machine'] == 'EM_AARCH64':
+ return 'AArch64'
else:
return '<unknown>'
@@ -241,8 +260,16 @@
return StringTableSection(section_header, name, self.stream)
elif sectype == 'SHT_NULL':
return NullSection(section_header, name, self.stream)
- elif sectype in ('SHT_SYMTAB', 'SHT_DYNSYM'):
+ elif sectype in ('SHT_SYMTAB', 'SHT_DYNSYM', 'SHT_SUNW_LDYNSYM'):
return self._make_symbol_table_section(section_header, name)
+ elif sectype == 'SHT_SUNW_syminfo':
+ return self._make_sunwsyminfo_table_section(section_header, name)
+ elif sectype == 'SHT_GNU_verneed':
+ return self._make_gnu_verneed_section(section_header, name)
+ elif sectype == 'SHT_GNU_verdef':
+ return self._make_gnu_verdef_section(section_header, name)
+ elif sectype == 'SHT_GNU_versym':
+ return self._make_gnu_versym_section(section_header, name)
elif sectype in ('SHT_REL', 'SHT_RELA'):
return RelocationSection(
section_header, name, self.stream, self)
@@ -261,6 +288,46 @@
elffile=self,
stringtable=strtab_section)
+ def _make_sunwsyminfo_table_section(self, section_header, name):
+ """ Create a SUNWSyminfoTableSection
+ """
+ linked_strtab_index = section_header['sh_link']
+ strtab_section = self.get_section(linked_strtab_index)
+ return SUNWSyminfoTableSection(
+ section_header, name, self.stream,
+ elffile=self,
+ symboltable=strtab_section)
+
+ def _make_gnu_verneed_section(self, section_header, name):
+ """ Create a GNUVerNeedSection
+ """
+ linked_strtab_index = section_header['sh_link']
+ strtab_section = self.get_section(linked_strtab_index)
+ return GNUVerNeedSection(
+ section_header, name, self.stream,
+ elffile=self,
+ stringtable=strtab_section)
+
+ def _make_gnu_verdef_section(self, section_header, name):
+ """ Create a GNUVerDefSection
+ """
+ linked_strtab_index = section_header['sh_link']
+ strtab_section = self.get_section(linked_strtab_index)
+ return GNUVerDefSection(
+ section_header, name, self.stream,
+ elffile=self,
+ stringtable=strtab_section)
+
+ def _make_gnu_versym_section(self, section_header, name):
+ """ Create a GNUVerSymSection
+ """
+ linked_strtab_index = section_header['sh_link']
+ strtab_section = self.get_section(linked_strtab_index)
+ return GNUVerSymSection(
+ section_header, name, self.stream,
+ elffile=self,
+ symboltable=strtab_section)
+
def _get_segment_header(self, n):
""" Find the header of segment #n, parse it and return the struct
"""
diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py
index aaa6c71..4aa449a 100644
--- a/elftools/elf/enums.py
+++ b/elftools/elf/enums.py
@@ -47,6 +47,7 @@
ELFOSABI_OPENVMS=13,
ELFOSABI_NSK=14,
ELFOSABI_AROS=15,
+ ELFOSABI_ARM_AEABI=64,
ELFOSABI_ARM=97,
ELFOSABI_STANDALONE=255,
_default_=Pass,
@@ -159,6 +160,7 @@
EM_ARCA=109,
EM_UNICORE=110,
EM_L10M=180,
+ EM_AARCH64=183,
_default_=Pass,
)
@@ -184,14 +186,20 @@
SHT_NUM=19,
SHT_LOOS=0x60000000,
SHT_GNU_HASH=0x6ffffff6,
- SHT_GNU_verdef=0x6ffffffd,
- SHT_GNU_verneed=0x6ffffffe,
- SHT_GNU_versym=0x6fffffff,
+ SHT_GNU_verdef=0x6ffffffd, # also SHT_SUNW_verdef
+ SHT_GNU_verneed=0x6ffffffe, # also SHT_SUNW_verneed
+ SHT_GNU_versym=0x6fffffff, # also SHT_SUNW_versym
SHT_LOPROC=0x70000000,
SHT_HIPROC=0x7fffffff,
SHT_LOUSER=0x80000000,
SHT_HIUSER=0xffffffff,
SHT_AMD64_UNWIND=0x70000001,
+ SHT_SUNW_LDYNSYM=0x6ffffff3,
+ SHT_SUNW_syminfo=0x6ffffffc,
+ SHT_ARM_EXIDX=0x70000001,
+ SHT_ARM_PREEMPTMAP=0x70000002,
+ SHT_ARM_ATTRIBUTES=0x70000003,
+ SHT_ARM_DEBUGOVERLAY=0x70000004,
_default_=Pass,
)
@@ -211,6 +219,11 @@
PT_GNU_EH_FRAME=0x6474e550,
PT_GNU_STACK=0x6474e551,
PT_GNU_RELRO=0x6474e552,
+ PT_ARM_ARCHEXT=0x70000000,
+ PT_ARM_EXIDX=0x70000001,
+ PT_ARM_UNWIND=0x70000001,
+ PT_AARCH64_ARCHEXT=0x70000000,
+ PT_AARCH64_UNWIND=0x70000001,
_default_=Pass,
)
@@ -252,6 +265,9 @@
STV_INTERNAL=1,
STV_HIDDEN=2,
STV_PROTECTED=3,
+ STV_EXPORTED=4,
+ STV_SINGLETON=5,
+ STV_ELIMINATE=6,
_default_=Pass,
)
@@ -301,6 +317,24 @@
DT_PREINIT_ARRAYSZ=33,
DT_NUM=34,
DT_LOOS=0x6000000d,
+ DT_SUNW_AUXILIARY=0x6000000d,
+ DT_SUNW_RTLDINF=0x6000000e,
+ DT_SUNW_FILTER=0x6000000f,
+ DT_SUNW_CAP=0x60000010,
+ DT_SUNW_SYMTAB=0x60000011,
+ DT_SUNW_SYMSZ=0x60000012,
+ DT_SUNW_ENCODING=0x60000013,
+ DT_SUNW_SORTENT=0x60000013,
+ DT_SUNW_SYMSORT=0x60000014,
+ DT_SUNW_SYMSORTSZ=0x60000015,
+ DT_SUNW_TLSSORT=0x60000016,
+ DT_SUNW_TLSSORTSZ=0x60000017,
+ DT_SUNW_CAPINFO=0x60000018,
+ DT_SUNW_STRPAD=0x60000019,
+ DT_SUNW_CAPCHAIN=0x6000001a,
+ DT_SUNW_LDMACH=0x6000001b,
+ DT_SUNW_CAPCHAINENT=0x6000001d,
+ DT_SUNW_CAPCHAINSZ=0x6000001f,
DT_HIOS=0x6ffff000,
DT_LOPROC=0x70000000,
DT_HIPROC=0x7fffffff,
@@ -428,3 +462,271 @@
_default_=Pass,
)
+# Sunw Syminfo Bound To special values
+ENUM_SUNW_SYMINFO_BOUNDTO = dict(
+ SYMINFO_BT_SELF=0xffff,
+ SYMINFO_BT_PARENT=0xfffe,
+ SYMINFO_BT_NONE=0xfffd,
+ SYMINFO_BT_EXTERN=0xfffc,
+ _default_=Pass,
+)
+
+# Versym section, version dependency index
+ENUM_VERSYM = dict(
+ VER_NDX_LOCAL=0,
+ VER_NDX_GLOBAL=1,
+ VER_NDX_LORESERVE=0xff00,
+ VER_NDX_ELIMINATE=0xff01,
+ _default_=Pass,
+)
+# Sunw Syminfo Bound To special values
+ENUM_SUNW_SYMINFO_BOUNDTO = dict(
+ SYMINFO_BT_SELF=0xffff,
+ SYMINFO_BT_PARENT=0xfffe,
+ SYMINFO_BT_NONE=0xfffd,
+ SYMINFO_BT_EXTERN=0xfffc,
+ _default_=Pass,
+)
+
+ENUM_RELOC_TYPE_ARM = dict(
+ R_ARM_NONE=0,
+ R_ARM_PC24=1,
+ R_ARM_ABS32=2,
+ R_ARM_REL32=3,
+ R_ARM_LDR_PC_G0=4,
+ R_ARM_ABS16=5,
+ R_ARM_ABS12=6,
+ R_ARM_THM_ABS5=7,
+ R_ARM_ABS8=8,
+ R_ARM_SBREL32=9,
+ R_ARM_THM_CALL=10,
+ R_ARM_THM_PC8=11,
+ R_ARM_BREL_ADJ=12,
+ R_ARM_SWI24=13,
+ R_ARM_THM_SWI8=14,
+ R_ARM_XPC25=15,
+ R_ARM_THM_XPC22=16,
+ R_ARM_TLS_DTPMOD32=17,
+ R_ARM_TLS_DTPOFF32=18,
+ R_ARM_TLS_TPOFF32=19,
+ R_ARM_COPY=20,
+ R_ARM_GLOB_DAT=21,
+ R_ARM_JUMP_SLOT=22,
+ R_ARM_RELATIVE=23,
+ R_ARM_GOTOFF32=24,
+ R_ARM_BASE_PREL=25,
+ R_ARM_GOT_BREL=26,
+ R_ARM_PLT32=27,
+ R_ARM_CALL=28,
+ R_ARM_JUMP24=29,
+ R_ARM_THM_JUMP24=30,
+ R_ARM_BASE_ABS=31,
+ R_ARM_ALU_PCREL_7_0=32,
+ R_ARM_ALU_PCREL_15_8=33,
+ R_ARM_ALU_PCREL_23_15=34,
+ R_ARM_LDR_SBREL_11_0_NC=35,
+ R_ARM_ALU_SBREL_19_12_NC=36,
+ R_ARM_ALU_SBREL_27_20_CK=37,
+ R_ARM_TARGET1=38,
+ R_ARM_SBREL31=39,
+ R_ARM_V4BX=40,
+ R_ARM_TARGET2=41,
+ R_ARM_PREL31=42,
+ R_ARM_MOVW_ABS_NC=43,
+ R_ARM_MOVT_ABS=44,
+ R_ARM_MOVW_PREL_NC=45,
+ R_ARM_MOVT_PREL=46,
+ R_ARM_THM_MOVW_ABS_NC=47,
+ R_ARM_THM_MOVT_ABS=48,
+ R_ARM_THM_MOVW_PREL_NC=49,
+ R_ARM_THM_MOVT_PREL=50,
+ R_ARM_THM_JUMP19=51,
+ R_ARM_THM_JUMP6=52,
+ R_ARM_THM_ALU_PREL_11_0=53,
+ R_ARM_THM_PC12=54,
+ R_ARM_ABS32_NOI=55,
+ R_ARM_REL32_NOI=56,
+ R_ARM_ALU_PC_G0_NC=57,
+ R_ARM_ALU_PC_G0=58,
+ R_ARM_ALU_PC_G1_NC=59,
+ R_ARM_ALU_PC_G1=60,
+ R_ARM_ALU_PC_G2=61,
+ R_ARM_LDR_PC_G1=62,
+ R_ARM_LDR_PC_G2=63,
+ R_ARM_LDRS_PC_G0=64,
+ R_ARM_LDRS_PC_G1=65,
+ R_ARM_LDRS_PC_G2=66,
+ R_ARM_LDC_PC_G0=67,
+ R_ARM_LDC_PC_G1=68,
+ R_ARM_LDC_PC_G2=69,
+ R_ARM_ALU_SB_G0_NC=70,
+ R_ARM_ALU_SB_G0=71,
+ R_ARM_ALU_SB_G1_NC=72,
+ R_ARM_ALU_SB_G1=73,
+ R_ARM_ALU_SB_G2=74,
+ R_ARM_LDR_SB_G0=75,
+ R_ARM_LDR_SB_G1=76,
+ R_ARM_LDR_SB_G2=77,
+ R_ARM_LDRS_SB_G0=78,
+ R_ARM_LDRS_SB_G1=79,
+ R_ARM_LDRS_SB_G2=80,
+ R_ARM_LDC_SB_G0=81,
+ R_ARM_LDC_SB_G1=82,
+ R_ARM_LDC_SB_G2=83,
+ R_ARM_MOVW_BREL_NC=84,
+ R_ARM_MOVT_BREL=85,
+ R_ARM_MOVW_BREL=86,
+ R_ARM_THM_MOVW_BREL_NC=87,
+ R_ARM_THM_MOVT_BREL=88,
+ R_ARM_THM_MOVW_BREL=89,
+ R_ARM_PLT32_ABS=94,
+ R_ARM_GOT_ABS=95,
+ R_ARM_GOT_PREL=96,
+ R_ARM_GOT_BREL12=97,
+ R_ARM_GOTOFF12=98,
+ R_ARM_GOTRELAX=99,
+ R_ARM_GNU_VTENTRY=100,
+ R_ARM_GNU_VTINHERIT=101,
+ R_ARM_THM_JUMP11=102,
+ R_ARM_THM_JUMP8=103,
+ R_ARM_TLS_GD32=104,
+ R_ARM_TLS_LDM32=105,
+ R_ARM_TLS_LDO32=106,
+ R_ARM_TLS_IE32=107,
+ R_ARM_TLS_LE32=108,
+ R_ARM_TLS_LDO12=109,
+ R_ARM_TLS_LE12=110,
+ R_ARM_TLS_IE12GP=111,
+ R_ARM_PRIVATE_0=112,
+ R_ARM_PRIVATE_1=113,
+ R_ARM_PRIVATE_2=114,
+ R_ARM_PRIVATE_3=115,
+ R_ARM_PRIVATE_4=116,
+ R_ARM_PRIVATE_5=117,
+ R_ARM_PRIVATE_6=118,
+ R_ARM_PRIVATE_7=119,
+ R_ARM_PRIVATE_8=120,
+ R_ARM_PRIVATE_9=121,
+ R_ARM_PRIVATE_10=122,
+ R_ARM_PRIVATE_11=123,
+ R_ARM_PRIVATE_12=124,
+ R_ARM_PRIVATE_13=125,
+ R_ARM_PRIVATE_14=126,
+ R_ARM_PRIVATE_15=127,
+ R_ARM_ME_TOO=128,
+ R_ARM_THM_TLS_DESCSEQ16=129,
+ R_ARM_THM_TLS_DESCSEQ32=130,
+ R_ARM_THM_GOT_BREL12=131,
+ R_ARM_IRELATIVE=140,
+)
+
+ENUM_RELOC_TYPE_AARCH64 = dict(
+ R_AARCH64_NONE=256,
+ R_AARCH64_ABS64=257,
+ R_AARCH64_ABS32=258,
+ R_AARCH64_ABS16=259,
+ R_AARCH64_PREL64=260,
+ R_AARCH64_PREL32=261,
+ R_AARCH64_PREL16=262,
+ R_AARCH64_MOVW_UABS_G0=263,
+ R_AARCH64_MOVW_UABS_G0_NC=264,
+ R_AARCH64_MOVW_UABS_G1=265,
+ R_AARCH64_MOVW_UABS_G1_NC=266,
+ R_AARCH64_MOVW_UABS_G2=267,
+ R_AARCH64_MOVW_UABS_G2_NC=268,
+ R_AARCH64_MOVW_UABS_G3=269,
+ R_AARCH64_MOVW_SABS_G0=270,
+ R_AARCH64_MOVW_SABS_G1=271,
+ R_AARCH64_MOVW_SABS_G2=272,
+ R_AARCH64_LD_PREL_LO19=273,
+ R_AARCH64_ADR_PREL_LO21=274,
+ R_AARCH64_ADR_PREL_PG_HI21=275,
+ R_AARCH64_ADR_PREL_PG_HI21_NC=276,
+ R_AARCH64_ADD_ABS_LO12_NC=277,
+ R_AARCH64_LDST8_ABS_LO12_NC=278,
+ R_AARCH64_TSTBR14=279,
+ R_AARCH64_CONDBR19=280,
+ R_AARCH64_JUMP26=282,
+ R_AARCH64_CALL26=283,
+ R_AARCH64_LDST16_ABS_LO12_NC=284,
+ R_AARCH64_LDST32_ABS_LO12_NC=285,
+ R_AARCH64_LDST64_ABS_LO12_NC=286,
+ R_AARCH64_MOVW_PREL_G0=287,
+ R_AARCH64_MOVW_PREL_G0_NC=288,
+ R_AARCH64_MOVW_PREL_G1=289,
+ R_AARCH64_MOVW_PREL_G1_NC=290,
+ R_AARCH64_MOVW_PREL_G2=291,
+ R_AARCH64_MOVW_PREL_G2_NC=292,
+ R_AARCH64_MOVW_PREL_G3=293,
+ R_AARCH64_MOVW_GOTOFF_G0=300,
+ R_AARCH64_MOVW_GOTOFF_G0_NC=301,
+ R_AARCH64_MOVW_GOTOFF_G1=302,
+ R_AARCH64_MOVW_GOTOFF_G1_NC=303,
+ R_AARCH64_MOVW_GOTOFF_G2=304,
+ R_AARCH64_MOVW_GOTOFF_G2_NC=305,
+ R_AARCH64_MOVW_GOTOFF_G3=306,
+ R_AARCH64_GOTREL64=307,
+ R_AARCH64_GOTREL32=308,
+ R_AARCH64_GOT_LD_PREL19=309,
+ R_AARCH64_LD64_GOTOFF_LO15=310,
+ R_AARCH64_ADR_GOT_PAGE=311,
+ R_AARCH64_LD64_GOT_LO12_NC=312,
+ R_AARCH64_TLSGD_ADR_PREL21=512,
+ R_AARCH64_TLSGD_ADR_PAGE21=513,
+ R_AARCH64_TLSGD_ADD_LO12_NC=514,
+ R_AARCH64_TLSGD_MOVW_G1=515,
+ R_AARCH64_TLSGD_MOVW_G0_NC=516,
+ R_AARCH64_TLSLD_ADR_PREL21=517,
+ R_AARCH64_TLSLD_ADR_PAGE21=518,
+ R_AARCH64_TLSLD_ADD_LO12_NC=519,
+ R_AARCH64_TLSLD_MOVW_G1=520,
+ R_AARCH64_TLSLD_MOVW_G0_NC=521,
+ R_AARCH64_TLSLD_LD_PREL19=522,
+ R_AARCH64_TLSLD_MOVW_DTPREL_G2=523,
+ R_AARCH64_TLSLD_MOVW_DTPREL_G1=524,
+ R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC=525,
+ R_AARCH64_TLSLD_MOVW_DTPREL_G0=526,
+ R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC=527,
+ R_AARCH64_TLSLD_ADD_DTPREL_HI12=528,
+ R_AARCH64_TLSLD_ADD_DTPREL_LO12=529,
+ R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC=530,
+ R_AARCH64_TLSLD_LDST8_DTPREL_LO12=531,
+ R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC=532,
+ R_AARCH64_TLSLD_LDST16_DTPREL_LO12=533,
+ R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC=534,
+ R_AARCH64_TLSLD_LDST32_DTPREL_LO12=535,
+ R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC=536,
+ R_AARCH64_TLSLD_LDST64_DTPREL_LO12=537,
+ R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC=538,
+ R_AARCH64_TLSIE_MOVW_GOTTPREL_G1=539,
+ R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC=540,
+ R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21=541,
+ R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC=542,
+ R_AARCH64_TLSIE_LD_GOTTPREL_PREL19=543,
+ R_AARCH64_TLSLE_MOVW_TPREL_G2=544,
+ R_AARCH64_TLSLE_MOVW_TPREL_G1=545,
+ R_AARCH64_TLSLE_MOVW_TPREL_G1_NC=546,
+ R_AARCH64_TLSLE_MOVW_TPREL_G0=547,
+ R_AARCH64_TLSLE_MOVW_TPREL_G0_NC=548,
+ R_AARCH64_TLSLE_ADD_TPREL_HI12=549,
+ R_AARCH64_TLSLE_ADD_TPREL_LO12=550,
+ R_AARCH64_TLSLE_ADD_TPREL_LO12_NC=551,
+ R_AARCH64_TLSLE_LDST8_TPREL_LO12=552,
+ R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC=553,
+ R_AARCH64_TLSLE_LDST16_TPREL_LO12=554,
+ R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC=555,
+ R_AARCH64_TLSLE_LDST32_TPREL_LO12=556,
+ R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC=557,
+ R_AARCH64_TLSLE_LDST64_TPREL_LO12=558,
+ R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC=559,
+ R_AARCH64_COPY=1024,
+ R_AARCH64_GLOB_DAT=1025,
+ R_AARCH64_JUMP_SLOT=1026,
+ R_AARCH64_RELATIVE=1027,
+ R_AARCH64_TLS_DTPREL64=1028,
+ R_AARCH64_TLS_DTPMOD64=1029,
+ R_AARCH64_TLS_TPREL64=1030,
+ R_AARCH64_TLS_DTPREL32=1031,
+ R_AARCH64_TLS_DTPMOD32=1032,
+ R_AARCH64_TLS_TPREL32=1033,
+)
diff --git a/elftools/elf/gnuversions.py b/elftools/elf/gnuversions.py
new file mode 100644
index 0000000..4a4473f
--- /dev/null
+++ b/elftools/elf/gnuversions.py
@@ -0,0 +1,228 @@
+#------------------------------------------------------------------------------
+# elftools: elf/gnuversions.py
+#
+# ELF sections
+#
+# Yann Rouillard (yann@pleiades.fr.eu.org)
+# This code is in the public domain
+#------------------------------------------------------------------------------
+from ..construct import CString
+from ..common.utils import struct_parse, elf_assert
+from .sections import Section, Symbol
+
+
+class Version(object):
+ """ Version object - representing a version definition or dependency
+ entry from a "Version Needed" or a "Version Dependency" table section.
+
+ This kind of entry contains a pointer to an array of auxiliary entries
+ that store the information about version names or dependencies.
+ These entries are not stored in this object and should be accessed
+ through the appropriate method of a section object which will return
+ an iterator of VersionAuxiliary objects.
+
+ Similarly to Section objects, allows dictionary-like access to
+ verdef/verneed entry
+ """
+ def __init__(self, entry, name=None):
+ self.entry = entry
+ self.name = name
+
+ def __getitem__(self, name):
+ """ Implement dict-like access to entry
+ """
+ return self.entry[name]
+
+
+class VersionAuxiliary(object):
+ """ Version Auxiliary object - representing an auxiliary entry of a version
+ definition or dependency entry
+
+ Similarly to Section objects, allows dictionary-like access to the
+ verdaux/vernaux entry
+ """
+ def __init__(self, entry, name):
+ self.entry = entry
+ self.name = name
+
+ def __getitem__(self, name):
+ """ Implement dict-like access to entries
+ """
+ return self.entry[name]
+
+
+class GNUVersionSection(Section):
+ """ Common ancestor class for ELF SUNW|GNU Version Needed/Dependency
+ sections class which contains shareable code
+ """
+
+ def __init__(self, header, name, stream, elffile, stringtable,
+ field_prefix, version_struct, version_auxiliaries_struct):
+ super(GNUVersionSection, self).__init__(header, name, stream)
+ self.elffile = elffile
+ self.stringtable = stringtable
+ self.field_prefix = field_prefix
+ self.version_struct = version_struct
+ self.version_auxiliaries_struct = version_auxiliaries_struct
+
+ def num_versions(self):
+ """ Number of version entries in the section
+ """
+ return self['sh_info']
+
+ def _field_name(self, name, auxiliary=False):
+ """ Return the real field's name of version or a version auxiliary
+ entry
+ """
+ middle = 'a_' if auxiliary else '_'
+ return self.field_prefix + middle + name
+
+ def _iter_version_auxiliaries(self, entry_offset, count):
+ """ Yield all auxiliary entries of a version entry
+ """
+ name_field = self._field_name('name', auxiliary=True)
+ next_field = self._field_name('next', auxiliary=True)
+
+ for _ in range(count):
+ entry = struct_parse(
+ self.version_auxiliaries_struct,
+ self.stream,
+ stream_pos=entry_offset)
+
+ name = self.stringtable.get_string(entry[name_field])
+ version_aux = VersionAuxiliary(entry, name)
+ yield version_aux
+
+ entry_offset += entry[next_field]
+
+ def iter_versions(self):
+ """ Yield all the version entries in the section
+ Each time it returns the main version structure
+ and an iterator to walk through its auxiliaries entries
+ """
+ aux_field = self._field_name('aux')
+ count_field = self._field_name('cnt')
+ next_field = self._field_name('next')
+
+ entry_offset = self['sh_offset']
+ for _ in range(self.num_versions()):
+ entry = struct_parse(
+ self.version_struct,
+ self.stream,
+ stream_pos=entry_offset)
+
+ elf_assert(entry[count_field] > 0,
+ 'Expected number of version auxiliary entries (%s) to be > 0'
+ 'for the following version entry: %s' % (
+ count_field, str(entry)))
+
+ version = Version(entry)
+ aux_entries_offset = entry_offset + entry[aux_field]
+ version_auxiliaries_iter = self._iter_version_auxiliaries(
+ aux_entries_offset, entry[count_field])
+
+ yield version, version_auxiliaries_iter
+
+ entry_offset += entry[next_field]
+
+
+class GNUVerNeedSection(GNUVersionSection):
+ """ ELF SUNW or GNU Version Needed table section.
+ Has an associated StringTableSection that's passed in the constructor.
+ """
+ def __init__(self, header, name, stream, elffile, stringtable):
+ super(GNUVerNeedSection, self).__init__(
+ header, name, stream, elffile, stringtable, 'vn',
+ elffile.structs.Elf_Verneed, elffile.structs.Elf_Vernaux)
+ self._has_indexes = None
+
+ def has_indexes(self):
+ """ Return True if at least one version definition entry has an index
+ that is stored in the vna_other field.
+ This information is used for symbol versioning
+ """
+ if self._has_indexes is None:
+ self._has_indexes = False
+ for _, vernaux_iter in self.iter_versions():
+ for vernaux in vernaux_iter:
+ if vernaux['vna_other']:
+ self._has_indexes = True
+ break
+
+ return self._has_indexes
+
+ def iter_versions(self):
+ for verneed, vernaux in super(GNUVerNeedSection, self).iter_versions():
+ verneed.name = self.stringtable.get_string(verneed['vn_file'])
+ yield verneed, vernaux
+
+ def get_version(self, index):
+ """ Get the version information located at index #n in the table
+ Return boths the verneed structure and the vernaux structure
+ that contains the name of the version
+ """
+ for verneed, vernaux_iter in self.iter_versions():
+ for vernaux in vernaux_iter:
+ if vernaux['vna_other'] == index:
+ return verneed, vernaux
+
+ return None
+
+
+class GNUVerDefSection(GNUVersionSection):
+ """ ELF SUNW or GNU Version Definition table section.
+ Has an associated StringTableSection that's passed in the constructor.
+ """
+ def __init__(self, header, name, stream, elffile, stringtable):
+ super(GNUVerDefSection, self).__init__(
+ header, name, stream, elffile, stringtable, 'vd',
+ elffile.structs.Elf_Verdef, elffile.structs.Elf_Verdaux)
+
+ def get_version(self, index):
+ """ Get the version information located at index #n in the table
+ Return boths the verdef structure and an iterator to retrieve
+ both the version names and dependencies in the form of
+ verdaux entries
+ """
+ for verdef, verdaux_iter in self.iter_versions():
+ if verdef['vd_ndx'] == index:
+ return verdef, verdaux_iter
+
+ return None
+
+
+class GNUVerSymSection(Section):
+ """ ELF SUNW or GNU Versym table section.
+ Has an associated SymbolTableSection that's passed in the constructor.
+ """
+ def __init__(self, header, name, stream, elffile, symboltable):
+ super(GNUVerSymSection, self).__init__(header, name, stream)
+ self.elffile = elffile
+ self.elfstructs = self.elffile.structs
+ self.symboltable = symboltable
+
+ def num_symbols(self):
+ """ Number of symbols in the table
+ """
+ return self['sh_size'] // self['sh_entsize']
+
+ def get_symbol(self, n):
+ """ Get the symbol at index #n from the table (Symbol object)
+ It begins at 1 and not 0 since the first entry is used to
+ store the current version of the syminfo table
+ """
+ # Grab the symbol's entry from the stream
+ entry_offset = self['sh_offset'] + n * self['sh_entsize']
+ entry = struct_parse(
+ self.elfstructs.Elf_Versym,
+ self.stream,
+ stream_pos=entry_offset)
+ # Find the symbol name in the associated symbol table
+ name = self.symboltable.get_symbol(n).name
+ return Symbol(entry, name)
+
+ def iter_symbols(self):
+ """ Yield all the symbols in the table
+ """
+ for i in range(self.num_symbols()):
+ yield self.get_symbol(i)
diff --git a/elftools/elf/relocation.py b/elftools/elf/relocation.py
index 7c2b74c..176f7c5 100644
--- a/elftools/elf/relocation.py
+++ b/elftools/elf/relocation.py
@@ -178,6 +178,11 @@
addend=reloc['r_addend'] if recipe.has_addend else 0)
# 3. Write the relocated value back into the stream
stream.seek(reloc['r_offset'])
+
+ # Make sure the relocated value fits back by wrapping it around. This
+ # looks like a problem, but it seems to be the way this is done in
+ # binutils too.
+ relocated_value = relocated_value % (2 ** (recipe.bytesize * 8))
value_struct.build_stream(relocated_value, stream)
# Relocations are represented by "recipes". Each recipe specifies:
@@ -202,6 +207,9 @@
def _reloc_calc_sym_plus_addend(value, sym_value, offset, addend=0):
return sym_value + addend
+ def _reloc_calc_sym_plus_addend_pcrel(value, sym_value, offset, addend=0):
+ return sym_value + addend - offset
+
_RELOCATION_RECIPES_X86 = {
ENUM_RELOC_TYPE_i386['R_386_NONE']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=False, calc_func=_reloc_calc_identity),
@@ -218,6 +226,9 @@
bytesize=8, has_addend=True, calc_func=_reloc_calc_identity),
ENUM_RELOC_TYPE_x64['R_X86_64_64']: _RELOCATION_RECIPE_TYPE(
bytesize=8, has_addend=True, calc_func=_reloc_calc_sym_plus_addend),
+ ENUM_RELOC_TYPE_x64['R_X86_64_PC32']: _RELOCATION_RECIPE_TYPE(
+ bytesize=8, has_addend=True,
+ calc_func=_reloc_calc_sym_plus_addend_pcrel),
ENUM_RELOC_TYPE_x64['R_X86_64_32']: _RELOCATION_RECIPE_TYPE(
bytesize=4, has_addend=True, calc_func=_reloc_calc_sym_plus_addend),
ENUM_RELOC_TYPE_x64['R_X86_64_32S']: _RELOCATION_RECIPE_TYPE(
diff --git a/elftools/elf/sections.py b/elftools/elf/sections.py
index 518c857..1380d6b 100644
--- a/elftools/elf/sections.py
+++ b/elftools/elf/sections.py
@@ -6,14 +6,13 @@
# Eli Bendersky (eliben@gmail.com)
# This code is in the public domain
#-------------------------------------------------------------------------------
-from ..construct import CString
from ..common.utils import struct_parse, elf_assert, parse_cstring_from_stream
class Section(object):
""" Base class for ELF sections. Also used for all sections types that have
no special functionality.
-
+
Allows dictionary-like access to the section header. For example:
> sec = Section(...)
> sec['sh_type'] # section type
@@ -22,7 +21,7 @@
self.header = header
self.name = name
self.stream = stream
-
+
def data(self):
""" The section data from the file.
"""
@@ -33,7 +32,7 @@
""" Is this a null section?
"""
return False
-
+
def __getitem__(self, name):
""" Implement dict-like access to header entries
"""
@@ -41,6 +40,8 @@
def __eq__(self, other):
return self.header == other.header
+ def __hash__(self):
+ return hash(self.header)
class NullSection(Section):
@@ -51,14 +52,14 @@
def is_null(self):
return True
-
+
class StringTableSection(Section):
""" ELF string table section.
"""
def __init__(self, header, name, stream):
super(StringTableSection, self).__init__(header, name, stream)
-
+
def get_string(self, offset):
""" Get the string stored at the given offset in this string table.
"""
@@ -77,15 +78,15 @@
self.elfstructs = self.elffile.structs
self.stringtable = stringtable
elf_assert(self['sh_entsize'] > 0,
- 'Expected entry size of section %s to be > 0' % name)
+ 'Expected entry size of section %r to be > 0' % name)
elf_assert(self['sh_size'] % self['sh_entsize'] == 0,
- 'Expected section size to be a multiple of entry size in section %s' % name)
+ 'Expected section size to be a multiple of entry size in section %r' % name)
def num_symbols(self):
""" Number of symbols in the table
"""
return self['sh_size'] // self['sh_entsize']
-
+
def get_symbol(self, n):
""" Get the symbol at index #n from the table (Symbol object)
"""
@@ -123,3 +124,38 @@
return self.entry[name]
+class SUNWSyminfoTableSection(Section):
+ """ ELF .SUNW Syminfo table section.
+ Has an associated SymbolTableSection that's passed in the constructor.
+ """
+ def __init__(self, header, name, stream, elffile, symboltable):
+ super(SUNWSyminfoTableSection, self).__init__(header, name, stream)
+ self.elffile = elffile
+ self.elfstructs = self.elffile.structs
+ self.symboltable = symboltable
+
+ def num_symbols(self):
+ """ Number of symbols in the table
+ """
+ return self['sh_size'] // self['sh_entsize'] - 1
+
+ def get_symbol(self, n):
+ """ Get the symbol at index #n from the table (Symbol object).
+ It begins at 1 and not 0 since the first entry is used to
+ store the current version of the syminfo table.
+ """
+ # Grab the symbol's entry from the stream
+ entry_offset = self['sh_offset'] + n * self['sh_entsize']
+ entry = struct_parse(
+ self.elfstructs.Elf_Sunw_Syminfo,
+ self.stream,
+ stream_pos=entry_offset)
+ # Find the symbol name in the associated symbol table
+ name = self.symboltable.get_symbol(n).name
+ return Symbol(entry, name)
+
+ def iter_symbols(self):
+ """ Yield all the symbols in the table
+ """
+ for i in range(1, self.num_symbols() + 1):
+ yield self.get_symbol(i)
diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py
index 08567de..0862400 100644
--- a/elftools/elf/structs.py
+++ b/elftools/elf/structs.py
@@ -19,20 +19,20 @@
class ELFStructs(object):
""" Accessible attributes:
-
+
Elf_{byte|half|word|word64|addr|offset|sword|xword|xsword}:
- Data chunks, as specified by the ELF standard, adjusted for
+ Data chunks, as specified by the ELF standard, adjusted for
correct endianness and word-size.
Elf_Ehdr:
ELF file header
-
+
Elf_Phdr:
Program header
-
+
Elf_Shdr:
Section header
-
+
Elf_Sym:
Symbol table entry
@@ -42,9 +42,9 @@
def __init__(self, little_endian=True, elfclass=32):
assert elfclass == 32 or elfclass == 64
self.little_endian = little_endian
- self.elfclass = elfclass
+ self.elfclass = elfclass
self._create_structs()
-
+
def _create_structs(self):
if self.little_endian:
self.Elf_byte = ULInt8
@@ -66,14 +66,18 @@
self.Elf_sword = SBInt32
self.Elf_xword = UBInt32 if self.elfclass == 32 else UBInt64
self.Elf_sxword = SBInt32 if self.elfclass == 32 else SBInt64
-
+
self._create_ehdr()
self._create_phdr()
self._create_shdr()
self._create_sym()
self._create_rel()
self._create_dyn()
-
+ self._create_sunw_syminfo()
+ self._create_gnu_verneed()
+ self._create_gnu_verdef()
+ self._create_gnu_versym()
+
def _create_ehdr(self):
self.Elf_Ehdr = Struct('Elf_Ehdr',
Struct('e_ident',
@@ -99,7 +103,7 @@
self.Elf_half('e_shnum'),
self.Elf_half('e_shstrndx'),
)
-
+
def _create_phdr(self):
if self.elfclass == 32:
self.Elf_Phdr = Struct('Elf_Phdr',
@@ -122,8 +126,8 @@
self.Elf_xword('p_filesz'),
self.Elf_xword('p_memsz'),
self.Elf_xword('p_align'),
- )
-
+ )
+
def _create_shdr(self):
self.Elf_Shdr = Struct('Elf_Shdr',
self.Elf_word('sh_name'),
@@ -137,7 +141,7 @@
self.Elf_xword('sh_addralign'),
self.Elf_xword('sh_entsize'),
)
-
+
def _create_rel(self):
# r_info is also taken apart into r_info_sym and r_info_type.
# This is done in Value to avoid endianity issues while parsing.
@@ -203,5 +207,50 @@
self.Elf_xword('st_size'),
)
+ def _create_sunw_syminfo(self):
+ self.Elf_Sunw_Syminfo = Struct('Elf_Sunw_Syminfo',
+ Enum(self.Elf_half('si_boundto'), **ENUM_SUNW_SYMINFO_BOUNDTO),
+ self.Elf_half('si_flags'),
+ )
+ def _create_gnu_verneed(self):
+ # Structure of "version needed" entries is documented in
+ # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format
+ self.Elf_Verneed = Struct('Elf_Verneed',
+ self.Elf_half('vn_version'),
+ self.Elf_half('vn_cnt'),
+ self.Elf_word('vn_file'),
+ self.Elf_word('vn_aux'),
+ self.Elf_word('vn_next'),
+ )
+ self.Elf_Vernaux = Struct('Elf_Vernaux',
+ self.Elf_word('vna_hash'),
+ self.Elf_half('vna_flags'),
+ self.Elf_half('vna_other'),
+ self.Elf_word('vna_name'),
+ self.Elf_word('vna_next'),
+ )
+ def _create_gnu_verdef(self):
+ # Structure off "version definition" entries are documented in
+ # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format
+ self.Elf_Verdef = Struct('Elf_Verdef',
+ self.Elf_half('vd_version'),
+ self.Elf_half('vd_flags'),
+ self.Elf_half('vd_ndx'),
+ self.Elf_half('vd_cnt'),
+ self.Elf_word('vd_hash'),
+ self.Elf_word('vd_aux'),
+ self.Elf_word('vd_next'),
+ )
+ self.Elf_Verdaux = Struct('Elf_Verdaux',
+ self.Elf_word('vda_name'),
+ self.Elf_word('vda_next'),
+ )
+
+ def _create_gnu_versym(self):
+ # Structure off "version symbol" entries are documented in
+ # Oracle "Linker and Libraries Guide", Chapter 7 Object File Format
+ self.Elf_Versym = Struct('Elf_Versym',
+ Enum(self.Elf_half('ndx'), **ENUM_VERSYM),
+ )
diff --git a/examples/dwarf_die_tree.py b/examples/dwarf_die_tree.py
index 9dcb6b6..ef108c3 100644
--- a/examples/dwarf_die_tree.py
+++ b/examples/dwarf_die_tree.py
@@ -14,7 +14,6 @@
# examples/ dir of the source distribution.
sys.path[0:0] = ['.', '..']
-from elftools.common.py3compat import bytes2str
from elftools.elf.elffile import ELFFile
@@ -44,15 +43,8 @@
top_DIE = CU.get_top_DIE()
print(' Top DIE with tag=%s' % top_DIE.tag)
- # Each DIE holds an OrderedDict of attributes, mapping names to
- # values. Values are represented by AttributeValue objects in
- # elftools/dwarf/die.py
- # We're interested in the DW_AT_name attribute. Note that its value
- # is usually a string taken from the .debug_string section. This
- # is done transparently by the library, and such a value will be
- # simply given as a string.
- name_attr = top_DIE.attributes['DW_AT_name']
- print(' name=%s' % bytes2str(name_attr.value))
+ # We're interested in the filename...
+ print(' name=%s' % top_DIE.get_full_path())
# Display DIEs recursively starting with top_DIE
die_info_rec(top_DIE)
diff --git a/examples/dwarf_range_lists.py b/examples/dwarf_range_lists.py
index fced6a6..6e8998d 100644
--- a/examples/dwarf_range_lists.py
+++ b/examples/dwarf_range_lists.py
@@ -37,6 +37,9 @@
# The range lists are extracted by DWARFInfo from the .debug_ranges
# section, and returned here as a RangeLists object.
range_lists = dwarfinfo.range_lists()
+ if range_lists is None:
+ print(' file has no .debug_ranges section')
+ return
for CU in dwarfinfo.iter_CUs():
# DWARFInfo allows to iterate over the compile units contained in
diff --git a/examples/examine_dwarf_info.py b/examples/examine_dwarf_info.py
index 1aa28c6..0e1619e 100644
--- a/examples/examine_dwarf_info.py
+++ b/examples/examine_dwarf_info.py
@@ -13,7 +13,6 @@
# examples/ dir of the source distribution.
sys.path[0:0] = ['.', '..']
-from elftools.common.py3compat import bytes2str
from elftools.elf.elffile import ELFFile
@@ -43,15 +42,8 @@
top_DIE = CU.get_top_DIE()
print(' Top DIE with tag=%s' % top_DIE.tag)
- # Each DIE holds an OrderedDict of attributes, mapping names to
- # values. Values are represented by AttributeValue objects in
- # elftools/dwarf/die.py
- # We're interested in the DW_AT_name attribute. Note that its value
- # is usually a string taken from the .debug_str section. This
- # is done transparently by the library, and such a value will be
- # simply given as a string.
- name_attr = top_DIE.attributes['DW_AT_name']
- print(' name=%s' % bytes2str(name_attr.value))
+ # We're interested in the filename...
+ print(' name=%s' % top_DIE.get_full_path())
if __name__ == '__main__':
for filename in sys.argv[1:]:
diff --git a/examples/reference_output/dwarf_die_tree.out b/examples/reference_output/dwarf_die_tree.out
index 143cbbb..4a81a8e 100644
--- a/examples/reference_output/dwarf_die_tree.out
+++ b/examples/reference_output/dwarf_die_tree.out
@@ -1,11 +1,11 @@
Processing file: ./examples/sample_exe64.elf
Found a compile unit at offset 0, length 115
Top DIE with tag=DW_TAG_compile_unit
- name=../sysdeps/x86_64/elf/start.S
+ name=/usr/src/packages/BUILD/glibc-2.11.1/csu/../sysdeps/x86_64/elf/start.S
DIE tag=DW_TAG_compile_unit
Found a compile unit at offset 119, length 135
Top DIE with tag=DW_TAG_compile_unit
- name=init.c
+ name=/usr/src/packages/BUILD/glibc-2.11.1/csu/init.c
DIE tag=DW_TAG_compile_unit
DIE tag=DW_TAG_base_type
DIE tag=DW_TAG_base_type
@@ -21,7 +21,7 @@
DIE tag=DW_TAG_const_type
Found a compile unit at offset 258, length 156
Top DIE with tag=DW_TAG_compile_unit
- name=z.c
+ name=/tmp/ebenders/z.c
DIE tag=DW_TAG_compile_unit
DIE tag=DW_TAG_subprogram
DIE tag=DW_TAG_formal_parameter
@@ -33,7 +33,7 @@
DIE tag=DW_TAG_variable
Found a compile unit at offset 418, length 300
Top DIE with tag=DW_TAG_compile_unit
- name=elf-init.c
+ name=/usr/src/packages/BUILD/glibc-2.11.1/csu/elf-init.c
DIE tag=DW_TAG_compile_unit
DIE tag=DW_TAG_base_type
DIE tag=DW_TAG_typedef
diff --git a/examples/reference_output/examine_dwarf_info.out b/examples/reference_output/examine_dwarf_info.out
index 968be29..5114626 100644
--- a/examples/reference_output/examine_dwarf_info.out
+++ b/examples/reference_output/examine_dwarf_info.out
@@ -1,13 +1,13 @@
Processing file: ./examples/sample_exe64.elf
Found a compile unit at offset 0, length 115
Top DIE with tag=DW_TAG_compile_unit
- name=../sysdeps/x86_64/elf/start.S
+ name=/usr/src/packages/BUILD/glibc-2.11.1/csu/../sysdeps/x86_64/elf/start.S
Found a compile unit at offset 119, length 135
Top DIE with tag=DW_TAG_compile_unit
- name=init.c
+ name=/usr/src/packages/BUILD/glibc-2.11.1/csu/init.c
Found a compile unit at offset 258, length 156
Top DIE with tag=DW_TAG_compile_unit
- name=z.c
+ name=/tmp/ebenders/z.c
Found a compile unit at offset 418, length 300
Top DIE with tag=DW_TAG_compile_unit
- name=elf-init.c
+ name=/usr/src/packages/BUILD/glibc-2.11.1/csu/elf-init.c
diff --git a/scripts/readelf.py b/scripts/readelf.py
index 720106d..8179c01 100755
--- a/scripts/readelf.py
+++ b/scripts/readelf.py
@@ -25,6 +25,10 @@
from elftools.elf.enums import ENUM_D_TAG
from elftools.elf.segments import InterpSegment
from elftools.elf.sections import SymbolTableSection
+from elftools.elf.gnuversions import (
+ GNUVerSymSection, GNUVerDefSection,
+ GNUVerNeedSection,
+ )
from elftools.elf.relocation import RelocationSection
from elftools.elf.descriptions import (
describe_ei_class, describe_ei_data, describe_ei_version,
@@ -33,7 +37,9 @@
describe_sh_type, describe_sh_flags,
describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
+ describe_ver_flags,
)
+from elftools.elf.constants import E_FLAGS
from elftools.dwarf.dwarfinfo import DWARFInfo
from elftools.dwarf.descriptions import (
describe_reg_name, describe_attr_value, set_global_machine_arch,
@@ -61,6 +67,8 @@
# Lazily initialized if a debug dump is requested
self._dwarfinfo = None
+ self._versioninfo = None
+
def display_file_header(self):
""" Display the ELF file header
"""
@@ -94,8 +102,9 @@
self._emit(' Start of section headers: %s' %
header['e_shoff'])
self._emitline(' (bytes into file)')
- self._emitline(' Flags: %s' %
- self._format_hex(header['e_flags']))
+ self._emitline(' Flags: %s%s' %
+ (self._format_hex(header['e_flags']),
+ self.decode_flags(header['e_flags'])))
self._emitline(' Size of this header: %s (bytes)' %
header['e_ehsize'])
self._emitline(' Size of program headers: %s (bytes)' %
@@ -109,6 +118,17 @@
self._emitline(' Section header string table index: %s' %
header['e_shstrndx'])
+ def decode_flags(self, flags):
+ description = ""
+ if self.elffile['e_machine'] == "EM_ARM":
+ if flags & E_FLAGS.EF_ARM_HASENTRY:
+ description += ", has entry point"
+
+ version = flags & E_FLAGS.EF_ARM_EABIMASK
+ if version == E_FLAGS.EF_ARM_EABI_VER5:
+ description += ", Version5 EABI"
+ return description
+
def display_program_headers(self, show_heading=True):
""" Display the ELF program headers.
If show_heading is True, displays the heading for this information
@@ -254,6 +274,8 @@
def display_symbol_tables(self):
""" Display the symbol tables contained in the file
"""
+ self._init_versioninfo()
+
for section in self.elffile.iter_sections():
if not isinstance(section, SymbolTableSection):
continue
@@ -272,24 +294,47 @@
self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
for nsym, symbol in enumerate(section.iter_symbols()):
+
+ version_info = ''
+ # readelf doesn't display version info for Solaris versioning
+ if (section['sh_type'] == 'SHT_DYNSYM' and
+ self._versioninfo['type'] == 'GNU'):
+ version = self._symbol_version(nsym)
+ if (version['name'] != bytes2str(symbol.name) and
+ version['index'] not in ('VER_NDX_LOCAL',
+ 'VER_NDX_GLOBAL')):
+ if version['filename']:
+ # external symbol
+ version_info = '@%(name)s (%(index)i)' % version
+ else:
+ # internal symbol
+ if version['hidden']:
+ version_info = '@%(name)s' % version
+ else:
+ version_info = '@@%(name)s' % version
+
# symbol names are truncated to 25 chars, similarly to readelf
- self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
+ self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
nsym,
- self._format_hex(symbol['st_value'], fullhex=True, lead0x=False),
+ self._format_hex(
+ symbol['st_value'], fullhex=True, lead0x=False),
symbol['st_size'],
describe_symbol_type(symbol['st_info']['type']),
describe_symbol_bind(symbol['st_info']['bind']),
describe_symbol_visibility(symbol['st_other']['visibility']),
describe_symbol_shndx(symbol['st_shndx']),
- bytes2str(symbol.name)))
+ bytes2str(symbol.name),
+ version_info))
def display_dynamic_tags(self):
""" Display the dynamic tags contained in the file
"""
+ has_dynamic_sections = False
for section in self.elffile.iter_sections():
if not isinstance(section, DynamicSection):
continue
+ has_dynamic_sections = True
self._emitline("\nDynamic section at offset %s contains %s entries:" % (
self._format_hex(section['sh_offset']),
section.num_tags()))
@@ -305,11 +350,9 @@
parsed = 'Library runpath: [%s]' % bytes2str(tag.runpath)
elif tag.entry.d_tag == 'DT_SONAME':
parsed = 'Library soname: [%s]' % bytes2str(tag.soname)
- elif (tag.entry.d_tag.endswith('SZ') or
- tag.entry.d_tag.endswith('ENT')):
+ elif tag.entry.d_tag.endswith(('SZ', 'ENT')):
parsed = '%i (bytes)' % tag['d_val']
- elif (tag.entry.d_tag.endswith('NUM') or
- tag.entry.d_tag.endswith('COUNT')):
+ elif tag.entry.d_tag.endswith(('NUM', 'COUNT')):
parsed = '%i' % tag['d_val']
elif tag.entry.d_tag == 'DT_PLTREL':
s = describe_dyn_tag(tag.entry.d_val)
@@ -325,6 +368,10 @@
padding,
'(%s)' % (tag.entry.d_tag[3:],),
parsed))
+ if not has_dynamic_sections:
+ # readelf only prints this if there is at least one segment
+ if self.elffile.num_segments():
+ self._emitline("\nThere is no dynamic section in this file.")
def display_relocations(self):
""" Display the relocations contained in the file
@@ -384,6 +431,111 @@
if not has_relocation_sections:
self._emitline('\nThere are no relocations in this file.')
+ def display_version_info(self):
+ """ Display the version info contained in the file
+ """
+ self._init_versioninfo()
+
+ if not self._versioninfo['type']:
+ self._emitline("\nNo version information found in this file.")
+ return
+
+ for section in self.elffile.iter_sections():
+ if isinstance(section, GNUVerSymSection):
+ self._print_version_section_header(
+ section, 'Version symbols', lead0x=False)
+
+ num_symbols = section.num_symbols()
+
+ # Symbol version info are printed four by four entries
+ for idx_by_4 in range(0, num_symbols, 4):
+
+ self._emit(' %03x:' % idx_by_4)
+
+ for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):
+
+ symbol_version = self._symbol_version(idx)
+ if symbol_version['index'] == 'VER_NDX_LOCAL':
+ version_index = 0
+ version_name = '(*local*)'
+ elif symbol_version['index'] == 'VER_NDX_GLOBAL':
+ version_index = 1
+ version_name = '(*global*)'
+ else:
+ version_index = symbol_version['index']
+ version_name = '(%(name)s)' % symbol_version
+
+ visibility = 'h' if symbol_version['hidden'] else ' '
+
+ self._emit('%4x%s%-13s' % (
+ version_index, visibility, version_name))
+
+ self._emitline()
+
+ elif isinstance(section, GNUVerDefSection):
+ self._print_version_section_header(
+ section, 'Version definition', indent=2)
+
+ offset = 0
+ for verdef, verdaux_iter in section.iter_versions():
+ verdaux = next(verdaux_iter)
+
+ name = verdaux.name
+ if verdef['vd_flags']:
+ flags = describe_ver_flags(verdef['vd_flags'])
+ # Mimic exactly the readelf output
+ flags += ' '
+ else:
+ flags = 'none'
+
+ self._emitline(' %s: Rev: %i Flags: %s Index: %i'
+ ' Cnt: %i Name: %s' % (
+ self._format_hex(offset, fieldsize=6,
+ alternate=True),
+ verdef['vd_version'], flags, verdef['vd_ndx'],
+ verdef['vd_cnt'], bytes2str(name)))
+
+ verdaux_offset = (
+ offset + verdef['vd_aux'] + verdaux['vda_next'])
+ for idx, verdaux in enumerate(verdaux_iter, start=1):
+ self._emitline(' %s: Parent %i: %s' %
+ (self._format_hex(verdaux_offset, fieldsize=4),
+ idx, bytes2str(verdaux.name)))
+ verdaux_offset += verdaux['vda_next']
+
+ offset += verdef['vd_next']
+
+ elif isinstance(section, GNUVerNeedSection):
+ self._print_version_section_header(section, 'Version needs')
+
+ offset = 0
+ for verneed, verneed_iter in section.iter_versions():
+
+ self._emitline(' %s: Version: %i File: %s Cnt: %i' % (
+ self._format_hex(offset, fieldsize=6,
+ alternate=True),
+ verneed['vn_version'], bytes2str(verneed.name),
+ verneed['vn_cnt']))
+
+ vernaux_offset = offset + verneed['vn_aux']
+ for idx, vernaux in enumerate(verneed_iter, start=1):
+ if vernaux['vna_flags']:
+ flags = describe_ver_flags(vernaux['vna_flags'])
+ # Mimic exactly the readelf output
+ flags += ' '
+ else:
+ flags = 'none'
+
+ self._emitline(
+ ' %s: Name: %s Flags: %s Version: %i' % (
+ self._format_hex(vernaux_offset, fieldsize=4),
+ bytes2str(vernaux.name), flags,
+ vernaux['vna_other']))
+
+ vernaux_offset += vernaux['vna_next']
+
+ offset += verneed['vn_next']
+
def display_hex_dump(self, section_spec):
""" Display a hex dump of a section. section_spec is either a section
number or a name.
@@ -486,7 +638,8 @@
else:
self._emitline('debug dump not yet supported for "%s"' % dump_what)
- def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True):
+ def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
+ alternate=False):
""" Format an address into a hexadecimal string.
fieldsize:
@@ -501,7 +654,20 @@
lead0x:
If True, leading 0x is added
+
+ alternate:
+ If True, override lead0x to emulate the alternate
+ hexadecimal form specified in format string with the #
+ character: only non-zero values are prefixed with 0x.
+ This form is used by readelf.
"""
+ if alternate:
+ if addr == 0:
+ lead0x = False
+ else:
+ lead0x = True
+ fieldsize -= 2
+
s = '0x' if lead0x else ''
if fullhex:
fieldsize = 8 if self.elffile.elfclass == 32 else 16
@@ -511,6 +677,97 @@
field = '%' + '0%sx' % fieldsize
return s + field % addr
+ def _print_version_section_header(self, version_section, name, lead0x=True,
+ indent=1):
+ """ Print a section header of one version related section (versym,
+ verneed or verdef) with some options to accomodate readelf
+ little differences between each header (e.g. indentation
+ and 0x prefixing).
+ """
+ if hasattr(version_section, 'num_versions'):
+ num_entries = version_section.num_versions()
+ else:
+ num_entries = version_section.num_symbols()
+
+ self._emitline("\n%s section '%s' contains %s entries:" %
+ (name, bytes2str(version_section.name), num_entries))
+ self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' % (
+ ' ' * indent,
+ self._format_hex(
+ version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
+ self._format_hex(
+ version_section['sh_offset'], fieldsize=6, lead0x=True),
+ version_section['sh_link'],
+ bytes2str(
+ self.elffile.get_section(version_section['sh_link']).name)
+ )
+ )
+
+ def _init_versioninfo(self):
+ """ Search and initialize informations about version related sections
+ and the kind of versioning used (GNU or Solaris).
+ """
+ if self._versioninfo is not None:
+ return
+
+ self._versioninfo = {'versym': None, 'verdef': None,
+ 'verneed': None, 'type': None}
+
+ for section in self.elffile.iter_sections():
+ if isinstance(section, GNUVerSymSection):
+ self._versioninfo['versym'] = section
+ elif isinstance(section, GNUVerDefSection):
+ self._versioninfo['verdef'] = section
+ elif isinstance(section, GNUVerNeedSection):
+ self._versioninfo['verneed'] = section
+ elif isinstance(section, DynamicSection):
+ for tag in section.iter_tags():
+ if tag['d_tag'] == 'DT_VERSYM':
+ self._versioninfo['type'] = 'GNU'
+ break
+
+ if not self._versioninfo['type'] and (
+ self._versioninfo['verneed'] or self._versioninfo['verdef']):
+ self._versioninfo['type'] = 'Solaris'
+
+ def _symbol_version(self, nsym):
+ """ Return a dict containing information on the
+ or None if no version information is available
+ """
+ self._init_versioninfo()
+
+ symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))
+
+ if (not self._versioninfo['versym'] or
+ nsym >= self._versioninfo['versym'].num_symbols()):
+ return None
+
+ symbol = self._versioninfo['versym'].get_symbol(nsym)
+ index = symbol.entry['ndx']
+ if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
+ index = int(index)
+
+ if self._versioninfo['type'] == 'GNU':
+ # In GNU versioning mode, the highest bit is used to
+ # store wether the symbol is hidden or not
+ if index & 0x8000:
+ index &= ~0x8000
+ symbol_version['hidden'] = True
+
+ if (self._versioninfo['verdef'] and
+ index <= self._versioninfo['verdef'].num_versions()):
+ _, verdaux_iter = \
+ self._versioninfo['verdef'].get_version(index)
+ symbol_version['name'] = bytes2str(next(verdaux_iter).name)
+ else:
+ verneed, vernaux = \
+ self._versioninfo['verneed'].get_version(index)
+ symbol_version['name'] = bytes2str(vernaux.name)
+ symbol_version['filename'] = bytes2str(verneed.name)
+
+ symbol_version['index'] = index
+ return symbol_version
+
def _section_from_spec(self, spec):
""" Retrieve a section given a "spec" (either number or name).
Return None if no such section exists in the file.
@@ -664,8 +921,10 @@
for entry in self._dwarfinfo.CFI_entries():
if isinstance(entry, CIE):
- self._emitline('\n%08x %08x %08x CIE' % (
- entry.offset, entry['length'], entry['CIE_id']))
+ self._emitline('\n%08x %s %s CIE' % (
+ entry.offset,
+ self._format_hex(entry['length'], fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_id'], fullhex=True, lead0x=False)))
self._emitline(' Version: %d' % entry['version'])
self._emitline(' Augmentation: "%s"' % bytes2str(entry['augmentation']))
self._emitline(' Code alignment factor: %u' % entry['code_alignment_factor'])
@@ -673,13 +932,15 @@
self._emitline(' Return address column: %d' % entry['return_address_register'])
self._emitline()
else: # FDE
- self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
+ self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
entry.offset,
- entry['length'],
- entry['CIE_pointer'],
+ self._format_hex(entry['length'], fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_pointer'], fullhex=True, lead0x=False),
entry.cie.offset,
- entry['initial_location'],
- entry['initial_location'] + entry['address_range']))
+ self._format_hex(entry['initial_location'], fullhex=True, lead0x=False),
+ self._format_hex(
+ entry['initial_location'] + entry['address_range'],
+ fullhex=True, lead0x=False)))
self._emit(describe_CFI_instructions(entry))
self._emitline()
@@ -694,23 +955,24 @@
for entry in self._dwarfinfo.CFI_entries():
if isinstance(entry, CIE):
- self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
+ self._emitline('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
entry.offset,
- entry['length'],
- entry['CIE_id'],
+ self._format_hex(entry['length'], fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_id'], fullhex=True, lead0x=False),
bytes2str(entry['augmentation']),
entry['code_alignment_factor'],
entry['data_alignment_factor'],
entry['return_address_register']))
ra_regnum = entry['return_address_register']
else: # FDE
- self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
+ self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
entry.offset,
- entry['length'],
- entry['CIE_pointer'],
+ self._format_hex(entry['length'], fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_pointer'], fullhex=True, lead0x=False),
entry.cie.offset,
- entry['initial_location'],
- entry['initial_location'] + entry['address_range']))
+ self._format_hex(entry['initial_location'], fullhex=True, lead0x=False),
+ self._format_hex(entry['initial_location'] + entry['address_range'],
+ fullhex=True, lead0x=False)))
ra_regnum = entry.cie['return_address_register']
# Print the heading row for the decoded table
@@ -802,6 +1064,9 @@
optparser.add_option('-p', '--string-dump',
action='store', dest='show_string_dump', metavar='<number|name>',
help='Dump the contents of section <number|name> as strings')
+ optparser.add_option('-V', '--version-info',
+ action='store_true', dest='show_version_info',
+ help='Display the version sections (if present)')
optparser.add_option('--debug-dump',
action='store', dest='debug_dump_what', metavar='<what>',
help=(
@@ -838,6 +1103,8 @@
readelf.display_symbol_tables()
if options.show_relocs:
readelf.display_relocations()
+ if options.show_version_info:
+ readelf.display_version_info()
if options.show_hex_dump:
readelf.display_hex_dump(options.show_hex_dump)
if options.show_string_dump:
diff --git a/setup.py b/setup.py
index c8d54b1..3fed12d 100644
--- a/setup.py
+++ b/setup.py
@@ -24,11 +24,11 @@
description='Library for analyzing ELF files and DWARF debugging information',
long_description=description,
license='Public domain',
- version='0.21',
+ version='0.22',
author='Eli Bendersky',
maintainer='Eli Bendersky',
author_email='eliben@gmail.com',
- url='https://bitbucket.org/eliben/pyelftools',
+ url='https://github.com/eliben/pyelftools',
platforms='Cross Platform',
classifiers = [
'Programming Language :: Python :: 2',
@@ -44,7 +44,5 @@
'elftools.construct', 'elftools.construct.lib',
],
- scripts=['scripts/readelf.py'],
+ scripts=['scripts/readelf.py']
)
-
-
diff --git a/test/all_tests.py b/test/all_tests.py
new file mode 100755
index 0000000..4cb8e3c
--- /dev/null
+++ b/test/all_tests.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+#-------------------------------------------------------------------------------
+# test/all_tests.py
+#
+# Run all pyelftools tests.
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+from __future__ import print_function
+import subprocess, sys
+from utils import is_in_rootdir
+
+def run_test_script(path):
+ cmd = [sys.executable, path]
+ print("Running '%s'" % ' '.join(cmd))
+ subprocess.check_call(cmd)
+
+def main():
+ if not is_in_rootdir():
+ testlog.error('Error: Please run me from the root dir of pyelftools!')
+ return 1
+ run_test_script('test/run_all_unittests.py')
+ run_test_script('test/run_examples_test.py')
+ run_test_script('test/run_readelf_tests.py')
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/test/external_tools/README.txt b/test/external_tools/README.txt
index a6c496a..3380cce 100644
--- a/test/external_tools/README.txt
+++ b/test/external_tools/README.txt
@@ -1,2 +1,4 @@
Some utilities that use libelf to create synthetic ELF files
+Also, readelf picked up from a built binutils. Run it with --version to version
+details. The binary is built on a 64-bit Ubuntu machine.
diff --git a/test/external_tools/readelf b/test/external_tools/readelf
new file mode 100755
index 0000000..fb5d91a
--- /dev/null
+++ b/test/external_tools/readelf
Binary files differ
diff --git a/test/run_all_unittests.py b/test/run_all_unittests.py
index f7291bc..70a57d6 100755
--- a/test/run_all_unittests.py
+++ b/test/run_all_unittests.py
@@ -9,16 +9,25 @@
#-------------------------------------------------------------------------------
from __future__ import print_function
+import os, sys
+
try:
import unittest2 as unittest
except ImportError:
import unittest
-if __name__ == '__main__':
- import os
+def main():
if not os.path.isdir('test'):
print('!! Please execute from the root directory of pyelftools')
+ return 1
else:
tests = unittest.TestLoader().discover('test', 'test*.py', 'test')
- unittest.TextTestRunner().run(tests)
+ result = unittest.TextTestRunner().run(tests)
+ if result.wasSuccessful():
+ return 0
+ else:
+ return 1
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/test/run_readelf_tests.py b/test/run_readelf_tests.py
index 0c542d8..2c1d5f3 100755
--- a/test/run_readelf_tests.py
+++ b/test/run_readelf_tests.py
@@ -23,14 +23,10 @@
testlog.setLevel(logging.DEBUG)
testlog.addHandler(logging.StreamHandler(sys.stdout))
-# Set the path for calling readelf. By default this is the system readelf.
-# The first assignment to READELF_PATH reflects the binutils version I used
-# to test the current pyelftools with.
-# Alas, binutils's readelf changes its output slightly even between minor
-# releases so a lot of bogus differences can occur; this is why an exact version
-# is specified to reproduce the tests.
-#
-READELF_PATH = '/home/eliben/test/binutils-2.23.52/binutils/readelf'
+# Set the path for calling readelf. We carry our own version of readelf around,
+# because binutils tend to change its output even between daily builds of the
+# same minor release and keeping track is a headache.
+READELF_PATH = 'test/external_tools/readelf'
if not os.path.exists(READELF_PATH):
READELF_PATH = 'readelf'
@@ -50,7 +46,7 @@
success = True
testlog.info("Test file '%s'" % filename)
for option in [
- '-e', '-d', '-s', '-r', '-x.text', '-p.shstrtab',
+ '-e', '-d', '-s', '-r', '-x.text', '-p.shstrtab', '-V',
'--debug-dump=info', '--debug-dump=decodedline',
'--debug-dump=frames', '--debug-dump=frames-interp']:
if verbose: testlog.info("..option='%s'" % option)
@@ -90,9 +86,9 @@
Note: this function contains some rather horrible hacks to ignore
differences which are not important for the verification of pyelftools.
This is due to some intricacies of binutils's readelf which pyelftools
- doesn't currently implement, or silly inconsistencies in the output of
- readelf, which I was reluctant to replicate.
- Read the documentation for more details.
+ doesn't currently implement, features that binutils doesn't support,
+ or silly inconsistencies in the output of readelf, which I was reluctant
+ to replicate. Read the documentation for more details.
"""
def prepare_lines(s):
return [line for line in s.lower().splitlines() if line.strip() != '']
@@ -125,8 +121,19 @@
# Compare ignoring whitespace
lines1_parts = lines1[i].split()
lines2_parts = lines2[i].split()
+
if ''.join(lines1_parts) != ''.join(lines2_parts):
ok = False
+
+ try:
+ # Ignore difference in precision of hex representation in the
+ # last part (i.e. 008f3b vs 8f3b)
+ if (''.join(lines1_parts[:-1]) == ''.join(lines2_parts[:-1]) and
+ int(lines1_parts[-1], 16) == int(lines2_parts[-1], 16)):
+ ok = True
+ except ValueError:
+ pass
+
sm = SequenceMatcher()
sm.set_seqs(lines1[i], lines2[i])
changes = sm.get_opcodes()
@@ -146,14 +153,17 @@
elif 'os/abi' in lines1[i]:
if 'unix - gnu' in lines1[i] and 'unix - linux' in lines2[i]:
ok = True
+ elif ( 'unknown at value' in lines1[i] and
+ 'dw_at_apple' in lines2[i]):
+ ok = True
else:
for s in ('t (tls)', 'l (large)'):
if s in lines1[i] or s in lines2[i]:
ok = True
break
if not ok:
- errmsg = 'Mismatch on line #%s:\n>>%s<<\n>>%s<<\n' % (
- i, lines1[i], lines2[i])
+ errmsg = 'Mismatch on line #%s:\n>>%s<<\n>>%s<<\n (%r)' % (
+ i, lines1[i], lines2[i], changes)
return False, errmsg
return True, ''
@@ -183,7 +193,7 @@
if len(args) > 0:
filenames = args
else:
- filenames = list(discover_testfiles('test/testfiles'))
+ filenames = list(discover_testfiles('test/testfiles_for_readelf'))
success = True
for filename in filenames:
diff --git a/test/test_arm_support.py b/test/test_arm_support.py
index b2b0ab2..6493663 100644
--- a/test/test_arm_support.py
+++ b/test/test_arm_support.py
@@ -15,7 +15,7 @@
class TestARMSupport(unittest.TestCase):
def test_hello(self):
- with open(os.path.join('test', 'testfiles',
+ with open(os.path.join('test', 'testfiles_for_unittests',
'simple_gcc.elf.arm'), 'rb') as f:
elf = ELFFile(f)
self.assertEqual(elf.get_machine_arch(), 'ARM')
@@ -25,6 +25,20 @@
self.assertEqual(elf.num_sections(), 14)
self.assertEqual(elf.num_segments(), 2)
+ def test_DWARF_indirect_forms(self):
+ # This file uses a lot of DW_FORM_indirect, and is also an ARM ELF
+ # with non-trivial DWARF info.
+ # So this is a simple sanity check that we can successfully parse it
+ # and extract the expected amount of CUs.
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'arm_with_form_indirect.elf'), 'rb') as f:
+ elffile = ELFFile(f)
+ self.assertTrue(elffile.has_dwarf_info())
+
+ dwarfinfo = elffile.get_dwarf_info()
+ all_CUs = list(dwarfinfo.iter_CUs())
+ self.assertEqual(len(all_CUs), 9)
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/test_double_dynstr_section.py b/test/test_double_dynstr_section.py
new file mode 100644
index 0000000..3078554
--- /dev/null
+++ b/test/test_double_dynstr_section.py
@@ -0,0 +1,61 @@
+#------------------------------------------------------------------------------
+# elftools tests
+#
+# Yann Rouillard (yann@pleiades.fr.eu.org)
+# This code is in the public domain
+#------------------------------------------------------------------------------
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+import os
+
+from utils import setup_syspath; setup_syspath()
+from elftools.elf.elffile import ELFFile
+from elftools.elf.dynamic import DynamicSection, DynamicTag
+
+
+class TestDoubleDynstrSections(unittest.TestCase):
+ """ This test make sure than dynamic tags
+ are properly analyzed when two .dynstr
+ sections are present in an elf file
+ """
+
+ reference_data = [
+ b'libz.so.1',
+ b'libc.so.6',
+ b'lib_versioned.so.1',
+ ]
+
+ def _test_double_dynstr_section_generic(self, testfile):
+
+ with open(os.path.join('test', 'testfiles_for_unittests', testfile),
+ 'rb') as f:
+ elf = ELFFile(f)
+ for section in elf.iter_sections():
+ if isinstance(section, DynamicSection):
+ d_tags = [getattr(x, x.entry.d_tag[3:].lower())
+ for x in section.iter_tags()
+ if x.entry.d_tag in DynamicTag._HANDLED_TAGS]
+ self.assertListEqual(
+ TestDoubleDynstrSections.reference_data,
+ d_tags)
+ return
+ self.fail('No dynamic section found !!')
+
+
+ def test_double_dynstr_section(self):
+ """ First test with the good dynstr section first
+ """
+ self._test_double_dynstr_section_generic(
+ 'lib_with_two_dynstr_sections.so.1.elf')
+
+ def test_double_dynstr_section_reverse(self):
+ """ Second test with the good dynstr section last
+ """
+ self._test_double_dynstr_section_generic(
+ 'lib_with_two_dynstr_sections_reversed.so.1.elf')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_dwarf_range_lists.py b/test/test_dwarf_range_lists.py
new file mode 100644
index 0000000..81dab9a
--- /dev/null
+++ b/test/test_dwarf_range_lists.py
@@ -0,0 +1,34 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com), Santhosh Kumar Mani (santhoshmani@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+import os
+
+from utils import setup_syspath; setup_syspath()
+from elftools.elf.elffile import ELFFile
+
+class TestRangeLists(unittest.TestCase):
+ # Test the absence of .debug_ranges section
+ def test_range_list_absence(self):
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'arm_with_form_indirect.elf'), 'rb') as f:
+ elffile = ELFFile(f)
+ self.assertTrue(elffile.has_dwarf_info())
+ self.assertIsNone(elffile.get_dwarf_info().range_lists())
+
+ # Test the presence of .debug_ranges section
+ def test_range_list_presence(self):
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'sample_exe64.elf'), 'rb') as f:
+ elffile = ELFFile(f)
+ self.assertTrue(elffile.has_dwarf_info())
+ self.assertIsNotNone(elffile.get_dwarf_info().range_lists())
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_dynamic.py b/test/test_dynamic.py
new file mode 100644
index 0000000..f25feba
--- /dev/null
+++ b/test/test_dynamic.py
@@ -0,0 +1,51 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+import os
+
+from utils import setup_syspath
+setup_syspath()
+from elftools.elf.elffile import ELFFile
+from elftools.common.exceptions import ELFError
+from elftools.elf.dynamic import DynamicTag
+
+
+class TestDynamicTag(unittest.TestCase):
+ """Tests for the DynamicTag class."""
+
+ def test_requires_stringtable(self):
+ with self.assertRaises(ELFError):
+ dt = DynamicTag('', None)
+
+
+class TestDynamic(unittest.TestCase):
+ """Tests for the Dynamic class."""
+
+ def test_missing_sections(self):
+ """Verify we can get dynamic strings w/out section headers"""
+
+ libs = []
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'aarch64_super_stripped.elf'), 'rb') as f:
+ elf = ELFFile(f)
+ for segment in elf.iter_segments():
+ if segment.header.p_type != 'PT_DYNAMIC':
+ continue
+
+ for t in segment.iter_tags():
+ if t.entry.d_tag == 'DT_NEEDED':
+ libs.append(t.needed.decode('utf-8'))
+
+ exp = ['libc.so.6']
+ self.assertEqual(libs, exp)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_elffile.py b/test/test_elffile.py
new file mode 100644
index 0000000..5c7b2b8
--- /dev/null
+++ b/test/test_elffile.py
@@ -0,0 +1,50 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+from utils import setup_syspath; setup_syspath()
+from elftools.elf.elffile import ELFFile
+
+class TestMap(unittest.TestCase):
+ def test_address_offsets(self):
+ class MockELF(ELFFile):
+ __init__ = object.__init__
+ def iter_segments(self):
+ return iter((
+ dict(p_vaddr=0x10200, p_filesz=0x200, p_offset=0x100),
+ dict(p_vaddr=0x10100, p_filesz=0x100, p_offset=0x400),
+ ))
+
+ elf = MockELF()
+
+ self.assertEqual(tuple(elf.address_offsets(0x10100)), (0x400,))
+ self.assertEqual(tuple(elf.address_offsets(0x10120)), (0x420,))
+ self.assertEqual(tuple(elf.address_offsets(0x101FF)), (0x4FF,))
+ self.assertEqual(tuple(elf.address_offsets(0x10200)), (0x100,))
+ self.assertEqual(tuple(elf.address_offsets(0x100FF)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x10400)), ())
+
+ self.assertEqual(
+ tuple(elf.address_offsets(0x10100, 0x100)), (0x400,))
+ self.assertEqual(tuple(elf.address_offsets(0x10100, 4)), (0x400,))
+ self.assertEqual(tuple(elf.address_offsets(0x10120, 4)), (0x420,))
+ self.assertEqual(tuple(elf.address_offsets(0x101FC, 4)), (0x4FC,))
+ self.assertEqual(tuple(elf.address_offsets(0x10200, 4)), (0x100,))
+ self.assertEqual(tuple(elf.address_offsets(0x10100, 0x200)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x10000, 0x800)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x100FC, 4)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x100FE, 4)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x101FE, 4)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x103FE, 4)), ())
+ self.assertEqual(tuple(elf.address_offsets(0x10400, 4)), ())
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_gnuversions.py b/test/test_gnuversions.py
new file mode 100644
index 0000000..8c1786a
--- /dev/null
+++ b/test/test_gnuversions.py
@@ -0,0 +1,160 @@
+#------------------------------------------------------------------------------
+# elftools tests
+#
+# Yann Rouillard (yann@pleiades.fr.eu.org)
+# This code is in the public domain
+#------------------------------------------------------------------------------
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+import os
+
+from utils import setup_syspath
+setup_syspath()
+from elftools.elf.elffile import ELFFile
+from elftools.elf.constants import VER_FLAGS
+from elftools.elf.gnuversions import (
+ GNUVerNeedSection, GNUVerDefSection,
+ GNUVerSymSection)
+
+
+class TestSymbolVersioning(unittest.TestCase):
+
+ versym_reference_data = [
+ {'name': b'', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'_ITM_deregisterTMCloneTable', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'puts', 'ndx': 5},
+ {'name': b'strlcat', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'__stack_chk_fail', 'ndx': 6},
+ {'name': b'__gmon_start__', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'gzoffset', 'ndx': 7},
+ {'name': b'_Jv_RegisterClasses', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'_ITM_registerTMCloneTable', 'ndx': 'VER_NDX_LOCAL'},
+ {'name': b'__cxa_finalize', 'ndx': 5},
+ {'name': b'_edata', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'VER_1.0', 'ndx': 2},
+ {'name': b'function1_ver1_1', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'_end', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'function1', 'ndx': 4 | 0x8000},
+ {'name': b'__bss_start', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'function1', 'ndx': 2},
+ {'name': b'VER_1.1', 'ndx': 3},
+ {'name': b'_init', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'function1_ver1_0', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'_fini', 'ndx': 'VER_NDX_GLOBAL'},
+ {'name': b'VER_1.2', 'ndx': 4},
+ {'name': b'function2', 'ndx': 3},
+ ]
+
+ def test_versym_section(self):
+
+ reference_data = TestSymbolVersioning.versym_reference_data
+
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'lib_versioned64.so.1.elf'), 'rb') as f:
+ elf = ELFFile(f)
+ versym_section = None
+ for section in elf.iter_sections():
+ if isinstance(section, GNUVerSymSection):
+ versym_section = section
+ break
+
+ self.assertIsNotNone(versym_section)
+
+ for versym, ref_versym in zip(section.iter_symbols(),
+ reference_data):
+ self.assertEqual(versym.name, ref_versym['name'])
+ self.assertEqual(versym['ndx'], ref_versym['ndx'])
+
+ verneed_reference_data = [
+ {'name': b'libz.so.1', 'vn_version': 1, 'vn_cnt': 1,
+ 'vernaux': [
+ {'name': b'ZLIB_1.2.3.5', 'vna_flags': 0, 'vna_other': 7}]},
+ {'name': b'libc.so.6', 'vn_version': 1, 'vn_cnt': 2,
+ 'vernaux': [
+ {'name': b'GLIBC_2.4', 'vna_flags': 0, 'vna_other': 6},
+ {'name': b'GLIBC_2.2.5', 'vna_flags': 0, 'vna_other': 5}]},
+ ]
+
+ def test_verneed_section(self):
+
+ reference_data = TestSymbolVersioning.verneed_reference_data
+
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'lib_versioned64.so.1.elf'), 'rb') as f:
+ elf = ELFFile(f)
+ verneed_section = None
+ for section in elf.iter_sections():
+ if isinstance(section, GNUVerNeedSection):
+ verneed_section = section
+ break
+
+ self.assertIsNotNone(verneed_section)
+
+ for (verneed, vernaux_iter), ref_verneed in zip(
+ section.iter_versions(), reference_data):
+
+ self.assertEqual(verneed.name, ref_verneed['name'])
+ self.assertEqual(verneed['vn_cnt'], ref_verneed['vn_cnt'])
+ self.assertEqual(verneed['vn_version'],
+ ref_verneed['vn_version'])
+
+ for vernaux, ref_vernaux in zip(
+ vernaux_iter, ref_verneed['vernaux']):
+
+ self.assertEqual(vernaux.name, ref_vernaux['name'])
+ self.assertEqual(vernaux['vna_flags'],
+ ref_vernaux['vna_flags'])
+ self.assertEqual(vernaux['vna_other'],
+ ref_vernaux['vna_other'])
+
+ verdef_reference_data = [
+ {'vd_ndx': 1, 'vd_version': 1, 'vd_flags': VER_FLAGS.VER_FLG_BASE,
+ 'vd_cnt': 1,
+ 'verdaux': [
+ {'name': b'lib_versioned.so.1'}]},
+ {'vd_ndx': 2, 'vd_version': 1, 'vd_flags': 0, 'vd_cnt': 1,
+ 'verdaux': [
+ {'name': b'VER_1.0'}]},
+ {'vd_ndx': 3, 'vd_version': 1, 'vd_flags': 0, 'vd_cnt': 2,
+ 'verdaux': [
+ {'name': b'VER_1.1'},
+ {'name': b'VER_1.0'}]},
+ {'vd_ndx': 4, 'vd_version': 1, 'vd_flags': 0, 'vd_cnt': 2,
+ 'verdaux': [
+ {'name': b'VER_1.2'},
+ {'name': b'VER_1.1'}]},
+ ]
+
+ def test_verdef_section(self):
+
+ reference_data = TestSymbolVersioning.verdef_reference_data
+
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ 'lib_versioned64.so.1.elf'), 'rb') as f:
+ elf = ELFFile(f)
+ verneed_section = None
+ for section in elf.iter_sections():
+ if isinstance(section, GNUVerDefSection):
+ verdef_section = section
+ break
+
+ self.assertIsNotNone(verdef_section)
+
+ for (verdef, verdaux_iter), ref_verdef in zip(
+ section.iter_versions(), reference_data):
+
+ self.assertEqual(verdef['vd_ndx'], ref_verdef['vd_ndx'])
+ self.assertEqual(verdef['vd_version'],
+ ref_verdef['vd_version'])
+ self.assertEqual(verdef['vd_flags'], ref_verdef['vd_flags'])
+ self.assertEqual(verdef['vd_cnt'], ref_verdef['vd_cnt'])
+
+ for verdaux, ref_verdaux in zip(
+ verdaux_iter, ref_verdef['verdaux']):
+ self.assertEqual(verdaux.name, ref_verdaux['name'])
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_solaris_support.py b/test/test_solaris_support.py
new file mode 100644
index 0000000..d243fe9
--- /dev/null
+++ b/test/test_solaris_support.py
@@ -0,0 +1,87 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Yann Rouillard (yann@pleiades.fr.eu.org)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+import os
+import copy
+
+from utils import setup_syspath; setup_syspath()
+from elftools.elf.elffile import ELFFile
+from elftools.elf.constants import SUNW_SYMINFO_FLAGS
+
+
+class TestSolarisSupport(unittest.TestCase):
+
+ def _test_SUNW_syminfo_section_generic(self, testfile):
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ testfile), 'rb') as f:
+ elf = ELFFile(f)
+ syminfo_section = elf.get_section_by_name(b'.SUNW_syminfo')
+ self.assertIsNotNone(syminfo_section)
+
+ # The test files were compiled against libc.so.1 with
+ # direct binding, hence the libc symbols used
+ # (exit, atexit and _exit) have the direct binding flags
+ # in the syminfo table.
+ # We check that this is properly detected.
+ exit_symbols = [s for s in syminfo_section.iter_symbols()
+ if b'exit' in s.name]
+ self.assertNotEqual(len(exit_symbols), 0)
+
+ for symbol in exit_symbols:
+ # libc.so.1 has the index 0 in the dynamic table
+ self.assertEqual(symbol['si_boundto'], 0)
+ self.assertEqual(symbol['si_flags'],
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECT |
+ SUNW_SYMINFO_FLAGS.SYMINFO_FLG_DIRECTBIND)
+
+ def test_SUNW_syminfo_section_x86(self):
+ self._test_SUNW_syminfo_section_generic('exe_solaris32_cc.elf')
+
+ def test_SUNW_syminfo_section_x64(self):
+ self._test_SUNW_syminfo_section_generic('exe_solaris64_cc.elf')
+
+ def test_SUNW_syminfo_section_sparc32(self):
+ self._test_SUNW_syminfo_section_generic('exe_solaris32_cc.sparc.elf')
+
+ def test_SUNW_syminfo_section_sparc64(self):
+ self._test_SUNW_syminfo_section_generic('exe_solaris64_cc.sparc.elf')
+
+ ldsynsym_reference_data = [b'', b'exe_solaris32.elf', b'crti.s', b'crt1.o',
+ b'crt1.s', b'fsr.s', b'values-Xa.c',
+ b'exe_solaris64.elf.c', b'crtn.s']
+
+ def _test_SUNW_ldynsym_section_generic(self, testfile, reference_data):
+ with open(os.path.join('test', 'testfiles_for_unittests',
+ testfile), 'rb') as f:
+ elf = ELFFile(f)
+ ldynsym_section = elf.get_section_by_name(b'.SUNW_ldynsym')
+ self.assertIsNotNone(ldynsym_section)
+
+ for symbol, ref_symbol_name in zip(
+ ldynsym_section.iter_symbols(), reference_data):
+
+ self.assertEqual(symbol.name, ref_symbol_name)
+
+ def test_SUNW_ldynsym_section_x86(self):
+ reference_data = TestSolarisSupport.ldsynsym_reference_data
+ self._test_SUNW_ldynsym_section_generic('exe_solaris32_cc.elf',
+ reference_data)
+
+ def test_SUNW_ldynsym_section_x64(self):
+ reference_data = copy.deepcopy(
+ TestSolarisSupport.ldsynsym_reference_data)
+ reference_data[1] = b'exe_solaris64.elf'
+ reference_data[3] = b'crt1x.o'
+ reference_data[5] = b'fsrx.s'
+ self._test_SUNW_ldynsym_section_generic('exe_solaris64_cc.elf',
+ reference_data)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test_utils.py b/test/test_utils.py
index 314b6b5..b74f2e5 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -45,17 +45,17 @@
self.assertEqual(parse_cstring_from_stream(sio, 2348), text[2348:5000])
-class Test_preserve_stream_pos(object):
+class Test_preserve_stream_pos(unittest.TestCase):
def test_basic(self):
- sio = BytesIO('abcdef')
+ sio = BytesIO(b'abcdef')
with preserve_stream_pos(sio):
sio.seek(4)
- self.assertEqual(stream.tell(), 0)
+ self.assertEqual(sio.tell(), 0)
sio.seek(5)
with preserve_stream_pos(sio):
sio.seek(0)
- self.assertEqual(stream.tell(), 5)
+ self.assertEqual(sio.tell(), 5)
if __name__ == '__main__':
diff --git a/test/testfiles_for_readelf/clang33-simple.o b/test/testfiles_for_readelf/clang33-simple.o
new file mode 100644
index 0000000..d64d2ef
--- /dev/null
+++ b/test/testfiles_for_readelf/clang33-simple.o
Binary files differ
diff --git a/test/testfiles/exe_simple32.elf b/test/testfiles_for_readelf/exe_simple32.elf
similarity index 100%
rename from test/testfiles/exe_simple32.elf
rename to test/testfiles_for_readelf/exe_simple32.elf
Binary files differ
diff --git a/test/testfiles/exe_simple64.elf b/test/testfiles_for_readelf/exe_simple64.elf
similarity index 100%
rename from test/testfiles/exe_simple64.elf
rename to test/testfiles_for_readelf/exe_simple64.elf
Binary files differ
diff --git a/test/testfiles/exe_stripped64.elf b/test/testfiles_for_readelf/exe_stripped64.elf
similarity index 100%
rename from test/testfiles/exe_stripped64.elf
rename to test/testfiles_for_readelf/exe_stripped64.elf
Binary files differ
diff --git a/test/testfiles_for_readelf/gcc48-simple.o b/test/testfiles_for_readelf/gcc48-simple.o
new file mode 100644
index 0000000..851a21a
--- /dev/null
+++ b/test/testfiles_for_readelf/gcc48-simple.o
Binary files differ
diff --git a/test/testfiles_for_readelf/gcc48-simple.src.c b/test/testfiles_for_readelf/gcc48-simple.src.c
new file mode 100644
index 0000000..23849b3
--- /dev/null
+++ b/test/testfiles_for_readelf/gcc48-simple.src.c
@@ -0,0 +1,19 @@
+/* Generated by compiling with gcc 4.8 as follows:
+**
+** gcc-4.8 -O0 -g -fno-dwarf2-cfi-asm -c dwarf4_simple.c -o gcc48-simple.
+**
+** Note: -fno-dwarf2-cfi-asm to tell gcc to generate .dwarf_frames as well
+** as the .eh_frames it generates by default.
+**
+*/
+
+extern int bar(int);
+extern int baz(int);
+
+int foo(int v) {
+ int x = bar(v);
+ int i;
+ for (i = 0; i < v; ++i)
+ x += bar(i) + bar(v) * baz(i);
+ return x;
+}
diff --git a/test/testfiles/libelf0_8_13_32bit.so.elf b/test/testfiles_for_readelf/libelf0_8_13_32bit.so.elf
similarity index 100%
rename from test/testfiles/libelf0_8_13_32bit.so.elf
rename to test/testfiles_for_readelf/libelf0_8_13_32bit.so.elf
Binary files differ
diff --git a/test/testfiles/obj_simple32.o.elf b/test/testfiles_for_readelf/obj_simple32.o.elf
similarity index 100%
rename from test/testfiles/obj_simple32.o.elf
rename to test/testfiles_for_readelf/obj_simple32.o.elf
Binary files differ
diff --git a/test/testfiles/penalty_32_gcc.o.elf b/test/testfiles_for_readelf/penalty_32_gcc.o.elf
similarity index 100%
rename from test/testfiles/penalty_32_gcc.o.elf
rename to test/testfiles_for_readelf/penalty_32_gcc.o.elf
Binary files differ
diff --git a/test/testfiles/penalty_64_clang.o.elf b/test/testfiles_for_readelf/penalty_64_clang.o.elf
similarity index 100%
rename from test/testfiles/penalty_64_clang.o.elf
rename to test/testfiles_for_readelf/penalty_64_clang.o.elf
Binary files differ
diff --git a/test/testfiles/penalty_64_gcc.o.elf b/test/testfiles_for_readelf/penalty_64_gcc.o.elf
similarity index 100%
rename from test/testfiles/penalty_64_gcc.o.elf
rename to test/testfiles_for_readelf/penalty_64_gcc.o.elf
Binary files differ
diff --git a/test/testfiles_for_readelf/reloc_aarch64_gcc.o.elf b/test/testfiles_for_readelf/reloc_aarch64_gcc.o.elf
new file mode 100644
index 0000000..25eb90c
--- /dev/null
+++ b/test/testfiles_for_readelf/reloc_aarch64_gcc.o.elf
Binary files differ
diff --git a/test/testfiles_for_readelf/simple_aarch64_gcc.o.elf b/test/testfiles_for_readelf/simple_aarch64_gcc.o.elf
new file mode 100644
index 0000000..9a60f83
--- /dev/null
+++ b/test/testfiles_for_readelf/simple_aarch64_gcc.o.elf
Binary files differ
diff --git a/test/testfiles/simple_gcc.elf.arm b/test/testfiles_for_readelf/simple_arm_gcc.o.elf
similarity index 100%
copy from test/testfiles/simple_gcc.elf.arm
copy to test/testfiles_for_readelf/simple_arm_gcc.o.elf
Binary files differ
diff --git a/test/testfiles/update32.o.elf b/test/testfiles_for_readelf/update32.o.elf
similarity index 100%
rename from test/testfiles/update32.o.elf
rename to test/testfiles_for_readelf/update32.o.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/aarch64_super_stripped.elf b/test/testfiles_for_unittests/aarch64_super_stripped.elf
new file mode 100755
index 0000000..0e5c2c4
--- /dev/null
+++ b/test/testfiles_for_unittests/aarch64_super_stripped.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/arm_with_form_indirect.elf b/test/testfiles_for_unittests/arm_with_form_indirect.elf
new file mode 100644
index 0000000..ff0a3ba
--- /dev/null
+++ b/test/testfiles_for_unittests/arm_with_form_indirect.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/exe_solaris32_cc.elf b/test/testfiles_for_unittests/exe_solaris32_cc.elf
new file mode 100644
index 0000000..51b925c
--- /dev/null
+++ b/test/testfiles_for_unittests/exe_solaris32_cc.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/exe_solaris32_cc.sparc.elf b/test/testfiles_for_unittests/exe_solaris32_cc.sparc.elf
new file mode 100644
index 0000000..7e879ef
--- /dev/null
+++ b/test/testfiles_for_unittests/exe_solaris32_cc.sparc.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/exe_solaris64_cc.elf b/test/testfiles_for_unittests/exe_solaris64_cc.elf
new file mode 100644
index 0000000..b6bad65
--- /dev/null
+++ b/test/testfiles_for_unittests/exe_solaris64_cc.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/exe_solaris64_cc.sparc.elf b/test/testfiles_for_unittests/exe_solaris64_cc.sparc.elf
new file mode 100644
index 0000000..b9e4a17
--- /dev/null
+++ b/test/testfiles_for_unittests/exe_solaris64_cc.sparc.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/lib_versioned64.so.1.elf b/test/testfiles_for_unittests/lib_versioned64.so.1.elf
new file mode 100644
index 0000000..09edd94
--- /dev/null
+++ b/test/testfiles_for_unittests/lib_versioned64.so.1.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/lib_with_two_dynstr_sections.so.1.elf b/test/testfiles_for_unittests/lib_with_two_dynstr_sections.so.1.elf
new file mode 100755
index 0000000..661950b
--- /dev/null
+++ b/test/testfiles_for_unittests/lib_with_two_dynstr_sections.so.1.elf
Binary files differ
diff --git a/test/testfiles_for_unittests/lib_with_two_dynstr_sections_reversed.so.1.elf b/test/testfiles_for_unittests/lib_with_two_dynstr_sections_reversed.so.1.elf
new file mode 100755
index 0000000..8d07de1
--- /dev/null
+++ b/test/testfiles_for_unittests/lib_with_two_dynstr_sections_reversed.so.1.elf
Binary files differ
diff --git a/test/testfiles/exe_simple64.elf b/test/testfiles_for_unittests/sample_exe64.elf
similarity index 100%
copy from test/testfiles/exe_simple64.elf
copy to test/testfiles_for_unittests/sample_exe64.elf
Binary files differ
diff --git a/test/testfiles/simple_gcc.elf.arm b/test/testfiles_for_unittests/simple_gcc.elf.arm
similarity index 100%
rename from test/testfiles/simple_gcc.elf.arm
rename to test/testfiles_for_unittests/simple_gcc.elf.arm
Binary files differ
diff --git a/test/utils.py b/test/utils.py
index 908cfcc..ff58678 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -19,7 +19,7 @@
sys.path.insert(0, '.')
-def run_exe(exe_path, args):
+def run_exe(exe_path, args=[]):
""" Runs the given executable as a subprocess, given the
list of arguments. Captures its return code (rc) and stdout and
returns a pair: rc, stdout_str
diff --git a/tox.ini b/tox.ini
index 1409ccb..600ca1c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27,py32
+envlist = py27,py33
[testenv]
commands =
@@ -10,4 +10,3 @@
[testenv:py26]
deps =
unittest2
-