Skip to content

Commit

Permalink
hash.py: observe endianness when reading hashes (#338)
Browse files Browse the repository at this point in the history
Reading the hashes from a GNUHashTable didn't properly use
the endianness of the underlying ELF file, so looking up
hashes would fail if the byte order of the analyzed file
did not match the native byte order of the current machine.

The test file consists of two functions:

int callee(){
    return 42;
}

int caller(){
    return callee();
}

and was compiled using `aarch64_be-linux-gcc` (version 8.3
on an x86_64 host) with the `-mbig-endian` and `-shared`
command line flags.
  • Loading branch information
rupran authored Oct 26, 2020
1 parent 9704150 commit d6b2913
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 2 deletions.
6 changes: 4 additions & 2 deletions elftools/elf/hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,11 @@ def get_number_of_symbols(self):
max_chain_pos = self._chain_pos + \
(max_idx - self.params['symoffset']) * self._wordsize
self.elffile.stream.seek(max_chain_pos)
hash_format = '<I' if self.elffile.little_endian else '>I'

# Walk the chain to its end (lowest bit is set)
while True:
cur_hash = struct.unpack('I', self.elffile.stream.read(self._wordsize))[0]
cur_hash = struct.unpack(hash_format, self.elffile.stream.read(self._wordsize))[0]
if cur_hash & 1:
return max_idx + 1

Expand Down Expand Up @@ -150,8 +151,9 @@ def get_symbol(self, name):
return None

self.elffile.stream.seek(self._chain_pos + (symidx - self.params['symoffset']) * self._wordsize)
hash_format = '<I' if self.elffile.little_endian else '>I'
while True:
cur_hash = struct.unpack('I', self.elffile.stream.read(self._wordsize))[0]
cur_hash = struct.unpack(hash_format, self.elffile.stream.read(self._wordsize))[0]
if cur_hash | 1 == namehash | 1:
symbol = self._symboltable.get_symbol(symidx)
if name == symbol.name:
Expand Down
14 changes: 14 additions & 0 deletions test/test_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,17 @@ def test_get_symbol(self):
symbol_f1 = hash_section.get_symbol('function1_ver1_1')
self.assertIsNotNone(symbol_f1)
self.assertEqual(symbol_f1['st_value'], int(0x9a2))

def test_get_symbol_big_endian(self):
""" Verify we can get a specific symbol from a GNU hash section in a
big-endian file.
"""
with open(os.path.join('test', 'testfiles_for_unittests',
'aarch64_be_gnu_hash.so.elf'), 'rb') as f:
elf = ELFFile(f)
self.assertFalse(elf.little_endian)
hash_section = elf.get_section_by_name('.gnu.hash')
self.assertIsNotNone(hash_section)
symbol_f1 = hash_section.get_symbol('caller')
self.assertIsNotNone(symbol_f1)
self.assertEqual(symbol_f1['st_value'], int(0x5a4))
Binary file not shown.

0 comments on commit d6b2913

Please sign in to comment.