[compiler-rt] [llvm] [ORC] Fix missing support of deinitialize on ELF platform and execution order by priority (PR #175981)
Yaxing Cai via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 16 07:23:34 PST 2026
https://github.com/cyx-6 updated https://github.com/llvm/llvm-project/pull/175981
>From 3b09625b73e53171ae713dc1434630e5975fb9b5 Mon Sep 17 00:00:00 2001
From: Yaxing Cai <caiyaxing666 at gmail.com>
Date: Tue, 13 Jan 2026 20:14:09 +0800
Subject: [PATCH 1/5] fix
---
compiler-rt/lib/orc/elfnix_platform.cpp | 105 ++++++++++++++++
.../llvm/ExecutionEngine/Orc/ELFNixPlatform.h | 7 ++
.../Orc/Shared/ObjectFormats.h | 2 +
.../ExecutionEngine/Orc/ELFNixPlatform.cpp | 115 ++++++++++++++++++
.../Orc/Shared/ObjectFormats.cpp | 15 +++
5 files changed, 244 insertions(+)
diff --git a/compiler-rt/lib/orc/elfnix_platform.cpp b/compiler-rt/lib/orc/elfnix_platform.cpp
index 0ec628be8b3b3..008f844a5f3d4 100644
--- a/compiler-rt/lib/orc/elfnix_platform.cpp
+++ b/compiler-rt/lib/orc/elfnix_platform.cpp
@@ -73,6 +73,7 @@ class ELFNixPlatformRuntimeState {
AtExitsVector AtExits;
std::vector<PerJITDylibState *> Deps;
RecordSectionsTracker<void (*)()> RecordedInits;
+ RecordSectionsTracker<void (*)()> RecordedFinis;
bool referenced() const {
return LinkedAgainstRefCount != 0 || RefCount != 0;
@@ -100,6 +101,10 @@ class ELFNixPlatformRuntimeState {
std::vector<ExecutorAddrRange> Inits);
Error deregisterInits(ExecutorAddr HeaderAddr,
std::vector<ExecutorAddrRange> Inits);
+ Error registerFinis(ExecutorAddr HeaderAddr,
+ std::vector<ExecutorAddrRange> Finis);
+ Error deregisterFinis(ExecutorAddr HeaderAddr,
+ std::vector<ExecutorAddrRange> Finis);
Error deregisterObjectSections(ELFNixPerObjectSectionsToRegister POSR);
const char *dlerror();
@@ -130,6 +135,8 @@ class ELFNixPlatformRuntimeState {
Error runInits(std::unique_lock<std::recursive_mutex> &JDStatesLock,
PerJITDylibState &JDS);
+ Error runFinis(std::unique_lock<std::recursive_mutex> &JDStatesLock,
+ PerJITDylibState &JDS);
Expected<void *> dlopenImpl(std::string_view Path, int Mode);
Error dlopenFull(std::unique_lock<std::recursive_mutex> &JDStatesLock,
PerJITDylibState &JDS);
@@ -301,6 +308,46 @@ Error ELFNixPlatformRuntimeState::deregisterInits(
return Error::success();
}
+Error ELFNixPlatformRuntimeState::registerFinis(
+ ExecutorAddr HeaderAddr, std::vector<ExecutorAddrRange> Finis) {
+ std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex);
+ PerJITDylibState *JDS =
+ getJITDylibStateByHeaderAddr(HeaderAddr.toPtr<void *>());
+
+ if (!JDS) {
+ std::ostringstream ErrStream;
+ ErrStream << "Could not register fini sections for unrecognized header "
+ << HeaderAddr.toPtr<void *>();
+ return make_error<StringError>(ErrStream.str());
+ }
+
+ for (auto &F : Finis) {
+ JDS->RecordedFinis.add(F.toSpan<void (*)()>());
+ }
+
+ return Error::success();
+}
+
+Error ELFNixPlatformRuntimeState::deregisterFinis(
+ ExecutorAddr HeaderAddr, std::vector<ExecutorAddrRange> Finis) {
+ std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex);
+ PerJITDylibState *JDS =
+ getJITDylibStateByHeaderAddr(HeaderAddr.toPtr<void *>());
+
+ if (!JDS) {
+ std::ostringstream ErrStream;
+ ErrStream << "Could not deregister fini sections for unrecognized header "
+ << HeaderAddr.toPtr<void *>();
+ return make_error<StringError>(ErrStream.str());
+ }
+
+ for (auto &F : Finis) {
+ JDS->RecordedFinis.removeIfPresent(F);
+ }
+
+ return Error::success();
+}
+
const char *ELFNixPlatformRuntimeState::dlerror() { return DLFcnError.c_str(); }
void *ELFNixPlatformRuntimeState::dlopen(std::string_view Path, int Mode) {
@@ -451,6 +498,33 @@ Error ELFNixPlatformRuntimeState::runInits(
return Error::success();
}
+Error ELFNixPlatformRuntimeState::runFinis(
+ std::unique_lock<std::recursive_mutex> &JDStatesLock,
+ PerJITDylibState &JDS) {
+ std::vector<span<void (*)()>> FiniSections;
+
+ // Collect all fini sections (reset to move all to "new" for processing)
+ JDS.RecordedFinis.reset();
+ FiniSections.reserve(JDS.RecordedFinis.numNewSections());
+
+ JDS.RecordedFinis.processNewSections(
+ [&](span<void (*)()> Finis) { FiniSections.push_back(Finis); });
+
+ JDStatesLock.unlock();
+
+ // Run in REVERSE order (LIFO semantics)
+ for (size_t I = FiniSections.size(); I > 0; --I) {
+ auto &Sec = FiniSections[I - 1];
+ for (size_t J = Sec.size(); J > 0; --J) {
+ Sec[J - 1]();
+ }
+ }
+
+ JDStatesLock.lock();
+
+ return Error::success();
+}
+
Expected<void *> ELFNixPlatformRuntimeState::dlopenImpl(std::string_view Path,
int Mode) {
std::unique_lock<std::recursive_mutex> Lock(JDStatesMutex);
@@ -602,8 +676,13 @@ Error ELFNixPlatformRuntimeState::dlcloseImpl(void *DSOHandle) {
Error ELFNixPlatformRuntimeState::dlcloseInitialize(
std::unique_lock<std::recursive_mutex> &JDStatesLock,
PerJITDylibState &JDS) {
+ // Run fini sections BEFORE atexits (mirrors static dtor order)
+ if (auto Err = runFinis(JDStatesLock, JDS))
+ return Err;
+
runAtExits(JDStatesLock, JDS);
JDS.RecordedInits.reset();
+ JDS.RecordedFinis.reset();
for (auto *DepJDS : JDS.Deps)
if (!JDS.referenced())
if (auto Err = dlcloseInitialize(JDStatesLock, *DepJDS))
@@ -726,6 +805,32 @@ __orc_rt_elfnix_deregister_init_sections(char *ArgData, size_t ArgSize) {
.release();
}
+ORC_RT_INTERFACE orc_rt_WrapperFunctionResult
+__orc_rt_elfnix_register_fini_sections(char *ArgData, size_t ArgSize) {
+ return WrapperFunction<SPSError(SPSExecutorAddr,
+ SPSSequence<SPSExecutorAddrRange>)>::
+ handle(ArgData, ArgSize,
+ [](ExecutorAddr HeaderAddr,
+ std::vector<ExecutorAddrRange> &Finis) {
+ return ELFNixPlatformRuntimeState::get().registerFinis(
+ HeaderAddr, std::move(Finis));
+ })
+ .release();
+}
+
+ORC_RT_INTERFACE orc_rt_WrapperFunctionResult
+__orc_rt_elfnix_deregister_fini_sections(char *ArgData, size_t ArgSize) {
+ return WrapperFunction<SPSError(SPSExecutorAddr,
+ SPSSequence<SPSExecutorAddrRange>)>::
+ handle(ArgData, ArgSize,
+ [](ExecutorAddr HeaderAddr,
+ std::vector<ExecutorAddrRange> &Finis) {
+ return ELFNixPlatformRuntimeState::get().deregisterFinis(
+ HeaderAddr, std::move(Finis));
+ })
+ .release();
+}
+
/// Wrapper function for registering metadata on a per-object basis.
ORC_RT_INTERFACE orc_rt_WrapperFunctionResult
__orc_rt_elfnix_register_object_sections(char *ArgData, size_t ArgSize) {
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h b/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h
index 6872c1ec2d54f..1d1c51d40be69 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h
@@ -205,6 +205,9 @@ class LLVM_ABI ELFNixPlatform : public Platform {
Error registerInitSections(jitlink::LinkGraph &G, JITDylib &JD,
bool IsBootstrapping);
+ Error registerFiniSections(jitlink::LinkGraph &G, JITDylib &JD,
+ bool IsBootstrapping);
+
Error fixTLVSectionsAndEdges(jitlink::LinkGraph &G, JITDylib &JD);
std::mutex PluginMutex;
@@ -262,6 +265,10 @@ class LLVM_ABI ELFNixPlatform : public Platform {
ES.intern("__orc_rt_elfnix_register_init_sections")};
RuntimeFunction DeregisterInitSections{
ES.intern("__orc_rt_elfnix_deregister_init_sections")};
+ RuntimeFunction RegisterFiniSections{
+ ES.intern("__orc_rt_elfnix_register_fini_sections")};
+ RuntimeFunction DeregisterFiniSections{
+ ES.intern("__orc_rt_elfnix_deregister_fini_sections")};
RuntimeFunction CreatePThreadKey{
ES.intern("__orc_rt_elfnix_create_pthread_key")};
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h
index 01c44709a7cea..a5cbc1324c770 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h
@@ -31,11 +31,13 @@ LLVM_ABI extern StringRef ELFCtorArrayFuncSectionName;
LLVM_ABI extern StringRef ELFDtorArrayFuncSectionName;
LLVM_ABI extern StringRef ELFInitSectionNames[3];
+LLVM_ABI extern StringRef ELFFiniSectionNames[3];
LLVM_ABI extern StringRef ELFThreadBSSSectionName;
LLVM_ABI extern StringRef ELFThreadDataSectionName;
LLVM_ABI bool isELFInitializerSection(StringRef SecName);
+LLVM_ABI bool isELFFinalizerSection(StringRef SecName);
LLVM_ABI bool isCOFFInitializerSection(StringRef Name);
diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
index 0a761290373aa..d3936df5fa391 100644
--- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
@@ -407,6 +407,7 @@ ELFNixPlatform::ELFNixPlatform(
{PlatformBootstrap.Name, PlatformShutdown.Name,
RegisterJITDylib.Name, DeregisterJITDylib.Name,
RegisterInitSections.Name, DeregisterInitSections.Name,
+ RegisterFiniSections.Name, DeregisterFiniSections.Name,
RegisterObjectSections.Name,
DeregisterObjectSections.Name, CreatePThreadKey.Name}))
.takeError()))
@@ -646,6 +647,8 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::
{*MP.DeregisterObjectSections.Name, &MP.DeregisterObjectSections.Addr},
{*MP.RegisterInitSections.Name, &MP.RegisterInitSections.Addr},
{*MP.DeregisterInitSections.Name, &MP.DeregisterInitSections.Addr},
+ {*MP.RegisterFiniSections.Name, &MP.RegisterFiniSections.Addr},
+ {*MP.DeregisterFiniSections.Name, &MP.DeregisterFiniSections.Addr},
{*MP.CreatePThreadKey.Name, &MP.CreatePThreadKey.Addr}};
bool RegisterELFNixHeader = false;
@@ -774,6 +777,12 @@ void ELFNixPlatform::ELFNixPlatformPlugin::modifyPassConfig(
return registerInitSections(G, JD, InBootstrapPhase);
});
+ // If the object contains finalizers then add passes to record them.
+ Config.PostFixupPasses.push_back([this, &JD = MR.getTargetJITDylib(),
+ InBootstrapPhase](jitlink::LinkGraph &G) {
+ return registerFiniSections(G, JD, InBootstrapPhase);
+ });
+
// If we're in the bootstrap phase then steal allocation actions and then
// decrement the active graphs.
if (InBootstrapPhase)
@@ -897,6 +906,32 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::preserveInitSections(
InitSym->getBlock().addEdge(jitlink::Edge::KeepAlive, 0, S, 0);
}
}
+
+ // Also preserve fini sections (.fini_array, .fini, .dtors)
+ for (auto &FiniSection : G.sections()) {
+ // Skip non-fini sections.
+ if (!isELFFinalizerSection(FiniSection.getName()) ||
+ FiniSection.empty())
+ continue;
+
+ // Create the init symbol if it has not been created already and attach it
+ // to the first fini block.
+ if (!InitSym) {
+ auto &B = **FiniSection.blocks().begin();
+ InitSym = &G.addDefinedSymbol(
+ B, 0, *InitSymName, B.getSize(), jitlink::Linkage::Strong,
+ jitlink::Scope::SideEffectsOnly, false, true);
+ }
+
+ // Add keep-alive edges to anonymous symbols in all fini blocks.
+ for (auto *B : FiniSection.blocks()) {
+ if (B == &InitSym->getBlock())
+ continue;
+
+ auto &S = G.addAnonymousSymbol(*B, 0, B->getSize(), false, true);
+ InitSym->getBlock().addEdge(jitlink::Edge::KeepAlive, 0, S, 0);
+ }
+ }
}
return Error::success();
@@ -984,6 +1019,86 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::registerInitSections(
return Error::success();
}
+Error ELFNixPlatform::ELFNixPlatformPlugin::registerFiniSections(
+ jitlink::LinkGraph &G, JITDylib &JD, bool IsBootstrapping) {
+ SmallVector<ExecutorAddrRange> ELFNixFiniSecs;
+ LLVM_DEBUG(dbgs() << "ELFNixPlatform::registerFiniSections\n");
+
+ SmallVector<jitlink::Section *> OrderedFiniSections;
+ for (auto &Sec : G.sections())
+ if (isELFFinalizerSection(Sec.getName()))
+ OrderedFiniSections.push_back(&Sec);
+
+ // Sort by priority (same order as init - will be reversed at runtime)
+ llvm::sort(OrderedFiniSections, [](const jitlink::Section *LHS,
+ const jitlink::Section *RHS) {
+ if (LHS->getName().starts_with(".fini_array")) {
+ if (RHS->getName().starts_with(".fini_array")) {
+ StringRef LHSPrioStr(LHS->getName());
+ StringRef RHSPrioStr(RHS->getName());
+ uint64_t LHSPriority;
+ bool LHSHasPriority = LHSPrioStr.consume_front(".fini_array.") &&
+ !LHSPrioStr.getAsInteger(10, LHSPriority);
+ uint64_t RHSPriority;
+ bool RHSHasPriority = RHSPrioStr.consume_front(".fini_array.") &&
+ !RHSPrioStr.getAsInteger(10, RHSPriority);
+ if (LHSHasPriority)
+ return RHSHasPriority ? LHSPriority < RHSPriority : true;
+ else if (RHSHasPriority)
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return LHS->getName() < RHS->getName();
+ });
+
+ for (auto *Sec : OrderedFiniSections)
+ ELFNixFiniSecs.push_back(jitlink::SectionRange(*Sec).getRange());
+
+ if (ELFNixFiniSecs.empty())
+ return Error::success();
+
+ // Dump the scraped finis.
+ LLVM_DEBUG({
+ dbgs() << "ELFNixPlatform: Scraped " << G.getName() << " fini sections:\n";
+ for (auto *Sec : OrderedFiniSections) {
+ jitlink::SectionRange R(*Sec);
+ dbgs() << " " << Sec->getName() << ": " << R.getRange() << "\n";
+ }
+ });
+
+ ExecutorAddr HeaderAddr;
+ {
+ std::lock_guard<std::mutex> Lock(MP.PlatformMutex);
+ auto I = MP.JITDylibToHandleAddr.find(&JD);
+ assert(I != MP.JITDylibToHandleAddr.end() && "No header registered for JD");
+ assert(I->second && "Null header registered for JD");
+ HeaderAddr = I->second;
+ }
+
+ using SPSRegisterFiniSectionsArgs =
+ SPSArgList<SPSExecutorAddr, SPSSequence<SPSExecutorAddrRange>>;
+
+ if (LLVM_UNLIKELY(IsBootstrapping)) {
+ MP.Bootstrap.load()->addArgumentsToRTFnMap(
+ &MP.RegisterFiniSections, &MP.DeregisterFiniSections,
+ getArgDataBufferType<SPSRegisterFiniSectionsArgs>(HeaderAddr,
+ ELFNixFiniSecs),
+ getArgDataBufferType<SPSRegisterFiniSectionsArgs>(HeaderAddr,
+ ELFNixFiniSecs));
+ return Error::success();
+ }
+
+ G.allocActions().push_back(
+ {cantFail(WrapperFunctionCall::Create<SPSRegisterFiniSectionsArgs>(
+ MP.RegisterFiniSections.Addr, HeaderAddr, ELFNixFiniSecs)),
+ cantFail(WrapperFunctionCall::Create<SPSRegisterFiniSectionsArgs>(
+ MP.DeregisterFiniSections.Addr, HeaderAddr, ELFNixFiniSecs))});
+
+ return Error::success();
+}
+
Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges(
jitlink::LinkGraph &G, JITDylib &JD) {
auto TLSGetAddrSymbolName = G.intern("__tls_get_addr");
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/ObjectFormats.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/ObjectFormats.cpp
index d95f2f602fbcd..6346c48a90cee 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/ObjectFormats.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/ObjectFormats.cpp
@@ -31,6 +31,12 @@ StringRef ELFInitSectionNames[3]{
ELFCtorArrayFuncSectionName,
};
+StringRef ELFFiniSectionNames[3]{
+ ELFFiniArrayFuncSectionName,
+ ELFFiniFuncSectionName,
+ ELFDtorArrayFuncSectionName,
+};
+
StringRef ELFThreadBSSSectionName = ".tbss";
StringRef ELFThreadDataSectionName = ".tdata";
@@ -47,6 +53,15 @@ bool isELFInitializerSection(StringRef SecName) {
return false;
}
+bool isELFFinalizerSection(StringRef SecName) {
+ for (StringRef FiniSection : ELFFiniSectionNames) {
+ StringRef Name = SecName;
+ if (Name.consume_front(FiniSection) && (Name.empty() || Name[0] == '.'))
+ return true;
+ }
+ return false;
+}
+
bool isCOFFInitializerSection(StringRef SecName) {
return SecName.starts_with(".CRT");
}
>From 16adf1384cd0b9fd744b6fa795a2e807a80ea3df Mon Sep 17 00:00:00 2001
From: Yaxing Cai <caiyaxing666 at gmail.com>
Date: Wed, 14 Jan 2026 17:31:58 +0800
Subject: [PATCH 2/5] fixing order
---
compiler-rt/lib/orc/elfnix_platform.cpp | 12 +-
.../ExecutionEngine/Orc/ELFNixPlatform.cpp | 150 +++++++++++++-----
2 files changed, 112 insertions(+), 50 deletions(-)
diff --git a/compiler-rt/lib/orc/elfnix_platform.cpp b/compiler-rt/lib/orc/elfnix_platform.cpp
index 008f844a5f3d4..54d6a557934db 100644
--- a/compiler-rt/lib/orc/elfnix_platform.cpp
+++ b/compiler-rt/lib/orc/elfnix_platform.cpp
@@ -512,13 +512,11 @@ Error ELFNixPlatformRuntimeState::runFinis(
JDStatesLock.unlock();
- // Run in REVERSE order (LIFO semantics)
- for (size_t I = FiniSections.size(); I > 0; --I) {
- auto &Sec = FiniSections[I - 1];
- for (size_t J = Sec.size(); J > 0; --J) {
- Sec[J - 1]();
- }
- }
+ // Run in forward order - sections are already sorted correctly by the JIT:
+ // .dtors first (in order), then .fini_array (in descending priority order)
+ for (auto Sec : FiniSections)
+ for (auto *Fini : Sec)
+ Fini();
JDStatesLock.lock();
diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
index d3936df5fa391..8bd682c16614d 100644
--- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
@@ -947,31 +947,60 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::registerInitSections(
if (isELFInitializerSection(Sec.getName()))
OrderedInitSections.push_back(&Sec);
- // FIXME: This handles priority order within the current graph, but we'll need
- // to include priority information in the initializer allocation
- // actions in order to respect the ordering across multiple graphs.
- llvm::sort(OrderedInitSections, [](const jitlink::Section *LHS,
- const jitlink::Section *RHS) {
- if (LHS->getName().starts_with(".init_array")) {
- if (RHS->getName().starts_with(".init_array")) {
- StringRef LHSPrioStr(LHS->getName());
- StringRef RHSPrioStr(RHS->getName());
- uint64_t LHSPriority;
- bool LHSHasPriority = LHSPrioStr.consume_front(".init_array.") &&
- !LHSPrioStr.getAsInteger(10, LHSPriority);
- uint64_t RHSPriority;
- bool RHSHasPriority = RHSPrioStr.consume_front(".init_array.") &&
- !RHSPrioStr.getAsInteger(10, RHSPriority);
- if (LHSHasPriority)
- return RHSHasPriority ? LHSPriority < RHSPriority : true;
- else if (RHSHasPriority)
- return false;
- // If we get here we'll fall through to the
- // LHS->getName() < RHS->getName() test below.
- } else {
- // .init_array[.N] comes before any non-.init_array[.N] section.
+ // Helper to get section type and priority for sorting.
+ // Returns: {type_order, priority, has_priority}
+ // type_order: 0 = .init_array, 1 = .init, 2 = .ctors
+ auto getInitSectionInfo = [](const jitlink::Section *Sec)
+ -> std::tuple<int, uint64_t, bool> {
+ StringRef Name = Sec->getName();
+ if (Name.starts_with(".init_array")) {
+ StringRef PrioStr = Name;
+ uint64_t Prio = 0;
+ bool HasPrio = PrioStr.consume_front(".init_array.") &&
+ !PrioStr.getAsInteger(10, Prio);
+ return {0, Prio, HasPrio};
+ }
+ if (Name.starts_with(".init"))
+ return {1, 0, false};
+ if (Name.starts_with(".ctors")) {
+ StringRef PrioStr = Name;
+ uint64_t Prio = 0;
+ bool HasPrio = PrioStr.consume_front(".ctors.") &&
+ !PrioStr.getAsInteger(10, Prio);
+ return {2, Prio, HasPrio};
+ }
+ return {3, 0, false};
+ };
+
+ // Sort init sections:
+ // 1. .init_array sections first (ascending priority - lower runs first)
+ // 2. .init sections next
+ // 3. .ctors sections last (descending priority - higher runs first, legacy behavior)
+ llvm::sort(OrderedInitSections, [&](const jitlink::Section *LHS,
+ const jitlink::Section *RHS) {
+ auto [LType, LPrio, LHasPrio] = getInitSectionInfo(LHS);
+ auto [RType, RPrio, RHasPrio] = getInitSectionInfo(RHS);
+
+ if (LType != RType)
+ return LType < RType;
+
+ // Same type - sort by priority
+ if (LType == 0) {
+ // .init_array: ascending priority (lower priority number runs first)
+ if (LHasPrio && RHasPrio)
+ return LPrio < RPrio;
+ if (LHasPrio)
return true;
- }
+ if (RHasPrio)
+ return false;
+ } else if (LType == 2) {
+ // .ctors: descending priority (higher priority number runs first)
+ if (LHasPrio && RHasPrio)
+ return LPrio > RPrio;
+ if (LHasPrio)
+ return true;
+ if (RHasPrio)
+ return false;
}
return LHS->getName() < RHS->getName();
});
@@ -1029,26 +1058,61 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::registerFiniSections(
if (isELFFinalizerSection(Sec.getName()))
OrderedFiniSections.push_back(&Sec);
- // Sort by priority (same order as init - will be reversed at runtime)
- llvm::sort(OrderedFiniSections, [](const jitlink::Section *LHS,
- const jitlink::Section *RHS) {
- if (LHS->getName().starts_with(".fini_array")) {
- if (RHS->getName().starts_with(".fini_array")) {
- StringRef LHSPrioStr(LHS->getName());
- StringRef RHSPrioStr(RHS->getName());
- uint64_t LHSPriority;
- bool LHSHasPriority = LHSPrioStr.consume_front(".fini_array.") &&
- !LHSPrioStr.getAsInteger(10, LHSPriority);
- uint64_t RHSPriority;
- bool RHSHasPriority = RHSPrioStr.consume_front(".fini_array.") &&
- !RHSPrioStr.getAsInteger(10, RHSPriority);
- if (LHSHasPriority)
- return RHSHasPriority ? LHSPriority < RHSPriority : true;
- else if (RHSHasPriority)
- return false;
- } else {
+ // Helper to get section type and priority for sorting.
+ // Returns: {type_order, priority, has_priority}
+ // type_order: 0 = .dtors, 1 = .fini, 2 = .fini_array
+ auto getFiniSectionInfo = [](const jitlink::Section *Sec)
+ -> std::tuple<int, uint64_t, bool> {
+ StringRef Name = Sec->getName();
+ if (Name.starts_with(".dtors")) {
+ StringRef PrioStr = Name;
+ uint64_t Prio = 0;
+ bool HasPrio = PrioStr.consume_front(".dtors.") &&
+ !PrioStr.getAsInteger(10, Prio);
+ return {0, Prio, HasPrio};
+ }
+ if (Name.starts_with(".fini"))
+ if (!Name.starts_with(".fini_array"))
+ return {1, 0, false};
+ if (Name.starts_with(".fini_array")) {
+ StringRef PrioStr = Name;
+ uint64_t Prio = 0;
+ bool HasPrio = PrioStr.consume_front(".fini_array.") &&
+ !PrioStr.getAsInteger(10, Prio);
+ return {2, Prio, HasPrio};
+ }
+ return {3, 0, false};
+ };
+
+ // Sort fini sections:
+ // 1. .dtors sections first (ascending priority, as they appear)
+ // 2. .fini sections next
+ // 3. .fini_array sections last (descending priority - higher runs first)
+ llvm::sort(OrderedFiniSections, [&](const jitlink::Section *LHS,
+ const jitlink::Section *RHS) {
+ auto [LType, LPrio, LHasPrio] = getFiniSectionInfo(LHS);
+ auto [RType, RPrio, RHasPrio] = getFiniSectionInfo(RHS);
+
+ if (LType != RType)
+ return LType < RType;
+
+ // Same type - sort by priority
+ if (LType == 0) {
+ // .dtors: ascending priority (lower runs first)
+ if (LHasPrio && RHasPrio)
+ return LPrio < RPrio;
+ if (LHasPrio)
return true;
- }
+ if (RHasPrio)
+ return false;
+ } else if (LType == 2) {
+ // .fini_array: descending priority (higher priority number runs first)
+ if (LHasPrio && RHasPrio)
+ return LPrio > RPrio;
+ if (LHasPrio)
+ return true;
+ if (RHasPrio)
+ return false;
}
return LHS->getName() < RHS->getName();
});
>From 60e85bf85e3971836e2344c07eeaaae1e8e38634 Mon Sep 17 00:00:00 2001
From: Yaxing Cai <caiyaxing666 at gmail.com>
Date: Fri, 16 Jan 2026 17:43:05 +0800
Subject: [PATCH 3/5] add test
---
.../orc/TestCases/Linux/Generic/ctor-dtor.cpp | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp
diff --git a/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp b/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp
new file mode 100644
index 0000000000000..eca0cc0eda047
--- /dev/null
+++ b/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp
@@ -0,0 +1,31 @@
+// Check that constructors and destructors are run in the expected order.
+//
+// RUN: %clang -c -o %t.o %s
+// RUN: %llvm_jitlink %t.o | FileCheck %s
+//
+// REQUIRES: system-linux && host-arch-compatible
+
+// CHECK: constructor #1
+// CHECK: constructor #2
+// CHECK: constructor #N
+// CHECK: destructor #N
+// CHECK: destructor #2
+// CHECK: destructor #1
+
+#include <stdio.h>
+
+int x = 0;
+
+__attribute__((constructor(101))) void constructor1() {
+ puts("constructor #1");
+}
+__attribute__((constructor(102))) void constructor2() {
+ puts("constructor #2");
+}
+__attribute__((constructor)) void constructorN() { puts("constructor #N"); }
+
+__attribute__((destructor(101))) void destructor1() { puts("destructor #1"); }
+__attribute__((destructor(102))) void destructor2() { puts("destructor #2"); }
+__attribute__((destructor)) void destructorN() { puts("destructor #N"); }
+
+int main(void) { return 0; }
\ No newline at end of file
>From b64f9d80b4359961866fbd478a7bdbb1f992fef4 Mon Sep 17 00:00:00 2001
From: Yaxing Cai <caiyaxing666 at gmail.com>
Date: Fri, 16 Jan 2026 22:14:08 +0800
Subject: [PATCH 4/5] upd test
---
.../orc/TestCases/Linux/Generic/ctor-dtor.cpp | 87 +++++++++++++++----
1 file changed, 71 insertions(+), 16 deletions(-)
diff --git a/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp b/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp
index eca0cc0eda047..753905e56e6e4 100644
--- a/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp
+++ b/compiler-rt/test/orc/TestCases/Linux/Generic/ctor-dtor.cpp
@@ -5,27 +5,82 @@
//
// REQUIRES: system-linux && host-arch-compatible
-// CHECK: constructor #1
-// CHECK: constructor #2
-// CHECK: constructor #N
-// CHECK: destructor #N
-// CHECK: destructor #2
-// CHECK: destructor #1
-
+// CHECK: <init_array.101>
+// CHECK: <init_array.102>
+// CHECK: <init_array.103>
+// CHECK: <init_array>
+// CHECK: <ctors.103>
+// CHECK: <ctors.102>
+// CHECK: <ctors.101>
+// CHECK: <ctors>
+// CHECK: <dtors>
+// CHECK: <dtors.101>
+// CHECK: <dtors.102>
+// CHECK: <dtors.103>
+// CHECK: <fini_array>
+// CHECK: <fini_array.103>
+// CHECK: <fini_array.102>
+// CHECK: <fini_array.101>
#include <stdio.h>
-int x = 0;
+typedef void (*ctor_t)(void);
+typedef void (*dtor_t)(void);
+
+__attribute__((constructor)) void init_array() { puts("<init_array>"); }
+
+__attribute__((constructor(101))) void init_array_101() {
+ puts("<init_array.101>");
+}
+
+__attribute__((constructor(102))) void init_array_102() {
+ puts("<init_array.102>");
+}
+__attribute__((constructor(103))) void init_array_103() {
+ puts("<init_array.103>");
+}
+
+static void ctors(void) { puts("<ctors>"); }
+__attribute__((section(".ctors"), used)) static ctor_t ctors_ptr = ctors;
+
+static void ctors_101(void) { puts("<ctors.101>"); }
+__attribute__((section(".ctors.101"), used)) static ctor_t ctors_1_ptr =
+ ctors_101;
-__attribute__((constructor(101))) void constructor1() {
- puts("constructor #1");
+static void ctors_102(void) { puts("<ctors.102>"); }
+__attribute__((section(".ctors.102"), used)) static ctor_t ctors_2_ptr =
+ ctors_102;
+
+static void ctors_103(void) { puts("<ctors.103>"); }
+__attribute__((section(".ctors.103"), used)) static ctor_t ctors_3_ptr =
+ ctors_103;
+
+__attribute__((destructor)) void fini_array() { puts("<fini_array>"); }
+
+__attribute__((destructor(101))) void fini_array_101() {
+ puts("<fini_array.101>");
}
-__attribute__((constructor(102))) void constructor2() {
- puts("constructor #2");
+
+__attribute__((destructor(102))) void fini_array_102() {
+ puts("<fini_array.102>");
}
-__attribute__((constructor)) void constructorN() { puts("constructor #N"); }
-__attribute__((destructor(101))) void destructor1() { puts("destructor #1"); }
-__attribute__((destructor(102))) void destructor2() { puts("destructor #2"); }
-__attribute__((destructor)) void destructorN() { puts("destructor #N"); }
+__attribute__((destructor(103))) void fini_array_103() {
+ puts("<fini_array.103>");
+}
+
+static void dtors(void) { puts("<dtors>"); }
+__attribute__((section(".dtors"), used)) static dtor_t dtors_ptr = dtors;
+
+static void dtors_101(void) { puts("<dtors.101>"); }
+__attribute__((section(".dtors.101"), used)) static dtor_t dtors_1_ptr =
+ dtors_101;
+
+static void dtors_102(void) { puts("<dtors.102>"); }
+__attribute__((section(".dtors.102"), used)) static dtor_t dtors_2_ptr =
+ dtors_102;
+
+static void dtors_103(void) { puts("<dtors.103>"); }
+__attribute__((section(".dtors.103"), used)) static dtor_t dtors_3_ptr =
+ dtors_103;
int main(void) { return 0; }
\ No newline at end of file
>From 2651a0b433a318b8455271475bb7bb7b1e8fd79b Mon Sep 17 00:00:00 2001
From: Yaxing Cai <caiyaxing666 at gmail.com>
Date: Fri, 16 Jan 2026 22:39:42 +0800
Subject: [PATCH 5/5] fix order
---
llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
index 8bd682c16614d..504d4c2a59e37 100644
--- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
@@ -1098,21 +1098,21 @@ Error ELFNixPlatform::ELFNixPlatformPlugin::registerFiniSections(
// Same type - sort by priority
if (LType == 0) {
- // .dtors: ascending priority (lower runs first)
+ // .dtors: no-priority first, then ascending priority (lower runs first)
if (LHasPrio && RHasPrio)
return LPrio < RPrio;
if (LHasPrio)
- return true;
- if (RHasPrio)
return false;
+ if (RHasPrio)
+ return true;
} else if (LType == 2) {
- // .fini_array: descending priority (higher priority number runs first)
+ // .fini_array: no-priority first, then descending priority (higher runs first)
if (LHasPrio && RHasPrio)
return LPrio > RPrio;
if (LHasPrio)
- return true;
- if (RHasPrio)
return false;
+ if (RHasPrio)
+ return true;
}
return LHS->getName() < RHS->getName();
});
More information about the llvm-commits
mailing list