Sort dynamic symbols

This commit is contained in:
Charles 2020-12-06 22:13:17 +08:00
parent 2145d79efb
commit 50a348e7f0
4 changed files with 83 additions and 1 deletions

View File

@ -93,6 +93,8 @@ class LIEF_API Builder {
template<typename ELF_T>
void build_symbol_gnuhash(void);
uint32_t sort_dynamic_symbols(void);
void build_empty_symbol_gnuhash(void);
template<typename ELF_T>

View File

@ -111,6 +111,46 @@ void Builder::write(const std::string& filename) const {
}
uint32_t Builder::sort_dynamic_symbols(void) {
auto it_begin = std::begin(this->binary_->dynamic_symbols_);
auto it_end = std::end(this->binary_->dynamic_symbols_);
auto it_first_non_local_symbol =
std::stable_partition(it_begin, it_end, [](const Symbol* sym) {
return sym->binding() == SYMBOL_BINDINGS::STB_LOCAL;
});
uint32_t first_non_local_symbol_index =
std::distance(it_begin, it_first_non_local_symbol);
std::string section_name = ".dynsym";
if (this->binary_->has_section(section_name)) {
Section& section = this->binary_->get_section(section_name);
if (section.information() != first_non_local_symbol_index) {
// TODO: Erase null entries of dynamic symbol table and symbol version table
// if information of .dynsym section is smaller than null entries num.
LIEF_WARN("information of {} section changes from {:d} to {:d}",
section_name,
section.information(),
first_non_local_symbol_index);
section.information(first_non_local_symbol_index);
}
}
auto it_first_exported_symbol = std::stable_partition(
it_first_non_local_symbol, it_end, [](const Symbol* sym) {
return sym->shndx() ==
static_cast<uint16_t>(SYMBOL_SECTION_INDEX::SHN_UNDEF);
});
uint32_t first_exported_symbol_index =
std::distance(it_begin, it_first_exported_symbol);
if (this->binary_->gnu_hash().symbol_index() != first_exported_symbol_index) {
LIEF_WARN("symndx of .gnu.hash section changes from {:d} to {:d}",
this->binary_->gnu_hash().symbol_index(),
first_exported_symbol_index);
}
return first_exported_symbol_index;
}
void Builder::build_empty_symbol_gnuhash(void) {
LIEF_DEBUG("Build empty GNU Hash");
auto&& it_gnuhash = std::find_if(

View File

@ -892,7 +892,7 @@ void Builder::build_symbol_gnuhash(void) {
const GnuHash& gnu_hash = this->binary_->gnu_hash();
const uint32_t nb_buckets = gnu_hash.nb_buckets();
const uint32_t symndx = gnu_hash.symbol_index();
const uint32_t symndx = sort_dynamic_symbols();
const uint32_t maskwords = gnu_hash.maskwords();
const uint32_t shift2 = gnu_hash.shift2();

View File

@ -126,6 +126,46 @@ class TestDynamic(TestCase):
def setUp(self):
self.logger = logging.getLogger(__name__)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_add_dynamic_symbols(self):
sample = LibAddSample()
libadd = lief.parse(sample.libadd)
binadd = lief.parse(sample.binadd)
dynamic_symbols = list(libadd.dynamic_symbols)
for sym in dynamic_symbols:
libadd.add_dynamic_symbol(sym)
dynamic_section = libadd.get_section(".dynsym")
libadd.extend(dynamic_section, dynamic_section.entry_size * (len(dynamic_symbols) * 2))
libadd.write(sample.libadd)
p = Popen([sample.binadd_bin, '1', '2'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env={"LD_LIBRARY_PATH": sample.directory})
stdout, _ = p.communicate()
if p.returncode > 0:
self.logger.fatal(stdout.decode("utf8"))
self.assertEqual(p.returncode, 0)
self.logger.debug(stdout.decode("utf8"))
self.assertIsNotNone(re.search(r'From myLIb, a \+ b = 3', stdout.decode("utf8")))
libadd = lief.parse(sample.libadd)
dynamic_section = libadd.get_section(".dynsym")
# TODO: Size of libadd.dynamic_symbols is larger than dynamic_symbols_size.
dynamic_symbols_size = int(dynamic_section.size / dynamic_section.entry_size)
dynamic_symbols = list(libadd.dynamic_symbols)[:dynamic_symbols_size]
first_not_null_symbol_index = dynamic_section.information
first_exported_symbol_index = next(
i for i, sym in enumerate(dynamic_symbols) if sym.shndx != 0)
self.assertTrue(all(map(
lambda sym: sym.shndx == 0 and sym.binding == lief.ELF.SYMBOL_BINDINGS.LOCAL,
dynamic_symbols[:first_not_null_symbol_index])))
self.assertTrue(all(map(
lambda sym: sym.shndx == 0 and sym.binding != lief.ELF.SYMBOL_BINDINGS.LOCAL,
dynamic_symbols[first_not_null_symbol_index:first_exported_symbol_index])))
self.assertTrue(all(map(
lambda sym: sym.shndx != 0,
dynamic_symbols[first_exported_symbol_index:])))
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_remove_library(self):