merge in upstream 0.21 release

Change-Id: I2803322a26044f194afe740635c051eeeb2bef2e
diff --git a/.hgignore b/.hgignore
index b5fbdde..4e7e920 100644
--- a/.hgignore
+++ b/.hgignore
@@ -8,5 +8,6 @@
 build
 dist
 MANIFEST
+*.sublime-workspace
 
 
diff --git a/CHANGES b/CHANGES
index dc64e66..54e772c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,10 +1,18 @@
 Changelog
 =========
 
-+ Version 0.21 (??)
++ Version 0.22 (17.04.2013)
 
   - Added new example: dwarf_decode_address - decode function name and
     file & line information from an address.
+  - Issue #7: parsing incorrect DWARF was made a bit more forgiving for cases
+    where serialized DIE trees have extra NULLs at the end.
+  - Very initial support for ARM ELF files (Matthew Fernandez - pull
+    request #6).
+  - Support support for dumping the dynamic section (Mike Frysinger - pull
+    request #7).
+  - Output of scripts/readelf.py now matches that of binutils 2.23.52.
+  - Added more machine EM_ values to ENUM_E_TYPE.
 
 + Version 0.20 (27.01.2012)
 
diff --git a/MANIFEST.in b/MANIFEST.in
index 3cac83f..872e667 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,11 +1,8 @@
-recursive-include elftools *.py 

-recursive-include scripts *.py

-recursive-include examples *.py *.elf *.out

-recursive-include test *.py *.elf

-include README

-include LICENSE

-include CHANGES

-include tox.ini

-

-

-

+recursive-include elftools *.py
+recursive-include scripts *.py
+recursive-include examples *.py *.elf *.out
+recursive-include test *.py *.elf *.arm
+include README
+include LICENSE
+include CHANGES
+include tox.ini
diff --git a/README b/README
deleted file mode 100644
index 836c1f2..0000000
--- a/README
+++ /dev/null
@@ -1,43 +0,0 @@
-Introduction: what is pyelftools?
----------------------------------
-
-**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>`_ for more details.
-
-Pre-requisites
---------------
-
-As a user of **pyelftools**, one only needs Python to run. It works with
-Python versions 2.6, 2.7 and 3.2. For hacking on **pyelftools** the
-requirements are a bit more strict, please see the
-`hacking guide <https://bitbucket.org/eliben/pyelftools/wiki/Hacking>`_.
-
-Installing
-----------
-
-When you unzip the source distribution, run::
-
-    > python setup.py install
-
-Alternatively, **pyelftools** can be installed from PyPI (Python package
-index)::
-
-    > pip install pyelftools
-
-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>`_.
-
-License
--------
-
-**pyelftools** is open source software. Its code is in the public domain. See
-the ``LICENSE`` file for more details.
-
-
-
-
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..264b898
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,50 @@
+Introduction: what is pyelftools?
+---------------------------------
+
+**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>`_
+for more details.
+
+Pre-requisites
+--------------
+
+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>`_.
+
+Installing
+----------
+
+**pyelftools** can be installed from PyPI (Python package index)::
+
+    > pip install pyelftools
+
+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
+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.
+
+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>`_.
+
+License
+-------
+
+**pyelftools** is open source software. Its code is in the public domain. See
+the ``LICENSE`` file for more details.
+
+
+
+
diff --git a/TODO b/TODO
index 1f8d11d..bc6ffdc 100644
--- a/TODO
+++ b/TODO
@@ -14,6 +14,7 @@
 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
 * Make sure new version was updated everywhere appropriate
 * Packaging done on Linux
diff --git a/elftools/__init__.py b/elftools/__init__.py
index 0f14043..90be2eb 100644
--- a/elftools/__init__.py
+++ b/elftools/__init__.py
@@ -1,3 +1,8 @@
-__version__ = '0.20'
-
+#-------------------------------------------------------------------------------
+# elftools
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+__version__ = '0.21'
 
diff --git a/elftools/common/ordereddict.py b/elftools/common/ordereddict.py
index 5e0f142..18ee045 100644
--- a/elftools/common/ordereddict.py
+++ b/elftools/common/ordereddict.py
@@ -1,264 +1,262 @@
-# http://code.activestate.com/recipes/576693/ (r9)

-# 	Created by Raymond Hettinger on Wed, 18 Mar 2009 (MIT) 

-#

-# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 

-# and pypy.

-# Passes Python2.7's test suite and incorporates all the latest updates.

-#

-

-try:

-    from thread import get_ident as _get_ident

-except ImportError:

-    from dummy_thread import get_ident as _get_ident

-

-try:

-    from _abcoll import KeysView, ValuesView, ItemsView

-except ImportError:

-    pass

-

-

-class OrderedDict(dict):

-    'Dictionary that remembers insertion order'

-    # An inherited dict maps keys to values.

-    # The inherited dict provides __getitem__, __len__, __contains__, and get.

-    # The remaining methods are order-aware.

-    # Big-O running times for all methods are the same as for regular dictionaries.

-

-    # The internal self.__map dictionary maps keys to links in a doubly linked list.

-    # The circular doubly linked list starts and ends with a sentinel element.

-    # The sentinel element never gets deleted (this simplifies the algorithm).

-    # Each link is stored as a list of length three:  [PREV, NEXT, KEY].

-

-    def __init__(self, *args, **kwds):

-        '''Initialize an ordered dictionary.  Signature is the same as for

-        regular dictionaries, but keyword arguments are not recommended

-        because their insertion order is arbitrary.

-

-        '''

-        if len(args) > 1:

-            raise TypeError('expected at most 1 arguments, got %d' % len(args))

-        try:

-            self.__root

-        except AttributeError:

-            self.__root = root = []                     # sentinel node

-            root[:] = [root, root, None]

-            self.__map = {}

-        self.__update(*args, **kwds)

-

-    def __setitem__(self, key, value, dict_setitem=dict.__setitem__):

-        'od.__setitem__(i, y) <==> od[i]=y'

-        # Setting a new item creates a new link which goes at the end of the linked

-        # list, and the inherited dictionary is updated with the new key/value pair.

-        if key not in self:

-            root = self.__root

-            last = root[0]

-            last[1] = root[0] = self.__map[key] = [last, root, key]

-        dict_setitem(self, key, value)

-

-    def __delitem__(self, key, dict_delitem=dict.__delitem__):

-        'od.__delitem__(y) <==> del od[y]'

-        # Deleting an existing item uses self.__map to find the link which is

-        # then removed by updating the links in the predecessor and successor nodes.

-        dict_delitem(self, key)

-        link_prev, link_next, key = self.__map.pop(key)

-        link_prev[1] = link_next

-        link_next[0] = link_prev

-

-    def __iter__(self):

-        'od.__iter__() <==> iter(od)'

-        root = self.__root

-        curr = root[1]

-        while curr is not root:

-            yield curr[2]

-            curr = curr[1]

-

-    def __reversed__(self):

-        'od.__reversed__() <==> reversed(od)'

-        root = self.__root

-        curr = root[0]

-        while curr is not root:

-            yield curr[2]

-            curr = curr[0]

-

-    def clear(self):

-        'od.clear() -> None.  Remove all items from od.'

-        try:

-            for node in self.__map.itervalues():

-                del node[:]

-            root = self.__root

-            root[:] = [root, root, None]

-            self.__map.clear()

-        except AttributeError:

-            pass

-        dict.clear(self)

-

-    def popitem(self, last=True):

-        '''od.popitem() -> (k, v), return and remove a (key, value) pair.

-        Pairs are returned in LIFO order if last is true or FIFO order if false.

-

-        '''

-        if not self:

-            raise KeyError('dictionary is empty')

-        root = self.__root

-        if last:

-            link = root[0]

-            link_prev = link[0]

-            link_prev[1] = root

-            root[0] = link_prev

-        else:

-            link = root[1]

-            link_next = link[1]

-            root[1] = link_next

-            link_next[0] = root

-        key = link[2]

-        del self.__map[key]

-        value = dict.pop(self, key)

-        return key, value

-

-    # -- the following methods do not depend on the internal structure --

-

-    def keys(self):

-        'od.keys() -> list of keys in od'

-        return list(self)

-

-    def values(self):

-        'od.values() -> list of values in od'

-        return [self[key] for key in self]

-

-    def items(self):

-        'od.items() -> list of (key, value) pairs in od'

-        return [(key, self[key]) for key in self]

-

-    def iterkeys(self):

-        'od.iterkeys() -> an iterator over the keys in od'

-        return iter(self)

-

-    def itervalues(self):

-        'od.itervalues -> an iterator over the values in od'

-        for k in self:

-            yield self[k]

-

-    def iteritems(self):

-        'od.iteritems -> an iterator over the (key, value) items in od'

-        for k in self:

-            yield (k, self[k])

-

-    def update(*args, **kwds):

-        '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.

-

-        If E is a dict instance, does:           for k in E: od[k] = E[k]

-        If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]

-        Or if E is an iterable of items, does:   for k, v in E: od[k] = v

-        In either case, this is followed by:     for k, v in F.items(): od[k] = v

-

-        '''

-        if len(args) > 2:

-            raise TypeError('update() takes at most 2 positional '

-                            'arguments (%d given)' % (len(args),))

-        elif not args:

-            raise TypeError('update() takes at least 1 argument (0 given)')

-        self = args[0]

-        # Make progressively weaker assumptions about "other"

-        other = ()

-        if len(args) == 2:

-            other = args[1]

-        if isinstance(other, dict):

-            for key in other:

-                self[key] = other[key]

-        elif hasattr(other, 'keys'):

-            for key in other.keys():

-                self[key] = other[key]

-        else:

-            for key, value in other:

-                self[key] = value

-        for key, value in kwds.items():

-            self[key] = value

-

-    __update = update  # let subclasses override update without breaking __init__

-

-    __marker = object()

-

-    def pop(self, key, default=__marker):

-        '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.

-        If key is not found, d is returned if given, otherwise KeyError is raised.

-

-        '''

-        if key in self:

-            result = self[key]

-            del self[key]

-            return result

-        if default is self.__marker:

-            raise KeyError(key)

-        return default

-

-    def setdefault(self, key, default=None):

-        'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'

-        if key in self:

-            return self[key]

-        self[key] = default

-        return default

-

-    def __repr__(self, _repr_running={}):

-        'od.__repr__() <==> repr(od)'

-        call_key = id(self), _get_ident()

-        if call_key in _repr_running:

-            return '...'

-        _repr_running[call_key] = 1

-        try:

-            if not self:

-                return '%s()' % (self.__class__.__name__,)

-            return '%s(%r)' % (self.__class__.__name__, self.items())

-        finally:

-            del _repr_running[call_key]

-

-    def __reduce__(self):

-        'Return state information for pickling'

-        items = [[k, self[k]] for k in self]

-        inst_dict = vars(self).copy()

-        for k in vars(OrderedDict()):

-            inst_dict.pop(k, None)

-        if inst_dict:

-            return (self.__class__, (items,), inst_dict)

-        return self.__class__, (items,)

-

-    def copy(self):

-        'od.copy() -> a shallow copy of od'

-        return self.__class__(self)

-

-    @classmethod

-    def fromkeys(cls, iterable, value=None):

-        '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S

-        and values equal to v (which defaults to None).

-

-        '''

-        d = cls()

-        for key in iterable:

-            d[key] = value

-        return d

-

-    def __eq__(self, other):

-        '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive

-        while comparison to a regular mapping is order-insensitive.

-

-        '''

-        if isinstance(other, OrderedDict):

-            return len(self)==len(other) and self.items() == other.items()

-        return dict.__eq__(self, other)

-

-    def __ne__(self, other):

-        return not self == other

-

-    # -- the following methods are only used in Python 2.7 --

-

-    def viewkeys(self):

-        "od.viewkeys() -> a set-like object providing a view on od's keys"

-        return KeysView(self)

-

-    def viewvalues(self):

-        "od.viewvalues() -> an object providing a view on od's values"

-        return ValuesView(self)

-

-    def viewitems(self):

-        "od.viewitems() -> a set-like object providing a view on od's items"

-        return ItemsView(self)

-

+#-------------------------------------------------------------------------------
+# elftools: port of OrderedDict to work on Python < 2.7
+#
+# Taken from http://code.activestate.com/recipes/576693/ , revision 9
+# Code by Raymond Hettinger. License: MIT
+#-------------------------------------------------------------------------------
+try:
+    from thread import get_ident as _get_ident
+except ImportError:
+    from dummy_thread import get_ident as _get_ident
+
+try:
+    from _abcoll import KeysView, ValuesView, ItemsView
+except ImportError:
+    pass
+
+
+class OrderedDict(dict):
+    'Dictionary that remembers insertion order'
+    # An inherited dict maps keys to values.
+    # The inherited dict provides __getitem__, __len__, __contains__, and get.
+    # The remaining methods are order-aware.
+    # Big-O running times for all methods are the same as for regular dictionaries.
+
+    # The internal self.__map dictionary maps keys to links in a doubly linked list.
+    # The circular doubly linked list starts and ends with a sentinel element.
+    # The sentinel element never gets deleted (this simplifies the algorithm).
+    # Each link is stored as a list of length three:  [PREV, NEXT, KEY].
+
+    def __init__(self, *args, **kwds):
+        '''Initialize an ordered dictionary.  Signature is the same as for
+        regular dictionaries, but keyword arguments are not recommended
+        because their insertion order is arbitrary.
+
+        '''
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        try:
+            self.__root
+        except AttributeError:
+            self.__root = root = []                     # sentinel node
+            root[:] = [root, root, None]
+            self.__map = {}
+        self.__update(*args, **kwds)
+
+    def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
+        'od.__setitem__(i, y) <==> od[i]=y'
+        # Setting a new item creates a new link which goes at the end of the linked
+        # list, and the inherited dictionary is updated with the new key/value pair.
+        if key not in self:
+            root = self.__root
+            last = root[0]
+            last[1] = root[0] = self.__map[key] = [last, root, key]
+        dict_setitem(self, key, value)
+
+    def __delitem__(self, key, dict_delitem=dict.__delitem__):
+        'od.__delitem__(y) <==> del od[y]'
+        # Deleting an existing item uses self.__map to find the link which is
+        # then removed by updating the links in the predecessor and successor nodes.
+        dict_delitem(self, key)
+        link_prev, link_next, key = self.__map.pop(key)
+        link_prev[1] = link_next
+        link_next[0] = link_prev
+
+    def __iter__(self):
+        'od.__iter__() <==> iter(od)'
+        root = self.__root
+        curr = root[1]
+        while curr is not root:
+            yield curr[2]
+            curr = curr[1]
+
+    def __reversed__(self):
+        'od.__reversed__() <==> reversed(od)'
+        root = self.__root
+        curr = root[0]
+        while curr is not root:
+            yield curr[2]
+            curr = curr[0]
+
+    def clear(self):
+        'od.clear() -> None.  Remove all items from od.'
+        try:
+            for node in self.__map.itervalues():
+                del node[:]
+            root = self.__root
+            root[:] = [root, root, None]
+            self.__map.clear()
+        except AttributeError:
+            pass
+        dict.clear(self)
+
+    def popitem(self, last=True):
+        '''od.popitem() -> (k, v), return and remove a (key, value) pair.
+        Pairs are returned in LIFO order if last is true or FIFO order if false.
+
+        '''
+        if not self:
+            raise KeyError('dictionary is empty')
+        root = self.__root
+        if last:
+            link = root[0]
+            link_prev = link[0]
+            link_prev[1] = root
+            root[0] = link_prev
+        else:
+            link = root[1]
+            link_next = link[1]
+            root[1] = link_next
+            link_next[0] = root
+        key = link[2]
+        del self.__map[key]
+        value = dict.pop(self, key)
+        return key, value
+
+    # -- the following methods do not depend on the internal structure --
+
+    def keys(self):
+        'od.keys() -> list of keys in od'
+        return list(self)
+
+    def values(self):
+        'od.values() -> list of values in od'
+        return [self[key] for key in self]
+
+    def items(self):
+        'od.items() -> list of (key, value) pairs in od'
+        return [(key, self[key]) for key in self]
+
+    def iterkeys(self):
+        'od.iterkeys() -> an iterator over the keys in od'
+        return iter(self)
+
+    def itervalues(self):
+        'od.itervalues -> an iterator over the values in od'
+        for k in self:
+            yield self[k]
+
+    def iteritems(self):
+        'od.iteritems -> an iterator over the (key, value) items in od'
+        for k in self:
+            yield (k, self[k])
+
+    def update(*args, **kwds):
+        '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.
+
+        If E is a dict instance, does:           for k in E: od[k] = E[k]
+        If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]
+        Or if E is an iterable of items, does:   for k, v in E: od[k] = v
+        In either case, this is followed by:     for k, v in F.items(): od[k] = v
+
+        '''
+        if len(args) > 2:
+            raise TypeError('update() takes at most 2 positional '
+                            'arguments (%d given)' % (len(args),))
+        elif not args:
+            raise TypeError('update() takes at least 1 argument (0 given)')
+        self = args[0]
+        # Make progressively weaker assumptions about "other"
+        other = ()
+        if len(args) == 2:
+            other = args[1]
+        if isinstance(other, dict):
+            for key in other:
+                self[key] = other[key]
+        elif hasattr(other, 'keys'):
+            for key in other.keys():
+                self[key] = other[key]
+        else:
+            for key, value in other:
+                self[key] = value
+        for key, value in kwds.items():
+            self[key] = value
+
+    __update = update  # let subclasses override update without breaking __init__
+
+    __marker = object()
+
+    def pop(self, key, default=__marker):
+        '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
+        If key is not found, d is returned if given, otherwise KeyError is raised.
+
+        '''
+        if key in self:
+            result = self[key]
+            del self[key]
+            return result
+        if default is self.__marker:
+            raise KeyError(key)
+        return default
+
+    def setdefault(self, key, default=None):
+        'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
+        if key in self:
+            return self[key]
+        self[key] = default
+        return default
+
+    def __repr__(self, _repr_running={}):
+        'od.__repr__() <==> repr(od)'
+        call_key = id(self), _get_ident()
+        if call_key in _repr_running:
+            return '...'
+        _repr_running[call_key] = 1
+        try:
+            if not self:
+                return '%s()' % (self.__class__.__name__,)
+            return '%s(%r)' % (self.__class__.__name__, self.items())
+        finally:
+            del _repr_running[call_key]
+
+    def __reduce__(self):
+        'Return state information for pickling'
+        items = [[k, self[k]] for k in self]
+        inst_dict = vars(self).copy()
+        for k in vars(OrderedDict()):
+            inst_dict.pop(k, None)
+        if inst_dict:
+            return (self.__class__, (items,), inst_dict)
+        return self.__class__, (items,)
+
+    def copy(self):
+        'od.copy() -> a shallow copy of od'
+        return self.__class__(self)
+
+    @classmethod
+    def fromkeys(cls, iterable, value=None):
+        '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
+        and values equal to v (which defaults to None).
+
+        '''
+        d = cls()
+        for key in iterable:
+            d[key] = value
+        return d
+
+    def __eq__(self, other):
+        '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive
+        while comparison to a regular mapping is order-insensitive.
+
+        '''
+        if isinstance(other, OrderedDict):
+            return len(self)==len(other) and self.items() == other.items()
+        return dict.__eq__(self, other)
+
+    def __ne__(self, other):
+        return not self == other
+
+    # -- the following methods are only used in Python 2.7 --
+
+    def viewkeys(self):
+        "od.viewkeys() -> a set-like object providing a view on od's keys"
+        return KeysView(self)
+
+    def viewvalues(self):
+        "od.viewvalues() -> an object providing a view on od's values"
+        return ValuesView(self)
+
+    def viewitems(self):
+        "od.viewitems() -> a set-like object providing a view on od's items"
+        return ItemsView(self)
+
diff --git a/elftools/common/utils.py b/elftools/common/utils.py
index 2daed04..7bd1d5b 100644
--- a/elftools/common/utils.py
+++ b/elftools/common/utils.py
@@ -41,6 +41,8 @@
         If stream_pos is provided, the stream is seeked to this position before
         the parsing is done. Otherwise, the current position of the stream is
         used.
+        Note: a bytes object is returned here, because this is what's read from
+        the binary file.
     """
     if stream_pos is not None:
         stream.seek(stream_pos)
diff --git a/elftools/construct/README b/elftools/construct/README
index 2c1b798..0d76f7f 100644
--- a/elftools/construct/README
+++ b/elftools/construct/README
@@ -1,8 +1,7 @@
-'construct' is the actual library, with my modifications for Python 3
-compatibility and various bug fixes.
+construct is a Python library for declarative parsing and building of binary
+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
 
-Taken from my fork: https://github.com/eliben/construct
-
-Take a look at LICENSE for the original license
+LICENSE is the original license.
 
 
diff --git a/elftools/dwarf/callframe.py b/elftools/dwarf/callframe.py
index de67a43..5b35af6 100644
--- a/elftools/dwarf/callframe.py
+++ b/elftools/dwarf/callframe.py
@@ -11,12 +11,12 @@
 from ..common.utils import (struct_parse, dwarf_assert, preserve_stream_pos)
 from ..common.py3compat import iterkeys
 from .structs import DWARFStructs
-from .constants import * 
+from .constants import *
 
 
 class CallFrameInfo(object):
     """ DWARF CFI (Call Frame Info)
-        
+
         stream, size:
             A stream holding the .debug_frame section, and the size of the
             section in it.
@@ -27,8 +27,7 @@
             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. I had a discussion about this on dwarf-discuss that confirms
-            this.
+            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
@@ -87,7 +86,7 @@
             entry_structs.Dwarf_offset(''), self.stream)
 
         is_CIE = (
-            (dwarf_format == 32 and CIE_id == 0xFFFFFFFF) or 
+            (dwarf_format == 32 and CIE_id == 0xFFFFFFFF) or
             CIE_id == 0xFFFFFFFFFFFFFFFF)
 
         if is_CIE:
@@ -251,7 +250,7 @@
             reg_order = []
         else: # FDE
             # For a FDE, we need to decode the attached CIE first, because its
-            # decoded table is needed. Its "initial instructions" describe a 
+            # decoded table is needed. Its "initial instructions" describe a
             # line that serves as the base (first) line in the FDE's table.
             cie = self.cie
             cie_decoded_table = cie.get_decoded()
@@ -259,7 +258,7 @@
             cur_line = last_line_in_CIE
             cur_line['pc'] = self['initial_location']
             reg_order = copy.copy(cie_decoded_table.reg_order)
-        
+
         table = []
 
         # Keeps a stack for the use of DW_CFA_{remember|restore}_state
diff --git a/elftools/dwarf/compileunit.py b/elftools/dwarf/compileunit.py
index fbe4a8b..f5011dd 100644
--- a/elftools/dwarf/compileunit.py
+++ b/elftools/dwarf/compileunit.py
@@ -10,36 +10,36 @@
 
 
 class CompileUnit(object):
-    """ A DWARF compilation unit (CU). 
-    
+    """ A DWARF compilation unit (CU).
+
             A normal compilation unit typically represents the text and data
             contributed to an executable by a single relocatable object file.
-            It may be derived from several source files, 
+            It may be derived from several source files,
             including pre-processed "include files"
-            
+
         Serves as a container and context to DIEs that describe objects and code
         belonging to a compilation unit.
-        
+
         CU header entries can be accessed as dict keys from this object, i.e.
            cu = CompileUnit(...)
            cu['version']  # version field of the CU header
-        
-        To get the top-level DIE describing the compilation unit, call the 
+
+        To get the top-level DIE describing the compilation unit, call the
         get_top_DIE method.
     """
     def __init__(self, header, dwarfinfo, structs, cu_offset, cu_die_offset):
         """ header:
                 CU header for this compile unit
-            
+
             dwarfinfo:
                 The DWARFInfo context object which created this one
-                        
+
             structs:
                 A DWARFStructs instance suitable for this compile unit
-            
+
             cu_offset:
                 Offset in the stream to the beginning of this CU (its header)
-            
+
             cu_die_offset:
                 Offset in the stream of the top DIE of this CU
         """
@@ -48,19 +48,19 @@
         self.structs = structs
         self.cu_offset = cu_offset
         self.cu_die_offset = cu_die_offset
-        
-        # The abbreviation table for this CU. Filled lazily when DIEs are 
+
+        # The abbreviation table for this CU. Filled lazily when DIEs are
         # requested.
         self._abbrev_table = None
-        
+
         # A list of DIEs belonging to this CU. Lazily parsed.
         self._dielist = []
-    
+
     def dwarf_format(self):
         """ Get the DWARF format (32 or 64) for this CU
         """
         return self.structs.dwarf_format
-    
+
     def get_abbrev_table(self):
         """ Get the abbreviation table (AbbrevTable object) for this CU
         """
@@ -70,31 +70,31 @@
         return self._abbrev_table
 
     def get_top_DIE(self):
-        """ Get the top DIE (which is either a DW_TAG_compile_unit or 
+        """ Get the top DIE (which is either a DW_TAG_compile_unit or
             DW_TAG_partial_unit) of this CU
         """
         return self._get_DIE(0)
-    
+
     def iter_DIEs(self):
         """ Iterate over all the DIEs in the CU, in order of their appearance.
             Note that null DIEs will also be returned.
         """
         self._parse_DIEs()
         return iter(self._dielist)
-    
+
     #------ PRIVATE ------#
-    
+
     def __getitem__(self, name):
         """ Implement dict-like access to header entries
         """
         return self.header[name]
 
     def _get_DIE(self, index):
-        """ Get the DIE at the given index 
+        """ Get the DIE at the given index
         """
         self._parse_DIEs()
         return self._dielist[index]
-    
+
     def _parse_DIEs(self):
         """ Parse all the DIEs pertaining to this CU from the stream and shove
             them sequentially into self._dielist.
@@ -103,13 +103,13 @@
         """
         if len(self._dielist) > 0:
             return
-            
-        # Compute the boundary (one byte past the bounds) of this CU in the 
+
+        # Compute the boundary (one byte past the bounds) of this CU in the
         # stream
-        cu_boundary = ( self.cu_offset + 
-                        self['unit_length'] + 
+        cu_boundary = ( self.cu_offset +
+                        self['unit_length'] +
                         self.structs.initial_length_field_size())
-        
+
         # First pass: parse all DIEs and place them into self._dielist
         die_offset = self.cu_die_offset
         while die_offset < cu_boundary:
@@ -122,18 +122,18 @@
 
         # Second pass - unflatten the DIE tree
         self._unflatten_tree()
-    
+
     def _unflatten_tree(self):
         """ "Unflatten" the DIE tree from it serial representation, by setting
             the child/sibling/parent links of DIEs.
-            
+
             Assumes self._dielist was already populated by a linear list of DIEs
             read from the stream section
         """
         # the first DIE in the list is the root node
         root = self._dielist[0]
         parentstack = [root]
-        
+
         for die in self._dielist[1:]:
             if not die.is_null():
                 cur_parent = parentstack[-1]
@@ -143,6 +143,11 @@
                 if die.has_children:
                     parentstack.append(die)
             else:
-                # end of children for the current parent
-                parentstack.pop()
+                # parentstack should not be really empty here. However, some
+                # compilers generate DWARF that has extra NULLs in the end and
+                # we don't want pyelftools to fail parsing them just because of
+                # this.
+                if len(parentstack) > 0:
+                    # end of children for the current parent
+                    parentstack.pop()
 
diff --git a/elftools/dwarf/descriptions.py b/elftools/dwarf/descriptions.py
index 721c97a..987e0d0 100644
--- a/elftools/dwarf/descriptions.py
+++ b/elftools/dwarf/descriptions.py
@@ -24,7 +24,7 @@
 def describe_attr_value(attr, die, section_offset):
     """ Given an attribute attr, return the textual representation of its
         value, suitable for tools like readelf.
-        
+
         To cover all cases, this function needs some extra arguments:
 
         die: the DIE this attribute was extracted from
@@ -32,11 +32,11 @@
     """
     descr_func = _ATTR_DESCRIPTION_MAP[attr.form]
     val_description = descr_func(attr, die, section_offset)
-    
+
     # For some attributes we can display further information
     extra_info_func = _EXTRA_INFO_DESCRIPTION_MAP[attr.name]
     extra_info = extra_info_func(attr, die, section_offset)
-    return str(val_description) + '\t' + extra_info    
+    return str(val_description) + '\t' + extra_info
 
 
 def describe_CFI_instructions(entry):
@@ -45,7 +45,7 @@
     """
     def _assert_FDE_instruction(instr):
         dwarf_assert(
-            isinstance(entry, FDE), 
+            isinstance(entry, FDE),
             'Unexpected instruction "%s" for a CIE' % instr)
 
     def _full_reg_name(regnum):
@@ -126,7 +126,7 @@
         return 'exp'
     else:
         return '%s%+d' % (describe_reg_name(rule.reg), rule.offset)
-    
+
 
 def describe_DWARF_expr(expr, structs):
     """ Textual description of a DWARF expression encoded in 'expr'.
@@ -199,21 +199,21 @@
 
 def _describe_attr_block(attr, die, section_offset):
     s = '%s byte block: ' % len(attr.value)
-    s += ' '.join('%x' % item for item in attr.value)
+    s += ' '.join('%x' % item for item in attr.value) + ' '
     return s
-    
+
 
 _ATTR_DESCRIPTION_MAP = defaultdict(
     lambda: _describe_attr_value_passthrough, # default_factory
-    
+
     DW_FORM_ref1=_describe_attr_ref,
     DW_FORM_ref2=_describe_attr_ref,
     DW_FORM_ref4=_describe_attr_ref,
     DW_FORM_ref8=_describe_attr_split_64bit,
-    DW_FORM_ref_udata=_describe_attr_ref,        
+    DW_FORM_ref_udata=_describe_attr_ref,
     DW_FORM_ref_addr=_describe_attr_hex_addr,
     DW_FORM_data4=_describe_attr_hex,
-    DW_FORM_data8=_describe_attr_split_64bit,
+    DW_FORM_data8=_describe_attr_hex,
     DW_FORM_addr=_describe_attr_hex,
     DW_FORM_sec_offset=_describe_attr_hex,
     DW_FORM_flag=_describe_attr_debool,
@@ -402,7 +402,7 @@
 
 _EXTRA_INFO_DESCRIPTION_MAP = defaultdict(
     lambda: _make_extra_string(''), # default_factory
-    
+
     DW_AT_inline=_make_extra_mapper(
         _DESCR_DW_INL, '(Unknown inline attribute value: %x',
         default_interpolate_value=True),
@@ -463,7 +463,7 @@
 class ExprDumper(GenericExprVisitor):
     """ A concrete visitor for DWARF expressions that dumps a textual
         representation of the complete expression.
-        
+
         Usage: after creation, call process_expr, and then get_str for a
         semicolon-delimited string representation of the decoded expression.
     """
@@ -485,10 +485,10 @@
             'DW_OP_pick', 'DW_OP_plus_uconst', 'DW_OP_bra', 'DW_OP_skip',
             'DW_OP_fbreg', 'DW_OP_piece', 'DW_OP_deref_size',
             'DW_OP_xderef_size', 'DW_OP_regx',])
-        
+
         for n in range(0, 32):
             self._ops_with_decimal_arg.add('DW_OP_breg%s' % n)
-        
+
         self._ops_with_two_decimal_args = set([
             'DW_OP_const8u', 'DW_OP_const8s', 'DW_OP_bregx', 'DW_OP_bit_piece'])
 
diff --git a/elftools/dwarf/die.py b/elftools/dwarf/die.py
index f0b5eb8..cb5d050 100644
--- a/elftools/dwarf/die.py
+++ b/elftools/dwarf/die.py
@@ -12,12 +12,12 @@
 from ..common.utils import struct_parse, preserve_stream_pos
 
 
-# AttributeValue - describes an attribute value in the DIE: 
+# AttributeValue - describes an attribute value in the DIE:
 #
 # name:
 #   The name (DW_AT_*) of this attribute
-# 
-# form: 
+#
+# form:
 #   The DW_FORM_* name of this attribute
 #
 # value:
@@ -39,37 +39,37 @@
 class DIE(object):
     """ A DWARF debugging information entry. On creation, parses itself from
         the stream. Each DIE is held by a CU.
-        
+
         Accessible attributes:
-        
+
             tag:
                 The DIE tag
-        
+
             size:
                 The size this DIE occupies in the section
-            
+
             offset:
                 The offset of this DIE in the stream
-            
+
             attributes:
-                An ordered dictionary mapping attribute names to values. It's 
+                An ordered dictionary mapping attribute names to values. It's
                 ordered to preserve the order of attributes in the section
-            
+
             has_children:
                 Specifies whether this DIE has children
-            
+
             abbrev_code:
-                The abbreviation code pointing to an abbreviation entry (not
-                that this is for informational pusposes only - this object 
+                The abbreviation code pointing to an abbreviation entry (note
+                that this is for informational pusposes only - this object
                 interacts with its abbreviation table transparently).
-        
+
         See also the public methods.
     """
     def __init__(self, cu, stream, offset):
         """ cu:
                 CompileUnit object this DIE belongs to. Used to obtain context
                 information (structs, abbrev table, etc.)
-                        
+
             stream, offset:
                 The stream and offset into it where this DIE's data is located
         """
@@ -77,7 +77,7 @@
         self.dwarfinfo = self.cu.dwarfinfo # get DWARFInfo context
         self.stream = stream
         self.offset = offset
-        
+
         self.attributes = OrderedDict()
         self.tag = None
         self.has_children = None
@@ -85,25 +85,25 @@
         self.size = 0
         self._children = []
         self._parent = None
-        
-        self._parse_DIE()   
-    
+
+        self._parse_DIE()
+
     def is_null(self):
         """ Is this a null entry?
         """
         return self.tag is None
-    
+
     def get_parent(self):
-        """ The parent DIE of this DIE. None if the DIE has no parent (i.e. a 
+        """ The parent DIE of this DIE. None if the DIE has no parent (i.e. a
             top-level DIE).
         """
         return self._parent
-    
+
     def iter_children(self):
         """ Yield all children of this DIE
         """
         return iter(self._children)
-    
+
     def iter_siblings(self):
         """ Yield all siblings of this DIE
         """
@@ -119,41 +119,41 @@
     #
     def add_child(self, die):
         self._children.append(die)
-    
+
     def set_parent(self, die):
         self._parent = die
 
     #------ PRIVATE ------#
-    
+
     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():
             s += '    |%-18s:  %s\n' % (attrname, attrval)
         return s
-    
+
     def __str__(self):
         return self.__repr__()
-    
+
     def _parse_DIE(self):
         """ Parses the DIE info from the section, based on the abbreviation
             table of the CU
         """
         structs = self.cu.structs
-        
-        # A DIE begins with the abbreviation code. Read it and use it to 
+
+        # A DIE begins with the abbreviation code. Read it and use it to
         # obtain the abbrev declaration for this DIE.
         # Note: here and elsewhere, preserve_stream_pos is used on operations
         # that manipulate the stream by reading data from it.
         #
         self.abbrev_code = struct_parse(
             structs.Dwarf_uleb128(''), self.stream, self.offset)
-        
+
         # This may be a null entry
         if self.abbrev_code == 0:
             self.size = self.stream.tell() - self.offset
             return
-        
+
         with preserve_stream_pos(self.stream):
             abbrev_decl = self.cu.get_abbrev_table().get_abbrev(
                 self.abbrev_code)
@@ -167,14 +167,14 @@
             attr_offset = self.stream.tell()
             raw_value = struct_parse(structs.Dwarf_dw_form[form], self.stream)
 
-            value = self._translate_attr_value(form, raw_value)            
+            value = self._translate_attr_value(form, raw_value)
             self.attributes[name] = AttributeValue(
                 name=name,
                 form=form,
                 value=value,
                 raw_value=raw_value,
                 offset=attr_offset)
-        
+
         self.size = self.stream.tell() - self.offset
 
     def _translate_attr_value(self, form, raw_value):
@@ -195,5 +195,3 @@
         else:
             value = raw_value
         return value
-    
-
diff --git a/elftools/elf/descriptions.py b/elftools/elf/descriptions.py
index 2cde281..df81000 100644
--- a/elftools/elf/descriptions.py
+++ b/elftools/elf/descriptions.py
@@ -6,7 +6,9 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-from .enums import ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
+from .enums import (
+    ENUM_D_TAG, ENUM_E_VERSION, ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
+    )
 from .constants import P_FLAGS, SH_FLAGS
 from ..common.py3compat import iteritems
 
@@ -22,7 +24,7 @@
     if x == 'EV_CURRENT':
         s += ' (current)'
     return s
-    
+
 def describe_ei_osabi(x):
     return _DESCR_EI_OSABI.get(x, _unknown)
 
@@ -41,7 +43,7 @@
 def describe_p_flags(x):
     s = ''
     for flag in (P_FLAGS.PF_R, P_FLAGS.PF_W, P_FLAGS.PF_X):
-        s += _DESCR_P_FLAGS[flag] if (x & flag) else ' ' 
+        s += _DESCR_P_FLAGS[flag] if (x & flag) else ' '
     return s
 
 def describe_sh_type(x):
@@ -78,11 +80,14 @@
     else:
         return 'unrecognized: %-7x' % (x & 0xFFFFFFFF)
 
+def describe_dyn_tag(x):
+    return _DESCR_D_TAG.get(x, _unknown)
+
 
 #-------------------------------------------------------------------------------
 _unknown = '<unknown>'
 
-    
+
 _DESCR_EI_CLASS = dict(
     ELFCLASSNONE='none',
     ELFCLASS32='ELF32',
@@ -237,4 +242,5 @@
 _DESCR_RELOC_TYPE_x64 = dict(
         (v, k) for k, v in iteritems(ENUM_RELOC_TYPE_x64))
 
-
+_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 2292470..a62ef16 100644
--- a/elftools/elf/dynamic.py
+++ b/elftools/elf/dynamic.py
@@ -12,24 +12,27 @@
 from .segments import Segment
 from ..common.utils import struct_parse
 
-from enums import ENUM_D_TAG
+from .enums import ENUM_D_TAG
 
 
 class DynamicTag(object):
     """ Dynamic Tag object - representing a single dynamic tag entry from a
         dynamic section.
 
-        Similarly to Section objects, allows dictionary-like access to the
-        dynamic tag.
+        Allows dictionary-like access to the dynamic structure. For special
+        tags (those listed in the _HANDLED_TAGS set below), creates additional
+        attributes for convenience. For example, .soname will contain the actual
+        value of DT_SONAME (fetched from the dynamic symbol table).
     """
-
-    _HANDLED_TAGS = frozenset(['DT_NEEDED', 'DT_RPATH', 'DT_RUNPATH'])
+    _HANDLED_TAGS = frozenset(
+        ['DT_NEEDED', 'DT_RPATH', 'DT_RUNPATH', 'DT_SONAME'])
 
     def __init__(self, entry, elffile):
         self.entry = entry
         if entry.d_tag in self._HANDLED_TAGS:
-            dynstr = elffile.get_section_by_name('.dynstr')
-            setattr(self, entry.d_tag[3:].lower(), dynstr.get_string(self.entry.d_val))
+            dynstr = elffile.get_section_by_name(b'.dynstr')
+            setattr(self, entry.d_tag[3:].lower(),
+                    dynstr.get_string(self.entry.d_val))
 
     def __getitem__(self, name):
         """ Implement dict-like access to entries
@@ -48,11 +51,13 @@
 
 
 class Dynamic(object):
+    """ Shared functionality between dynamic sections and segments.
+    """
     def __init__(self, stream, elffile, position):
         self._stream = stream
         self._elffile = elffile
         self._elfstructs = elffile.structs
-        self._num_tags = -1;
+        self._num_tags = -1
         self._offset = position
         self._tagsize = self._elfstructs.Elf_Dyn.sizeof()
 
@@ -76,7 +81,6 @@
             stream_pos=offset)
         return DynamicTag(entry, self._elffile)
 
-    @property
     def num_tags(self):
         """ Number of dynamic tags in the file
         """
@@ -86,8 +90,8 @@
         for n in itertools.count():
             tag = self.get_tag(n)
             if tag.entry.d_tag == 'DT_NULL':
-                self._num_tags = n
-                return n
+                self._num_tags = n + 1
+                return self._num_tags
 
 
 class DynamicSection(Section, Dynamic):
diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py
index e2bd904..1d8d6de 100644
--- a/elftools/elf/elffile.py
+++ b/elftools/elf/elffile.py
@@ -23,18 +23,18 @@
 class ELFFile(object):
     """ Creation: the constructor accepts a stream (file-like object) with the
         contents of an ELF file.
-    
+
         Accessible attributes:
 
             stream:
                 The stream holding the data of the file - must be a binary
                 stream (bytes, not string).
 
-            elfclass: 
+            elfclass:
                 32 or 64 - specifies the word size of the target machine
-            
+
             little_endian:
-                boolean - specifies the target machine's endianness     
+                boolean - specifies the target machine's endianness
 
             header:
                 the complete ELF file header
@@ -52,24 +52,24 @@
 
         self.stream.seek(0)
         self.e_ident_raw = self.stream.read(16)
-        
+
         self._file_stringtable_section = self._get_file_stringtable()
         self._section_name_map = None
-    
+
     def num_sections(self):
         """ Number of sections in the file
         """
         return self['e_shnum']
-    
+
     def get_section(self, n):
         """ Get the section at index #n from the file (Section object or a
             subclass)
         """
         section_header = self._get_section_header(n)
         return self._make_section(section_header)
-    
+
     def get_section_by_name(self, name):
-        """ Get a section from the file, by name. Return None if no such 
+        """ Get a section from the file, by name. Return None if no such
             section exists.
         """
         # The first time this method is called, construct a name to number
@@ -81,18 +81,18 @@
                 self._section_name_map[sec.name] = i
         secnum = self._section_name_map.get(name, None)
         return None if secnum is None else self.get_section(secnum)
-    
+
     def iter_sections(self):
         """ Yield all the sections in the file
         """
         for i in range(self.num_sections()):
             yield self.get_section(i)
-    
+
     def num_segments(self):
         """ Number of segments in the file
         """
         return self['e_phnum']
-    
+
     def get_segment(self, n):
         """ Get the segment at index #n from the file (Segment object)
         """
@@ -106,7 +106,7 @@
             yield self.get_segment(i)
 
     def has_dwarf_info(self):
-        """ Check whether this file appears to have debugging information. 
+        """ Check whether this file appears to have debugging information.
             We assume that if it has the debug_info section, it has all theother
             required sections as well.
         """
@@ -119,12 +119,12 @@
             If relocate_dwarf_sections is True, relocations for DWARF sections
             are looked up and applied.
         """
-        # Expect that has_dwarf_info was called, so at least .debug_info is 
-        # present. 
+        # Expect that has_dwarf_info was called, so at least .debug_info is
+        # present.
         # Sections that aren't found will be passed as None to DWARFInfo.
         #
         debug_sections = {}
-        for secname in (b'.debug_info', b'.debug_abbrev', b'.debug_str', 
+        for secname in (b'.debug_info', b'.debug_abbrev', b'.debug_str',
                         b'.debug_line', b'.debug_frame', b'.debug_loc',
                         b'.debug_ranges'):
             section = self.get_section_by_name(secname)
@@ -150,12 +150,14 @@
 
     def get_machine_arch(self):
         """ Return the machine architecture, as detected from the ELF header.
-            At the moment the only supported architectures are x86 and x64.
+            Not all architectures are supported at the moment.
         """
         if self['e_machine'] == 'EM_X86_64':
             return 'x64'
         elif self['e_machine'] in ('EM_386', 'EM_486'):
             return 'x86'
+        elif self['e_machine'] == 'EM_ARM':
+            return 'ARM'
         else:
             return '<unknown>'
 
@@ -170,7 +172,7 @@
         """ Verify the ELF file and identify its class and endianness.
         """
         # Note: this code reads the stream directly, without using ELFStructs,
-        # since we don't yet know its exact format. ELF was designed to be 
+        # since we don't yet know its exact format. ELF was designed to be
         # read like this - its e_ident field is word-size and endian agnostic.
         #
         self.stream.seek(0)
@@ -192,17 +194,17 @@
             self.little_endian = False
         else:
             raise ELFError('Invalid EI_DATA %s' % repr(ei_data))
-    
+
     def _section_offset(self, n):
         """ Compute the offset of section #n in the file
         """
         return self['e_shoff'] + n * self['e_shentsize']
-    
+
     def _segment_offset(self, n):
         """ Compute the offset of segment #n in the file
         """
         return self['e_phoff'] + n * self['e_phentsize']
-    
+
     def _make_segment(self, segment_header):
         """ Create a Segment object of the appropriate type
         """
@@ -215,13 +217,13 @@
             return Segment(segment_header, self.stream)
 
     def _get_section_header(self, n):
-        """ Find the header of section #n, parse it and return the struct 
+        """ Find the header of section #n, parse it and return the struct
         """
         return struct_parse(
             self.structs.Elf_Shdr,
             self.stream,
             stream_pos=self._section_offset(n))
-    
+
     def _get_section_name(self, section_header):
         """ Given a section header, find this section's name in the file's
             string table
@@ -234,7 +236,7 @@
         """
         name = self._get_section_name(section_header)
         sectype = section_header['sh_type']
-        
+
         if sectype == 'SHT_STRTAB':
             return StringTableSection(section_header, name, self.stream)
         elif sectype == 'SHT_NULL':
@@ -266,7 +268,7 @@
             self.structs.Elf_Phdr,
             self.stream,
             stream_pos=self._segment_offset(n))
-    
+
     def _get_file_stringtable(self):
         """ Find the file's string table section
         """
diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py
index 804fc45..aaa6c71 100644
--- a/elftools/elf/enums.py
+++ b/elftools/elf/enums.py
@@ -65,7 +65,6 @@
 )
 
 # e_machine in the ELF header
-# (this list is currently somewhat partial...)
 ENUM_E_MACHINE = dict(
     EM_NONE=0,
     EM_M32=1,
@@ -73,14 +72,92 @@
     EM_386=3,
     EM_68K=4,
     EM_88K=5,
-    EM_486=6,
     EM_860=7,
     EM_MIPS=8,
     EM_S370=9,
-    EM_MIPS_RS4_BE=10,
+    EM_MIPS_RS3_LE=10,
+    EM_PARISC=15,
+    EM_VPP500=17,
+    EM_SPARC32PLUS=18,
+    EM_960=19,
+    EM_PPC=20,
+    EM_PPC64=21,
+    EM_S390=22,
+    EM_V800=36,
+    EM_FR20=37,
+    EM_RH32=38,
+    EM_RCE=39,
+    EM_ARM=40,
+    EM_ALPHA=41,
+    EM_SH=42,
+    EM_SPARCV9=43,
+    EM_TRICORE=44,
+    EM_ARC=45,
+    EM_H8_300=46,
+    EM_H8_300H=47,
+    EM_H8S=48,
+    EM_H8_500=49,
     EM_IA_64=50,
+    EM_MIPS_X=51,
+    EM_COLDFIRE=52,
+    EM_68HC12=53,
+    EM_MMA=54,
+    EM_PCP=55,
+    EM_NCPU=56,
+    EM_NDR1=57,
+    EM_STARCORE=58,
+    EM_ME16=59,
+    EM_ST100=60,
+    EM_TINYJ=61,
     EM_X86_64=62,
+    EM_PDSP=63,
+    EM_PDP10=64,
+    EM_PDP11=65,
+    EM_FX66=66,
+    EM_ST9PLUS=67,
+    EM_ST7=68,
+    EM_68HC16=69,
+    EM_68HC11=70,
+    EM_68HC08=71,
+    EM_68HC05=72,
+    EM_SVX=73,
+    EM_ST19=74,
+    EM_VAX=75,
+    EM_CRIS=76,
+    EM_JAVELIN=77,
+    EM_FIREPATH=78,
+    EM_ZSP=79,
+    EM_MMIX=80,
+    EM_HUANY=81,
+    EM_PRISM=82,
     EM_AVR=83,
+    EM_FR30=84,
+    EM_D10V=85,
+    EM_D30V=86,
+    EM_V850=87,
+    EM_M32R=88,
+    EM_MN10300=89,
+    EM_MN10200=90,
+    EM_PJ=91,
+    EM_OPENRISC=92,
+    EM_ARC_A5=93,
+    EM_XTENSA=94,
+    EM_VIDEOCORE=95,
+    EM_TMM_GPP=96,
+    EM_NS32K=97,
+    EM_TPC=98,
+    EM_SNP1K=99,
+    EM_ST200=100,
+    EM_IP2K=101,
+    EM_MAX=102,
+    EM_CR=103,
+    EM_F2MC16=104,
+    EM_MSP430=105,
+    EM_BLACKFIN=106,
+    EM_SE_C33=107,
+    EM_SEP=108,
+    EM_ARCA=109,
+    EM_UNICORE=110,
     EM_L10M=180,
     _default_=Pass,
 )
diff --git a/elftools/elf/relocation.py b/elftools/elf/relocation.py
index 5ff853b..7c2b74c 100644
--- a/elftools/elf/relocation.py
+++ b/elftools/elf/relocation.py
@@ -23,12 +23,12 @@
     def __init__(self, entry, elffile):
         self.entry = entry
         self.elffile = elffile
-        
+
     def is_RELA(self):
         """ Is this a RELA relocation? If not, it's REL.
         """
         return 'r_addend' in self.entry
-        
+
     def __getitem__(self, name):
         """ Dict-like access to entries
         """
@@ -112,7 +112,7 @@
                     relsection.name in reloc_section_names):
                 return relsection
         return None
-        
+
     def apply_section_relocations(self, stream, reloc_section):
         """ Apply all relocations in reloc_section (a RelocationSection object)
             to the given stream, that contains the data of the section that is
@@ -162,7 +162,7 @@
         elif recipe.bytesize == 8:
             value_struct = self.elffile.structs.Elf_word64('')
         else:
-            raise ELFRelocationError('Invalid bytesize %s for relocation' % 
+            raise ELFRelocationError('Invalid bytesize %s for relocation' %
                     recipe_bytesize)
 
         # 1. Read the value from the stream (with correct size and endianness)
@@ -198,10 +198,10 @@
 
     def _reloc_calc_sym_plus_value_pcrel(value, sym_value, offset, addend=0):
         return sym_value + value - offset
-        
+
     def _reloc_calc_sym_plus_addend(value, sym_value, offset, addend=0):
         return sym_value + addend
-        
+
     _RELOCATION_RECIPES_X86 = {
         ENUM_RELOC_TYPE_i386['R_386_NONE']: _RELOCATION_RECIPE_TYPE(
             bytesize=4, has_addend=False, calc_func=_reloc_calc_identity),
@@ -212,7 +212,7 @@
             bytesize=4, has_addend=False,
             calc_func=_reloc_calc_sym_plus_value_pcrel),
     }
-    
+
     _RELOCATION_RECIPES_X64 = {
         ENUM_RELOC_TYPE_x64['R_X86_64_NONE']: _RELOCATION_RECIPE_TYPE(
             bytesize=8, has_addend=True, calc_func=_reloc_calc_identity),
diff --git a/elftools/elf/segments.py b/elftools/elf/segments.py
index 217ba08..bc54da2 100644
--- a/elftools/elf/segments.py
+++ b/elftools/elf/segments.py
@@ -15,7 +15,7 @@
     def __init__(self, header, stream):
         self.header = header
         self.stream = stream
-    
+
     def data(self):
         """ The segment data from the file.
         """
@@ -30,8 +30,8 @@
     def section_in_segment(self, section):
         """ Is the given section contained in this segment?
 
-            Note: this tries to reproduce the intricate rules of the 
-            ELF_SECTION_IN_SEGMENT_STRICT macro of the header 
+            Note: this tries to reproduce the intricate rules of the
+            ELF_SECTION_IN_SEGMENT_STRICT macro of the header
             elf/include/internal.h in the source of binutils.
         """
         # Only the 'strict' checks from ELF_SECTION_IN_SEGMENT_1 are included
@@ -41,7 +41,7 @@
 
         # Only PT_LOAD, PT_GNU_RELR0 and PT_TLS segments can contain SHF_TLS
         # sections
-        if (    secflags & SH_FLAGS.SHF_TLS and 
+        if (    secflags & SH_FLAGS.SHF_TLS and
                 segtype in ('PT_TLS', 'PT_GNU_RELR0', 'PT_LOAD')):
             return False
         # PT_TLS segment contains only SHF_TLS sections, PT_PHDR no sections
@@ -61,7 +61,7 @@
             # not match at the very end of the segment (unless the segment is
             # also zero size, which is handled by the second condition).
             if not (secaddr >= vaddr and
-                    secaddr - vaddr + section['sh_size'] <= self['p_memsz'] and 
+                    secaddr - vaddr + section['sh_size'] <= self['p_memsz'] and
                     secaddr - vaddr <= self['p_memsz'] - 1):
                 return False
 
@@ -74,7 +74,7 @@
 
         # Same logic as with secaddr vs. vaddr checks above, just on offsets in
         # the file
-        return (secoffset >= poffset and 
+        return (secoffset >= poffset and
                 secoffset - poffset + section['sh_size'] <= self['p_filesz'] and
                 secoffset - poffset <= self['p_filesz'] - 1)
 
diff --git a/examples/dwarf_decode_address.py b/examples/dwarf_decode_address.py
index 831b4fc..80b53ee 100644
--- a/examples/dwarf_decode_address.py
+++ b/examples/dwarf_decode_address.py
@@ -10,12 +10,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import maxint, bytes2str
 from elftools.elf.elffile import ELFFile
@@ -45,7 +42,7 @@
 def decode_funcname(dwarfinfo, address):
     # Go over all DIEs in the DWARF information, looking for a subprogram
     # entry with an address range that includes the given address. Note that
-    # this simplifies things by disregarding subprograms that may have 
+    # this simplifies things by disregarding subprograms that may have
     # split address ranges.
     for CU in dwarfinfo.iter_CUs():
         for DIE in CU.iter_DIEs():
@@ -66,16 +63,18 @@
     for CU in dwarfinfo.iter_CUs():
         # First, look at line programs to find the file/line for the address
         lineprog = dwarfinfo.line_program_for_CU(CU)
-        prevaddr = maxint
+        prevstate = None
         for entry in lineprog.get_entries():
             # We're interested in those entries where a new state is assigned
-            state = entry.state
-            if state is not None and not state.end_sequence:
-                if prevaddr <= address <= state.address:
-                    filename = lineprog['file_entry'][state.file - 1].name
-                    line = state.line
-                    return filename, line
-                prevaddr = state.address
+            if entry.state is None or entry.state.end_sequence:
+                continue
+            # Looking for a range of addresses in two consecutive states that
+            # contain the required address.
+            if prevstate and prevstate.address <= address < entry.state.address:
+                filename = lineprog['file_entry'][prevstate.file - 1].name
+                line = prevstate.line
+                return filename, line
+            prevstate = entry.state
     return None, None
 
 
diff --git a/examples/dwarf_die_tree.py b/examples/dwarf_die_tree.py
index 7eb07ff..9dcb6b6 100644
--- a/examples/dwarf_die_tree.py
+++ b/examples/dwarf_die_tree.py
@@ -10,12 +10,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import bytes2str
 from elftools.elf.elffile import ELFFile
diff --git a/examples/dwarf_location_lists.py b/examples/dwarf_location_lists.py
index 6289618..d8ee1e9 100644
--- a/examples/dwarf_location_lists.py
+++ b/examples/dwarf_location_lists.py
@@ -10,12 +10,10 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
+
 
 from elftools.common.py3compat import itervalues
 from elftools.elf.elffile import ELFFile
@@ -86,7 +84,7 @@
         else:
             d.append(str(loc_entity))
     return '\n'.join(indent + s for s in d)
-    
+
 
 def attribute_has_location_list(attr):
     """ Only some attributes can have location list values, if they have the
diff --git a/examples/dwarf_range_lists.py b/examples/dwarf_range_lists.py
index c310e58..fced6a6 100644
--- a/examples/dwarf_range_lists.py
+++ b/examples/dwarf_range_lists.py
@@ -1,7 +1,7 @@
 #-------------------------------------------------------------------------------
 # elftools example: dwarf_range_lists.py
 #
-# Examine DIE entries which have range list values, and decode these range 
+# Examine DIE entries which have range list values, and decode these range
 # lists.
 #
 # Eli Bendersky (eliben@gmail.com)
@@ -10,12 +10,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import itervalues
 from elftools.elf.elffile import ELFFile
diff --git a/examples/elf_low_high_api.py b/examples/elf_low_high_api.py
index 05d7564..e2d7f49 100644
--- a/examples/elf_low_high_api.py
+++ b/examples/elf_low_high_api.py
@@ -11,12 +11,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import bytes2str
 from elftools.elf.elffile import ELFFile
diff --git a/examples/elf_relocations.py b/examples/elf_relocations.py
index fd65541..0086266 100644
--- a/examples/elf_relocations.py
+++ b/examples/elf_relocations.py
@@ -10,12 +10,10 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
+
 
 from elftools.common.py3compat import bytes2str
 from elftools.elf.elffile import ELFFile
diff --git a/examples/elf_show_debug_sections.py b/examples/elf_show_debug_sections.py
index 96e1dd9..4e4da61 100644
--- a/examples/elf_show_debug_sections.py
+++ b/examples/elf_show_debug_sections.py
@@ -9,12 +9,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import bytes2str
 from elftools.elf.elffile import ELFFile
diff --git a/examples/elfclass_address_size.py b/examples/elfclass_address_size.py
index d6b19d3..ac2bfaa 100644
--- a/examples/elfclass_address_size.py
+++ b/examples/elfclass_address_size.py
@@ -10,12 +10,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.elf.elffile import ELFFile
 
diff --git a/examples/examine_dwarf_info.py b/examples/examine_dwarf_info.py
index 35ce35b..1aa28c6 100644
--- a/examples/examine_dwarf_info.py
+++ b/examples/examine_dwarf_info.py
@@ -9,12 +9,9 @@
 from __future__ import print_function
 import sys
 
-# If elftools is not installed, maybe we're running from the root or examples
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# If pyelftools is not installed, the example can also run from the root or
+# examples/ dir of the source distribution.
+sys.path[0:0] = ['.', '..']
 
 from elftools.common.py3compat import bytes2str
 from elftools.elf.elffile import ELFFile
diff --git a/examples/reference_output/dwarf_decode_address.out b/examples/reference_output/dwarf_decode_address.out
index 73ca9ee..e14d84a 100644
--- a/examples/reference_output/dwarf_decode_address.out
+++ b/examples/reference_output/dwarf_decode_address.out
@@ -1,4 +1,4 @@
 Processing file: ./examples/sample_exe64.elf
 Function: main
 File: z.c
-Line: 4
+Line: 3
diff --git a/pyelftools.sublime-project b/pyelftools.sublime-project
new file mode 100644
index 0000000..a92140c
--- /dev/null
+++ b/pyelftools.sublime-project
@@ -0,0 +1,9 @@
+{
+    "folders":
+    [
+        {
+            "path": ".",
+            "folder_exclude_patterns": ["build", "dist", ".tox", ".hg"]
+        }
+    ]
+}
diff --git a/scripts/readelf.py b/scripts/readelf.py
index 04c75ba..720106d 100755
--- a/scripts/readelf.py
+++ b/scripts/readelf.py
@@ -11,18 +11,18 @@
 from optparse import OptionParser
 import string
 
-# If elftools is not installed, maybe we're running from the root or scripts
-# dir of the source distribution
-try:
-    import elftools
-except ImportError:
-    sys.path.extend(['.', '..'])
+# For running from development directory. It should take precedence over the
+# installed pyelftools.
+sys.path.insert(0, '.')
+
 
 from elftools import __version__
 from elftools.common.exceptions import ELFError
 from elftools.common.py3compat import (
         ifilter, byte2int, bytes2str, itervalues, str2bytes)
 from elftools.elf.elffile import ELFFile
+from elftools.elf.dynamic import DynamicSection, DynamicSegment
+from elftools.elf.enums import ENUM_D_TAG
 from elftools.elf.segments import InterpSegment
 from elftools.elf.sections import SymbolTableSection
 from elftools.elf.relocation import RelocationSection
@@ -32,7 +32,7 @@
     describe_e_version_numeric, describe_p_type, describe_p_flags,
     describe_sh_type, describe_sh_flags,
     describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
-    describe_symbol_shndx, describe_reloc_type,
+    describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
     )
 from elftools.dwarf.dwarfinfo import DWARFInfo
 from elftools.dwarf.descriptions import (
@@ -49,15 +49,15 @@
     """ display_* methods are used to emit output into the output stream
     """
     def __init__(self, file, output):
-        """ file: 
+        """ file:
                 stream object with the ELF file to read
-            
+
             output:
                 output stream to write to
         """
         self.elffile = ELFFile(file)
         self.output = output
-        
+
         # Lazily initialized if a debug dump is requested
         self._dwarfinfo = None
 
@@ -70,31 +70,31 @@
                                     for b in self.elffile.e_ident_raw))
         header = self.elffile.header
         e_ident = header['e_ident']
-        self._emitline('  Class:                             %s' % 
+        self._emitline('  Class:                             %s' %
                 describe_ei_class(e_ident['EI_CLASS']))
-        self._emitline('  Data:                              %s' % 
+        self._emitline('  Data:                              %s' %
                 describe_ei_data(e_ident['EI_DATA']))
-        self._emitline('  Version:                           %s' % 
+        self._emitline('  Version:                           %s' %
                 describe_ei_version(e_ident['EI_VERSION']))
         self._emitline('  OS/ABI:                            %s' %
                 describe_ei_osabi(e_ident['EI_OSABI']))
-        self._emitline('  ABI Version:                       %d' % 
+        self._emitline('  ABI Version:                       %d' %
                 e_ident['EI_ABIVERSION'])
         self._emitline('  Type:                              %s' %
                 describe_e_type(header['e_type']))
-        self._emitline('  Machine:                           %s' % 
+        self._emitline('  Machine:                           %s' %
                 describe_e_machine(header['e_machine']))
         self._emitline('  Version:                           %s' %
                 describe_e_version_numeric(header['e_version']))
-        self._emitline('  Entry point address:               %s' % 
+        self._emitline('  Entry point address:               %s' %
                 self._format_hex(header['e_entry']))
-        self._emit('  Start of program headers:          %s' % 
+        self._emit('  Start of program headers:          %s' %
                 header['e_phoff'])
         self._emitline(' (bytes into file)')
-        self._emit('  Start of section headers:          %s' % 
+        self._emit('  Start of section headers:          %s' %
                 header['e_shoff'])
         self._emitline(' (bytes into file)')
-        self._emitline('  Flags:                             %s' % 
+        self._emitline('  Flags:                             %s' %
                 self._format_hex(header['e_flags']))
         self._emitline('  Size of this header:               %s (bytes)' %
                 header['e_ehsize'])
@@ -173,14 +173,14 @@
                     self._format_hex(segment['p_align'], lead0x=False)))
 
             if isinstance(segment, InterpSegment):
-                self._emitline('      [Requesting program interpreter: %s]' % 
+                self._emitline('      [Requesting program interpreter: %s]' %
                     bytes2str(segment.get_interp_name()))
 
         # Sections to segments mapping
         #
         if self.elffile.num_sections() == 0:
             # No sections? We're done
-            return 
+            return
 
         self._emitline('\n Section to Segment mapping:')
         self._emitline('  Segment Sections...')
@@ -189,7 +189,7 @@
             self._emit('   %2.2d     ' % nseg)
 
             for section in self.elffile.iter_sections():
-                if (    not section.is_null() and 
+                if (    not section.is_null() and
                         segment.section_in_segment(section)):
                     self._emit('%s ' % bytes2str(section.name))
 
@@ -282,7 +282,50 @@
                     describe_symbol_visibility(symbol['st_other']['visibility']),
                     describe_symbol_shndx(symbol['st_shndx']),
                     bytes2str(symbol.name)))
-        
+
+    def display_dynamic_tags(self):
+        """ Display the dynamic tags contained in the file
+        """
+        for section in self.elffile.iter_sections():
+            if not isinstance(section, DynamicSection):
+                continue
+
+            self._emitline("\nDynamic section at offset %s contains %s entries:" % (
+                self._format_hex(section['sh_offset']),
+                section.num_tags()))
+            self._emitline("  Tag        Type                         Name/Value")
+
+            padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
+            for tag in section.iter_tags():
+                if tag.entry.d_tag == 'DT_NEEDED':
+                    parsed = 'Shared library: [%s]' % bytes2str(tag.needed)
+                elif tag.entry.d_tag == 'DT_RPATH':
+                    parsed = 'Library rpath: [%s]' % bytes2str(tag.rpath)
+                elif tag.entry.d_tag == 'DT_RUNPATH':
+                    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')):
+                    parsed = '%i (bytes)' % tag['d_val']
+                elif (tag.entry.d_tag.endswith('NUM') or
+                      tag.entry.d_tag.endswith('COUNT')):
+                    parsed = '%i' % tag['d_val']
+                elif tag.entry.d_tag == 'DT_PLTREL':
+                    s = describe_dyn_tag(tag.entry.d_val)
+                    if s.startswith('DT_'):
+                        s = s[3:]
+                    parsed = '%s' % s
+                else:
+                    parsed = '%#x' % tag['d_val']
+
+                self._emitline(" %s %-*s %s" % (
+                    self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
+                        fullhex=True, lead0x=True),
+                    padding,
+                    '(%s)' % (tag.entry.d_tag[3:],),
+                    parsed))
+
     def display_relocations(self):
         """ Display the relocations contained in the file
         """
@@ -307,9 +350,9 @@
             for rel in section.iter_relocations():
                 hexwidth = 8 if self.elffile.elfclass == 32 else 12
                 self._emit('%s  %s %-17.17s' % (
-                    self._format_hex(rel['r_offset'], 
+                    self._format_hex(rel['r_offset'],
                         fieldsize=hexwidth, lead0x=False),
-                    self._format_hex(rel['r_info'], 
+                    self._format_hex(rel['r_info'],
                         fieldsize=hexwidth, lead0x=False),
                     describe_reloc_type(
                         rel['r_info_type'], self.elffile)))
@@ -340,7 +383,7 @@
 
         if not has_relocation_sections:
             self._emitline('\nThere are no relocations in this file.')
-        
+
     def display_hex_dump(self, section_spec):
         """ Display a hex dump of a section. section_spec is either a section
             number or a name.
@@ -401,7 +444,7 @@
         dataptr = 0
 
         while dataptr < len(data):
-            while ( dataptr < len(data) and 
+            while ( dataptr < len(data) and
                     not (32 <= byte2int(data[dataptr]) <= 127)):
                 dataptr += 1
 
@@ -429,7 +472,7 @@
         self._init_dwarfinfo()
         if self._dwarfinfo is None:
             return
-        
+
         set_global_machine_arch(self.elffile.get_machine_arch())
 
         if dump_what == 'info':
@@ -453,7 +496,7 @@
                 If None, the minimal required field size will be used.
 
             fullhex:
-                If True, override fieldsize to set it to the maximal size 
+                If True, override fieldsize to set it to the maximal size
                 needed for the elfclass
 
             lead0x:
@@ -467,7 +510,7 @@
         else:
             field = '%' + '0%sx' % fieldsize
         return s + field % addr
-        
+
     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.
@@ -475,7 +518,7 @@
         try:
             num = int(spec)
             if num < self.elffile.num_sections():
-                return self.elffile.get_section(num) 
+                return self.elffile.get_section(num)
             else:
                 return None
         except ValueError:
@@ -500,7 +543,7 @@
         """
         if self._dwarfinfo is not None:
             return
-        
+
         if self.elffile.has_dwarf_info():
             self._dwarfinfo = self.elffile.get_dwarf_info()
         else:
@@ -510,7 +553,7 @@
         """ Dump the debugging info section.
         """
         self._emitline('Contents of the .debug_info section:\n')
-        
+
         # Offset of the .debug_info section in the stream
         section_offset = self._dwarfinfo.debug_info_sec.global_offset
 
@@ -521,10 +564,11 @@
                 self._format_hex(cu['unit_length']),
                 '%s-bit' % cu.dwarf_format()))
             self._emitline('   Version:       %s' % cu['version']),
-            self._emitline('   Abbrev Offset: %s' % cu['debug_abbrev_offset']),
+            self._emitline('   Abbrev Offset: %s' % (
+                self._format_hex(cu['debug_abbrev_offset']))),
             self._emitline('   Pointer Size:  %s' % cu['address_size'])
-            
-            # The nesting depth of each DIE within the tree of DIEs must be 
+
+            # The nesting depth of each DIE within the tree of DIEs must be
             # displayed. To implement this, a counter is incremented each time
             # the current DIE has children, and decremented when a null die is
             # encountered. Due to the way the DIE tree is serialized, this will
@@ -532,15 +576,15 @@
             #
             die_depth = 0
             for die in cu.iter_DIEs():
-                if die.is_null():
-                    die_depth -= 1
-                    continue
-                self._emitline(' <%s><%x>: Abbrev Number: %s (%s)' % (
+                self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
                     die_depth,
                     die.offset,
                     die.abbrev_code,
-                    die.tag))
-                
+                    (' (%s)' % die.tag) if not die.is_null() else ''))
+                if die.is_null():
+                    die_depth -= 1
+                    continue
+
                 for attr in itervalues(die.attributes):
                     name = attr.name
                     # Unknown attribute values are passed-through as integers
@@ -551,10 +595,10 @@
                         name,
                         describe_attr_value(
                             attr, die, section_offset)))
-                
+
                 if die.has_children:
                     die_depth += 1
-                    
+
         self._emitline()
 
     def _dump_debug_line_programs(self):
@@ -566,13 +610,14 @@
         for cu in self._dwarfinfo.iter_CUs():
             lineprogram = self._dwarfinfo.line_program_for_CU(cu)
 
-            cu_filename = ''
+            cu_filename = bytes2str(lineprogram['file_entry'][0].name)
             if len(lineprogram['include_directory']) > 0:
-                cu_filename = '%s/%s' % (
-                    bytes2str(lineprogram['include_directory'][0]),
-                    bytes2str(lineprogram['file_entry'][0].name))
-            else:
-                cu_filename = bytes2str(lineprogram['file_entry'][0].name)
+                dir_index = lineprogram['file_entry'][0].dir_index
+                if dir_index > 0:
+                    dir = lineprogram['include_directory'][dir_index - 1]
+                else:
+                    dir = b'.'
+                cu_filename = '%s/%s' % (bytes2str(dir), cu_filename)
 
             self._emitline('CU: %s:' % cu_filename)
             self._emitline('File name                            Line number    Starting address')
@@ -604,7 +649,7 @@
                     self._emitline('%-35s  %11d  %18s' % (
                         bytes2str(lineprogram['file_entry'][state.file - 1].name),
                         state.line,
-                        '0' if state.address == 0 else 
+                        '0' if state.address == 0 else
                                self._format_hex(state.address)))
                 if entry.command == DW_LNS_copy:
                     # Another readelf oddity...
@@ -679,14 +724,14 @@
             # ra_regnum is always listed last with a special heading.
             decoded_table = entry.get_decoded()
             reg_order = sorted(ifilter(
-                lambda r: r != ra_regnum, 
+                lambda r: r != ra_regnum,
                 decoded_table.reg_order))
 
             # Headings for the registers
             for regnum in reg_order:
                 self._emit('%-6s' % describe_reg_name(regnum))
             self._emitline('ra      ')
-            
+
             # Now include ra_regnum in reg_order to print its values similarly
             # to the other registers.
             reg_order.append(ra_regnum)
@@ -727,6 +772,9 @@
             add_help_option=False, # -h is a real option of readelf
             prog='readelf.py',
             version=VERSION_STRING)
+    optparser.add_option('-d', '--dynamic',
+            action='store_true', dest='show_dynamic_tags',
+            help='Display the dynamic section')
     optparser.add_option('-H', '--help',
             action='store_true', dest='help',
             help='Display this information')
@@ -784,6 +832,8 @@
             if do_program_header:
                 readelf.display_program_headers(
                         show_heading=not do_file_header)
+            if options.show_dynamic_tags:
+                readelf.display_dynamic_tags()
             if options.show_symbols:
                 readelf.display_symbol_tables()
             if options.show_relocs:
diff --git a/setup.py b/setup.py
index 50b5f95..c8d54b1 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@
     description='Library for analyzing ELF files and DWARF debugging information',

     long_description=description,

     license='Public domain',

-    version='0.20',

+    version='0.21',

     author='Eli Bendersky',

     maintainer='Eli Bendersky',

     author_email='eliben@gmail.com',

diff --git a/test/README b/test/README
new file mode 100644
index 0000000..2ee6883
--- /dev/null
+++ b/test/README
@@ -0,0 +1 @@
+All tests should be run from the root development directory of pyelftools
diff --git a/test/run_all_unittests.py b/test/run_all_unittests.py
index 1130251..f7291bc 100755
--- a/test/run_all_unittests.py
+++ b/test/run_all_unittests.py
@@ -16,11 +16,9 @@
 
 
 if __name__ == '__main__':
-    try:
+    import os
+    if not os.path.isdir('test'):
+        print('!! Please execute from the root directory of pyelftools')
+    else:
         tests = unittest.TestLoader().discover('test', 'test*.py', 'test')
         unittest.TextTestRunner().run(tests)
-    except ImportError as err:
-        print(err)
-        print('!! Please execute from the root directory of pyelftools')
-
-
diff --git a/test/run_examples_test.py b/test/run_examples_test.py
index 06e8585..0ac5859 100755
--- a/test/run_examples_test.py
+++ b/test/run_examples_test.py
@@ -9,8 +9,8 @@
 #-------------------------------------------------------------------------------
 import os, sys
 import logging
-sys.path.insert(0, '.')
-from test.utils import run_exe, is_in_rootdir, dump_output_to_temp_files
+from utils import setup_syspath; setup_syspath()
+from utils import run_exe, is_in_rootdir, dump_output_to_temp_files
 
 
 # Create a global logger object
@@ -54,7 +54,7 @@
     if rc != 0:
         testlog.info('.......ERROR - example returned error code %s' % rc)
         return False
-    
+
     # Comparison is done as lists of lines, to avoid EOL problems
     if example_out.split() == ref_str.split():
         return True
diff --git a/test/run_readelf_tests.py b/test/run_readelf_tests.py
index 984b7a5..0c542d8 100755
--- a/test/run_readelf_tests.py
+++ b/test/run_readelf_tests.py
@@ -13,8 +13,8 @@
 from optparse import OptionParser
 import logging
 import platform
-sys.path.insert(0, '.') # to load *our* test, not Python's test
-from test.utils import run_exe, is_in_rootdir, dump_output_to_temp_files
+from utils import setup_syspath; setup_syspath()
+from utils import run_exe, is_in_rootdir, dump_output_to_temp_files
 
 
 # Create a global logger object
@@ -23,6 +23,16 @@
 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'
+if not os.path.exists(READELF_PATH):
+    READELF_PATH = 'readelf'
 
 def discover_testfiles(rootdir):
     """ Discover test files in the given directory. Yield them one by one.
@@ -40,14 +50,14 @@
     success = True
     testlog.info("Test file '%s'" % filename)
     for option in [
-            '-e', '-s', '-r', '-x.text', '-p.shstrtab',
+            '-e', '-d', '-s', '-r', '-x.text', '-p.shstrtab',
             '--debug-dump=info', '--debug-dump=decodedline',
             '--debug-dump=frames', '--debug-dump=frames-interp']:
         if verbose: testlog.info("..option='%s'" % option)
-        # stdouts will be a 2-element list: output of readelf and output 
+        # stdouts will be a 2-element list: output of readelf and output
         # of scripts/readelf.py
         stdouts = []
-        for exe_path in ['readelf', 'scripts/readelf.py']:
+        for exe_path in [READELF_PATH, 'scripts/readelf.py']:
             args = [option, filename]
             if verbose: testlog.info("....executing: '%s %s'" % (
                 exe_path, ' '.join(args)))
@@ -64,6 +74,7 @@
             success = False
             testlog.info('.......................FAIL')
             testlog.info('....for option "%s"' % option)
+            testlog.info('....Output #1 is readelf, Output #2 is pyelftools')
             testlog.info('@@ ' + errmsg)
             dump_output_to_temp_files(testlog, *stdouts)
     return success
@@ -95,7 +106,7 @@
             if not filter_out:
                 if not line.startswith('unknown: length'):
                     yield line
-        
+
     lines1 = prepare_lines(s1)
     lines2 = prepare_lines(s2)
 
@@ -120,7 +131,7 @@
             sm.set_seqs(lines1[i], lines2[i])
             changes = sm.get_opcodes()
             if flag_after_symtable:
-                # Detect readelf's adding @ with lib and version after 
+                # Detect readelf's adding @ with lib and version after
                 # symbol name.
                 if (    len(changes) == 2 and changes[1][0] == 'delete' and
                         lines1[i][changes[1][1]] == '@'):
@@ -128,13 +139,14 @@
             elif 'at_const_value' in lines1[i]:
                 # On 32-bit machines, readelf doesn't correctly represent
                 # some boundary LEB128 numbers
-                num2 = int(lines2_parts[-1])
+                val = lines2_parts[-1]
+                num2 = int(val, 16 if val.startswith('0x') else 10)
                 if num2 <= -2**31 and '32' in platform.architecture()[0]:
                     ok = True
             elif 'os/abi' in lines1[i]:
                 if 'unix - gnu' in lines1[i] and 'unix - linux' in lines2[i]:
                     ok = True
-            else: 
+            else:
                 for s in ('t (tls)', 'l (large)'):
                     if s in lines1[i] or s in lines2[i]:
                         ok = True
@@ -161,6 +173,8 @@
 
     if options.verbose:
         testlog.info('Running in verbose mode')
+        testlog.info('Python executable = %s' % sys.executable)
+        testlog.info('readelf path = %s' % READELF_PATH)
         testlog.info('Given list of files: %s' % args)
 
     # If file names are given as command-line arguments, only these files
@@ -175,7 +189,7 @@
     for filename in filenames:
         if success:
             success = success and run_test_on_file(
-                                    filename, 
+                                    filename,
                                     verbose=options.verbose)
 
     if success:
@@ -189,6 +203,3 @@
 if __name__ == '__main__':
     sys.exit(main())
 
-
-
-
diff --git a/test/test_arm_support.py b/test/test_arm_support.py
new file mode 100644
index 0000000..b2b0ab2
--- /dev/null
+++ b/test/test_arm_support.py
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------------
+# 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
+
+class TestARMSupport(unittest.TestCase):
+    def test_hello(self):
+        with open(os.path.join('test', 'testfiles',
+                               'simple_gcc.elf.arm'), 'rb') as f:
+            elf = ELFFile(f)
+            self.assertEqual(elf.get_machine_arch(), 'ARM')
+
+            # Check some other properties of this ELF file derived from readelf
+            self.assertEqual(elf['e_entry'], 0x8018)
+            self.assertEqual(elf.num_sections(), 14)
+            self.assertEqual(elf.num_segments(), 2)
+
+if __name__ == '__main__':
+    unittest.main()
+
diff --git a/test/test_callframe.py b/test/test_callframe.py
index 33fb1e7..79317d6 100644
--- a/test/test_callframe.py
+++ b/test/test_callframe.py
@@ -1,10 +1,15 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
 try:
     import unittest2 as unittest
 except ImportError:
     import unittest
-import sys
 
-sys.path.extend(['.', '..'])
+from utils import setup_syspath; setup_syspath()
 from elftools.common.py3compat import BytesIO
 from elftools.dwarf.callframe import (
     CallFrameInfo, CIE, FDE, instruction_name, CallFrameInstruction,
@@ -19,7 +24,7 @@
         self.assertIsInstance(instr, CallFrameInstruction)
         self.assertEqual(instruction_name(instr.opcode), name)
         self.assertEqual(instr.args, args)
-       
+
     def test_spec_sample_d6(self):
         # D.6 sample in DWARFv3
         s = BytesIO()
@@ -40,7 +45,7 @@
             b'\x08\x07' +
             b'\x09\x08\x01' +
             b'\x00' +
-            
+
             # then comes the FDE
             b'\x28\x00\x00\x00' +        # length
             b'\x00\x00\x00\x00' +        # CIE_pointer (to CIE at 0)
@@ -125,7 +130,7 @@
         self.assertEqual(decoded_FDE.table[9]['pc'], 0x11223344 + 76)
 
     def test_describe_CFI_instructions(self):
-        # The data here represents a single CIE 
+        # The data here represents a single CIE
         data = (b'' +
             b'\x16\x00\x00\x00' +        # length
             b'\xff\xff\xff\xff' +        # CIE_id
@@ -141,7 +146,7 @@
 
         set_global_machine_arch('x86')
         self.assertEqual(describe_CFI_instructions(entries[0]),
-            (   '  DW_CFA_def_cfa: r7 (edi) ofs 2\n' + 
+            (   '  DW_CFA_def_cfa: r7 (edi) ofs 2\n' +
                 '  DW_CFA_expression: r2 (edx) (DW_OP_addr: 201; DW_OP_deref; DW_OP_deref)\n'))
 
 
diff --git a/test/test_dwarf_expr.py b/test/test_dwarf_expr.py
index 0efface..22a250f 100644
--- a/test/test_dwarf_expr.py
+++ b/test/test_dwarf_expr.py
@@ -1,10 +1,15 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
 try:
     import unittest2 as unittest
 except ImportError:
     import unittest
-import sys
 
-sys.path.extend(('..', '.'))
+from utils import setup_syspath; setup_syspath()
 from elftools.dwarf.descriptions import ExprDumper, set_global_machine_arch
 from elftools.dwarf.structs import DWARFStructs
 
@@ -23,12 +28,12 @@
         self.visitor.process_expr([0x1b])
         self.assertEqual(self.visitor.get_str(),
             'DW_OP_div')
-        
+
         self.setUp()
         self.visitor.process_expr([0x74, 0x82, 0x01])
         self.assertEqual(self.visitor.get_str(),
             'DW_OP_breg4 (rsi): 130')
-        
+
         self.setUp()
         self.visitor.process_expr([0x91, 0x82, 0x01])
         self.assertEqual(self.visitor.get_str(),
diff --git a/test/test_dwarf_lineprogram.py b/test/test_dwarf_lineprogram.py
index 75c88c8..3a10932 100644
--- a/test/test_dwarf_lineprogram.py
+++ b/test/test_dwarf_lineprogram.py
@@ -1,10 +1,15 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
 try:
     import unittest2 as unittest
 except ImportError:
     import unittest
-import sys
 
-sys.path.extend(['.', '..'])
+from utils import setup_syspath; setup_syspath()
 from elftools.common.py3compat import BytesIO, iteritems
 from elftools.dwarf.lineprogram import LineProgram, LineState, LineProgramEntry
 from elftools.dwarf.structs import DWARFStructs
@@ -24,9 +29,9 @@
             b'\x0A' +                # opcode_base
             b'\x00\x01\x04\x08\x0C\x01\x01\x01\x00' + # standard_opcode_lengths
             # 2 dir names followed by a NULL
-            b'\x61\x62\x00\x70\x00\x00' + 
+            b'\x61\x62\x00\x70\x00\x00' +
             # a file entry
-            b'\x61\x72\x00\x0C\x0D\x0F' + 
+            b'\x61\x72\x00\x0C\x0D\x0F' +
             # and another entry
             b'\x45\x50\x51\x00\x86\x12\x07\x08' +
             # followed by NULL
@@ -34,14 +39,14 @@
 
         lp = LineProgram(header, stream, ds, 0, len(stream.getvalue()))
         return lp
-        
+
     def assertLineState(self, state, **kwargs):
         """ Assert that the state attributes specified in kwargs have the given
             values (the rest are default).
         """
         for k, v in iteritems(kwargs):
             self.assertEqual(getattr(state, k), v)
-        
+
     def test_spec_sample_59(self):
         # Sample in figure 59 of DWARFv3
         s = BytesIO()
diff --git a/test/test_dwarf_structs.py b/test/test_dwarf_structs.py
index eaf972c..94f77c1 100644
--- a/test/test_dwarf_structs.py
+++ b/test/test_dwarf_structs.py
@@ -1,10 +1,15 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
 try:
     import unittest2 as unittest
 except ImportError:
     import unittest
-import sys
 
-sys.path.extend(['.', '..'])
+from utils import setup_syspath; setup_syspath()
 from elftools.dwarf.structs import DWARFStructs
 
 
@@ -20,9 +25,9 @@
             b'\x06' +                # opcode_base
             b'\x00\x01\x04\x08\x0C' + # standard_opcode_lengths
             # 2 dir names followed by a NULL
-            b'\x61\x62\x00\x70\x00\x00' + 
+            b'\x61\x62\x00\x70\x00\x00' +
             # a file entry
-            b'\x61\x72\x00\x0C\x0D\x0F' + 
+            b'\x61\x72\x00\x0C\x0D\x0F' +
             # and another entry
             b'\x45\x50\x51\x00\x86\x12\x07\x08' +
             # followed by NULL
diff --git a/test/test_utils.py b/test/test_utils.py
index f0142d8..314b6b5 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -1,11 +1,16 @@
+#-------------------------------------------------------------------------------
+# elftools tests
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
 try:
     import unittest2 as unittest
 except ImportError:
     import unittest
-import sys
 from random import randint
 
-sys.path.extend(['.', '..'])
+from utils import setup_syspath; setup_syspath()
 from elftools.common.py3compat import int2byte, BytesIO
 from elftools.common.utils import (parse_cstring_from_stream,
         preserve_stream_pos)
@@ -14,7 +19,7 @@
 class Test_parse_cstring_from_stream(unittest.TestCase):
     def _make_random_bytes(self, n):
         return b''.join(int2byte(randint(32, 127)) for i in range(n))
-        
+
     def test_small1(self):
         sio = BytesIO(b'abcdefgh\x0012345')
         self.assertEqual(parse_cstring_from_stream(sio), b'abcdefgh')
diff --git a/test/testfiles/simple_gcc.elf.arm b/test/testfiles/simple_gcc.elf.arm
new file mode 100644
index 0000000..b678393
--- /dev/null
+++ b/test/testfiles/simple_gcc.elf.arm
Binary files differ
diff --git a/test/utils.py b/test/utils.py
index 0ee0ebf..908cfcc 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -6,8 +6,17 @@
 # Eli Bendersky (eliben@gmail.com)
 # This code is in the public domain
 #-------------------------------------------------------------------------------
-import os, subprocess, tempfile
-from elftools.common.py3compat import bytes2str
+import os, sys, subprocess, tempfile
+
+# This module should not import elftools before setup_syspath() is called!
+# See the Hacking Guide in the documentation for more details.
+
+def setup_syspath():
+    """ Setup sys.path so that tests pick up local pyelftools before the
+        installed one when run from development directory.
+    """
+    if sys.path[0] != '.':
+        sys.path.insert(0, '.')
 
 
 def run_exe(exe_path, args):
@@ -17,18 +26,18 @@
     """
     popen_cmd = [exe_path] + args
     if os.path.splitext(exe_path)[1] == '.py':
-        popen_cmd.insert(0, 'python')
+        popen_cmd.insert(0, sys.executable)
     proc = subprocess.Popen(popen_cmd, stdout=subprocess.PIPE)
     proc_stdout = proc.communicate()[0]
+    from elftools.common.py3compat import bytes2str
     return proc.returncode, bytes2str(proc_stdout)
-    
+
 
 def is_in_rootdir():
     """ Check whether the current dir is the root dir of pyelftools
     """
-    dirstuff = os.listdir('.')
-    return 'test' in dirstuff and 'elftools' in dirstuff
-    
+    return os.path.isdir('test') and os.path.isdir('elftools')
+
 
 def dump_output_to_temp_files(testlog, *args):
     """ Dumps the output strings given in 'args' to temp files: one for each
@@ -42,4 +51,4 @@
         file.write(s)
         file.close()
         testlog.info('@@ Output #%s dumped to file: %s' % (i + 1, path))
-    
+
diff --git a/tox.ini b/tox.ini
index 73fbb84..1409ccb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py27,py26,py32
+envlist = py27,py32
 
 [testenv]
 commands =
diff --git a/z.py b/z.py
index d30feb3..18f7d35 100644
--- a/z.py
+++ b/z.py
@@ -1,3 +1,10 @@
+#-------------------------------------------------------------------------------
+# elftools
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+
 # Just a script for playing around with pyelftools during testing
 # please ignore it!
 #