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!
#