[U-Boot,01/13] binman: Add a function to read ELF symbols

Message ID 20171114015506.25673-2-sjg@chromium.org
State New
Delegated to: Simon Glass
Headers show
Series
  • binman: Support run-time access to binman image positions
Related show

Commit Message

Simon Glass Nov. 14, 2017, 1:54 a.m.
In some cases we need to read symbols from U-Boot. At present we have a
a few cases which does this via 'nm' and 'grep'.

It is better to use objdump since that tells us the size of the symbols
and also whether it is weak or not.

Add a new module which reads ELF information from files. Update existing
uses of 'nm' to use this module.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 tools/binman/binman.py                      |  3 +-
 tools/binman/elf.py                         | 77 +++++++++++++++++++++++++++++
 tools/binman/elf_test.py                    | 32 ++++++++++++
 tools/binman/etype/u_boot_spl_bss_pad.py    |  7 +--
 tools/binman/etype/u_boot_with_ucode_ptr.py |  9 ++--
 tools/binman/ftest.py                       |  7 +++
 tools/binman/test/bss_data.c                |  2 +-
 tools/binman/test/u_boot_ucode_ptr.c        |  2 +-
 8 files changed, 128 insertions(+), 11 deletions(-)
 create mode 100644 tools/binman/elf.py
 create mode 100644 tools/binman/elf_test.py

Patch

diff --git a/tools/binman/binman.py b/tools/binman/binman.py
index 3ccf25f1f88..81a613ddc40 100755
--- a/tools/binman/binman.py
+++ b/tools/binman/binman.py
@@ -33,6 +33,7 @@  import control
 
 def RunTests():
     """Run the functional tests and any embedded doctests"""
+    import elf_test
     import entry_test
     import fdt_test
     import ftest
@@ -50,7 +51,7 @@  def RunTests():
     # 'entry' module.
     suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry)
     suite.run(result)
-    for module in (ftest.TestFunctional, fdt_test.TestFdt):
+    for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf):
         suite = unittest.TestLoader().loadTestsFromTestCase(module)
         suite.run(result)
 
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
new file mode 100644
index 00000000000..97208b17950
--- /dev/null
+++ b/tools/binman/elf.py
@@ -0,0 +1,77 @@ 
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Handle various things related to ELF images
+#
+
+from collections import namedtuple, OrderedDict
+import command
+import os
+import re
+import struct
+
+import tools
+
+Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
+
+# Used for tests which don't have an ELF file to read
+ignore_missing_files = False
+
+
+def GetSymbols(fname, patterns):
+    """Get the symbols from an ELF file
+
+    Args:
+        fname: Filename of the ELF file to read
+        patterns: List of regex patterns to search for, each a string
+
+    Returns:
+        None, if the file does not exist, or Dict:
+          key: Name of symbol
+          value: Hex value of symbol
+    """
+    stdout = command.Output('objdump', '-t', fname, raise_on_error=False)
+    lines = stdout.splitlines()
+    if patterns:
+        re_syms = re.compile('|'.join(patterns))
+    else:
+        re_syms = None
+    syms = {}
+    syms_started = False
+    for line in lines:
+        if not line or not syms_started:
+            if 'SYMBOL TABLE' in line:
+                syms_started = True
+            line = None  # Otherwise code coverage complains about 'continue'
+            continue
+        if re_syms and not re_syms.search(line):
+            continue
+
+        space_pos = line.find(' ')
+        value, rest = line[:space_pos], line[space_pos + 1:]
+        flags = rest[:7]
+        parts = rest[7:].split()
+        section, size =  parts[:2]
+        if len(parts) > 2:
+            name = parts[2]
+            syms[name] = Symbol(section, int(value, 16), int(size,16),
+                                flags[1] == 'w')
+    return syms
+
+def GetSymbolAddress(fname, sym_name):
+    """Get a value of a symbol from an ELF file
+
+    Args:
+        fname: Filename of the ELF file to read
+        patterns: List of regex patterns to search for, each a string
+
+    Returns:
+        Symbol value (as an integer) or None if not found
+    """
+    syms = GetSymbols(fname, [sym_name])
+    sym = syms.get(sym_name)
+    if not sym:
+        return None
+    return sym.address
diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py
new file mode 100644
index 00000000000..dca7bc59f99
--- /dev/null
+++ b/tools/binman/elf_test.py
@@ -0,0 +1,32 @@ 
+#
+# Copyright (c) 2017 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Test for the elf module
+
+import os
+import sys
+import unittest
+
+import elf
+
+binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr')
+
+class TestElf(unittest.TestCase):
+    def testAllSymbols(self):
+        syms = elf.GetSymbols(fname, [])
+        self.assertIn('.ucode', syms)
+
+    def testRegexSymbols(self):
+        syms = elf.GetSymbols(fname, ['ucode'])
+        self.assertIn('.ucode', syms)
+        syms = elf.GetSymbols(fname, ['missing'])
+        self.assertNotIn('.ucode', syms)
+        syms = elf.GetSymbols(fname, ['missing', 'ucode'])
+        self.assertIn('.ucode', syms)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py
index c005f28191f..c37f61db235 100644
--- a/tools/binman/etype/u_boot_spl_bss_pad.py
+++ b/tools/binman/etype/u_boot_spl_bss_pad.py
@@ -9,6 +9,7 @@ 
 #
 
 import command
+import elf
 from entry import Entry
 from blob import Entry_blob
 import tools
@@ -19,8 +20,8 @@  class Entry_u_boot_spl_bss_pad(Entry_blob):
 
     def ObtainContents(self):
         fname = tools.GetInputFilename('spl/u-boot-spl')
-        args = [['nm', fname], ['grep', '__bss_size']]
-        out = command.RunPipe(args, capture=True).stdout.splitlines()
-        bss_size = int(out[0].split()[0], 16)
+        bss_size = elf.GetSymbolAddress(fname, '__bss_size')
+        if not bss_size:
+            self.Raise('Expected __bss_size symbol in spl/u-boot-spl')
         self.data = chr(0) * bss_size
         self.contents_size = bss_size
diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py
index 6f01adb9701..99b437130db 100644
--- a/tools/binman/etype/u_boot_with_ucode_ptr.py
+++ b/tools/binman/etype/u_boot_with_ucode_ptr.py
@@ -9,6 +9,7 @@ 
 import struct
 
 import command
+import elf
 from entry import Entry
 from blob import Entry_blob
 import fdt_util
@@ -31,11 +32,9 @@  class Entry_u_boot_with_ucode_ptr(Entry_blob):
     def ObtainContents(self):
         # Figure out where to put the microcode pointer
         fname = tools.GetInputFilename(self.elf_fname)
-        args = [['nm', fname], ['grep', '-w', '_dt_ucode_base_size']]
-        out = (command.RunPipe(args, capture=True, raise_on_error=False).
-               stdout.splitlines())
-        if len(out) == 1:
-            self.target_pos = int(out[0].split()[0], 16)
+        sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size')
+        if sym:
+           self.target_pos = sym
         elif not fdt_util.GetBool(self._node, 'optional-ucode'):
             self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
 
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 9083143894f..c8155788afc 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -835,6 +835,13 @@  class TestFunctional(unittest.TestCase):
         data = self._DoReadFile('47_spl_bss_pad.dts')
         self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
 
+        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
+            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
+        with self.assertRaises(ValueError) as e:
+            data = self._DoReadFile('47_spl_bss_pad.dts')
+        self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
+                      str(e.exception))
+
     def testPackStart16Spl(self):
         """Test that an image with an x86 start16 region can be created"""
         data = self._DoReadFile('48_x86-start16-spl.dts')
diff --git a/tools/binman/test/bss_data.c b/tools/binman/test/bss_data.c
index f865a9d9f67..e0305c382c3 100644
--- a/tools/binman/test/bss_data.c
+++ b/tools/binman/test/bss_data.c
@@ -4,7 +4,7 @@ 
  * SPDX-License-Identifier:     GPL-2.0+
  *
  * Simple program to create a _dt_ucode_base_size symbol which can be read
- * by 'nm'. This is used by binman tests.
+ * by binutils. This is used by binman tests.
  */
 
 int bss_data[10];
diff --git a/tools/binman/test/u_boot_ucode_ptr.c b/tools/binman/test/u_boot_ucode_ptr.c
index 24f349fb9e4..734d54f0d41 100644
--- a/tools/binman/test/u_boot_ucode_ptr.c
+++ b/tools/binman/test/u_boot_ucode_ptr.c
@@ -4,7 +4,7 @@ 
  * SPDX-License-Identifier:     GPL-2.0+
  *
  * Simple program to create a _dt_ucode_base_size symbol which can be read
- * by 'nm'. This is used by binman tests.
+ * by binutils. This is used by binman tests.
  */
 
 static unsigned long _dt_ucode_base_size[2]