enumclass RelocMode { // Fast path for JUMP_SLOT relocations.
JumpTable, // Fast path for typical relocations: ABSOLUTE, GLOB_DAT, or RELATIVE.
Typical, // Handle all relocation types, relocations in text sections, and statistics/tracing.
General,
};
if (r_sym != 0) {
sym_name = relocator.get_string(relocator.si_symtab[r_sym].st_name);
}
// While relocating a DSO that either has text relocations (obsolete and 32-bit only) or is loaded // in 16KiB compat mode, the .text segment is writable (but not executable). To call an ifunc, // temporarily remap the segment as executable (but not writable). Then switch it back to continue // applying relocations in the segment. #ifdefined(__LP64__) constbool handle_text_protection = relocator.si->should_use_16kib_app_compat(); auto protect_segments = [&]() { return relocator.si->protect_16kib_app_compat_code();
}; auto unprotect_segments = [&]() { return relocator.si->unprotect_16kib_app_compat_code();
}; #else constbool handle_text_protection = IsGeneral && relocator.si->has_text_relocations; auto protect_segments = [&]() { // Make .text executable. return phdr_table_protect_segments(relocator.si->phdr, relocator.si->phnum,
relocator.si->load_bias, relocator.si->should_pad_segments(),
relocator.si->should_use_16kib_app_compat()) == 0;
}; auto unprotect_segments = [&]() { // Make .text writable. return phdr_table_unprotect_segments(relocator.si->phdr, relocator.si->phnum,
relocator.si->load_bias, relocator.si->should_pad_segments(),
relocator.si->should_use_16kib_app_compat()) == 0;
}; #endif
// Skip symbol lookup for R_GENERIC_NONE relocations. if (__predict_false(r_type == R_GENERIC_NONE)) {
LD_DEBUG(reloc && IsGeneral, "RELO NONE"); returntrue;
}
if (!IsGeneral && __predict_false(is_tls_reloc(r_type))) { // Always process TLS relocations using the slow code path, so that STB_LOCAL symbols are // diagnosed, and ifunc processing is skipped. return process_relocation_general(relocator, reloc);
}
if (IsGeneral && is_tls_reloc(r_type)) { if (r_sym == 0) { // By convention in ld.bfd and lld, an omitted symbol on a TLS relocation // is a reference to the current module.
found_in = relocator.si;
} elseif (ELF_ST_BIND(relocator.si_symtab[r_sym].st_info) == STB_LOCAL) { // In certain situations, the Gold linker accesses a TLS symbol using a // relocation to an STB_LOCAL symbol in .dynsym of either STT_SECTION or // STT_TLS type. Bionic doesn't support these relocations, so issue an // error. References: // - https://groups.google.com/d/topic/generic-abi/dJ4_Y78aQ2M/discussion // - https://sourceware.org/bugzilla/show_bug.cgi?id=17699
sym = &relocator.si_symtab[r_sym]; auto sym_type = ELF_ST_TYPE(sym->st_info); if (sym_type == STT_SECTION) {
DL_ERR("unexpected TLS reference to local section in \"%s\": sym type %d, rel type %u",
relocator.si->get_realpath(), sym_type, r_type);
} else {
DL_ERR( "unexpected TLS reference to local symbol \"%s\" in \"%s\": sym type %d, rel type %u",
sym_name, relocator.si->get_realpath(), sym_type, r_type);
} returnfalse;
} elseif (!lookup_symbol<IsGeneral>(relocator, r_sym, sym_name, &found_in, &sym)) { returnfalse;
} if (found_in != nullptr && found_in->get_tls() == nullptr) { // sym_name can be nullptr if r_sym is 0. A linker should never output an ELF file like this.
DL_ERR("TLS relocation refers to symbol \"%s\" in solib \"%s\" with no TLS segment",
sym_name, found_in->get_realpath()); returnfalse;
} if (sym != nullptr) { if (ELF_ST_TYPE(sym->st_info) != STT_TLS) { // A toolchain should never output a relocation like this.
DL_ERR("reference to non-TLS symbol \"%s\" from TLS relocation in \"%s\"",
sym_name, relocator.si->get_realpath()); returnfalse;
}
sym_addr = sym->st_value;
}
} else { if (r_sym == 0) { // Do nothing.
} else { if (!lookup_symbol<IsGeneral>(relocator, r_sym, sym_name, &found_in, &sym)) returnfalse; if (sym != nullptr) { constbool should_protect_segments = handle_text_protection && found_in == relocator.si &&
ELF_ST_TYPE(sym->st_info) == STT_GNU_IFUNC; if (should_protect_segments && !protect_segments()) returnfalse;
sym_addr = found_in->resolve_symbol_address(sym); if (should_protect_segments && !unprotect_segments()) returnfalse;
} elseif constexpr (IsGeneral) { // A weak reference to an undefined symbol. We typically use a zero symbol address, but // use the relocation base for PC-relative relocations, so that the value written is zero. switch (r_type) { #ifdefined(__x86_64__) case R_X86_64_PC32:
sym_addr = reinterpret_cast<ElfW(Addr)>(rel_target); break; #elifdefined(__i386__) case R_386_PC32:
sym_addr = reinterpret_cast<ElfW(Addr)>(rel_target); break; #endif
}
}
}
}
if constexpr (IsGeneral || Mode == RelocMode::Typical) { // Almost all dynamic relocations are of one of these types, and most will be // R_GENERIC_ABSOLUTE. The platform typically uses RELR instead, but R_GENERIC_RELATIVE is // common in non-platform binaries. if (r_type == R_GENERIC_ABSOLUTE) {
count_relocation_if<IsGeneral>(kRelocAbsolute); if (found_in) sym_addr = found_in->apply_memtag_if_mte_globals(sym_addr); const ElfW(Addr) result = sym_addr + get_addend_rel();
LD_DEBUG(reloc && IsGeneral, "RELO ABSOLUTE %16p <- %16p %s",
rel_target, reinterpret_cast<void*>(result), sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = result; returntrue;
} elseif (r_type == R_GENERIC_GLOB_DAT) { // The i386 psABI specifies that R_386_GLOB_DAT doesn't have an addend. The ARM ELF ABI // document (IHI0044F) specifies that R_ARM_GLOB_DAT has an addend, but Bionic isn't adding // it.
count_relocation_if<IsGeneral>(kRelocAbsolute); if (found_in) sym_addr = found_in->apply_memtag_if_mte_globals(sym_addr); const ElfW(Addr) result = sym_addr + get_addend_norel();
LD_DEBUG(reloc && IsGeneral, "RELO GLOB_DAT %16p <- %16p %s",
rel_target, reinterpret_cast<void*>(result), sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = result; returntrue;
} elseif (r_type == R_GENERIC_RELATIVE) { // In practice, r_sym is always zero, but if it weren't, the linker would still look up the // referenced symbol (and abort if the symbol isn't found), even though it isn't used.
count_relocation_if<IsGeneral>(kRelocRelative);
ElfW(Addr) result = relocator.si->load_bias + get_addend_rel(); // MTE globals reuses the place bits for additional tag-derivation metadata for // R_AARCH64_RELATIVE relocations, which makes it incompatible with // `-Wl,--apply-dynamic-relocs`. This is enforced by lld, however there's nothing stopping // Android binaries (particularly prebuilts) from building with this linker flag if they're // not built with MTE globals. Thus, don't use the new relocation semantics if this DSO // doesn't have MTE globals. if (relocator.si->should_tag_memtag_globals()) {
int64_t* place = static_cast<int64_t*>(rel_target);
int64_t offset = *place;
result = relocator.si->apply_memtag_if_mte_globals(result + offset) - offset;
}
LD_DEBUG(reloc && IsGeneral, "RELO RELATIVE %16p <- %16p",
rel_target, reinterpret_cast<void*>(result));
*static_cast<ElfW(Addr)*>(rel_target) = result; returntrue;
}
}
if constexpr (!IsGeneral) { // Almost all relocations are handled above. Handle the remaining relocations below, in a // separate function call. The symbol lookup will be repeated, but the result should be served // from the 1-symbol lookup cache. return process_relocation_general(relocator, reloc);
}
switch (r_type) { case R_GENERIC_IRELATIVE: // In the linker, ifuncs are called as soon as possible so that string functions work. We must // not call them again. (e.g. On arm32, resolving an ifunc changes the meaning of the addend // from a resolver function to the implementation.) if (!relocator.si->is_linker()) {
count_relocation_if<IsGeneral>(kRelocRelative); const ElfW(Addr) ifunc_addr = relocator.si->load_bias + get_addend_rel();
LD_DEBUG(reloc && IsGeneral, "RELO IRELATIVE %16p <- %16p",
rel_target, reinterpret_cast<void*>(ifunc_addr)); if (handle_text_protection && !protect_segments()) {
DL_ERR("can't protect segments for \"%s\": %m", relocator.si->get_realpath()); returnfalse;
} const ElfW(Addr) result = call_ifunc_resolver(ifunc_addr); if (handle_text_protection && !unprotect_segments()) {
DL_ERR("can't unprotect segments for \"%s\": %m", relocator.si->get_realpath()); returnfalse;
}
*static_cast<ElfW(Addr)*>(rel_target) = result;
} break; case R_GENERIC_COPY: // Copy relocations allow read-only data or code in a non-PIE executable to access a // variable from a DSO. The executable reserves extra space in its .bss section, and the // linker copies the variable into the extra space. The executable then exports its copy // to interpose the copy in the DSO. // // Bionic only supports PIE executables, so copy relocations aren't supported. The ARM and // AArch64 ABI documents only allow them for ET_EXEC (non-PIE) objects. See IHI0056B and // IHI0044F.
DL_ERR("%s COPY relocations are not supported", relocator.si->get_realpath()); returnfalse; case R_GENERIC_TLS_TPREL:
count_relocation_if<IsGeneral>(kRelocRelative);
{
ElfW(Addr) tpoff = 0; if (found_in == nullptr) { // Unresolved weak relocation. Leave tpoff at 0 to resolve // &weak_tls_symbol to __get_tls().
} else {
CHECK(found_in->get_tls() != nullptr); // We rejected a missing TLS segment above. const TlsModule& mod = get_tls_module(found_in->get_tls()->module_id); if (mod.static_offset != SIZE_MAX) {
tpoff += mod.static_offset - relocator.tls_tp_base;
} else {
DL_ERR("TLS symbol \"%s\" in dlopened \"%s\" referenced from \"%s\" using IE access model",
sym_name, found_in->get_realpath(), relocator.si->get_realpath()); returnfalse;
}
}
tpoff += sym_addr + get_addend_rel();
LD_DEBUG(reloc && IsGeneral, "RELO TLS_TPREL %16p <- %16p %s",
rel_target, reinterpret_cast<void*>(tpoff), sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = tpoff;
} break; case R_GENERIC_TLS_DTPMOD:
count_relocation_if<IsGeneral>(kRelocRelative);
{
size_t module_id = 0; if (found_in == nullptr) { // Unresolved weak relocation. Evaluate the module ID to 0.
} else {
CHECK(found_in->get_tls() != nullptr); // We rejected a missing TLS segment above.
module_id = found_in->get_tls()->module_id;
CHECK(module_id != kTlsUninitializedModuleId);
}
LD_DEBUG(reloc && IsGeneral, "RELO TLS_DTPMOD %16p <- %zu %s",
rel_target, module_id, sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = module_id;
} break; case R_GENERIC_TLS_DTPREL:
count_relocation_if<IsGeneral>(kRelocRelative);
{ const ElfW(Addr) result = sym_addr + get_addend_rel() - TLS_DTV_OFFSET;
LD_DEBUG(reloc && IsGeneral, "RELO TLS_DTPREL %16p <- %16p %s",
rel_target, reinterpret_cast<void*>(result), sym_name);
*static_cast<ElfW(Addr)*>(rel_target) = result;
} break;
#ifdefined(__aarch64__) || defined(__riscv) // Bionic currently implements TLSDESC for arm64 and riscv64. This implementation should work // with other architectures, as long as the resolver functions are implemented. case R_GENERIC_TLSDESC:
count_relocation_if<IsGeneral>(kRelocRelative);
{
ElfW(Addr) addend = reloc.r_addend;
TlsDescriptor* desc = static_cast<TlsDescriptor*>(rel_target); if (found_in == nullptr) { // Unresolved weak relocation.
desc->func = tlsdesc_resolver_unresolved_weak;
desc->arg = addend;
LD_DEBUG(reloc && IsGeneral, "RELO TLSDESC %16p <- unresolved weak, addend 0x%zx %s",
rel_target, static_cast<size_t>(addend), sym_name);
} else {
CHECK(found_in->get_tls() != nullptr); // We rejected a missing TLS segment above.
size_t module_id = found_in->get_tls()->module_id; const TlsModule& mod = get_tls_module(module_id); if (mod.static_offset != SIZE_MAX) {
desc->func = tlsdesc_resolver_static;
desc->arg = mod.static_offset - relocator.tls_tp_base + sym_addr + addend;
LD_DEBUG(reloc && IsGeneral, "RELO TLSDESC %16p <- static (0x%zx - 0x%zx + 0x%zx + 0x%zx) %s",
rel_target, mod.static_offset, relocator.tls_tp_base, static_cast<size_t>(sym_addr), static_cast<size_t>(addend),
sym_name);
} else {
relocator.tlsdesc_args->push_back({
.generation = mod.first_generation,
.index.module_id = module_id,
.index.offset = sym_addr + addend,
}); // Defer the TLSDESC relocation until the address of the TlsDynamicResolverArg object // is finalized.
relocator.deferred_tlsdesc_relocs.push_back({
desc, relocator.tlsdesc_args->size() - 1
}); const TlsDynamicResolverArg& desc_arg = relocator.tlsdesc_args->back();
LD_DEBUG(reloc && IsGeneral, "RELO TLSDESC %16p <- dynamic (gen %zu, mod %zu, off %zu) %s",
rel_target, desc_arg.generation, desc_arg.index.module_id,
desc_arg.index.offset, sym_name);
}
}
} break; #endif// defined(__aarch64__) || defined(__riscv)
bool soinfo::relocate(const SymbolLookupList& lookup_list) { // For ldd, don't apply relocations because TLS segments are not registered. // We don't care whether ldd diagnoses unresolved symbols. if (g_is_ldd) { returntrue;
}
// The linker already applied its RELR relocations in an earlier pass, so // skip the RELR relocations for the linker. if (relr_ != nullptr && !is_linker()) {
LD_DEBUG(reloc, "[ relocating %s relr ]", get_realpath()); const ElfW(Relr)* begin = relr_; const ElfW(Relr)* end = relr_ + relr_count_; if (!relocate_relr(begin, end, load_bias, should_tag_memtag_globals())) { returnfalse;
}
}
// Once the tlsdesc_args_ vector's size is finalized, we can write the addresses of its elements // into the TLSDESC relocations. #ifdefined(__aarch64__) || defined(__riscv) // Bionic currently only implements TLSDESC for arm64 and riscv64. for (const std::pair<TlsDescriptor*, size_t>& pair : relocator.deferred_tlsdesc_relocs) {
TlsDescriptor* desc = pair.first;
desc->func = tlsdesc_resolver_dynamic;
desc->arg = reinterpret_cast<size_t>(&tlsdesc_args_[pair.second]);
} #endif// defined(__aarch64__) || defined(__riscv)
returntrue;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.