add Elf_Dyn support

This adds support for Elf_Dyn tags.  Pull request sent to upstream:
	https://bitbucket.org/eliben/pyelftools/pull-request/5/add-support-for-parsing-of-the-dynamic/diff

BUG=chromium-os:36453
TEST=lddtree.py works

Change-Id: Iaf383eacc45adccb4dfc33e275028a8e7f58ff1e
Reviewed-on: https://gerrit.chromium.org/gerrit/38667
Reviewed-by: David James <davidjames@chromium.org>
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
diff --git a/elftools/elf/dynamic.py b/elftools/elf/dynamic.py
new file mode 100644
index 0000000..2292470
--- /dev/null
+++ b/elftools/elf/dynamic.py
@@ -0,0 +1,106 @@
+#-------------------------------------------------------------------------------
+# elftools: elf/dynamic.py
+#
+# ELF Dynamic Tags
+#
+# Mike Frysinger (vapier@gentoo.org)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+import itertools
+
+from .sections import Section
+from .segments import Segment
+from ..common.utils import struct_parse
+
+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.
+    """
+
+    _HANDLED_TAGS = frozenset(['DT_NEEDED', 'DT_RPATH', 'DT_RUNPATH'])
+
+    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))
+
+    def __getitem__(self, name):
+        """ Implement dict-like access to entries
+        """
+        return self.entry[name]
+
+    def __repr__(self):
+        return '<DynamicTag (%s): %r>' % (self.entry.d_tag, self.entry)
+
+    def __str__(self):
+        if self.entry.d_tag in self._HANDLED_TAGS:
+            s = '"%s"' % getattr(self, self.entry.d_tag[3:].lower())
+        else:
+            s = '%#x' % self.entry.d_ptr
+        return '<DynamicTag (%s) %s>' % (self.entry.d_tag, s)
+
+
+class Dynamic(object):
+    def __init__(self, stream, elffile, position):
+        self._stream = stream
+        self._elffile = elffile
+        self._elfstructs = elffile.structs
+        self._num_tags = -1;
+        self._offset = position
+        self._tagsize = self._elfstructs.Elf_Dyn.sizeof()
+
+    def iter_tags(self, type=None):
+        """ Yield all tags (limit to |type| if specified)
+        """
+        for n in itertools.count():
+            tag = self.get_tag(n)
+            if type is None or tag.entry.d_tag == type:
+                yield tag
+            if tag.entry.d_tag == 'DT_NULL':
+                break
+
+    def get_tag(self, n):
+        """ Get the tag at index #n from the file (DynamicTag object)
+        """
+        offset = self._offset + n * self._tagsize
+        entry = struct_parse(
+            self._elfstructs.Elf_Dyn,
+            self._stream,
+            stream_pos=offset)
+        return DynamicTag(entry, self._elffile)
+
+    @property
+    def num_tags(self):
+        """ Number of dynamic tags in the file
+        """
+        if self._num_tags != -1:
+            return self._num_tags
+
+        for n in itertools.count():
+            tag = self.get_tag(n)
+            if tag.entry.d_tag == 'DT_NULL':
+                self._num_tags = n
+                return n
+
+
+class DynamicSection(Section, Dynamic):
+    """ ELF dynamic table section.  Knows how to process the list of tags.
+    """
+    def __init__(self, header, name, stream, elffile):
+        Section.__init__(self, header, name, stream)
+        Dynamic.__init__(self, stream, elffile, self['sh_offset'])
+
+
+class DynamicSegment(Segment, Dynamic):
+    """ ELF dynamic table segment.  Knows how to process the list of tags.
+    """
+    def __init__(self, header, stream, elffile):
+        Segment.__init__(self, header, stream)
+        Dynamic.__init__(self, stream, elffile, self['p_offset'])
diff --git a/elftools/elf/elffile.py b/elftools/elf/elffile.py
index bda6cae..e2bd904 100644
--- a/elftools/elf/elffile.py
+++ b/elftools/elf/elffile.py
@@ -13,6 +13,7 @@
 from .structs import ELFStructs
 from .sections import (
         Section, StringTableSection, SymbolTableSection, NullSection)
+from .dynamic import DynamicSection, DynamicSegment
 from .relocation import RelocationSection, RelocationHandler
 from .segments import Segment, InterpSegment
 from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
@@ -208,6 +209,8 @@
         segtype = segment_header['p_type']
         if segtype == 'PT_INTERP':
             return InterpSegment(segment_header, self.stream)
+        elif segtype == 'PT_DYNAMIC':
+            return DynamicSegment(segment_header, self.stream, self)
         else:
             return Segment(segment_header, self.stream)
 
@@ -241,6 +244,8 @@
         elif sectype in ('SHT_REL', 'SHT_RELA'):
             return RelocationSection(
                 section_header, name, self.stream, self)
+        elif sectype == 'SHT_DYNAMIC':
+            return DynamicSection(section_header, name, self.stream, self)
         else:
             return Section(section_header, name, self.stream)
 
diff --git a/elftools/elf/enums.py b/elftools/elf/enums.py
index 78302ae..804fc45 100644
--- a/elftools/elf/enums.py
+++ b/elftools/elf/enums.py
@@ -186,6 +186,82 @@
     _default_=Pass,
 )
 
+# d_tag
+ENUM_D_TAG = dict(
+    DT_NULL=0,
+    DT_NEEDED=1,
+    DT_PLTRELSZ=2,
+    DT_PLTGOT=3,
+    DT_HASH=4,
+    DT_STRTAB=5,
+    DT_SYMTAB=6,
+    DT_RELA=7,
+    DT_RELASZ=8,
+    DT_RELAENT=9,
+    DT_STRSZ=10,
+    DT_SYMENT=11,
+    DT_INIT=12,
+    DT_FINI=13,
+    DT_SONAME=14,
+    DT_RPATH=15,
+    DT_SYMBOLIC=16,
+    DT_REL=17,
+    DT_RELSZ=18,
+    DT_RELENT=19,
+    DT_PLTREL=20,
+    DT_DEBUG=21,
+    DT_TEXTREL=22,
+    DT_JMPREL=23,
+    DT_BIND_NOW=24,
+    DT_INIT_ARRAY=25,
+    DT_FINI_ARRAY=26,
+    DT_INIT_ARRAYSZ=27,
+    DT_FINI_ARRAYSZ=28,
+    DT_RUNPATH=29,
+    DT_FLAGS=30,
+    DT_ENCODING=32,
+    DT_PREINIT_ARRAY=32,
+    DT_PREINIT_ARRAYSZ=33,
+    DT_NUM=34,
+    DT_LOOS=0x6000000d,
+    DT_HIOS=0x6ffff000,
+    DT_LOPROC=0x70000000,
+    DT_HIPROC=0x7fffffff,
+    DT_PROCNUM=0x35,
+    DT_VALRNGLO=0x6ffffd00,
+    DT_GNU_PRELINKED=0x6ffffdf5,
+    DT_GNU_CONFLICTSZ=0x6ffffdf6,
+    DT_GNU_LIBLISTSZ=0x6ffffdf7,
+    DT_CHECKSUM=0x6ffffdf8,
+    DT_PLTPADSZ=0x6ffffdf9,
+    DT_MOVEENT=0x6ffffdfa,
+    DT_MOVESZ=0x6ffffdfb,
+    DT_SYMINSZ=0x6ffffdfe,
+    DT_SYMINENT=0x6ffffdff,
+    DT_GNU_HASH=0x6ffffef5,
+    DT_TLSDESC_PLT=0x6ffffef6,
+    DT_TLSDESC_GOT=0x6ffffef7,
+    DT_GNU_CONFLICT=0x6ffffef8,
+    DT_GNU_LIBLIST=0x6ffffef9,
+    DT_CONFIG=0x6ffffefa,
+    DT_DEPAUDIT=0x6ffffefb,
+    DT_AUDIT=0x6ffffefc,
+    DT_PLTPAD=0x6ffffefd,
+    DT_MOVETAB=0x6ffffefe,
+    DT_SYMINFO=0x6ffffeff,
+    DT_VERSYM=0x6ffffff0,
+    DT_RELACOUNT=0x6ffffff9,
+    DT_RELCOUNT=0x6ffffffa,
+    DT_FLAGS_1=0x6ffffffb,
+    DT_VERDEF=0x6ffffffc,
+    DT_VERDEFNUM=0x6ffffffd,
+    DT_VERNEED=0x6ffffffe,
+    DT_VERNEEDNUM=0x6fffffff,
+    DT_AUXILIARY=0x7ffffffd,
+    DT_FILTER=0x7fffffff,
+    _default_=Pass,
+)
+
 ENUM_RELOC_TYPE_i386 = dict(
     R_386_NONE=0,
     R_386_32=1,
diff --git a/elftools/elf/structs.py b/elftools/elf/structs.py
index 2c55d5b..08567de 100644
--- a/elftools/elf/structs.py
+++ b/elftools/elf/structs.py
@@ -72,6 +72,7 @@
         self._create_shdr()
         self._create_sym()
         self._create_rel()
+        self._create_dyn()
     
     def _create_ehdr(self):
         self.Elf_Ehdr = Struct('Elf_Ehdr',
@@ -165,6 +166,13 @@
             self.Elf_sxword('r_addend'),
         )
 
+    def _create_dyn(self):
+        self.Elf_Dyn = Struct('Elf_Dyn',
+            Enum(self.Elf_sxword('d_tag'), **ENUM_D_TAG),
+            self.Elf_xword('d_val'),
+            Value('d_ptr', lambda ctx: ctx['d_val']),
+        )
+
     def _create_sym(self):
         # st_info is hierarchical. To access the type, use
         # container['st_info']['type']