import logging
from cle.address_translator import AT
from cle.backends.symbol import SymbolType
from cle.errors import CLEInvalidBinaryError, CLEOperationError
from .elfreloc import ELFReloc
log = logging.getLogger(name=__name__)
__all__ = [
"GenericTLSDoffsetReloc",
"GenericTLSOffsetReloc",
"GenericTLSDescriptorReloc",
"GenericTLSModIdReloc",
"GenericIRelativeReloc",
"GenericAbsoluteAddendReloc",
"GenericPCRelativeAddendReloc",
"GenericJumpslotReloc",
"GenericRelativeReloc",
"GenericAbsoluteReloc",
"GenericCopyReloc",
"MipsGlobalReloc",
"MipsLocalReloc",
"RelocTruncate32Mixin",
"RelocGOTMixin",
]
[docs]
class GenericTLSDoffsetReloc(ELFReloc):
@property
def value(self):
return self.addend + self.symbol.relative_addr
[docs]
def resolve_symbol(self, solist, **kwargs): # pylint: disable=unused-argument
self.resolve(None)
return True
[docs]
class GenericTLSOffsetReloc(ELFReloc):
AUTO_HANDLE_NONE = True
[docs]
def relocate(self):
hell_offset = self.owner.arch.elf_tls.tp_offset
if self.resolvedby is None:
obj = self.owner
addr = 0
else:
obj = self.resolvedby.owner
addr = self.resolvedby.relative_addr
if obj.tls_block_offset is None:
raise CLEInvalidBinaryError("Illegal relocation - dynamically loaded object using static TLS")
self.owner.memory.pack_word(self.relative_addr, obj.tls_block_offset + self.addend + addr - hell_offset)
[docs]
class GenericTLSDescriptorReloc(ELFReloc):
# Going VERY far out on a limb here
# "TLS descriptors" are a thing I'm seeing in aarch64 binaries which seem to want to relocate by
# sticking a pointer to a resolver function followed by some arbitrary data. The resolver function
# is passed a pointer to the descriptor. My guess is the resolver is supposed to basically perform
# _tls_get_addr, but the intention is probably to make it possible to work with dynamically loaded objects.
RESOLVER_ADDR = NotImplemented
AUTO_HANDLE_NONE = True
[docs]
def relocate(self):
if self.resolvedby is None:
obj = self.owner
else:
obj = self.resolvedby.owner
if obj.tls_block_offset is None:
raise CLEInvalidBinaryError("Illegal relocation? - dynamically loaded object using static TLS? Maybe?")
self.owner.memory.pack_word(self.relative_addr, self.RESOLVER_ADDR)
self.owner.memory.pack_word(
self.relative_addr + self.arch.bytes, obj.tls_block_offset + self.addend + self.symbol.relative_addr
) # Should this include the hell offset?
[docs]
class GenericTLSModIdReloc(ELFReloc):
AUTO_HANDLE_NONE = True
[docs]
def relocate(self):
if self.symbol.type == SymbolType.TYPE_NONE:
obj = self.owner
else:
obj = self.resolvedby.owner
self.owner.memory.pack_word(self.relative_addr, obj.tls_module_id)
[docs]
class GenericIRelativeReloc(ELFReloc):
AUTO_HANDLE_NONE = True
[docs]
def relocate(self):
if self.symbol.type == SymbolType.TYPE_NONE:
self.owner.irelatives.append((AT.from_lva(self.addend, self.owner).to_mva(), self.relative_addr))
else:
self.owner.irelatives.append((self.resolvedby.rebased_addr, self.relative_addr))
[docs]
class GenericAbsoluteAddendReloc(ELFReloc):
@property
def value(self):
return self.resolvedby.rebased_addr + self.addend
[docs]
class GenericPCRelativeAddendReloc(ELFReloc):
@property
def value(self):
return self.resolvedby.rebased_addr + self.addend - self.rebased_addr
[docs]
class GenericJumpslotReloc(ELFReloc):
@property
def value(self):
if self.is_rela:
return self.resolvedby.rebased_addr + self.addend
else:
return self.resolvedby.rebased_addr
[docs]
class GenericRelativeReloc(ELFReloc):
AUTO_HANDLE_NONE = True
@property
def value(self):
if self.resolvedby is not None:
return self.resolvedby.rebased_addr
return self.owner.mapped_base + self.addend
[docs]
class GenericAbsoluteReloc(ELFReloc):
@property
def value(self):
return self.resolvedby.rebased_addr
[docs]
class GenericCopyReloc(ELFReloc):
[docs]
def resolve_symbol(self, solist, **kwargs):
new_solist = [x for x in solist if x is not self.owner]
super().resolve_symbol(new_solist, **kwargs)
[docs]
def relocate(self):
if self.resolvedby.size != self.symbol.size and (self.resolvedby.size != 0 or not self.resolvedby.is_extern):
log.error("Export symbol is different size than import symbol for copy relocation: %s", self.symbol.name)
else:
self.owner.memory.store(
self.relative_addr,
self.resolvedby.owner.memory.load(self.resolvedby.relative_addr, self.resolvedby.size),
)
return True
[docs]
class MipsGlobalReloc(GenericAbsoluteReloc):
pass
[docs]
class MipsLocalReloc(ELFReloc):
AUTO_HANDLE_NONE = True
[docs]
def resolve_symbol(self, solist, **kwargs):
self.resolve(None)
[docs]
def relocate(self):
if self.owner.mapped_base == 0:
return # don't touch local relocations on the main bin
delta = self.owner.mapped_base - self.owner._dynamic["DT_MIPS_BASE_ADDRESS"]
if delta == 0:
return
val = self.owner.memory.unpack_word(self.relative_addr)
newval = val + delta
self.owner.memory.pack_word(self.relative_addr, newval)
[docs]
class RelocTruncate32Mixin:
"""
A mix-in class for relocations that cover a 32-bit field regardless of the architecture's address word length.
"""
# If True, 32-bit truncated value must equal to its original when zero-extended
check_zero_extend = False
# If True, 32-bit truncated value must equal to its original when sign-extended
check_sign_extend = False
[docs]
def relocate(self):
arch_bits = self.owner.arch.bits
assert arch_bits >= 32 # 16-bit makes no sense here
val = self.value % (2**arch_bits) # we must truncate it to native range first
if (
self.check_zero_extend
and val >> 32 != 0
or self.check_sign_extend
and val >> 32 != ((1 << (arch_bits - 32)) - 1)
if ((val >> 31) & 1) == 1
else 0
):
raise CLEOperationError(
"relocation truncated to fit: %s; consider making"
" relevant addresses fit in the 32-bit address space." % self.__class__.__name__
)
self.owner.memory.pack_word(self.dest_addr, val, size=4, signed=False)
return True
[docs]
class RelocGOTMixin:
"""
A mix-in class which will cause the symbol to be resolved to a pointer to the symbol instead of the symbol
"""
[docs]
def resolve(self, symbol, extern_object=None, **kwargs):
assert extern_object is not None, "I have no idea how this would happen"
got_symbol = extern_object.make_extern("got.%s" % symbol.name, sym_type=SymbolType.TYPE_OBJECT, point_to=symbol)
super().resolve(got_symbol)