[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