Skip to content

Commit

Permalink
Fix bug. and enhanced dynamic relocation table parsing.
Browse files Browse the repository at this point in the history
  • Loading branch information
WangONC committed Nov 7, 2024
1 parent 83b2f95 commit 67ba6af
Showing 1 changed file with 198 additions and 56 deletions.
254 changes: 198 additions & 56 deletions src/androidemu/internal/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ def calculate_sh_offset(elf, vaddr):
if p_vaddr <= vaddr < (p_vaddr + p_filesz):
return p_offset + (vaddr - p_vaddr)
raise Exception(f"Cannot find segment containing address {vaddr:#x}")

@staticmethod
def create_reloc_section(elf,name, is_rela, addr, size, entsize, sym_idx):
if not addr or not size:
return None
if elf.elfclass == 32:
entsize = entsize or (12 if is_rela else 8)
else: # 64 bit
entsize = entsize or (24 if is_rela else 16)
fake_rel_header = Container(
sh_name=0,
sh_type='SHT_RELA' if is_rela else 'SHT_REL',
sh_flags=SH_FLAGS.SHF_ALLOC,
sh_addr=addr,
sh_offset=Modules.calculate_sh_offset(elf, addr),
sh_size=size,
sh_link=sym_idx,
sh_info=0,
sh_addralign=8 if elf.elfclass == 64 else 4,
sh_entsize=entsize

)
return RelocationSection(fake_rel_header, name, elf)

def load_module(self, filename):
logger.debug("Loading module '%s'." % filename)
Expand Down Expand Up @@ -114,24 +137,23 @@ def load_module(self, filename):
self.emu.uc.mem_map(seg_addr, seg_size, prot)
self.emu.uc.mem_write(load_base + segment.header.p_vaddr, segment.data())

rel_section = None
for section in elf.iter_sections():
if not isinstance(section, RelocationSection):
continue
rel_section = section
break
rel_sections = []

# Parse section header (Linking view).
dynsym = elf.get_section_by_name(".dynsym")
dynstr = elf.get_section_by_name(".dynstr")

# Find rel section if not found.
if rel_section is None or dynsym is None or dynstr is None:
# Find relocation table and symbol table by dynamic segment
if dynsym is None or dynstr is None:
rel_info = {
'rel': {'addr': None, 'size': None, 'entsize': None, 'count': None},
'rela': {'addr': None, 'size': None, 'entsize': None, 'count': None},
'jmprel': {'addr': None, 'size': None, 'entsize': None},
'android_rela': {'addr': None, 'size': None, 'entsize': None},
'relr': {'addr': None, 'size': None, 'entsize': None},
'pltrel': None, # DT_PLTREL
'textrel': False, # DT_TEXTREL
'sym': None,
'type': None
}

sym_info = {
Expand Down Expand Up @@ -174,35 +196,52 @@ def load_module(self, filename):
elif tag.entry.d_tag == 'DT_SYMENT':
sym_info['dynsym']['entsize'] = tag.entry.d_val

if rel_section is None:
if rel_info['rel']['addr'] and rel_info['rel']['size']:
rel_info['type'] = 'REL'
active_rel = rel_info['rel']
has_reloc_info = True
elif rel_info['rela']['addr'] and rel_info['rela']['size']:
rel_info['type'] = 'RELA'
active_rel = rel_info['rela']
has_reloc_info = True
else:
has_reloc_info = False

if has_reloc_info and active_rel['addr'] and active_rel['size'] and active_rel['entsize']:
is_rela = rel_info['type'] == 'RELA'
fake_rel_header = Container(
sh_name=0, # we don't know the name
sh_type='SHT_RELA' if is_rela else 'SHT_REL',
sh_flags=SH_FLAGS.SHF_ALLOC,
sh_addr=active_rel['addr'],
sh_offset=self.calculate_sh_offset(elf, active_rel['addr']),
sh_size=active_rel['size'],
sh_link=rel_info['sym'], # link to dynsym
sh_info = 0,
sh_addralign=8 if elf.elfclass == 64 else 4,
sh_entsize=active_rel['entsize']
)
rel_section = RelocationSection(fake_rel_header,
'.rela.dyn' if is_rela else '.rel.dyn',
elf)
# other Relocation information
elif tag.entry.d_tag == 'DT_TEXTREL':
rel_info['textrel'] = True
elif tag.entry.d_tag == 'DT_PLTREL':
rel_info['pltrel'] = 'RELA' if tag.entry.d_val == 7 else 'REL'
elif tag.entry.d_tag == 'DT_JMPREL':
rel_info['jmprel']['addr'] = tag.entry.d_val
elif tag.entry.d_tag == 'DT_PLTRELSZ':
rel_info['jmprel']['size'] = tag.entry.d_val
elif tag.entry.d_tag == 'DT_ANDROID_RELA':
rel_info['android_rela']['addr'] = tag.entry.d_val
elif tag.entry.d_tag == 'DT_ANDROID_RELASZ':
rel_info['android_rela']['size'] = tag.entry.d_val
elif tag.entry.d_tag == 'DT_ANDROID_RELR':
rel_info['relr']['addr'] = tag.entry.d_val
elif tag.entry.d_tag == 'DT_ANDROID_RELRSZ':
rel_info['relr']['size'] = tag.entry.d_val

if rel_info['rel']['addr'] and rel_info['rel']['size']:
rel_info['type'] = 'REL'
active_rel = rel_info['rel']
has_reloc_info = True
elif rel_info['rela']['addr'] and rel_info['rela']['size']:
rel_info['type'] = 'RELA'
active_rel = rel_info['rela']
has_reloc_info = True
else:
has_reloc_info = False

if has_reloc_info and active_rel['addr'] and active_rel['size'] and active_rel['entsize']:
is_rela = rel_info['type'] == 'RELA'
fake_rel_header = Container(
sh_name=0, # we don't know the name,but it's not important
sh_type='SHT_RELA' if is_rela else 'SHT_REL',
sh_flags=SH_FLAGS.SHF_ALLOC,
sh_addr=active_rel['addr'],
sh_offset=self.calculate_sh_offset(elf, active_rel['addr']),
sh_size=active_rel['size'],
sh_link=rel_info['sym'], # link to dynsym
sh_info = 0,
sh_addralign=8 if elf.elfclass == 64 else 4,
sh_entsize=active_rel['entsize']
)
rel_sections.append(RelocationSection(fake_rel_header,
'.rela.dyn' if is_rela else '.rel.dyn',
elf))

# create dynsym and dynstr if not found
if dynstr is None or dynsym is None:
Expand Down Expand Up @@ -241,17 +280,63 @@ def load_module(self, filename):
)
dynsym = SymbolTableSection(fake_sym_header, '.dynsym', elf, dynstr)

# create all fake relocation section
if rel_info['rel']['addr']:
rel = self.create_reloc_section(elf,'.rel.dyn', False,
rel_info['rel']['addr'],
rel_info['rel']['size'],
rel_info['rel']['entsize'],
rel_info['sym'])
if rel:
rel_sections.append(rel)

if rel_info['rela']['addr']:
rela = self.create_reloc_section(elf,'.rela.dyn', True,
rel_info['rela']['addr'],
rel_info['rela']['size'],
rel_info['rela']['entsize'],
rel_info['sym'])
if rela:
rel_sections.append(rela)

if rel_info['jmprel']['addr']:
is_rela = rel_info['pltrel'] == 'RELA'
jmprel = self.create_reloc_section(elf,'.rela.plt' if is_rela else '.rel.plt',
is_rela,
rel_info['jmprel']['addr'],
rel_info['jmprel']['size'],
rel_info['jmprel']['entsize'],
rel_info['sym'])
if jmprel:
rel_sections.append(jmprel)

if rel_info['android_rela']['addr']:
android_rela = self.create_reloc_section(elf,'.rela.android', True,
rel_info['android_rela']['addr'],
rel_info['android_rela']['size'],
rel_info['android_rela']['entsize'],
rel_info['sym'])
if android_rela:
rel_sections.append(android_rela)

# Find init array.
init_array_size = 0
init_array_offset = 0
init_array = []
init = None
for x in elf.iter_segments():
if x.header.p_type == "PT_DYNAMIC":
for tag in x.iter_tags():
if tag.entry.d_tag == "DT_INIT_ARRAYSZ":
init_array_size = tag.entry.d_val
elif tag.entry.d_tag == "DT_INIT_ARRAY":
init_array_offset = tag.entry.d_val
elif tag.entry.d_tag == "DT_INIT":
init = tag.entry.d_val

# DT_INIT should be called before DT_INIT_ARRAY if both are present
if init:
init = load_base + init

for _ in range(int(init_array_size / 4)):
# covert va to file offset
Expand All @@ -267,23 +352,22 @@ def load_module(self, filename):
# print ("find init array for :%s %x" % (filename, fun_ptr))
else:
# search in reloc
for rel in rel_section.iter_relocations():
rel_info_type = rel['r_info_type']
rel_addr = rel['r_offset']
if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset:
sym = dynsym.get_symbol(rel['r_info_sym'])
sym_value = sym['st_value']
init_array.append(load_base + sym_value)
# print ("find init array for :%s %x" % (filename, sym_value))
break
for rel_section in rel_sections:
for rel in rel_section.iter_relocations():
rel_info_type = rel['r_info_type']
rel_addr = rel['r_offset']
if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset:
sym = dynsym.get_symbol(rel['r_info_sym'])
sym_value = sym['st_value']
init_array.append(load_base + sym_value)
# print ("find init array for :%s %x" % (filename, sym_value))
break
break
init_array_offset += 4

# Resolve all symbols.
symbols_resolved = dict()

# for section in elf.iter_sections():
# if not isinstance(section, SymbolTableSection):
# continue
if dynsym:
itersymbols = dynsym.iter_symbols()
next(itersymbols) # Skip first symbol which is always NULL.
Expand All @@ -292,13 +376,23 @@ def load_module(self, filename):
if symbol_address is not None:
# TODO: Maybe we need to do something with uname symbols?
symbols_resolved[symbol.name] = SymbolResolved(symbol_address, symbol)

# only for debug and call local function by symbol name directly, not by address.
for section in elf.iter_sections():
if not isinstance(section, SymbolTableSection):
continue
for symbol in itersymbols:
symbol_address = self._elf_get_symval(elf, load_base, symbol)
if symbol_address is not None and symbol.name not in symbols_resolved:
symbols_resolved[symbol.name] = SymbolResolved(symbol_address, symbol)

# Relocate.
# for section in elf.iter_sections():
# if not isinstance(section, RelocationSection):
# continue
if rel_section:
for rel in rel_section.iter_relocations():
processed_relocs = set() # Keep track of processed relocations to avoid double processing.

# process relocation in DT_DYNAMIC first
for section in rel_sections:
processed_relocs.add(section.header.sh_addr)
for rel in section.iter_relocations():
sym = dynsym.get_symbol(rel['r_info_sym'])
sym_value = sym['st_value']

Expand Down Expand Up @@ -341,8 +435,56 @@ def load_module(self, filename):
else:
logger.error("Unhandled relocation type %i." % rel_info_type)

# then process relocation in Section Header(in fact, it's not necessary most of the time)
for section in elf.iter_sections():
if isinstance(section, RelocationSection):
if section.header.sh_addr in processed_relocs:
continue
for rel in section.iter_relocations():
sym = dynsym.get_symbol(rel['r_info_sym'])
sym_value = sym['st_value']

rel_addr = load_base + rel['r_offset'] # Location where relocation should happen
rel_info_type = rel['r_info_type']

# https://static.docs.arm.com/ihi0044/e/IHI0044E_aaelf.pdf
# Relocation table for ARM
if rel_info_type == arm.R_ARM_ABS32:
# Read value.
offset = int.from_bytes(self.emu.uc.mem_read(rel_addr, 4), byteorder='little')
# Create the new value.
value = load_base + sym_value + offset
# Check thumb.
if sym['st_info']['type'] == 'STT_FUNC':
value = value | 1
# Write the new value
self.emu.uc.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
elif rel_info_type == arm.R_ARM_GLOB_DAT or \
rel_info_type == arm.R_ARM_JUMP_SLOT:
# Resolve the symbol.
if sym.name in symbols_resolved:
value = symbols_resolved[sym.name].address

# Write the new value
self.emu.uc.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
elif rel_info_type == arm.R_ARM_RELATIVE:
if sym_value == 0:
# Load address at which it was linked originally.
value_orig_bytes = self.emu.uc.mem_read(rel_addr, 4)
value_orig = int.from_bytes(value_orig_bytes, byteorder='little')

# Create the new value
value = load_base + value_orig

# Write the new value
self.emu.uc.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
else:
raise NotImplementedError()
else:
logger.error("Unhandled relocation type %i." % rel_info_type)

# Store information about loaded module.
module = Module(filename, load_base, bound_high - bound_low, symbols_resolved, init_array)
module = Module(filename, load_base, bound_high - bound_low, symbols_resolved, init_array, init)
self.modules.append(module)

return module
Expand Down

0 comments on commit 67ba6af

Please sign in to comment.