binman: Add a function to read ELF symbols

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>
This commit is contained in:
Simon Glass 2017-11-13 18:54:54 -07:00
parent cf71338ee7
commit b50e5611a6
8 changed files with 128 additions and 11 deletions

View File

@ -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)

77
tools/binman/elf.py Normal file
View File

@ -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

32
tools/binman/elf_test.py Normal file
View File

@ -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()

View File

@ -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

View File

@ -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')

View File

@ -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')

View File

@ -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];

View File

@ -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]