2021-11-21 23:59:44 +00:00
|
|
|
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
2022-11-02 00:05:16 +00:00
|
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
2021-11-20 00:55:42 +00:00
|
|
|
import tempfile
|
|
|
|
from typing import NamedTuple
|
|
|
|
|
|
|
|
from drgn import Program, SymbolBinding, SymbolKind
|
2022-01-07 22:28:01 +00:00
|
|
|
from tests import TestCase
|
2021-11-20 00:55:42 +00:00
|
|
|
from tests.dwarfwriter import dwarf_sections
|
|
|
|
from tests.elf import ET, PT, SHT, STB, STT
|
|
|
|
from tests.elfwriter import ElfSection, ElfSymbol, create_elf_file
|
|
|
|
|
|
|
|
|
|
|
|
def create_elf_symbol_file(symbols):
|
|
|
|
# We need some DWARF data so that libdwfl will load the file.
|
|
|
|
sections = dwarf_sections(())
|
|
|
|
# Create a section for the symbols to reference and the corresponding
|
|
|
|
# segment for address lookups.
|
|
|
|
min_address = min(symbol.value for symbol in symbols)
|
|
|
|
max_address = max(symbol.value + symbol.size for symbol in symbols)
|
|
|
|
sections.append(
|
|
|
|
ElfSection(
|
|
|
|
name=".foo",
|
2021-12-17 23:30:43 +00:00
|
|
|
sh_type=SHT.NOBITS,
|
2021-11-20 00:55:42 +00:00
|
|
|
p_type=PT.LOAD,
|
|
|
|
vaddr=min_address,
|
|
|
|
memsz=max_address - min_address,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
symbols = [
|
|
|
|
symbol._replace(
|
|
|
|
shindex=len(sections) if symbol.shindex is None else symbol.shindex
|
|
|
|
)
|
|
|
|
for symbol in symbols
|
|
|
|
]
|
|
|
|
return create_elf_file(ET.EXEC, sections, symbols)
|
|
|
|
|
|
|
|
|
|
|
|
def elf_symbol_program(*modules):
|
|
|
|
prog = Program()
|
|
|
|
for symbols in modules:
|
|
|
|
with tempfile.NamedTemporaryFile() as f:
|
|
|
|
f.write(create_elf_symbol_file(symbols))
|
|
|
|
f.flush()
|
|
|
|
prog.load_debug_info([f.name])
|
|
|
|
return prog
|
|
|
|
|
|
|
|
|
|
|
|
# We don't want to support creating drgn.Symbol instances yet, so use this dumb
|
|
|
|
# class for testing.
|
|
|
|
class Symbol(NamedTuple):
|
|
|
|
name: str
|
|
|
|
address: int
|
|
|
|
size: int
|
|
|
|
binding: SymbolBinding
|
|
|
|
kind: SymbolKind
|
|
|
|
|
|
|
|
|
2022-01-07 22:28:01 +00:00
|
|
|
class TestElfSymbol(TestCase):
|
2021-11-20 00:55:42 +00:00
|
|
|
def assert_symbol_equal(self, drgn_symbol, symbol):
|
|
|
|
self.assertEqual(
|
|
|
|
Symbol(
|
|
|
|
drgn_symbol.name,
|
|
|
|
drgn_symbol.address,
|
|
|
|
drgn_symbol.size,
|
|
|
|
drgn_symbol.binding,
|
|
|
|
drgn_symbol.kind,
|
|
|
|
),
|
|
|
|
symbol,
|
|
|
|
)
|
|
|
|
|
2021-08-02 21:34:30 +01:00
|
|
|
def assert_symbols_equal_unordered(self, drgn_symbols, symbols):
|
|
|
|
self.assertEqual(len(drgn_symbols), len(symbols))
|
|
|
|
drgn_symbols = sorted(drgn_symbols, key=lambda x: (x.address, x.name))
|
|
|
|
symbols = sorted(symbols, key=lambda x: (x.address, x.name))
|
|
|
|
for drgn_symbol, symbol in zip(drgn_symbols, symbols):
|
|
|
|
self.assert_symbol_equal(drgn_symbol, symbol)
|
|
|
|
|
2021-11-20 00:55:42 +00:00
|
|
|
def test_by_address(self):
|
|
|
|
elf_first = ElfSymbol("first", 0xFFFF0000, 0x8, STT.OBJECT, STB.LOCAL)
|
|
|
|
elf_second = ElfSymbol("second", 0xFFFF0008, 0x8, STT.OBJECT, STB.LOCAL)
|
|
|
|
first = Symbol("first", 0xFFFF0000, 0x8, SymbolBinding.LOCAL, SymbolKind.OBJECT)
|
|
|
|
second = Symbol(
|
|
|
|
"second", 0xFFFF0008, 0x8, SymbolBinding.LOCAL, SymbolKind.OBJECT
|
|
|
|
)
|
|
|
|
|
|
|
|
same_module = ((elf_first, elf_second),)
|
|
|
|
different_modules = ((elf_first,), (elf_second,))
|
|
|
|
|
|
|
|
for modules in same_module, different_modules:
|
|
|
|
with self.subTest(modules=len(modules)):
|
|
|
|
prog = elf_symbol_program(*modules)
|
|
|
|
self.assertRaises(LookupError, prog.symbol, 0xFFFEFFFF)
|
2021-08-02 21:34:30 +01:00
|
|
|
self.assertEqual(prog.symbols(0xFFFEFFFF), [])
|
2021-11-20 00:55:42 +00:00
|
|
|
self.assert_symbol_equal(prog.symbol(0xFFFF0000), first)
|
2021-08-02 21:34:30 +01:00
|
|
|
self.assert_symbols_equal_unordered(prog.symbols(0xFFFF0000), [first])
|
2021-11-20 00:55:42 +00:00
|
|
|
self.assert_symbol_equal(prog.symbol(0xFFFF0004), first)
|
2021-08-02 21:34:30 +01:00
|
|
|
self.assert_symbols_equal_unordered(prog.symbols(0xFFFF0004), [first])
|
2021-11-20 00:55:42 +00:00
|
|
|
self.assert_symbol_equal(prog.symbol(0xFFFF0008), second)
|
2021-08-02 21:34:30 +01:00
|
|
|
self.assert_symbols_equal_unordered(prog.symbols(0xFFFF0008), [second])
|
2021-11-20 00:55:42 +00:00
|
|
|
self.assert_symbol_equal(prog.symbol(0xFFFF000C), second)
|
2021-08-02 21:34:30 +01:00
|
|
|
self.assert_symbols_equal_unordered(prog.symbols(0xFFFF000C), [second])
|
2021-11-20 00:55:42 +00:00
|
|
|
self.assertRaises(LookupError, prog.symbol, 0xFFFF0010)
|
|
|
|
|
2021-11-20 01:07:23 +00:00
|
|
|
def test_by_address_precedence(self):
|
|
|
|
precedence = (STB.GLOBAL, STB.WEAK, STB.LOCAL)
|
2021-08-02 21:34:30 +01:00
|
|
|
drgn_precedence = (
|
|
|
|
SymbolBinding.GLOBAL,
|
|
|
|
SymbolBinding.WEAK,
|
|
|
|
SymbolBinding.LOCAL,
|
|
|
|
)
|
2021-11-20 01:07:23 +00:00
|
|
|
|
|
|
|
def assert_find_higher(*modules):
|
|
|
|
self.assertEqual(
|
|
|
|
elf_symbol_program(*modules).symbol(0xFFFF0000).name, "foo"
|
|
|
|
)
|
|
|
|
|
2021-08-02 21:34:30 +01:00
|
|
|
def assert_finds_both(symbols, *modules):
|
|
|
|
self.assert_symbols_equal_unordered(
|
|
|
|
elf_symbol_program(*modules).symbols(0xFFFF0000),
|
|
|
|
symbols,
|
|
|
|
)
|
|
|
|
|
2021-11-20 01:07:23 +00:00
|
|
|
for i in range(len(precedence) - 1):
|
|
|
|
higher_binding = precedence[i]
|
2021-08-02 21:34:30 +01:00
|
|
|
higher_binding_drgn = drgn_precedence[i]
|
2021-11-20 01:07:23 +00:00
|
|
|
for j in range(i + 1, len(precedence)):
|
|
|
|
lower_binding = precedence[j]
|
2021-08-02 21:34:30 +01:00
|
|
|
lower_binding_drgn = drgn_precedence[j]
|
2021-11-20 01:07:23 +00:00
|
|
|
with self.subTest(higher=higher_binding, lower=lower_binding):
|
|
|
|
higher = ElfSymbol(
|
|
|
|
"foo", 0xFFFF0000, 0x8, STT.OBJECT, higher_binding
|
|
|
|
)
|
|
|
|
lower = ElfSymbol("bar", 0xFFFF0000, 0x8, STT.OBJECT, lower_binding)
|
2021-08-02 21:34:30 +01:00
|
|
|
symbols = [
|
|
|
|
Symbol(
|
|
|
|
"foo",
|
|
|
|
0xFFFF0000,
|
|
|
|
0x8,
|
|
|
|
higher_binding_drgn,
|
|
|
|
SymbolKind.OBJECT,
|
|
|
|
),
|
|
|
|
Symbol(
|
|
|
|
"bar",
|
|
|
|
0xFFFF0000,
|
|
|
|
0x8,
|
|
|
|
lower_binding_drgn,
|
|
|
|
SymbolKind.OBJECT,
|
|
|
|
),
|
|
|
|
]
|
2021-11-20 01:07:23 +00:00
|
|
|
# Local symbols must be before global symbols.
|
|
|
|
if lower_binding != STB.LOCAL:
|
|
|
|
with self.subTest("higher before lower"):
|
|
|
|
assert_find_higher((higher, lower))
|
|
|
|
with self.subTest("lower before higher"):
|
|
|
|
assert_find_higher((lower, higher))
|
2021-08-02 21:34:30 +01:00
|
|
|
assert_finds_both(symbols, (lower, higher))
|
2021-11-20 01:07:23 +00:00
|
|
|
|
2021-11-20 00:55:42 +00:00
|
|
|
def test_by_name(self):
|
|
|
|
elf_first = ElfSymbol("first", 0xFFFF0000, 0x8, STT.OBJECT, STB.GLOBAL)
|
|
|
|
elf_second = ElfSymbol("second", 0xFFFF0008, 0x8, STT.OBJECT, STB.GLOBAL)
|
|
|
|
first = Symbol(
|
|
|
|
"first", 0xFFFF0000, 0x8, SymbolBinding.GLOBAL, SymbolKind.OBJECT
|
|
|
|
)
|
|
|
|
second = Symbol(
|
|
|
|
"second", 0xFFFF0008, 0x8, SymbolBinding.GLOBAL, SymbolKind.OBJECT
|
|
|
|
)
|
|
|
|
|
|
|
|
same_module = ((elf_first, elf_second),)
|
|
|
|
different_modules = ((elf_first,), (elf_second,))
|
|
|
|
|
|
|
|
for modules in same_module, different_modules:
|
|
|
|
with self.subTest(modules=len(modules)):
|
|
|
|
prog = elf_symbol_program(*modules)
|
|
|
|
self.assert_symbol_equal(prog.symbol("first"), first)
|
|
|
|
self.assert_symbol_equal(prog.symbol("second"), second)
|
|
|
|
self.assertRaises(LookupError, prog.symbol, "third")
|
|
|
|
|
2021-08-02 21:34:30 +01:00
|
|
|
self.assert_symbols_equal_unordered(prog.symbols("first"), [first])
|
|
|
|
self.assert_symbols_equal_unordered(prog.symbols("second"), [second])
|
|
|
|
self.assertEqual(prog.symbols("third"), [])
|
|
|
|
|
2021-11-20 01:07:23 +00:00
|
|
|
def test_by_name_precedence(self):
|
|
|
|
precedence = (
|
|
|
|
(STB.GLOBAL, STB.GNU_UNIQUE),
|
|
|
|
(STB.WEAK,),
|
|
|
|
(STB.LOCAL, STB.HIPROC),
|
|
|
|
)
|
|
|
|
|
|
|
|
expected = 0xFFFF0008
|
2021-08-02 21:34:30 +01:00
|
|
|
other = expected - 0x8
|
2021-11-20 01:07:23 +00:00
|
|
|
|
|
|
|
def assert_find_higher(*modules):
|
2021-08-02 21:34:30 +01:00
|
|
|
prog = elf_symbol_program(*modules)
|
|
|
|
self.assertEqual(prog.symbol("foo").address, expected)
|
|
|
|
# assert symbols() always finds both
|
|
|
|
symbols = sorted(prog.symbols("foo"), key=lambda s: s.address)
|
|
|
|
self.assertEqual(len(symbols), 2)
|
|
|
|
self.assertEqual(symbols[0].address, other)
|
|
|
|
self.assertEqual(symbols[1].address, expected)
|
2021-11-20 01:07:23 +00:00
|
|
|
|
|
|
|
for i in range(len(precedence) - 1):
|
|
|
|
for higher_binding in precedence[i]:
|
|
|
|
for j in range(i + 1, len(precedence)):
|
|
|
|
for lower_binding in precedence[j]:
|
|
|
|
with self.subTest(higher=higher_binding, lower=lower_binding):
|
|
|
|
higher = ElfSymbol(
|
|
|
|
"foo", expected, 0x8, STT.OBJECT, higher_binding
|
|
|
|
)
|
|
|
|
lower = ElfSymbol(
|
2021-08-02 21:34:30 +01:00
|
|
|
"foo", other, 0x8, STT.OBJECT, lower_binding
|
2021-11-20 01:07:23 +00:00
|
|
|
)
|
|
|
|
# Local symbols must be before global symbols.
|
|
|
|
if lower_binding not in precedence[-1]:
|
|
|
|
with self.subTest("same module, higher before lower"):
|
|
|
|
assert_find_higher((higher, lower))
|
|
|
|
with self.subTest("same module, lower before higher"):
|
|
|
|
assert_find_higher((lower, higher))
|
|
|
|
with self.subTest("different modules, higher before lower"):
|
|
|
|
assert_find_higher((higher,), (lower,))
|
|
|
|
with self.subTest("different modules, lower before higher"):
|
|
|
|
assert_find_higher((lower,), (higher,))
|
|
|
|
|
2021-11-20 00:55:42 +00:00
|
|
|
def test_binding(self):
|
2021-11-20 01:07:23 +00:00
|
|
|
for by in "name", "address":
|
|
|
|
for elf_binding, drgn_binding in (
|
|
|
|
(STB.LOCAL, SymbolBinding.LOCAL),
|
|
|
|
(STB.GLOBAL, SymbolBinding.GLOBAL),
|
|
|
|
(STB.WEAK, SymbolBinding.WEAK),
|
|
|
|
(STB.GNU_UNIQUE, SymbolBinding.UNIQUE),
|
|
|
|
(STB.HIPROC, SymbolBinding.UNKNOWN),
|
|
|
|
):
|
|
|
|
with self.subTest(by=by, binding=elf_binding):
|
|
|
|
prog = elf_symbol_program(
|
|
|
|
(ElfSymbol("foo", 0xFFFF0000, 1, STT.OBJECT, elf_binding),)
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
prog.symbol("foo" if by == "name" else 0xFFFF0000).binding,
|
|
|
|
drgn_binding,
|
|
|
|
)
|
2021-08-02 21:34:30 +01:00
|
|
|
if by == "name":
|
|
|
|
symbols = prog.symbols("foo")
|
|
|
|
self.assertEqual(len(symbols), 1)
|
|
|
|
self.assertEqual(symbols[0].binding, drgn_binding)
|
2021-11-20 00:55:42 +00:00
|
|
|
|
|
|
|
def test_kind(self):
|
|
|
|
for elf_type, drgn_kind in (
|
|
|
|
(STT.NOTYPE, SymbolKind.UNKNOWN),
|
|
|
|
(STT.OBJECT, SymbolKind.OBJECT),
|
|
|
|
(STT.FUNC, SymbolKind.FUNC),
|
2021-11-20 01:07:23 +00:00
|
|
|
(STT.SECTION, SymbolKind.SECTION),
|
|
|
|
(STT.FILE, SymbolKind.FILE),
|
2021-11-20 00:55:42 +00:00
|
|
|
(STT.COMMON, SymbolKind.COMMON),
|
2021-11-20 01:07:23 +00:00
|
|
|
(STT.TLS, SymbolKind.TLS),
|
2021-11-20 00:55:42 +00:00
|
|
|
(STT.GNU_IFUNC, SymbolKind.IFUNC),
|
|
|
|
):
|
|
|
|
with self.subTest(type=elf_type):
|
|
|
|
prog = elf_symbol_program(
|
|
|
|
(ElfSymbol("foo", 0xFFFF0000, 1, elf_type, STB.GLOBAL),)
|
|
|
|
)
|
2021-08-02 21:34:30 +01:00
|
|
|
symbol = Symbol("foo", 0xFFFF0000, 1, SymbolBinding.GLOBAL, drgn_kind)
|
|
|
|
self.assert_symbol_equal(prog.symbol("foo"), symbol)
|
|
|
|
symbols = prog.symbols("foo")
|
|
|
|
self.assert_symbols_equal_unordered(symbols, [symbol])
|
|
|
|
|
|
|
|
def test_all_symbols(self):
|
|
|
|
elf_syms = (
|
|
|
|
(
|
|
|
|
ElfSymbol("two", 0xFFFF0012, 1, STT.OBJECT, STB.LOCAL),
|
|
|
|
ElfSymbol("three", 0xFFFF0013, 1, STT.OBJECT, STB.LOCAL),
|
|
|
|
ElfSymbol("one", 0xFFFF0011, 1, STT.OBJECT, STB.GLOBAL),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
ElfSymbol("three", 0xFFFF0023, 1, STT.OBJECT, STB.LOCAL),
|
|
|
|
ElfSymbol("two", 0xFFFF0022, 1, STT.OBJECT, STB.GLOBAL),
|
|
|
|
),
|
|
|
|
(ElfSymbol("three", 0xFFFF0033, 1, STT.OBJECT, STB.GLOBAL),),
|
|
|
|
)
|
|
|
|
kind = SymbolKind.OBJECT
|
|
|
|
syms = [
|
|
|
|
Symbol("two", 0xFFFF0012, 1, SymbolBinding.LOCAL, kind),
|
|
|
|
Symbol("three", 0xFFFF0013, 1, SymbolBinding.LOCAL, kind),
|
|
|
|
Symbol("one", 0xFFFF0011, 1, SymbolBinding.GLOBAL, kind),
|
|
|
|
Symbol("three", 0xFFFF0023, 1, SymbolBinding.LOCAL, kind),
|
|
|
|
Symbol("two", 0xFFFF0022, 1, SymbolBinding.GLOBAL, kind),
|
|
|
|
Symbol("three", 0xFFFF0033, 1, SymbolBinding.GLOBAL, kind),
|
|
|
|
]
|
|
|
|
prog = elf_symbol_program(*elf_syms)
|
|
|
|
self.assert_symbols_equal_unordered(prog.symbols(), syms)
|