Skip to content

[JITLink][XCOFF] Setup initial build support for XCOFF #127266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 3, 2025

Conversation

mustartt
Copy link
Member

This patch starts the initial implementation of JITLink for XCOFF (Object format for AIX).

Copy link

github-actions bot commented Feb 14, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@scui-ibm scui-ibm requested a review from lhames February 18, 2025 15:46
@lhames
Copy link
Contributor

lhames commented Feb 18, 2025

Looks great so far!

Do you have a timeline in mind for supporting a minimal program like:

int main(int argc, char *argv[]) {
  return 0;
}

?

@mustartt
Copy link
Member Author

Looks great so far!

Do you have a timeline in mind for supporting a minimal program like:

int main(int argc, char *argv[]) {
  return 0;
}

?

So far I have a prototype of XCOFFLinkGraphBuilder that can construct the LinkGraph properly for the most commonly used relocation types. I need to consult with some more people to get the relocation correct. So I should have a patch in a week or two.

For PPC64, we have already implemented a large portion of the relocation fixup code for ELF that we can use for XCOFF as well. Implmenting the linking portion for XCOFF should not take much more than another week or 2. Which should allow us to compile simple programs.

@lhames
Copy link
Contributor

lhames commented Feb 19, 2025

So far I have a prototype of XCOFFLinkGraphBuilder that can construct the LinkGraph properly for the most commonly used relocation types. I need to consult with some more people to get the relocation correct. So I should have a patch in a week or two.

Sounds great. I just tried the following:

% cat main-ret-zero.c
int main(int argc, char *argv[]) {
  return 0;
}
% clang --target=powerpc64-ibm-aix-xcoff -fPIC -c -o main-ret-zero.o main-ret-zero.c 
% llvm-objdump -r main-ret-zero.o                                                    

main-ret-zero.o:	file format aix5coff64-rs6000

RELOCATION RECORDS FOR [.data]:
OFFSET           TYPE                     VALUE
0000000000000000 R_POS                    .main
0000000000000008 R_POS                    TOC

So it looks like you only need support for R_POS and generation of a TOC pointer -- as soon as you have that I'd say you're good to land this with a return 0; testcase, along the same lines that we did for 8313507.

For PPC64, we have already implemented a large portion of the relocation fixup code for ELF that we can use for XCOFF as well. Implmenting the linking portion for XCOFF should not take much more than another week or 2. Which should allow us to compile simple programs.

Very cool!

@mustartt
Copy link
Member Author

mustartt commented Feb 24, 2025

I completed the work required to run a simple main with an immediate return code. I will clean up the patch and post it soon. But I ran into 2 problems that I need some suggestions for:

First, in order to call the main function, we need to be able to indirectly call through the function descriptor .csect main[DS], which has the following structure:

  .globl	main[DS]                        # -- Begin function main
  .globl	.main
  
  .csect main[DS],3
  .vbyte	8, .main                        # @main
  .vbyte	8, TOC[TC0]
  .vbyte	8, 0

So the symbol .main and main both should have default visibility, and in the LinkGraph, they have Scope::Default and Linkage::Strong, but

assert(KV.second.getFlags() == I->second &&
is expecting JITSymbolFlag::None for main and JITSymbolFlag::Callable only.

In main resolving { (".main": 0xa00000000001000 [Callable]), ("main": 0xa00000000000000 [Data]) }
  Flags for .main: expected 0x20 found 0x30 // expected Callabled, found Callable and Exported
  Flags for main: expected 0x0 found 0x10 // expected None, found Exported

Second, llvm-mc is not very well supported on AIX yet (the .csect directive is not supported) so it will not work for our case. I'm wondering if its okay to have an C test case. I'm considering leaving out the -check for the testcase (as we need to register some FileInfo for the checks to work, which will come in the next patch) in favour of jit the object file directly on aix. It would be something like the following:

// REQUIRES: target=powerpc64-ibm-aix{{.*}}

// RUN: rm -rf %t && mkdir -p %t
// RUN: clang --target=powerpc64-ibm-aix -c -O3 -fPIC -o %t/xcoff_ppc64.o %s
// RUN: llvm-jitlink -triple=powerpc64-ibm-aix %t/xcoff_ppc64.o

int main(void) { return 0; }

@lhames
Copy link
Contributor

lhames commented Feb 24, 2025

In main resolving { (".main": 0xa00000000001000 [Callable]), ("main": 0xa00000000000000 [Data]) }
  Flags for .main: expected 0x20 found 0x30 // expected Callabled, found Callable and Exported
  Flags for main: expected 0x0 found 0x10 // expected None, found Exported

The initial JITSymbolFlags values should be created as part of object file interface construction here, with flags for individual symbols within the interface computed here. In particular I would expect these lines to set the Exported flag on every symbol that isn't explicitly hidden / local:

  if (!GV.hasLocalLinkage() && !GV.hasHiddenVisibility())
    Flags |= JITSymbolFlags::Exported;

It might be worth stepping through a test case in the debugger to see what interface is produced.

Second, llvm-mc is not very well supported on AIX yet (the .csect directive is not supported) so it will not work for our case. I'm wondering if it's okay to have an C test case.

We can't use C test cases as llvm tests can't assume that clang is available (e.g. someone might be doing an LLVM-only build on a system with gcc as the only available compiler).

You can use obj2yaml to produce a yaml representation of your object if that helps, but if MC doesn't work then I suspect obj2yaml might also have issues.

Binary object files are allowable as a last resort.

I'm considering leaving out the -check for the testcase (as we need to register some FileInfo for the checks to work, which > will come in the next patch) in favour of jit the object file directly on aix. It would be something like the following:

// REQUIRES: target=powerpc64-ibm-aix{{.*}}

// RUN: rm -rf %t && mkdir -p %t
// RUN: clang --target=powerpc64-ibm-aix -c -O3 -fPIC -o %t/xcoff_ppc64.o %s
// RUN: llvm-jitlink -triple=powerpc64-ibm-aix %t/xcoff_ppc64.o

int main(void) { return 0; }

That seems fine to me as a test case for the initial checkin, except that it'll need to be an assembly, yaml or binary test case rather than C.

@llvmbot
Copy link
Member

llvmbot commented Feb 25, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Henry Jiang (mustartt)

Changes

This patch starts the initial implementation of JITLink for XCOFF (Object format for AIX).


Patch is 34.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/127266.diff

12 Files Affected:

  • (added) llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h (+37)
  • (added) llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h (+37)
  • (modified) llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt (+5)
  • (modified) llvm/lib/ExecutionEngine/JITLink/JITLink.cpp (+5)
  • (added) llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp (+43)
  • (added) llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp (+420)
  • (added) llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.h (+63)
  • (added) llvm/lib/ExecutionEngine/JITLink/XCOFF_ppc64.cpp (+121)
  • (modified) llvm/lib/ExecutionEngine/Orc/LoadLinkableFile.cpp (+16)
  • (modified) llvm/lib/ExecutionEngine/Orc/ObjectFileInterface.cpp (+45)
  • (modified) llvm/lib/Object/XCOFFObjectFile.cpp (+7-3)
  • (added) llvm/test/ExecutionEngine/JITLink/ppc64/XCOFF_ppc64.ll (+24)
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h
new file mode 100644
index 0000000000000..3d181d0786eb7
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF.h
@@ -0,0 +1,37 @@
+//===------- XCOFF.h - Generic JIT link function for XCOFF ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// jit-link functions for XCOFF.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_JITLINK_XCOFF_H
+#define LLVM_EXECUTIONENGINE_JITLINK_XCOFF_H
+
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+
+namespace llvm {
+namespace jitlink {
+
+/// Create a LinkGraph from an XCOFF relocatable object.
+///
+/// Note: The graph does not take ownership of the underlying buffer, nor copy
+/// its contents. The caller is responsible for ensuring that the object buffer
+/// outlives the graph.
+Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromXCOFFObject(MemoryBufferRef ObjectBuffer,
+                               std::shared_ptr<orc::SymbolStringPool> SSP);
+
+/// Link the given graph.
+void link_XCOFF(std::unique_ptr<LinkGraph> G,
+                std::unique_ptr<JITLinkContext> Ctx);
+
+} // namespace jitlink
+} // namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_JITLINK_XCOFF_H
diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h
new file mode 100644
index 0000000000000..ec5c8a37bda27
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h
@@ -0,0 +1,37 @@
+//===------ XCOFF_ppc64.h - JIT link functions for XCOFF/ppc64 ------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// jit-link functions for XCOFF/ppc64.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_JITLINK_XCOFF_PPC64_H
+#define LLVM_EXECUTIONENGINE_JITLINK_XCOFF_PPC64_H
+
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+
+namespace llvm::jitlink {
+
+/// Create a LinkGraph from an XCOFF/ppc64 relocatable object.
+///
+/// Note: The graph does not take ownership of the underlying buffer, nor copy
+/// its contents. The caller is responsible for ensuring that the object buffer
+/// outlives the graph.
+///
+Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromXCOFFObject_ppc64(
+    MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP);
+
+/// jit-link the given object buffer, which must be a XCOFF ppc64 object file.
+///
+void link_XCOFF_ppc64(std::unique_ptr<LinkGraph> G,
+                      std::unique_ptr<JITLinkContext> Ctx);
+
+} // end namespace llvm::jitlink
+
+#endif // LLVM_EXECUTIONENGINE_JITLINK_XCOFF_PPC64_H
diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
index 65dd0c7468ae1..22e4513e1374c 100644
--- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
@@ -35,6 +35,11 @@ add_llvm_component_library(LLVMJITLink
   COFFLinkGraphBuilder.cpp
   COFF_x86_64.cpp
 
+  # XCOFF
+  XCOFF.cpp
+  XCOFF_ppc64.cpp
+  XCOFFLinkGraphBuilder.cpp
+
   # Architectures:
   aarch32.cpp
   aarch64.cpp
diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
index e8ce9b2b9527d..15a8fcf312ade 100644
--- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp
@@ -13,6 +13,7 @@
 #include "llvm/ExecutionEngine/JITLink/COFF.h"
 #include "llvm/ExecutionEngine/JITLink/ELF.h"
 #include "llvm/ExecutionEngine/JITLink/MachO.h"
+#include "llvm/ExecutionEngine/JITLink/XCOFF.h"
 #include "llvm/ExecutionEngine/JITLink/aarch64.h"
 #include "llvm/ExecutionEngine/JITLink/i386.h"
 #include "llvm/ExecutionEngine/JITLink/loongarch.h"
@@ -501,6 +502,8 @@ createLinkGraphFromObject(MemoryBufferRef ObjectBuffer,
     return createLinkGraphFromELFObject(ObjectBuffer, std::move(SSP));
   case file_magic::coff_object:
     return createLinkGraphFromCOFFObject(ObjectBuffer, std::move(SSP));
+  case file_magic::xcoff_object_64:
+    return createLinkGraphFromXCOFFObject(ObjectBuffer, std::move(SSP));
   default:
     return make_error<JITLinkError>("Unsupported file format");
   };
@@ -532,6 +535,8 @@ void link(std::unique_ptr<LinkGraph> G, std::unique_ptr<JITLinkContext> Ctx) {
     return link_ELF(std::move(G), std::move(Ctx));
   case Triple::COFF:
     return link_COFF(std::move(G), std::move(Ctx));
+  case Triple::XCOFF:
+    return link_XCOFF(std::move(G), std::move(Ctx));
   default:
     Ctx->notifyFailed(make_error<JITLinkError>("Unsupported object format"));
   };
diff --git a/llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp b/llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp
new file mode 100644
index 0000000000000..cb026538632a9
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/XCOFF.cpp
@@ -0,0 +1,43 @@
+//===-------------- XCOFF.cpp - JIT linker function for XCOFF -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// XCOFF jit-link function.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/JITLink/XCOFF.h"
+#include "llvm/ExecutionEngine/JITLink/XCOFF_ppc64.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "jitlink"
+
+namespace llvm {
+namespace jitlink {
+
+Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromXCOFFObject(MemoryBufferRef ObjectBuffer,
+                               std::shared_ptr<orc::SymbolStringPool> SSP) {
+  // Check magic
+  file_magic Magic = identify_magic(ObjectBuffer.getBuffer());
+  if (Magic != file_magic::xcoff_object_64)
+    return make_error<JITLinkError>("Invalid XCOFF 64 Header");
+
+  // TODO: See if we need to add more checks
+  //
+  return createLinkGraphFromXCOFFObject_ppc64(ObjectBuffer, std::move(SSP));
+}
+
+void link_XCOFF(std::unique_ptr<LinkGraph> G,
+                std::unique_ptr<JITLinkContext> Ctx) {
+  link_XCOFF_ppc64(std::move(G), std::move(Ctx));
+}
+
+} // namespace jitlink
+} // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp
new file mode 100644
index 0000000000000..0a76dcc77536b
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp
@@ -0,0 +1,420 @@
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic XCOFF LinkGraph building code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "XCOFFLinkGraphBuilder.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/XCOFF.h"
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/JITLink/ppc64.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
+#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "jitlink"
+
+namespace llvm {
+namespace jitlink {
+
+XCOFFLinkGraphBuilder::XCOFFLinkGraphBuilder(
+    const object::XCOFFObjectFile &Obj,
+    std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
+    SubtargetFeatures Features,
+    LinkGraph::GetEdgeKindNameFunction GetEdgeKindName)
+    : Obj(Obj),
+      G(std::make_unique<LinkGraph>(
+          std::string(Obj.getFileName()), std::move(SSP), std::move(TT),
+          std::move(Features), std::move(GetEdgeKindName))) {}
+
+static llvm::StringRef getStorageClassString(XCOFF::StorageClass SC) {
+  switch (SC) {
+  case XCOFF::StorageClass::C_FILE:
+    return "C_FILE (File name)";
+  case XCOFF::StorageClass::C_BINCL:
+    return "C_BINCL (Beginning of include file)";
+  case XCOFF::StorageClass::C_EINCL:
+    return "C_EINCL (Ending of include file)";
+  case XCOFF::StorageClass::C_GSYM:
+    return "C_GSYM (Global variable)";
+  case XCOFF::StorageClass::C_STSYM:
+    return "C_STSYM (Statically allocated symbol)";
+  case XCOFF::StorageClass::C_BCOMM:
+    return "C_BCOMM (Beginning of common block)";
+  case XCOFF::StorageClass::C_ECOMM:
+    return "C_ECOMM (End of common block)";
+  case XCOFF::StorageClass::C_ENTRY:
+    return "C_ENTRY (Alternate entry)";
+  case XCOFF::StorageClass::C_BSTAT:
+    return "C_BSTAT (Beginning of static block)";
+  case XCOFF::StorageClass::C_ESTAT:
+    return "C_ESTAT (End of static block)";
+  case XCOFF::StorageClass::C_GTLS:
+    return "C_GTLS (Global thread-local variable)";
+  case XCOFF::StorageClass::C_STTLS:
+    return "C_STTLS (Static thread-local variable)";
+  case XCOFF::StorageClass::C_DWARF:
+    return "C_DWARF (DWARF section symbol)";
+  case XCOFF::StorageClass::C_LSYM:
+    return "C_LSYM (Automatic variable allocated on stack)";
+  case XCOFF::StorageClass::C_PSYM:
+    return "C_PSYM (Argument to subroutine allocated on stack)";
+  case XCOFF::StorageClass::C_RSYM:
+    return "C_RSYM (Register variable)";
+  case XCOFF::StorageClass::C_RPSYM:
+    return "C_RPSYM (Argument to function stored in register)";
+  case XCOFF::StorageClass::C_ECOML:
+    return "C_ECOML (Local member of common block)";
+  case XCOFF::StorageClass::C_FUN:
+    return "C_FUN (Function or procedure)";
+  case XCOFF::StorageClass::C_EXT:
+    return "C_EXT (External symbol)";
+  case XCOFF::StorageClass::C_WEAKEXT:
+    return "C_WEAKEXT (Weak external symbol)";
+  case XCOFF::StorageClass::C_NULL:
+    return "C_NULL";
+  case XCOFF::StorageClass::C_STAT:
+    return "C_STAT (Static)";
+  case XCOFF::StorageClass::C_BLOCK:
+    return "C_BLOCK (\".bb\" or \".eb\")";
+  case XCOFF::StorageClass::C_FCN:
+    return "C_FCN (\".bf\" or \".ef\")";
+  case XCOFF::StorageClass::C_HIDEXT:
+    return "C_HIDEXT (Un-named external symbol)";
+  case XCOFF::StorageClass::C_INFO:
+    return "C_INFO (Comment string in .info section)";
+  case XCOFF::StorageClass::C_DECL:
+    return "C_DECL (Declaration of object)";
+  case XCOFF::StorageClass::C_AUTO:
+    return "C_AUTO (Automatic variable)";
+  case XCOFF::StorageClass::C_REG:
+    return "C_REG (Register variable)";
+  case XCOFF::StorageClass::C_EXTDEF:
+    return "C_EXTDEF (External definition)";
+  case XCOFF::StorageClass::C_LABEL:
+    return "C_LABEL (Label)";
+  case XCOFF::StorageClass::C_ULABEL:
+    return "C_ULABEL (Undefined label)";
+  case XCOFF::StorageClass::C_MOS:
+    return "C_MOS (Member of structure)";
+  case XCOFF::StorageClass::C_ARG:
+    return "C_ARG (Function argument)";
+  case XCOFF::StorageClass::C_STRTAG:
+    return "C_STRTAG (Structure tag)";
+  case XCOFF::StorageClass::C_MOU:
+    return "C_MOU (Member of union)";
+  case XCOFF::StorageClass::C_UNTAG:
+    return "C_UNTAG (Union tag)";
+  case XCOFF::StorageClass::C_TPDEF:
+    return "C_TPDEF (Type definition)";
+  case XCOFF::StorageClass::C_USTATIC:
+    return "C_USTATIC (Undefined static)";
+  case XCOFF::StorageClass::C_ENTAG:
+    return "C_ENTAG (Enumeration tag)";
+  case XCOFF::StorageClass::C_MOE:
+    return "C_MOE (Member of enumeration)";
+  case XCOFF::StorageClass::C_REGPARM:
+    return "C_REGPARM (Register parameter)";
+  case XCOFF::StorageClass::C_FIELD:
+    return "C_FIELD (Bit field)";
+  case XCOFF::StorageClass::C_EOS:
+    return "C_EOS (End of structure)";
+  case XCOFF::StorageClass::C_LINE:
+    return "C_LINE";
+  case XCOFF::StorageClass::C_ALIAS:
+    return "C_ALIAS (Duplicate tag)";
+  case XCOFF::StorageClass::C_HIDDEN:
+    return "C_HIDDEN (Special storage class for external)";
+  case XCOFF::StorageClass::C_EFCN:
+    return "C_EFCN (Physical end of function)";
+  case XCOFF::StorageClass::C_TCSYM:
+    return "C_TCSYM (Reserved)";
+  }
+}
+
+Error XCOFFLinkGraphBuilder::processSections() {
+  LLVM_DEBUG(dbgs() << "  Creating graph sections...\n");
+
+  UndefSection = &G->createSection("*UND*", orc::MemProt::None);
+
+  for (object::SectionRef Section : Obj.sections()) {
+    auto SectionName = Section.getName();
+    if (!SectionName)
+      return SectionName.takeError();
+
+    LLVM_DEBUG({
+      dbgs() << "    section = " << *SectionName
+             << ", idx = " << Section.getIndex()
+             << ", size = " << format_hex_no_prefix(Section.getSize(), 8)
+             << ", vma = " << format_hex(Section.getAddress(), 16) << "\n";
+    });
+
+    // We can skip debug (including dawrf) and pad sections
+    if (Section.isDebugSection() || *SectionName == "pad")
+      continue;
+    LLVM_DEBUG(dbgs() << "        creating graph section\n");
+
+    orc::MemProt Prot = orc::MemProt::Read;
+    if (Section.isText())
+      Prot |= orc::MemProt::Exec;
+    if (Section.isData() || Section.isBSS())
+      Prot |= orc::MemProt::Write;
+
+    jitlink::Section *GraphSec = &G->createSection(*SectionName, Prot);
+    // TODO: Check for no_alloc for certain sections
+
+    assert(!SectionTable.contains(Section.getIndex()) &&
+           "Section with same index already exists");
+    SectionTable[Section.getIndex()] = {GraphSec, Section};
+  }
+
+  return Error::success();
+}
+
+static std::optional<object::XCOFFSymbolRef>
+getXCOFFSymbolContainingSymbolRef(const object::XCOFFObjectFile &Obj,
+                                  const object::SymbolRef &Sym) {
+  const object::XCOFFSymbolRef SymRef =
+      Obj.toSymbolRef(Sym.getRawDataRefImpl());
+  if (!SymRef.isCsectSymbol())
+    return std::nullopt;
+
+  Expected<object::XCOFFCsectAuxRef> CsectAuxEntOrErr =
+      SymRef.getXCOFFCsectAuxRef();
+  if (!CsectAuxEntOrErr || !CsectAuxEntOrErr.get().isLabel())
+    return std::nullopt;
+  uint32_t Idx =
+      static_cast<uint32_t>(CsectAuxEntOrErr.get().getSectionOrLength());
+  object::DataRefImpl DRI;
+  DRI.p = Obj.getSymbolByIndex(Idx);
+  return object::XCOFFSymbolRef(DRI, &Obj);
+}
+
+static void printSymbolEntry(raw_ostream &OS,
+                             const object::XCOFFObjectFile &Obj,
+                             const object::XCOFFSymbolRef &Sym) {
+  OS << "    " << format_hex(cantFail(Sym.getAddress()), 16);
+  OS << " " << left_justify(cantFail(Sym.getName()), 10);
+  if (Sym.isCsectSymbol()) {
+    auto CsectAuxEntry = cantFail(Sym.getXCOFFCsectAuxRef());
+    if (!CsectAuxEntry.isLabel()) {
+      std::string MCStr =
+          "[" +
+          XCOFF::getMappingClassString(CsectAuxEntry.getStorageMappingClass())
+              .str() +
+          "]";
+      OS << left_justify(MCStr, 3);
+    }
+  }
+  OS << " " << format_hex(Sym.getSize(), 8);
+  OS << " " << Sym.getSectionNumber();
+  OS << " " << getStorageClassString(Sym.getStorageClass());
+  OS << " (idx: " << Obj.getSymbolIndex(Sym.getRawDataRefImpl().p) << ")";
+  if (Sym.isCsectSymbol()) {
+    if (auto ParentSym = getXCOFFSymbolContainingSymbolRef(Obj, Sym)) {
+      OS << " (csect idx: "
+         << Obj.getSymbolIndex(ParentSym->getRawDataRefImpl().p) << ")";
+    }
+  }
+  OS << "\n";
+}
+
+Error XCOFFLinkGraphBuilder::processCsectsAndSymbols() {
+  LLVM_DEBUG(dbgs() << "  Creating graph blocks and symbols...\n");
+
+  for (auto [K, V] : SectionTable) {
+    LLVM_DEBUG(dbgs() << "    section entry(idx: " << K
+                      << " section: " << V.Section->getName() << ")\n");
+  }
+
+  for (object::XCOFFSymbolRef Symbol : Obj.symbols()) {
+    LLVM_DEBUG({ printSymbolEntry(dbgs(), Obj, Symbol); });
+
+    auto Flags = Symbol.getFlags();
+    if (!Flags)
+      return Flags.takeError();
+
+    bool External = *Flags & object::SymbolRef::SF_Undefined;
+    bool Weak = *Flags & object::SymbolRef::SF_Weak;
+    bool Hidden = *Flags & object::SymbolRef::SF_Hidden;
+    bool Global = *Flags & object::SymbolRef::SF_Global;
+
+    auto SymbolIndex = Obj.getSymbolIndex(Symbol.getEntryAddress());
+    auto SymbolName = Symbol.getName();
+    if (!SymbolName)
+      return SymbolName.takeError();
+
+    if (External) {
+      LLVM_DEBUG(dbgs() << "      created external symbol\n");
+      SymbolIndexTable[SymbolIndex] =
+          &G->addExternalSymbol(*SymbolName, Symbol.getSize(), Weak);
+      continue;
+    }
+
+    if (!Symbol.isCsectSymbol()) {
+      LLVM_DEBUG(dbgs() << "      skipped: not a csect symbol\n");
+      continue;
+    }
+
+    auto ParentSym = getXCOFFSymbolContainingSymbolRef(Obj, Symbol);
+    object::XCOFFSymbolRef CsectSymbol = ParentSym ? *ParentSym : Symbol;
+
+    auto CsectSymbolIndex = Obj.getSymbolIndex(CsectSymbol.getEntryAddress());
+    auto ParentSectionNumber = CsectSymbol.getSectionNumber();
+
+    bool IsUndefinedSection = !SectionTable.contains(ParentSectionNumber);
+    Section *ParentSection = !IsUndefinedSection
+                                 ? SectionTable[ParentSectionNumber].Section
+                                 : UndefSection;
+    Block *B = nullptr;
+
+    // TODO: Clean up the logic for handling undefined symbols
+    if (!CsectTable.contains(CsectSymbolIndex) && !IsUndefinedSection) {
+      object::SectionRef &SectionRef =
+          SectionTable[ParentSectionNumber].SectionData;
+      auto Data = SectionRef.getContents();
+      if (!Data)
+        return Data.takeError();
+      auto CsectSymbolAddr = CsectSymbol.getAddress();
+      if (!CsectSymbolAddr)
+        return CsectSymbolAddr.takeError();
+
+      ArrayRef<char> SectionBuffer{Data->data(), Data->size()};
+      auto Offset = *CsectSymbolAddr - SectionRef.getAddress();
+
+      LLVM_DEBUG(dbgs() << "      symbol entry: offset = " << Offset
+                        << ", size = " << CsectSymbol.getSize()
+                        << ", storage class = "
+                        << getStorageClassString(CsectSymbol.getStorageClass())
+                        << "\n");
+
+      B = &G->createContentBlock(
+          *ParentSection, SectionBuffer.slice(Offset, CsectSymbol.getSize()),
+          orc::ExecutorAddr(*CsectSymbolAddr), CsectSymbol.getAlignment(), 0);
+
+      CsectTable[CsectSymbolIndex] = B;
+    } else {
+      B = CsectTable[CsectSymbolIndex];
+    }
+
+    Scope S{Scope::Default};
+    if (Hidden)
+      S = Scope::Hidden;
+    // TODO: Got this from llvm-objdump.cpp:2938 not sure if its correct
+    if (!Weak) {
+      if (Global)
+        S = Scope::Default;
+      else
+        S = Scope::Local;
+    }
+
+    // TODO: figure out all symbols that should have Scope::SideEffectsOnly
+    Linkage L = Weak ? Linkage::Weak : Linkage::Strong;
+    auto SymbolAddr = Symbol.getAddress();
+    if (!SymbolAddr)
+      return SymbolAddr.takeError();
+    auto IsCallableOrErr = Symbol.isFunction();
+    if (!IsCallableOrErr)
+      return IsCallableOrErr.takeError();
+
+    auto BlockOffset = *SymbolAddr - B->getAddress().getValue();
+
+    LLVM_DEBUG(dbgs() << "      creating with linkage = " << getLinkageName(L)
+                      << ", scope = " << getScopeName(S) << ", B = "
+                      << format_hex(B->getAddress().getValue(), 16) << "\n");
+
+    SymbolIndexTable[SymbolIndex] =
+        &G->addDefinedSymbol(*B, BlockOffset, *SymbolName, Symbol.getSize(), L,
+                             S, *IsCallableOrErr, true);
+  }
+
+  return Error::success();
+}
+
+Error XCOFFLinkGraphBuilder::processRelocations() {
+  LLVM_DEBUG(dbgs() << "  Creating relocations...\n");
+
+  for (object::SectionRef Section : Obj.sections())...
[truncated]

@mustartt mustartt requested review from w2yehia and scui-ibm February 25, 2025 05:03
@mustartt
Copy link
Member Author

Okay so I had the time to clean up the initial patch to support building simple programs. Here is my plan for the next following few patches:

  1. Implement registerXCOFFGraphInfo so that actual -check can be used and add few more tests to verify the correctness of the block address and final fixups
  2. Refactor some of the common PowerPC fixup from the elf portion
  3. Implement rest of the work that needs to be done for the TOC
  4. Support rest of the commonly used relocation types

return SymFlags.takeError();

// TODO: Global symbols should have default visibility for now
*SymFlags |= JITSymbolFlags::Exported;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like XCOFFObjectFile::getSymbolFlags isn't returning SF_Exported for these symbols. The relevant logic from XCOFFObjectFile.cpp seems to be:

  // There is no visibility in old 32 bit XCOFF object file interpret.
  if (is64Bit() || (auxiliaryHeader32() && (auxiliaryHeader32()->getVersion() ==
                                            NEW_XCOFF_INTERPRET))) {
    uint16_t SymType = XCOFFSym.getSymbolType();
    if ((SymType & VISIBILITY_MASK) == SYM_V_HIDDEN)
      Result |= SymbolRef::SF_Hidden;

    if ((SymType & VISIBILITY_MASK) == SYM_V_EXPORTED)
      Result |= SymbolRef::SF_Exported;
  }
  return Result;

Does that look right, or is it missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does look correct. Need to verify with the xcoff experts to be sure.

Copy link
Member Author

@mustartt mustartt Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After verifying, added this to the comments

By default, only shared library with F_SHROBJ in the xcoff header will have SF_Exported. On AIX, we require the linker with the help of an export list to determine the visibility of the symbol. This mimics the behaviour of -bautoexp and CreateExportList utilities. In the abscence of the export list or dlexport attribute in the IR, this is the best we can do for now.

https://www.ibm.com/docs/en/xl-c-aix/13.1.0?topic=library-exporting-symbols-createexportlist-utility

Its expected that given a XCOFF object, without going through the linker will not have SF_Exported flag set.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's questionable whether XCOFFObjectFile::getSymbolFlags is doing what is desired here. Firstly, the description of SF_Exported ("Symbol is visible to other DSOs") should apply to symbols with "protected" visibility. Secondly, if the input file has a loader section, the loader symbol table should be consulted (and if what the loader symbol table says is overridden here, there needs to be strong, well-articulated rationale).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on https://www.ibm.com/docs/en/aix/7.3?topic=l-ld-command#ld__title__13, we should be exporting SF_Exported based on the ld documentation:

Protected The symbol is exported but cannot be rebounded (or preempted), even if runtime linking is being used.
Exported The symbol is exported with the global export attribute.

Will separate this change to another patch in case of test breaks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that changing XCOFFObjectFile::getSymbolFlags should be separate from the current patch.

With respect to that patch (but off-topic for the current one), I want to emphasize that the getSymbolFlags implementation should ideally consult the loader symbol table when provided with an object that contains one. I do not know whether or not that has a practical effect for BOLT or other use cases.

S = Scope::Local;
}

// TODO: figure out all symbols that should have Scope::SideEffectsOnly
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SideEffectsOnly is special, and closely related to the initializer symbol (which should always be SideEffectsOnly):

A graph should have an initializer symbol if there are any static constructors in the graph (or related registrations, like ObjC / Swift metadata registrations on Darwin) that should run at dlopen time.

A side-effects only symbol is one that can be looked up to trigger linking of a graph, but which does not show up in the final symbol table (so it must always be looked up with WeaklyReferencedSymbol to avoid a SymbolsNotFound error). The initializer symbol should be marked with SideEffectsOnly, but API clients (e.g. plugins) can add their own special symbols if they want.

@lhames
Copy link
Contributor

lhames commented Mar 3, 2025

I'd love to resolve the SF_Exported flag issue first, but then I think this can land in-tree: It passes a basic testcase and it sounds like you've got a plan for the remaining work.

@mustartt
Copy link
Member Author

mustartt commented Mar 3, 2025

I'd love to resolve the SF_Exported flag issue first, but then I think this can land in-tree: It passes a basic testcase and it sounds like you've got a plan for the remaining work.

Thanks for taking a look at the patch. The issue is that SF_Exported flag is not set for main in the object file. But in order for the LinkGraph to resolve the main symbol it does need to have Scope::Default.

But for now I think it should be okay to land this if you are okay with it. I will fix this in the next patch or 2.

@lhames
Copy link
Contributor

lhames commented Mar 4, 2025

I think the Exported flag thing should be resolved before this lands. Hopefully it will be straightforward.

From the "Symbol Visibility" section in https://www.ibm.com/docs/en/aix/7.1?topic=l-ld-command:

  • Internal: Symbol is not exported. The address of the symbol must not be provided to other programs or shared objects, but the linker does not verify this.
  • Hidden: Symbol is not exported
  • Protected: Symbol is exported but cannot be rebound (or preempted), even if runtime linking is being used.
  • Exported: Symbol is exported with the global export attribute.

So my question for the XCOFF experts is: What does Exported mean, as distinct from !Hidden? Right now C symbols default to non-Hidden, non-Exported. Should these symbols be publicly visible if linked into a dynamic library? not publicly visible? Or is it underspecified (i.e. do we need to add some extra step to disambiguate).

@mustartt
Copy link
Member Author

mustartt commented Mar 5, 2025

So my question for the XCOFF experts is: What does Exported mean, as distinct from !Hidden? Right now C symbols default to non-Hidden, non-Exported. Should these symbols be publicly visible if linked into a dynamic library? not publicly visible? Or is it underspecified (i.e. do we need to add some extra step to disambiguate).

From my understanding, C symbols will have n_type = 0 which is unspecified, and needs to be disambiguated when linking (see the next point).

Should these symbols be publicly visible if linked into a dynamic library? not publicly visible? Or is it underspecified (i.e. do we need to add some extra step to disambiguate).

With AIX ld with the default option -bautoexp or an export list that contains the symbol, then it will have visibility Exported in the final dynamic library.

@hubert-reinterpretcast Does that sound correct?

For JITLink, I think its reasonable to mimic what the linker would have done for object files linked with -bautoexp in the absence of a export list, which is to mark global symbols with default visibility so it can be resolved from other LinkGraphs.

@hubert-reinterpretcast
Copy link
Collaborator

So my question for the XCOFF experts is: What does Exported mean, as distinct from !Hidden?

On AIX, symbols may have No Visibility. !Hidden includes symbols with no visibility.

Right now C symbols default to non-Hidden, non-Exported. Should these symbols be publicly visible if linked into a dynamic library? not publicly visible? Or is it underspecified (i.e. do we need to add some extra step to disambiguate).

It is the last case. Symbols with no visibility may be "exported" (but not given Exported visibility) via linker options (such as the use of an export file).

@hubert-reinterpretcast
Copy link
Collaborator

With AIX ld with the default option -bautoexp or an export list that contains the symbol, then it will have visibility Exported in the final dynamic library.

@hubert-reinterpretcast Does that sound correct?

As explained in my other response. The property of being exported is separate from the Visibility. Additionally, symbols with "protected" visibility are generally exported.

Your understanding of -bautoexp does not match mine. My understanding of -bautoexp is that it is the default only when not linking a shared library (i.e., it is the default when linking a main program). Additionally, it only causes export of symbols with definitions where it is known that said definitions (if exported) will be used by other modules listed as inputs to the link step.

@mustartt
Copy link
Member Author

mustartt commented Mar 5, 2025

With AIX ld with the default option -bautoexp or an export list that contains the symbol, then it will have visibility Exported in the final dynamic library.
@hubert-reinterpretcast Does that sound correct?

As explained in my other response. The property of being exported is separate from the Visibility. Additionally, symbols with "protected" visibility are generally exported.

Your understanding of -bautoexp does not match mine. My understanding of -bautoexp is that it is the default only when not linking a shared library (i.e., it is the default when linking a main program). Additionally, it only causes export of symbols with definitions where it is known that said definitions (if exported) will be used by other modules listed as inputs to the link step.

Correction:

For JITLink, I think its reasonable to mimic what the linker would have done for object files linked with -bautoexp in the absence of a export list using the export list generated by CreateExportList or -bexpall, which is to mark global symbols with default visibility so it can be resolved from other LinkGraphs.

This is the equivalent of linking with -bexpall not -bautoexp (my bad).

This is also a problem we are facing with the Rust drivers as well where we don't have access to the export list, and as a result we end up passing -bexpall as a temporary fix.

@hubert-reinterpretcast
Copy link
Collaborator

Correction:

For JITLink, I think its reasonable to mimic what the linker would have done for object files linked with -bautoexp in the absence of a export list using the export list generated by CreateExportList or -bexpall, which is to mark global symbols with default visibility so it can be resolved from other LinkGraphs.

This is the equivalent of linking with -bexpall not -bautoexp (my bad).

If you mean to consider XCOFF C_EXT/C_WEAKEXT symbols with no specified visibility as having "Default" Scope (https://llvm.org/docs/JITLink.html#linkgraph) for the purposes of a LinkGraph, then I think that is reasonable.

Aside: The document linked above seems rather silent on the topic of symbol interpositioning.

This is also a problem we are facing with the Rust drivers as well where we don't have access to the export list, and as a result we end up passing -bexpall as a temporary fix.

Off-topic, but that is likely going to run into trouble due to AIX's generally eager resolution behaviour.

@hubert-reinterpretcast
Copy link
Collaborator

hubert-reinterpretcast commented Mar 5, 2025

Okay so I had the time to clean up the initial patch to support building simple programs. Here is my plan for the next following few patches:

1. Implement `registerXCOFFGraphInfo` so that actual `-check` can be used and add few more tests to verify the correctness of the block address and final fixups

2. Refactor some of the common PowerPC fixup from the elf portion

3. Implement rest of the work that needs to be done for the TOC

4. Support rest of the commonly used relocation types

I am not familiar with the capabilities of the JIT linker. After (3), can we expect to be able to call (from a JIT-linked function) a function from the link target process that throws a C++ exception and be able to catch it in the JIT-linked function? I think that it would additionally make sense to observe how unwinding works for a similar scenario with C code.

Also, are C++ static constructors (also applicable to C with the __constructor__ GNU attribute) on your roadmap?

@mustartt
Copy link
Member Author

mustartt commented Mar 5, 2025

I am not familiar with the capabilities of the JIT linker. After (3), can we expect to be able to call (from a JIT-linked function) a function from the link target process that throws a C++ exception and be able to catch it in the JIT-linked function? I think that it would additionally make sense to observe how unwinding works for a similar scenario with C code.
Also, are C++ static constructors (also applicable to C with the constructor GNU attribute) on your roadmap?

After 4, when we get all the relocation types used in eh_frames, working then we should be able to catch exceptions.

Yes, C++ static initializers are on the roadmap. We need special registration for those symbols.

@hubert-reinterpretcast
Copy link
Collaborator

Yes, C++ static initializers are on the roadmap. We need special registration for those symbols.

From our offline discussion, you indicated that your primary interest in JITLink support is to support BOLT.

I don't understand how BOLT uses JITLink, but it seems to me that the requirements associated with C++ static initializer support for JITLink (to collect them and arrange to run them without using dlopen/the system loader) is different than the ones needed for BOLT (where the linker already collected them and produced the relevant post-link structures in the module).

@mustartt
Copy link
Member Author

mustartt commented Mar 6, 2025

Yes, C++ static initializers are on the roadmap. We need special registration for those symbols.

From our offline discussion, you indicated that your primary interest in JITLink support is to support BOLT.

I don't understand how BOLT uses JITLink, but it seems to me that the requirements associated with C++ static initializer support for JITLink (to collect them and arrange to run them without using dlopen/the system loader) is different than the ones needed for BOLT (where the linker already collected them and produced the relevant post-link structures in the module).

It's in the roadmap but it is not the top priority. I'd like to support all the features and relocations implement first, and worry about things like the static initializers and such after.

@hubert-reinterpretcast
Copy link
Collaborator

It's in the roadmap but it is not the top priority. I'd like to support all the features and relocations implement first, and worry about things like the static initializers and such after.

Can you give me an answer as to how JITLink fits into BOLT? I am trying to emphasize that the particular usage of JITLink for the purposes of BOLT can have additional complications in the context of AIX (which is not directly tied to XCOFF, but extends to additional linker behaviours).

@hubert-reinterpretcast
Copy link
Collaborator

Should these symbols be publicly visible if linked into a dynamic library? not publicly visible? Or is it underspecified (i.e. do we need to add some extra step to disambiguate).

@lhames, has the clarification regarding the treatment of visibility in XCOFF been sufficient to confirm the proper direction for this patch?

To summarize, it is technically "underspecified", but common (if not generally advisable) practice is to have these symbols be publicly visible if linked into a dynamic library (as opposed to a main executable).

return SymFlags.takeError();

// TODO: Global symbols should have default visibility for now
*SymFlags |= JITSymbolFlags::Exported;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's questionable whether XCOFFObjectFile::getSymbolFlags is doing what is desired here. Firstly, the description of SF_Exported ("Symbol is visible to other DSOs") should apply to symbols with "protected" visibility. Secondly, if the input file has a loader section, the loader symbol table should be consulted (and if what the loader symbol table says is overridden here, there needs to be strong, well-articulated rationale).

if (!SymFlags)
return SymFlags.takeError();

// By default, only shared library with F_SHROBJ in the xcoff header will
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "default" here? What conditions count as "default" for this claim?

@mustartt
Copy link
Member Author

mustartt commented Mar 7, 2025

Can you give me an answer as to how JITLink fits into BOLT? I am trying to emphasize that the particular usage of JITLink for the purposes of BOLT can have additional complications in the context of AIX (which is not directly tied to XCOFF, but extends to additional linker behaviours).

I can only give an high level overview. @scui-ibm would you mind giving a more in detailed summary?

BOLT uses JITLink as an platform independent linker that has control over exactly how the sections are layed out in the final executable in order to minimize icache and dcache misses.

After parsing the entire call graph and performing all the bolt related optimization, bolt will emit in memory object files for JITLink to resolve the relocations and reordering of the sections.

@mustartt
Copy link
Member Author

mustartt commented Mar 7, 2025

Updated the rationale for keeping default visibility for C_EXT and C_WEAKEXT symbols in ObjectFileInterface based on our discussion.

@scui-ibm
Copy link
Contributor

scui-ibm commented Mar 7, 2025

I can only give an high level overview. @scui-ibm would you mind giving a more in detailed summary?

I also only have high levels view of the interaction between BOLT and JITLinker. Basically after BOLT transformations, BOLT JITLinkLinker calls jitlink::createLinkGraphFromObject to create a LinkGraph and then calls jitlink::link to link it (section allocation and relocation fixups). There are some details/discussions in this PR (https://reviews.llvm.org/D147544,  [BOLT] Move from RuntimeDyld to JITLink).

@lhames
Copy link
Contributor

lhames commented Mar 11, 2025

On AIX, symbols may have No Visibility. !Hidden includes symbols with no visibility.

When you say "no" visibility, do you mean local / private scope? Basically the same as static in C?

JITLink's relevant concepts are:

  1. Scope: Local = only visible within the current graph; Hidden = visible to other symbols in the dylib, but not outside; Default = visible outside the dylib.
  2. Linkage: Strong = references to this name should bind this definition (no duplicate strong defs allowed within a dylib), Weak = Discarded unless this is the first instance of this symbol.

Aside: The document linked above seems rather silent on the topic of symbol interpositioning.

Yep. We need to address that, but just haven't gotten to it yet.

I'll try to catch up on the rest of this discussion later tonight.

@hubert-reinterpretcast
Copy link
Collaborator

When you say "no" visibility, do you mean local / private scope? Basically the same as static in C?

Not the same as static in C. I mean symbols with what C considers external linkage and are (without reference to how the linker is invoked) unknown in terms of whether they are visible in another loadable object or not.

When clang --shared is used to link on the platform, these symbols would generally be made visible by default to other loadable objects (that is they would have a Scope value of Default).

@lhames
Copy link
Contributor

lhames commented Mar 11, 2025

Thanks for the extra details @hubert-reinterpretcast.

@lhames, has the clarification regarding the treatment of visibility in XCOFF been sufficient to confirm the proper direction for this patch?

To summarize, it is technically "underspecified", but common (if not generally advisable) practice is to have these symbols be publicly visible if linked into a dynamic library (as opposed to a main executable).

I think so. It looks like we should map SYM_V_INTERNAL to Scope::Local, SYM_V_HIDDEN to Scope::Hidden, and anything else to Scope::Default.

Off-topic, but that is likely going to run into trouble due to AIX's generally eager resolution behaviour.

If you have any time to elaborate on this I'd be interested to hear. It sounds like the primary motivation here is Bolt, but it'd be nice to make ORC / JITLink work for AIX JIT use cases too.

@hubert-reinterpretcast
Copy link
Collaborator

I think so. It looks like we should map SYM_V_INTERNAL to Scope::Local, SYM_V_HIDDEN to Scope::Hidden, and anything else to Scope::Default.

Like ELF "internal" and "hidden" visibility, both SYM_V_INTERNAL and SYM_V_HIDDEN are likely Scope::Hidden.

@mustartt
Copy link
Member Author

Like ELF "internal" and "hidden" visibility, both SYM_V_INTERNAL and SYM_V_HIDDEN are likely Scope::Hidden.

I mapped both SYM_V_INTERNAL and SYM_V_HIDDEN to Scope::Hidden and C_EXT and C_WEAKEXT to Scope::Default.

With this we should be fine to land this now @hubert-reinterpretcast @lhames.

@mustartt mustartt merged commit 7d3dfc8 into llvm:main Apr 3, 2025
11 checks passed
@mustartt mustartt deleted the jitlink-xcoff branch April 3, 2025 21:01
return object::XCOFFSymbolRef(DRI, &Obj);
}

static void printSymbolEntry(raw_ostream &OS,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If compiling with
-DLLVM_ENABLE_ASSERTIONS=OFF
we get

../lib/ExecutionEngine/JITLink/XCOFFLinkGraphBuilder.cpp:206:13: error: unused function 'printSymbolEntry' [-Werror,-Wunused-function]
  206 | static void printSymbolEntry(raw_ostream &OS,
      |             ^~~~~~~~~~~~~~~~
1 error generated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I will get a fix up soon

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the fix up now: #134413

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants