binman: Refactor much of the image code into 'section'

We want to support multiple sections within a single image. To do this,
move most of the Image class implementation into a new Section class. An
Image contains only a single Section, but at some point we will support
a new 'section' entry, thus allowing Sections within Sections.

Use the name 'bsection' for the module so we can use 'section' for the
etype module.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py
new file mode 100644
index 0000000..07c72d3
--- /dev/null
+++ b/tools/binman/bsection.py
@@ -0,0 +1,302 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Base class for sections (collections of entries)
+#
+
+from __future__ import print_function
+
+from collections import OrderedDict
+import sys
+
+import fdt_util
+import re
+import tools
+
+class Section(object):
+    """A section which contains multiple entries
+
+    A section represents a collection of entries. There must be one or more
+    sections in an image. Sections are used to group entries together.
+
+    Attributes:
+        _node: Node object that contains the section definition in device tree
+        _size: Section size in bytes, or None if not known yet
+        _align_size: Section size alignment, or None
+        _pad_before: Number of bytes before the first entry starts. This
+            effectively changes the place where entry position 0 starts
+        _pad_after: Number of bytes after the last entry ends. The last
+            entry will finish on or before this boundary
+        _pad_byte: Byte to use to pad the section where there is no entry
+        _sort: True if entries should be sorted by position, False if they
+            must be in-order in the device tree description
+        _skip_at_start: Number of bytes before the first entry starts. These
+            effectively adjust the starting position of entries. For example,
+            if _pad_before is 16, then the first entry would start at 16.
+            An entry with pos = 20 would in fact be written at position 4
+            in the image file.
+        _end_4gb: Indicates that the section ends at the 4GB boundary. This is
+            used for x86 images, which want to use positions such that a
+             memory address (like 0xff800000) is the first entry position.
+             This causes _skip_at_start to be set to the starting memory
+             address.
+        _entries: OrderedDict() of entries
+    """
+    def __init__(self, name, node, test=False):
+        global entry
+        global Entry
+        import entry
+        from entry import Entry
+
+        self._node = node
+        self._size = None
+        self._align_size = None
+        self._pad_before = 0
+        self._pad_after = 0
+        self._pad_byte = 0
+        self._sort = False
+        self._skip_at_start = 0
+        self._end_4gb = False
+        self._entries = OrderedDict()
+        if not test:
+            self._ReadNode()
+            self._ReadEntries()
+
+    def _ReadNode(self):
+        """Read properties from the section node"""
+        self._size = fdt_util.GetInt(self._node, 'size')
+        self._align_size = fdt_util.GetInt(self._node, 'align-size')
+        if tools.NotPowerOfTwo(self._align_size):
+            self._Raise("Alignment size %s must be a power of two" %
+                        self._align_size)
+        self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
+        self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
+        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
+        self._sort = fdt_util.GetBool(self._node, 'sort-by-pos')
+        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
+        if self._end_4gb and not self._size:
+            self._Raise("Section size must be provided when using end-at-4gb")
+        if self._end_4gb:
+            self._skip_at_start = 0x100000000 - self._size
+
+    def _ReadEntries(self):
+        for node in self._node.subnodes:
+            self._entries[node.name] = Entry.Create(self, node)
+
+    def CheckSize(self):
+        """Check that the section contents does not exceed its size, etc."""
+        contents_size = 0
+        for entry in self._entries.values():
+            contents_size = max(contents_size, entry.pos + entry.size)
+
+        contents_size -= self._skip_at_start
+
+        size = self._size
+        if not size:
+            size = self._pad_before + contents_size + self._pad_after
+            size = tools.Align(size, self._align_size)
+
+        if self._size and contents_size > self._size:
+            self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
+                       (contents_size, contents_size, self._size, self._size))
+        if not self._size:
+            self._size = size
+        if self._size != tools.Align(self._size, self._align_size):
+            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
+                  (self._size, self._size, self._align_size, self._align_size))
+        return size
+
+    def _Raise(self, msg):
+        """Raises an error for this section
+
+        Args:
+            msg: Error message to use in the raise string
+        Raises:
+            ValueError()
+        """
+        raise ValueError("Section '%s': %s" % (self._node.path, msg))
+
+    def GetPath(self):
+        """Get the path of an image (in the FDT)
+
+        Returns:
+            Full path of the node for this image
+        """
+        return self._node.path
+
+    def FindEntryType(self, etype):
+        """Find an entry type in the section
+
+        Args:
+            etype: Entry type to find
+        Returns:
+            entry matching that type, or None if not found
+        """
+        for entry in self._entries.values():
+            if entry.etype == etype:
+                return entry
+        return None
+
+    def GetEntryContents(self):
+        """Call ObtainContents() for each entry
+
+        This calls each entry's ObtainContents() a few times until they all
+        return True. We stop calling an entry's function once it returns
+        True. This allows the contents of one entry to depend on another.
+
+        After 3 rounds we give up since it's likely an error.
+        """
+        todo = self._entries.values()
+        for passnum in range(3):
+            next_todo = []
+            for entry in todo:
+                if not entry.ObtainContents():
+                    next_todo.append(entry)
+            todo = next_todo
+            if not todo:
+                break
+
+    def _SetEntryPosSize(self, name, pos, size):
+        """Set the position and size of an entry
+
+        Args:
+            name: Entry name to update
+            pos: New position
+            size: New size
+        """
+        entry = self._entries.get(name)
+        if not entry:
+            self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
+        entry.SetPositionSize(self._skip_at_start + pos, size)
+
+    def GetEntryPositions(self):
+        """Handle entries that want to set the position/size of other entries
+
+        This calls each entry's GetPositions() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        for entry in self._entries.values():
+            pos_dict = entry.GetPositions()
+            for name, info in pos_dict.iteritems():
+                self._SetEntryPosSize(name, *info)
+
+    def PackEntries(self):
+        """Pack all entries into the section"""
+        pos = self._skip_at_start
+        for entry in self._entries.values():
+            pos = entry.Pack(pos)
+
+    def _SortEntries(self):
+        """Sort entries by position"""
+        entries = sorted(self._entries.values(), key=lambda entry: entry.pos)
+        self._entries.clear()
+        for entry in entries:
+            self._entries[entry._node.name] = entry
+
+    def CheckEntries(self):
+        """Check that entries do not overlap or extend outside the section"""
+        if self._sort:
+            self._SortEntries()
+        pos = 0
+        prev_name = 'None'
+        for entry in self._entries.values():
+            if (entry.pos < self._skip_at_start or
+                entry.pos >= self._skip_at_start + self._size):
+                entry.Raise("Position %#x (%d) is outside the section starting "
+                            "at %#x (%d)" %
+                            (entry.pos, entry.pos, self._skip_at_start,
+                             self._skip_at_start))
+            if entry.pos < pos:
+                entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
+                            "ending at %#x (%d)" %
+                            (entry.pos, entry.pos, prev_name, pos, pos))
+            pos = entry.pos + entry.size
+            prev_name = entry.GetPath()
+
+    def ProcessEntryContents(self):
+        """Call the ProcessContents() method for each entry
+
+        This is intended to adjust the contents as needed by the entry type.
+        """
+        for entry in self._entries.values():
+            entry.ProcessContents()
+
+    def WriteSymbols(self):
+        """Write symbol values into binary files for access at run time"""
+        for entry in self._entries.values():
+            entry.WriteSymbols(self)
+
+    def BuildSection(self, fd, base_pos):
+        """Write the section to a file"""
+        fd.seek(base_pos)
+        fd.write(self.GetData())
+
+    def GetData(self):
+        """Write the section to a file"""
+        section_data = chr(self._pad_byte) * self._size
+
+        for entry in self._entries.values():
+            data = entry.GetData()
+            base = self._pad_before + entry.pos - self._skip_at_start
+            section_data = (section_data[:base] + data +
+                            section_data[base + len(data):])
+        return section_data
+
+    def LookupSymbol(self, sym_name, optional, msg):
+        """Look up a symbol in an ELF file
+
+        Looks up a symbol in an ELF file. Only entry types which come from an
+        ELF image can be used by this function.
+
+        At present the only entry property supported is pos.
+
+        Args:
+            sym_name: Symbol name in the ELF file to look up in the format
+                _binman_<entry>_prop_<property> where <entry> is the name of
+                the entry and <property> is the property to find (e.g.
+                _binman_u_boot_prop_pos). As a special case, you can append
+                _any to <entry> to have it search for any matching entry. E.g.
+                _binman_u_boot_any_prop_pos will match entries called u-boot,
+                u-boot-img and u-boot-nodtb)
+            optional: True if the symbol is optional. If False this function
+                will raise if the symbol is not found
+            msg: Message to display if an error occurs
+
+        Returns:
+            Value that should be assigned to that symbol, or None if it was
+                optional and not found
+
+        Raises:
+            ValueError if the symbol is invalid or not found, or references a
+                property which is not supported
+        """
+        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
+        if not m:
+            raise ValueError("%s: Symbol '%s' has invalid format" %
+                             (msg, sym_name))
+        entry_name, prop_name = m.groups()
+        entry_name = entry_name.replace('_', '-')
+        entry = self._entries.get(entry_name)
+        if not entry:
+            if entry_name.endswith('-any'):
+                root = entry_name[:-4]
+                for name in self._entries:
+                    if name.startswith(root):
+                        rest = name[len(root):]
+                        if rest in ['', '-img', '-nodtb']:
+                            entry = self._entries[name]
+        if not entry:
+            err = ("%s: Entry '%s' not found in list (%s)" %
+                   (msg, entry_name, ','.join(self._entries.keys())))
+            if optional:
+                print('Warning: %s' % err, file=sys.stderr)
+                return None
+            raise ValueError(err)
+        if prop_name == 'pos':
+            return entry.pos
+        else:
+            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
+
+    def GetEntries(self):
+        return self._entries
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index b5e8736..4a95a9c 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -413,7 +413,7 @@
         self.assertEqual(0, retcode)
         self.assertIn('image', control.images)
         image = control.images['image']
-        entries = image._entries
+        entries = image.GetEntries()
         self.assertEqual(5, len(entries))
 
         # First u-boot
@@ -456,7 +456,7 @@
         self.assertEqual(0, retcode)
         self.assertIn('image', control.images)
         image = control.images['image']
-        entries = image._entries
+        entries = image.GetEntries()
         self.assertEqual(5, len(entries))
 
         # First u-boot with padding before and after
@@ -540,7 +540,7 @@
         """Test that entries which overflow the image size are detected"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('16_pack_image_overflow.dts')
-        self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image "
+        self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
                       "size 0x3 (3)", str(e.exception))
 
     def testPackImageSize(self):
@@ -563,14 +563,14 @@
         """Test that invalid image alignment is detected"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('19_pack_inv_image_align.dts')
-        self.assertIn("Image '/binman': Size 0x7 (7) does not match "
+        self.assertIn("Section '/binman': Size 0x7 (7) does not match "
                       "align-size 0x8 (8)", str(e.exception))
 
     def testPackAlignPowerOf2(self):
         """Test that invalid image alignment is detected"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('20_pack_inv_image_align_power2.dts')
-        self.assertIn("Image '/binman': Alignment size 131 must be a power of "
+        self.assertIn("Section '/binman': Alignment size 131 must be a power of "
                       "two", str(e.exception))
 
     def testImagePadByte(self):
@@ -620,7 +620,7 @@
         """Test that the end-at-4gb property requires a size property"""
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('27_pack_4gb_no_size.dts')
-        self.assertIn("Image '/binman': Image size must be provided when "
+        self.assertIn("Section '/binman': Section size must be provided when "
                       "using end-at-4gb", str(e.exception))
 
     def testPackX86RomOutside(self):
@@ -628,7 +628,7 @@
         with self.assertRaises(ValueError) as e:
             self._DoTestFile('28_pack_4gb_outside.dts')
         self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
-                      "the image starting at 0xffffffe0 (4294967264)",
+                      "the section starting at 0xffffffe0 (4294967264)",
                       str(e.exception))
 
     def testPackX86Rom(self):
@@ -823,7 +823,7 @@
         """Test that microcode must be placed within the image"""
         with self.assertRaises(ValueError) as e:
             self._DoReadFile('41_unknown_pos_size.dts', True)
-        self.assertIn("Image '/binman': Unable to set pos/size for unknown "
+        self.assertIn("Section '/binman': Unable to set pos/size for unknown "
                 "entry 'invalid-entry'", str(e.exception))
 
     def testPackFsp(self):
diff --git a/tools/binman/image.py b/tools/binman/image.py
index b10b188..1d23496 100644
--- a/tools/binman/image.py
+++ b/tools/binman/image.py
@@ -13,6 +13,7 @@
 import sys
 
 import fdt_util
+import bsection
 import tools
 
 class Image:
@@ -27,158 +28,31 @@
         _node: Node object that contains the image definition in device tree
         _name: Image name
         _size: Image size in bytes, or None if not known yet
-        _align_size: Image size alignment, or None
-        _pad_before: Number of bytes before the first entry starts. This
-            effectively changes the place where entry position 0 starts
-        _pad_after: Number of bytes after the last entry ends. The last
-            entry will finish on or before this boundary
-        _pad_byte: Byte to use to pad the image where there is no entry
         _filename: Output filename for image
-        _sort: True if entries should be sorted by position, False if they
-            must be in-order in the device tree description
-        _skip_at_start: Number of bytes before the first entry starts. These
-            effecively adjust the starting position of entries. For example,
-            if _pad_before is 16, then the first entry would start at 16.
-            An entry with pos = 20 would in fact be written at position 4
-            in the image file.
-        _end_4gb: Indicates that the image ends at the 4GB boundary. This is
-            used for x86 images, which want to use positions such that a
-             memory address (like 0xff800000) is the first entry position.
-             This causes _skip_at_start to be set to the starting memory
-             address.
-        _entries: OrderedDict() of entries
+        _sections: Sections present in this image (may be one or more)
     """
     def __init__(self, name, node, test=False):
-        global entry
-        global Entry
-        import entry
-        from entry import Entry
-
         self._node = node
         self._name = name
         self._size = None
-        self._align_size = None
-        self._pad_before = 0
-        self._pad_after = 0
-        self._pad_byte = 0
         self._filename = '%s.bin' % self._name
-        self._sort = False
-        self._skip_at_start = 0
-        self._end_4gb = False
-        self._entries = OrderedDict()
-
-        if not test:
+        if test:
+            self._section = bsection.Section('main-section', self._node, True)
+        else:
             self._ReadNode()
-            self._ReadEntries()
 
     def _ReadNode(self):
         """Read properties from the image node"""
         self._size = fdt_util.GetInt(self._node, 'size')
-        self._align_size = fdt_util.GetInt(self._node, 'align-size')
-        if tools.NotPowerOfTwo(self._align_size):
-            self._Raise("Alignment size %s must be a power of two" %
-                        self._align_size)
-        self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
-        self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
-        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
         filename = fdt_util.GetString(self._node, 'filename')
         if filename:
             self._filename = filename
-        self._sort = fdt_util.GetBool(self._node, 'sort-by-pos')
-        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
-        if self._end_4gb and not self._size:
-            self._Raise("Image size must be provided when using end-at-4gb")
-        if self._end_4gb:
-            self._skip_at_start = 0x100000000 - self._size
-
-    def CheckSize(self):
-        """Check that the image contents does not exceed its size, etc."""
-        contents_size = 0
-        for entry in self._entries.values():
-            contents_size = max(contents_size, entry.pos + entry.size)
-
-        contents_size -= self._skip_at_start
-
-        size = self._size
-        if not size:
-            size = self._pad_before + contents_size + self._pad_after
-            size = tools.Align(size, self._align_size)
-
-        if self._size and contents_size > self._size:
-            self._Raise("contents size %#x (%d) exceeds image size %#x (%d)" %
-                       (contents_size, contents_size, self._size, self._size))
-        if not self._size:
-            self._size = size
-        if self._size != tools.Align(self._size, self._align_size):
-            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
-                  (self._size, self._size, self._align_size, self._align_size))
-
-    def _Raise(self, msg):
-        """Raises an error for this image
-
-        Args:
-            msg: Error message to use in the raise string
-        Raises:
-            ValueError()
-        """
-        raise ValueError("Image '%s': %s" % (self._node.path, msg))
-
-    def GetPath(self):
-        """Get the path of an image (in the FDT)
-
-        Returns:
-            Full path of the node for this image
-        """
-        return self._node.path
-
-    def _ReadEntries(self):
-        for node in self._node.subnodes:
-            self._entries[node.name] = Entry.Create(self, node)
-
-    def FindEntryType(self, etype):
-        """Find an entry type in the image
-
-        Args:
-            etype: Entry type to find
-        Returns:
-            entry matching that type, or None if not found
-        """
-        for entry in self._entries.values():
-            if entry.etype == etype:
-                return entry
-        return None
+        self._section = bsection.Section('main-section', self._node)
 
     def GetEntryContents(self):
-        """Call ObtainContents() for each entry
-
-        This calls each entry's ObtainContents() a few times until they all
-        return True. We stop calling an entry's function once it returns
-        True. This allows the contents of one entry to depend on another.
-
-        After 3 rounds we give up since it's likely an error.
+        """Call ObtainContents() for the section
         """
-        todo = self._entries.values()
-        for passnum in range(3):
-            next_todo = []
-            for entry in todo:
-                if not entry.ObtainContents():
-                    next_todo.append(entry)
-            todo = next_todo
-            if not todo:
-                break
-
-    def _SetEntryPosSize(self, name, pos, size):
-        """Set the position and size of an entry
-
-        Args:
-            name: Entry name to update
-            pos: New position
-            size: New size
-        """
-        entry = self._entries.get(name)
-        if not entry:
-            self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
-        entry.SetPositionSize(self._skip_at_start + pos, size)
+        self._section.GetEntryContents()
 
     def GetEntryPositions(self):
         """Handle entries that want to set the position/size of other entries
@@ -186,119 +60,36 @@
         This calls each entry's GetPositions() method. If it returns a list
         of entries to update, it updates them.
         """
-        for entry in self._entries.values():
-            pos_dict = entry.GetPositions()
-            for name, info in pos_dict.iteritems():
-                self._SetEntryPosSize(name, *info)
+        self._section.GetEntryPositions()
 
     def PackEntries(self):
         """Pack all entries into the image"""
-        pos = self._skip_at_start
-        for entry in self._entries.values():
-            pos = entry.Pack(pos)
+        self._section.PackEntries()
 
-    def _SortEntries(self):
-        """Sort entries by position"""
-        entries = sorted(self._entries.values(), key=lambda entry: entry.pos)
-        self._entries.clear()
-        for entry in entries:
-            self._entries[entry._node.name] = entry
+    def CheckSize(self):
+        """Check that the image contents does not exceed its size, etc."""
+        self._size = self._section.CheckSize()
 
     def CheckEntries(self):
         """Check that entries do not overlap or extend outside the image"""
-        if self._sort:
-            self._SortEntries()
-        pos = 0
-        prev_name = 'None'
-        for entry in self._entries.values():
-            if (entry.pos < self._skip_at_start or
-                entry.pos >= self._skip_at_start + self._size):
-                entry.Raise("Position %#x (%d) is outside the image starting "
-                            "at %#x (%d)" %
-                            (entry.pos, entry.pos, self._skip_at_start,
-                             self._skip_at_start))
-            if entry.pos < pos:
-                entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
-                            "ending at %#x (%d)" %
-                            (entry.pos, entry.pos, prev_name, pos, pos))
-            pos = entry.pos + entry.size
-            prev_name = entry.GetPath()
+        self._section.CheckEntries()
 
     def ProcessEntryContents(self):
         """Call the ProcessContents() method for each entry
 
         This is intended to adjust the contents as needed by the entry type.
         """
-        for entry in self._entries.values():
-            entry.ProcessContents()
+        self._section.ProcessEntryContents()
 
     def WriteSymbols(self):
         """Write symbol values into binary files for access at run time"""
-        for entry in self._entries.values():
-            entry.WriteSymbols(self)
+        self._section.WriteSymbols()
 
     def BuildImage(self):
         """Write the image to a file"""
         fname = tools.GetOutputFilename(self._filename)
         with open(fname, 'wb') as fd:
-            fd.write(chr(self._pad_byte) * self._size)
+            self._section.BuildSection(fd, 0)
 
-            for entry in self._entries.values():
-                data = entry.GetData()
-                fd.seek(self._pad_before + entry.pos - self._skip_at_start)
-                fd.write(data)
-
-    def LookupSymbol(self, sym_name, optional, msg):
-        """Look up a symbol in an ELF file
-
-        Looks up a symbol in an ELF file. Only entry types which come from an
-        ELF image can be used by this function.
-
-        At present the only entry property supported is pos.
-
-        Args:
-            sym_name: Symbol name in the ELF file to look up in the format
-                _binman_<entry>_prop_<property> where <entry> is the name of
-                the entry and <property> is the property to find (e.g.
-                _binman_u_boot_prop_pos). As a special case, you can append
-                _any to <entry> to have it search for any matching entry. E.g.
-                _binman_u_boot_any_prop_pos will match entries called u-boot,
-                u-boot-img and u-boot-nodtb)
-            optional: True if the symbol is optional. If False this function
-                will raise if the symbol is not found
-            msg: Message to display if an error occurs
-
-        Returns:
-            Value that should be assigned to that symbol, or None if it was
-                optional and not found
-
-        Raises:
-            ValueError if the symbol is invalid or not found, or references a
-                property which is not supported
-        """
-        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
-        if not m:
-            raise ValueError("%s: Symbol '%s' has invalid format" %
-                             (msg, sym_name))
-        entry_name, prop_name = m.groups()
-        entry_name = entry_name.replace('_', '-')
-        entry = self._entries.get(entry_name)
-        if not entry:
-            if entry_name.endswith('-any'):
-                root = entry_name[:-4]
-                for name in self._entries:
-                    if name.startswith(root):
-                        rest = name[len(root):]
-                        if rest in ['', '-img', '-nodtb']:
-                            entry = self._entries[name]
-        if not entry:
-            err = ("%s: Entry '%s' not found in list (%s)" %
-                   (msg, entry_name, ','.join(self._entries.keys())))
-            if optional:
-                print('Warning: %s' % err, file=sys.stderr)
-                return None
-            raise ValueError(err)
-        if prop_name == 'pos':
-            return entry.pos
-        else:
-            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
+    def GetEntries(self):
+        return self._section.GetEntries()
diff --git a/tools/binman/image_test.py b/tools/binman/image_test.py
index 44a5a2c..45dd237 100644
--- a/tools/binman/image_test.py
+++ b/tools/binman/image_test.py
@@ -12,25 +12,28 @@
 class TestImage(unittest.TestCase):
     def testInvalidFormat(self):
         image = Image('name', 'node', test=True)
+        section = image._section
         with self.assertRaises(ValueError) as e:
-            image.LookupSymbol('_binman_something_prop_', False, 'msg')
+            section.LookupSymbol('_binman_something_prop_', False, 'msg')
         self.assertIn(
             "msg: Symbol '_binman_something_prop_' has invalid format",
             str(e.exception))
 
     def testMissingSymbol(self):
         image = Image('name', 'node', test=True)
-        image._entries = {}
+        section = image._section
+        section._entries = {}
         with self.assertRaises(ValueError) as e:
-            image.LookupSymbol('_binman_type_prop_pname', False, 'msg')
+            section.LookupSymbol('_binman_type_prop_pname', False, 'msg')
         self.assertIn("msg: Entry 'type' not found in list ()",
                       str(e.exception))
 
     def testMissingSymbolOptional(self):
         image = Image('name', 'node', test=True)
-        image._entries = {}
+        section = image._section
+        section._entries = {}
         with capture_sys_output() as (stdout, stderr):
-            val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg')
+            val = section.LookupSymbol('_binman_type_prop_pname', True, 'msg')
         self.assertEqual(val, None)
         self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n",
                          stderr.getvalue())
@@ -38,7 +41,8 @@
 
     def testBadProperty(self):
         image = Image('name', 'node', test=True)
-        image._entries = {'u-boot': 1}
+        section = image._section
+        section._entries = {'u-boot': 1}
         with self.assertRaises(ValueError) as e:
-            image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
+            section.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
         self.assertIn("msg: No such property 'bad", str(e.exception))