1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 17:37:37 +00:00

LibELF: Only call IFUNC resolvers after populating the PLT

As IFUNC resolvers may call arbitrary functions though the PLT, they can
only be called after the PLT has been populated. This is true of the
`[[gnu::target_clones]]` attribute, which makes a call to
`__cpu_indicator_init`, which is defined in `libgcc_s.so`, through the
PLT.

`do_plt_relocation` and `do_direct_relocation` are given a parameter
that controls whether IFUNCs are immediately resolved. In the first
pass, relocations pointing to IFUNCs are put on a worklist, while all
other relocations are performed. Only after non-IFUNC relocations are
done and the PLT is set up do we deal with these.
This commit is contained in:
Daniel Bertalan 2023-04-22 12:09:00 +02:00 committed by Andreas Kling
parent cd45c2d295
commit e2b1f9447c
2 changed files with 53 additions and 15 deletions

View file

@ -203,39 +203,47 @@ void DynamicLoader::do_main_relocations()
do_relr_relocations();
m_dynamic_object->relocation_section().for_each_relocation([&](DynamicObject::Relocation const& relocation) {
switch (do_direct_relocation(relocation, ShouldInitializeWeak::No)) {
switch (do_direct_relocation(relocation, ShouldInitializeWeak::No, ShouldCallIfuncResolver::No)) {
case RelocationResult::Failed:
dbgln("Loader.so: {} unresolved symbol '{}'", m_filepath, relocation.symbol().name());
VERIFY_NOT_REACHED();
case RelocationResult::ResolveLater:
m_unresolved_relocations.append(relocation);
break;
case RelocationResult::CallIfuncResolver:
m_direct_ifunc_relocations.append(relocation);
break;
case RelocationResult::Success:
break;
}
});
// If the object is position-independent, the pointer to the PLT trampoline needs to be relocated.
auto fixup_trampoline_pointer = [&](DynamicObject::Relocation const& relocation) {
VERIFY(relocation.type() == R_X86_64_JUMP_SLOT || relocation.type() == R_AARCH64_JUMP_SLOT);
if (image().is_dynamic())
*((FlatPtr*)relocation.address().as_ptr()) += m_dynamic_object->base_address().get();
};
// FIXME: Or LD_BIND_NOW is set?
if (m_dynamic_object->must_bind_now()) {
m_dynamic_object->plt_relocation_section().for_each_relocation([&](DynamicObject::Relocation const& relocation) {
RelocationResult result;
if (relocation.type() == R_X86_64_IRELATIVE || relocation.type() == R_AARCH64_IRELATIVE) {
result = do_direct_relocation(relocation, ShouldInitializeWeak::No);
} else {
result = do_plt_relocation(relocation);
m_direct_ifunc_relocations.append(relocation);
return;
}
switch (result) {
switch (do_plt_relocation(relocation, ShouldCallIfuncResolver::No)) {
case RelocationResult::Failed:
dbgln("Loader.so: {} unresolved symbol '{}'", m_filepath, relocation.symbol().name());
VERIFY_NOT_REACHED();
case RelocationResult::ResolveLater:
VERIFY_NOT_REACHED();
case RelocationResult::CallIfuncResolver:
m_plt_ifunc_relocations.append(relocation);
// Set up lazy binding, in case an IFUNC resolver calls another IFUNC that hasn't been resolved yet.
fixup_trampoline_pointer(relocation);
break;
case RelocationResult::Success:
break;
}
@ -243,7 +251,7 @@ void DynamicLoader::do_main_relocations()
} else {
m_dynamic_object->plt_relocation_section().for_each_relocation([&](DynamicObject::Relocation const& relocation) {
if (relocation.type() == R_X86_64_IRELATIVE || relocation.type() == R_AARCH64_IRELATIVE) {
do_direct_relocation(relocation, ShouldInitializeWeak::No);
m_direct_ifunc_relocations.append(relocation);
return;
}
fixup_trampoline_pointer(relocation);
@ -259,6 +267,18 @@ Result<NonnullRefPtr<DynamicObject>, DlErrorMessage> DynamicLoader::load_stage_3
setup_plt_trampoline();
}
// IFUNC resolvers can only be called after the PLT has been populated,
// as they may call arbitrary functions via the PLT.
for (auto const& relocation : m_plt_ifunc_relocations) {
auto result = do_plt_relocation(relocation, ShouldCallIfuncResolver::Yes);
VERIFY(result == RelocationResult::Success);
}
for (auto const& relocation : m_direct_ifunc_relocations) {
auto result = do_direct_relocation(relocation, ShouldInitializeWeak::No, ShouldCallIfuncResolver::Yes);
VERIFY(result == RelocationResult::Success);
}
if (m_dynamic_object->has_text_relocations()) {
// If we don't have textrels, .text has already been made executable by this point in load_stage_2.
for (auto& text_segment : m_text_segments) {
@ -295,7 +315,7 @@ void DynamicLoader::load_stage_4()
void DynamicLoader::do_lazy_relocations()
{
for (auto const& relocation : m_unresolved_relocations) {
if (auto res = do_direct_relocation(relocation, ShouldInitializeWeak::Yes); res != RelocationResult::Success) {
if (auto res = do_direct_relocation(relocation, ShouldInitializeWeak::Yes, ShouldCallIfuncResolver::Yes); res != RelocationResult::Success) {
dbgln("Loader.so: {} unresolved symbol '{}'", m_filepath, relocation.symbol().name());
VERIFY_NOT_REACHED();
}
@ -495,7 +515,7 @@ void DynamicLoader::load_program_headers()
}
}
DynamicLoader::RelocationResult DynamicLoader::do_direct_relocation(DynamicObject::Relocation const& relocation, ShouldInitializeWeak should_initialize_weak)
DynamicLoader::RelocationResult DynamicLoader::do_direct_relocation(DynamicObject::Relocation const& relocation, ShouldInitializeWeak should_initialize_weak, ShouldCallIfuncResolver should_call_ifunc_resolver)
{
FlatPtr* patch_ptr = nullptr;
if (is_dynamic())
@ -523,6 +543,9 @@ DynamicLoader::RelocationResult DynamicLoader::do_direct_relocation(DynamicObjec
dbgln("ERROR: symbol not found: {}.", symbol.name());
return RelocationResult::Failed;
}
if (res.value().type == STT_GNU_IFUNC && should_call_ifunc_resolver == ShouldCallIfuncResolver::No)
return RelocationResult::CallIfuncResolver;
auto symbol_address = res.value().address;
if (relocation.addend_used())
*patch_ptr = symbol_address.get() + relocation.addend();
@ -550,6 +573,8 @@ DynamicLoader::RelocationResult DynamicLoader::do_direct_relocation(DynamicObjec
} else {
symbol_location = res.value().address;
if (res.value().type == STT_GNU_IFUNC) {
if (should_call_ifunc_resolver == ShouldCallIfuncResolver::No)
return RelocationResult::CallIfuncResolver;
if (res.value().dynamic_object != nullptr && res.value().dynamic_object->has_text_relocations()) {
dbgln("\033[31mError:\033[0m Refusing to call IFUNC resolver defined in an object with text relocations.");
return RelocationResult::Failed;
@ -602,6 +627,8 @@ DynamicLoader::RelocationResult DynamicLoader::do_direct_relocation(DynamicObjec
}
case R_AARCH64_IRELATIVE:
case R_X86_64_IRELATIVE: {
if (should_call_ifunc_resolver == ShouldCallIfuncResolver::No)
return RelocationResult::CallIfuncResolver;
VirtualAddress resolver;
if (relocation.addend_used())
resolver = m_dynamic_object->base_address().offset(relocation.addend());
@ -627,7 +654,7 @@ DynamicLoader::RelocationResult DynamicLoader::do_direct_relocation(DynamicObjec
return RelocationResult::Success;
}
DynamicLoader::RelocationResult DynamicLoader::do_plt_relocation(DynamicObject::Relocation const& relocation)
DynamicLoader::RelocationResult DynamicLoader::do_plt_relocation(DynamicObject::Relocation const& relocation, ShouldCallIfuncResolver should_call_ifunc_resolver)
{
VERIFY(relocation.type() == R_X86_64_JUMP_SLOT || relocation.type() == R_AARCH64_JUMP_SLOT);
auto symbol = relocation.symbol();
@ -638,6 +665,8 @@ DynamicLoader::RelocationResult DynamicLoader::do_plt_relocation(DynamicObject::
auto address = result.value().address;
if (result.value().type == STT_GNU_IFUNC) {
if (should_call_ifunc_resolver == ShouldCallIfuncResolver::No)
return RelocationResult::CallIfuncResolver;
symbol_location = VirtualAddress { reinterpret_cast<DynamicObject::IfuncResolver>(address.get())() };
} else {
symbol_location = address;
@ -701,7 +730,7 @@ void DynamicLoader::setup_plt_trampoline()
extern "C" FlatPtr _fixup_plt_entry(DynamicObject* object, u32 relocation_offset)
{
auto const& relocation = object->plt_relocation_section().relocation_at_offset(relocation_offset);
auto result = DynamicLoader::do_plt_relocation(relocation);
auto result = DynamicLoader::do_plt_relocation(relocation, ShouldCallIfuncResolver::Yes);
if (result != DynamicLoader::RelocationResult::Success) {
dbgln("Loader.so: {} unresolved symbol '{}'", object->filepath(), relocation.symbol().name());
VERIFY_NOT_REACHED();