summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Bertalan <dani@danielbertalan.dev>2023-04-22 12:09:00 +0200
committerAndreas Kling <kling@serenityos.org>2023-05-14 13:47:53 +0200
commite2b1f9447cd2fc32670568a7cf3f539e33b81e7a (patch)
tree68203b9b3f97901e3be892063bf5a7532572bdaa
parentcd45c2d295f638adc73fd0730b7e83c9351449a8 (diff)
downloadserenity-e2b1f9447cd2fc32670568a7cf3f539e33b81e7a.zip
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.
-rw-r--r--Userland/Libraries/LibELF/DynamicLoader.cpp51
-rw-r--r--Userland/Libraries/LibELF/DynamicLoader.h17
2 files changed, 53 insertions, 15 deletions
diff --git a/Userland/Libraries/LibELF/DynamicLoader.cpp b/Userland/Libraries/LibELF/DynamicLoader.cpp
index 279b0cf5d8..718a9406c2 100644
--- a/Userland/Libraries/LibELF/DynamicLoader.cpp
+++ b/Userland/Libraries/LibELF/DynamicLoader.cpp
@@ -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();
diff --git a/Userland/Libraries/LibELF/DynamicLoader.h b/Userland/Libraries/LibELF/DynamicLoader.h
index 47b7e303ea..cce345c1d7 100644
--- a/Userland/Libraries/LibELF/DynamicLoader.h
+++ b/Userland/Libraries/LibELF/DynamicLoader.h
@@ -40,6 +40,11 @@ enum class ShouldInitializeWeak {
No
};
+enum class ShouldCallIfuncResolver {
+ Yes,
+ No
+};
+
extern "C" FlatPtr _fixup_plt_entry(DynamicObject* object, u32 relocation_offset);
class DynamicLoader : public RefCounted<DynamicLoader> {
@@ -115,8 +120,6 @@ private:
ElfW(Phdr) m_program_header; // Explicitly a copy of the PHDR in the image
};
- friend FlatPtr _fixup_plt_entry(DynamicObject*, u32);
-
// Stage 1
void load_program_headers();
@@ -132,13 +135,17 @@ private:
bool validate();
+ friend FlatPtr _fixup_plt_entry(DynamicObject*, u32);
+
enum class RelocationResult : uint8_t {
Failed = 0,
Success = 1,
ResolveLater = 2,
+ CallIfuncResolver = 3,
};
- RelocationResult do_direct_relocation(DynamicObject::Relocation const&, ShouldInitializeWeak);
- static RelocationResult do_plt_relocation(DynamicObject::Relocation const&);
+ RelocationResult do_direct_relocation(DynamicObject::Relocation const&, ShouldInitializeWeak, ShouldCallIfuncResolver);
+ // Will be called from _fixup_plt_entry, as part of the PLT trampoline
+ static RelocationResult do_plt_relocation(DynamicObject::Relocation const&, ShouldCallIfuncResolver);
void do_relr_relocations();
void find_tls_size_and_alignment();
@@ -164,6 +171,8 @@ private:
size_t m_tls_alignment_of_current_object { 0 };
Vector<DynamicObject::Relocation> m_unresolved_relocations;
+ Vector<DynamicObject::Relocation> m_direct_ifunc_relocations;
+ Vector<DynamicObject::Relocation> m_plt_ifunc_relocations;
mutable RefPtr<DynamicObject> m_cached_dynamic_object;