from .sections import Section
 from .segments import Segment
 from ..common.exceptions import ELFError
-from ..common.utils import struct_parse
+from ..common.utils import struct_parse, parse_cstring_from_stream
+
+
+class _DynamicStringTable(object):
+    """ Bare string table based on values found via ELF dynamic tags and
+        loadable segments only.  Good enough for get_string() only.
+    """
+    def __init__(self, stream, table_offset):
+        self._stream = stream
+        self._table_offset = table_offset
+
+    def get_string(self, offset):
+        """ Get the string stored at the given offset in this string table.
+        """
+        return parse_cstring_from_stream(self._stream,
+                                         self._table_offset + offset)
 
 
 class DynamicTag(object):
         self._num_tags = -1
         self._offset = position
         self._tagsize = self._elfstructs.Elf_Dyn.sizeof()
+
+        # Do not access this directly yourself; use _get_stringtable() instead.
         self._stringtable = stringtable
 
-    def iter_tags(self, type=None):
-        """ Yield all tags (limit to |type| if specified)
+    def _get_stringtable(self):
+        """ Return a string table for looking up dynamic tag related strings.
+
+            This won't be a "full" string table object, but will at least
+            support the get_string() function.
+        """
+        if self._stringtable:
+            return self._stringtable
+
+        # If the ELF has stripped its section table (which is unusual, but
+        # perfectly valid), we need to use the dynamic tags to locate the
+        # dynamic string table.
+        strtab = None
+        for tag in self._iter_tags(type='DT_STRTAB'):
+            strtab = tag['d_val']
+            break
+        # If we found a dynamic string table, locate the offset in the file
+        # by using the program headers.
+        if strtab:
+            table_offset = next(self._elffile.address_offsets(strtab), None)
+            if table_offset is not None:
+                self._stringtable = _DynamicStringTable(self._stream, table_offset)
+                return self._stringtable
+
+        # That didn't work for some reason.  Let's use the section header
+        # even though this ELF is super weird.
+        self._stringtable = self._elffile.get_section_by_name(b'.dynstr')
+        return self._stringtable
+
+    def _iter_tags(self, type=None):
+        """ Yield all raw tags (limit to |type| if specified)
         """
         for n in itertools.count():
-            tag = self.get_tag(n)
-            if type is None or tag.entry.d_tag == type:
+            tag = self._get_tag(n)
+            if type is None or tag['d_tag'] == type:
                 yield tag
-            if tag.entry.d_tag == 'DT_NULL':
+            if tag['d_tag'] == 'DT_NULL':
                 break
 
-    def get_tag(self, n):
-        """ Get the tag at index #n from the file (DynamicTag object)
+    def iter_tags(self, type=None):
+        """ Yield all tags (limit to |type| if specified)
+        """
+        for tag in self._iter_tags(type=type):
+            yield DynamicTag(tag, self._get_stringtable())
+
+    def _get_tag(self, n):
+        """ Get the raw tag at index #n from the file
         """
         offset = self._offset + n * self._tagsize
-        entry = struct_parse(
+        return struct_parse(
             self._elfstructs.Elf_Dyn,
             self._stream,
             stream_pos=offset)
-        return DynamicTag(entry, self._stringtable)
+
+    def get_tag(self, n):
+        """ Get the tag at index #n from the file (DynamicTag object)
+        """
+        return DynamicTag(self._get_tag(n), self._get_stringtable())
 
     def num_tags(self):
         """ Number of dynamic tags in the file
 
     import unittest
 import os
 
-from utils import setup_syspath; setup_syspath()
+from utils import setup_syspath
+setup_syspath()
+from elftools.elf.elffile import ELFFile
 from elftools.common.exceptions import ELFError
 from elftools.elf.dynamic import DynamicTag
 
 
 class TestDynamicTag(unittest.TestCase):
+    """Tests for the DynamicTag class."""
+
     def test_requires_stringtable(self):
         with self.assertRaises(ELFError):
             dt = DynamicTag('', None)
 
 
+class TestDynamic(unittest.TestCase):
+    """Tests for the Dynamic class."""
+
+    def test_missing_sections(self):
+        """Verify we can get dynamic strings w/out section headers"""
+
+        libs = []
+        with open(os.path.join('test', 'testfiles_for_unittests',
+                               'aarch64_super_stripped.elf'), 'rb') as f:
+            elf = ELFFile(f)
+            for segment in elf.iter_segments():
+                if segment.header.p_type != 'PT_DYNAMIC':
+                    continue
+
+                for t in segment.iter_tags():
+                    if t.entry.d_tag == 'DT_NEEDED':
+                        libs.append(t.needed.decode('utf-8'))
+
+        exp = ['libc.so.6']
+        self.assertEqual(libs, exp)
+
+
 if __name__ == '__main__':
     unittest.main()