Skip to content

Commit 417c81f

Browse files
author
WangONC
committed
Improve dynamic relocation table parsing and fix related issues
1 parent 83b2f95 commit 417c81f

File tree

1 file changed

+197
-55
lines changed

1 file changed

+197
-55
lines changed

src/androidemu/internal/modules.py

+197-55
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,29 @@ def calculate_sh_offset(elf, vaddr):
6767
if p_vaddr <= vaddr < (p_vaddr + p_filesz):
6868
return p_offset + (vaddr - p_vaddr)
6969
raise Exception(f"Cannot find segment containing address {vaddr:#x}")
70+
71+
@staticmethod
72+
def create_reloc_section(elf,name, is_rela, addr, size, entsize, sym_idx):
73+
if not addr or not size:
74+
return None
75+
if elf.elfclass == 32:
76+
entsize = entsize or (12 if is_rela else 8)
77+
else: # 64 bit
78+
entsize = entsize or (24 if is_rela else 16)
79+
fake_rel_header = Container(
80+
sh_name=0,
81+
sh_type='SHT_RELA' if is_rela else 'SHT_REL',
82+
sh_flags=SH_FLAGS.SHF_ALLOC,
83+
sh_addr=addr,
84+
sh_offset=Modules.calculate_sh_offset(elf, addr),
85+
sh_size=size,
86+
sh_link=sym_idx,
87+
sh_info=0,
88+
sh_addralign=8 if elf.elfclass == 64 else 4,
89+
sh_entsize=entsize
90+
91+
)
92+
return RelocationSection(fake_rel_header, name, elf)
7093

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

117-
rel_section = None
118-
for section in elf.iter_sections():
119-
if not isinstance(section, RelocationSection):
120-
continue
121-
rel_section = section
122-
break
140+
rel_sections = []
123141

124142
# Parse section header (Linking view).
125143
dynsym = elf.get_section_by_name(".dynsym")
126144
dynstr = elf.get_section_by_name(".dynstr")
127145

128-
# Find rel section if not found.
129-
if rel_section is None or dynsym is None or dynstr is None:
146+
# Find relocation table and symbol table by dynamic segment
147+
if dynsym is None or dynstr is None:
130148
rel_info = {
131149
'rel': {'addr': None, 'size': None, 'entsize': None, 'count': None},
132150
'rela': {'addr': None, 'size': None, 'entsize': None, 'count': None},
151+
'jmprel': {'addr': None, 'size': None, 'entsize': None},
152+
'android_rela': {'addr': None, 'size': None, 'entsize': None},
153+
'relr': {'addr': None, 'size': None, 'entsize': None},
154+
'pltrel': None, # DT_PLTREL
155+
'textrel': False, # DT_TEXTREL
133156
'sym': None,
134-
'type': None
135157
}
136158

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

177-
if rel_section is None:
178-
if rel_info['rel']['addr'] and rel_info['rel']['size']:
179-
rel_info['type'] = 'REL'
180-
active_rel = rel_info['rel']
181-
has_reloc_info = True
182-
elif rel_info['rela']['addr'] and rel_info['rela']['size']:
183-
rel_info['type'] = 'RELA'
184-
active_rel = rel_info['rela']
185-
has_reloc_info = True
186-
else:
187-
has_reloc_info = False
188-
189-
if has_reloc_info and active_rel['addr'] and active_rel['size'] and active_rel['entsize']:
190-
is_rela = rel_info['type'] == 'RELA'
191-
fake_rel_header = Container(
192-
sh_name=0, # we don't know the name
193-
sh_type='SHT_RELA' if is_rela else 'SHT_REL',
194-
sh_flags=SH_FLAGS.SHF_ALLOC,
195-
sh_addr=active_rel['addr'],
196-
sh_offset=self.calculate_sh_offset(elf, active_rel['addr']),
197-
sh_size=active_rel['size'],
198-
sh_link=rel_info['sym'], # link to dynsym
199-
sh_info = 0,
200-
sh_addralign=8 if elf.elfclass == 64 else 4,
201-
sh_entsize=active_rel['entsize']
202-
)
203-
rel_section = RelocationSection(fake_rel_header,
204-
'.rela.dyn' if is_rela else '.rel.dyn',
205-
elf)
199+
# other Relocation information
200+
elif tag.entry.d_tag == 'DT_TEXTREL':
201+
rel_info['textrel'] = True
202+
elif tag.entry.d_tag == 'DT_PLTREL':
203+
rel_info['pltrel'] = 'RELA' if tag.entry.d_val == 7 else 'REL'
204+
elif tag.entry.d_tag == 'DT_JMPREL':
205+
rel_info['jmprel']['addr'] = tag.entry.d_val
206+
elif tag.entry.d_tag == 'DT_PLTRELSZ':
207+
rel_info['jmprel']['size'] = tag.entry.d_val
208+
elif tag.entry.d_tag == 'DT_ANDROID_RELA':
209+
rel_info['android_rela']['addr'] = tag.entry.d_val
210+
elif tag.entry.d_tag == 'DT_ANDROID_RELASZ':
211+
rel_info['android_rela']['size'] = tag.entry.d_val
212+
elif tag.entry.d_tag == 'DT_ANDROID_RELR':
213+
rel_info['relr']['addr'] = tag.entry.d_val
214+
elif tag.entry.d_tag == 'DT_ANDROID_RELRSZ':
215+
rel_info['relr']['size'] = tag.entry.d_val
216+
217+
if rel_info['rel']['addr'] and rel_info['rel']['size']:
218+
rel_info['type'] = 'REL'
219+
active_rel = rel_info['rel']
220+
has_reloc_info = True
221+
elif rel_info['rela']['addr'] and rel_info['rela']['size']:
222+
rel_info['type'] = 'RELA'
223+
active_rel = rel_info['rela']
224+
has_reloc_info = True
225+
else:
226+
has_reloc_info = False
227+
228+
if has_reloc_info and active_rel['addr'] and active_rel['size'] and active_rel['entsize']:
229+
is_rela = rel_info['type'] == 'RELA'
230+
fake_rel_header = Container(
231+
sh_name=0, # we don't know the name,but it's not important
232+
sh_type='SHT_RELA' if is_rela else 'SHT_REL',
233+
sh_flags=SH_FLAGS.SHF_ALLOC,
234+
sh_addr=active_rel['addr'],
235+
sh_offset=self.calculate_sh_offset(elf, active_rel['addr']),
236+
sh_size=active_rel['size'],
237+
sh_link=rel_info['sym'], # link to dynsym
238+
sh_info = 0,
239+
sh_addralign=8 if elf.elfclass == 64 else 4,
240+
sh_entsize=active_rel['entsize']
241+
)
242+
rel_sections.append(RelocationSection(fake_rel_header,
243+
'.rela.dyn' if is_rela else '.rel.dyn',
244+
elf))
206245

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

283+
# create all fake relocation section
284+
if rel_info['rel']['addr']:
285+
rel = self.create_reloc_section(elf,'.rel.dyn', False,
286+
rel_info['rel']['addr'],
287+
rel_info['rel']['size'],
288+
rel_info['rel']['entsize'],
289+
rel_info['sym'])
290+
if rel:
291+
rel_sections.append(rel)
292+
293+
if rel_info['rela']['addr']:
294+
rela = self.create_reloc_section(elf,'.rela.dyn', True,
295+
rel_info['rela']['addr'],
296+
rel_info['rela']['size'],
297+
rel_info['rela']['entsize'],
298+
rel_info['sym'])
299+
if rela:
300+
rel_sections.append(rela)
301+
302+
if rel_info['jmprel']['addr']:
303+
is_rela = rel_info['pltrel'] == 'RELA'
304+
jmprel = self.create_reloc_section(elf,'.rela.plt' if is_rela else '.rel.plt',
305+
is_rela,
306+
rel_info['jmprel']['addr'],
307+
rel_info['jmprel']['size'],
308+
rel_info['jmprel']['entsize'],
309+
rel_info['sym'])
310+
if jmprel:
311+
rel_sections.append(jmprel)
312+
313+
if rel_info['android_rela']['addr']:
314+
android_rela = self.create_reloc_section(elf,'.rela.android', True,
315+
rel_info['android_rela']['addr'],
316+
rel_info['android_rela']['size'],
317+
rel_info['android_rela']['entsize'],
318+
rel_info['sym'])
319+
if android_rela:
320+
rel_sections.append(android_rela)
321+
244322
# Find init array.
245323
init_array_size = 0
246324
init_array_offset = 0
247325
init_array = []
326+
init = None
248327
for x in elf.iter_segments():
249328
if x.header.p_type == "PT_DYNAMIC":
250329
for tag in x.iter_tags():
251330
if tag.entry.d_tag == "DT_INIT_ARRAYSZ":
252331
init_array_size = tag.entry.d_val
253332
elif tag.entry.d_tag == "DT_INIT_ARRAY":
254333
init_array_offset = tag.entry.d_val
334+
elif tag.entry.d_tag == "DT_INIT":
335+
init = tag.entry.d_val
336+
337+
# DT_INIT should be called before DT_INIT_ARRAY if both are present
338+
if init:
339+
init = load_base + init
255340

256341
for _ in range(int(init_array_size / 4)):
257342
# covert va to file offset
@@ -267,23 +352,22 @@ def load_module(self, filename):
267352
# print ("find init array for :%s %x" % (filename, fun_ptr))
268353
else:
269354
# search in reloc
270-
for rel in rel_section.iter_relocations():
271-
rel_info_type = rel['r_info_type']
272-
rel_addr = rel['r_offset']
273-
if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset:
274-
sym = dynsym.get_symbol(rel['r_info_sym'])
275-
sym_value = sym['st_value']
276-
init_array.append(load_base + sym_value)
277-
# print ("find init array for :%s %x" % (filename, sym_value))
278-
break
355+
for rel_section in rel_sections:
356+
for rel in rel_section.iter_relocations():
357+
rel_info_type = rel['r_info_type']
358+
rel_addr = rel['r_offset']
359+
if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset:
360+
sym = dynsym.get_symbol(rel['r_info_sym'])
361+
sym_value = sym['st_value']
362+
init_array.append(load_base + sym_value)
363+
# print ("find init array for :%s %x" % (filename, sym_value))
364+
break
365+
break
279366
init_array_offset += 4
280367

281368
# Resolve all symbols.
282369
symbols_resolved = dict()
283370

284-
# for section in elf.iter_sections():
285-
# if not isinstance(section, SymbolTableSection):
286-
# continue
287371
if dynsym:
288372
itersymbols = dynsym.iter_symbols()
289373
next(itersymbols) # Skip first symbol which is always NULL.
@@ -292,13 +376,23 @@ def load_module(self, filename):
292376
if symbol_address is not None:
293377
# TODO: Maybe we need to do something with uname symbols?
294378
symbols_resolved[symbol.name] = SymbolResolved(symbol_address, symbol)
379+
380+
# only for debug and call local function by symbol name directly, not by address.
381+
for section in elf.iter_sections():
382+
if not isinstance(section, SymbolTableSection):
383+
continue
384+
for symbol in itersymbols:
385+
symbol_address = self._elf_get_symval(elf, load_base, symbol)
386+
if symbol_address is not None and symbol.name not in symbols_resolved:
387+
symbols_resolved[symbol.name] = SymbolResolved(symbol_address, symbol)
295388

296389
# Relocate.
297-
# for section in elf.iter_sections():
298-
# if not isinstance(section, RelocationSection):
299-
# continue
300-
if rel_section:
301-
for rel in rel_section.iter_relocations():
390+
processed_relocs = set() # Keep track of processed relocations to avoid double processing.
391+
392+
# process relocation in DT_DYNAMIC first
393+
for section in rel_sections:
394+
processed_relocs.add(section.header.sh_addr)
395+
for rel in section.iter_relocations():
302396
sym = dynsym.get_symbol(rel['r_info_sym'])
303397
sym_value = sym['st_value']
304398

@@ -341,6 +435,54 @@ def load_module(self, filename):
341435
else:
342436
logger.error("Unhandled relocation type %i." % rel_info_type)
343437

438+
# then process relocation in Section Header(in fact, it's not necessary most of the time)
439+
for section in elf.iter_sections():
440+
if isinstance(section, RelocationSection):
441+
if section.header.sh_addr in processed_relocs:
442+
continue
443+
for rel in section.iter_relocations():
444+
sym = dynsym.get_symbol(rel['r_info_sym'])
445+
sym_value = sym['st_value']
446+
447+
rel_addr = load_base + rel['r_offset'] # Location where relocation should happen
448+
rel_info_type = rel['r_info_type']
449+
450+
# https://static.docs.arm.com/ihi0044/e/IHI0044E_aaelf.pdf
451+
# Relocation table for ARM
452+
if rel_info_type == arm.R_ARM_ABS32:
453+
# Read value.
454+
offset = int.from_bytes(self.emu.uc.mem_read(rel_addr, 4), byteorder='little')
455+
# Create the new value.
456+
value = load_base + sym_value + offset
457+
# Check thumb.
458+
if sym['st_info']['type'] == 'STT_FUNC':
459+
value = value | 1
460+
# Write the new value
461+
self.emu.uc.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
462+
elif rel_info_type == arm.R_ARM_GLOB_DAT or \
463+
rel_info_type == arm.R_ARM_JUMP_SLOT:
464+
# Resolve the symbol.
465+
if sym.name in symbols_resolved:
466+
value = symbols_resolved[sym.name].address
467+
468+
# Write the new value
469+
self.emu.uc.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
470+
elif rel_info_type == arm.R_ARM_RELATIVE:
471+
if sym_value == 0:
472+
# Load address at which it was linked originally.
473+
value_orig_bytes = self.emu.uc.mem_read(rel_addr, 4)
474+
value_orig = int.from_bytes(value_orig_bytes, byteorder='little')
475+
476+
# Create the new value
477+
value = load_base + value_orig
478+
479+
# Write the new value
480+
self.emu.uc.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
481+
else:
482+
raise NotImplementedError()
483+
else:
484+
logger.error("Unhandled relocation type %i." % rel_info_type)
485+
344486
# Store information about loaded module.
345487
module = Module(filename, load_base, bound_high - bound_low, symbols_resolved, init_array)
346488
self.modules.append(module)

0 commit comments

Comments
 (0)