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
-