symtab-reader: add support for binaries compiled with CFI

Control-Flow-Integrity (CFI) when enabled in clang built binaries
introduces an indirection when looking up ELF symbols. For DSO, the
symbol table (.dynsym) will still contain the symbols, but additional
symbols with suffix .cfi will be added to the full .symtab.
Unfortunately, the DWARF debug information refers to CFI symbols by
address to the .cfi suffixed variants as they point to the actual
implementation.

When the dwarf reader is determining whether to suppress variable or
function declarations, it does so by identifying if there is an
associated ELF symbol at the given address read from DWARF. Unless we
know about the alternative address, this will fail and the type
information will be suppressed.

Hence add the .cfi symbol values to the lookup map to associate their
address with the corresponding publicly exported symbol.

	* src/abg-symtab-reader.cc (symtab::load_): use new
	add_alternative_address_lookups method.
	(add_alternative_address_lookups): New method.
	* src/abg-symtab-reader.h (add_alternative_address_lookups): new
	function declaration.
	* tests/data/test-read-dwarf/test-libaaudio.so: New test data.
	* tests/data/test-read-dwarf/test-libaaudio.so.abi: New test data.
	* tests/data/Makefile.am: Add the two new tests input to source
	distribution.
	* tests/test-read-dwarf.cc: New test case.

Reported-by: Dan Albert <danalbert@google.com>
Reviewed-by: Giuliano Procida <gprocida@google.com>
Signed-off-by: Matthias Maennich <maennich@google.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Matthias Maennich 2021-06-14 11:07:36 +01:00 committed by Dodji Seketeli
parent 3a22dfaff6
commit 578ba12139
6 changed files with 2166 additions and 0 deletions

View File

@ -376,6 +376,8 @@ symtab::load_(Elf* elf_handle,
}
}
add_alternative_address_lookups(elf_handle);
is_kernel_binary_ = elf_helpers::is_linux_kernel(elf_handle);
// Now apply the ksymtab_exported attribute to the symbols we collected.
@ -564,5 +566,84 @@ symtab::update_function_entry_address_symbol_map(
}
}
/// Fill up the lookup maps with alternative keys
///
/// Due to special features like Control-Flow-Integrity (CFI), the symbol
/// lookup could be done indirectly. E.g. enabling CFI causes clang to
/// associate the DWARF information with the actual CFI protected function
/// (suffix .cfi) instead of with the entry symbol in the symtab.
///
/// This function adds additional lookup keys to compensate for that.
///
/// So far, this only implements CFI support, by adding addr->symbol pairs
/// where
/// addr : symbol value of the <foo>.cfi valyue
/// symbol : symbol_sptr looked up via "<foo>"
///
/// @param elf_handle the ELF handle to operate on
void
symtab::add_alternative_address_lookups(Elf* elf_handle)
{
Elf_Scn* symtab_section = elf_helpers::find_symtab_section(elf_handle);
if (!symtab_section)
return;
GElf_Shdr symtab_sheader;
gelf_getshdr(symtab_section, &symtab_sheader);
const size_t number_syms =
symtab_sheader.sh_size / symtab_sheader.sh_entsize;
Elf_Data* symtab = elf_getdata(symtab_section, 0);
for (size_t i = 0; i < number_syms; ++i)
{
GElf_Sym *sym, sym_mem;
sym = gelf_getsym(symtab, i, &sym_mem);
if (!sym)
{
std::cerr << "Could not load symbol with index " << i
<< ": Skipping alternative symbol load.\n";
continue;
}
const char* const name_str =
elf_strptr(elf_handle, symtab_sheader.sh_link, sym->st_name);
// no name, no game
if (!name_str)
continue;
const std::string name = name_str;
if (name.empty())
continue;
// Add alternative lookup addresses for CFI symbols
static const std::string cfi = ".cfi";
if (name.size() > cfi.size()
&& name.compare(name.size() - cfi.size(), cfi.size(), cfi) == 0)
// ... name.ends_with(".cfi")
{
const auto candidate_name = name.substr(0, name.size() - cfi.size());
auto symbols = lookup_symbol(candidate_name);
// lookup_symbol returns a vector of symbols. For this case we handle
// only the case that there has been exactly one match. Otherwise we
// can't reasonably handle it and need to bail out.
ABG_ASSERT(symbols.size() <= 1);
if (symbols.size() == 1)
{
const auto& symbol_sptr = symbols[0];
GElf_Addr symbol_value =
elf_helpers::maybe_adjust_et_rel_sym_addr_to_abs_addr(
elf_handle, sym);
const auto result =
addr_symbol_map_.emplace(symbol_value, symbol_sptr);
ABG_ASSERT(result.second);
}
}
}
}
} // end namespace symtab_reader
} // end namespace abigail

View File

@ -293,6 +293,9 @@ private:
update_function_entry_address_symbol_map(Elf* elf_handle,
GElf_Sym* native_symbol,
const elf_symbol_sptr& symbol_sptr);
void
add_alternative_address_lookups(Elf* elf_handle);
};
/// Helper class to allow range-for loops on symtabs for C++11 and later code.

View File

@ -566,6 +566,8 @@ test-read-dwarf/PR27700/pub-incdir/inc.h \
test-read-dwarf/PR27700/test-PR27700.abi \
test-read-dwarf/PR27700/test-PR27700.c \
test-read-dwarf/PR27700/test-PR27700.o \
test-read-dwarf/test-libaaudio.so \
test-read-dwarf/test-libaaudio.so.abi \
\
test-annotate/test0.abi \
test-annotate/test1.abi \

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -488,6 +488,14 @@ InOutSpec in_out_specs[] =
"data/test-read-dwarf/PR27700/test-PR27700.abi",
"output/test-read-dwarf/PR27700/test-PR27700.abi",
},
{
"data/test-read-dwarf/test-libaaudio.so",
"",
"",
HASH_TYPE_ID_STYLE,
"data/test-read-dwarf/test-libaaudio.so.abi",
"output/test-read-dwarf/test-libaaudio.so.abi",
},
// This should be the last entry.
{NULL, NULL, NULL, SEQUENCE_TYPE_ID_STYLE, NULL, NULL}
};