Skip to content

[LLD][MinGW] Add support for wrapped symbols on ARM64X #126296

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 1 commit into from
Feb 10, 2025

Conversation

cjacek
Copy link
Contributor

@cjacek cjacek commented Feb 7, 2025

Apply -wrap arguments to both symbol tables.

Apply -wrap arguments to both symbol tables.
@llvmbot
Copy link
Member

llvmbot commented Feb 7, 2025

@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-coff

Author: Jacek Caban (cjacek)

Changes

Apply -wrap arguments to both symbol tables.


Full diff: https://github.com/llvm/llvm-project/pull/126296.diff

5 Files Affected:

  • (modified) lld/COFF/Driver.cpp (+11-7)
  • (modified) lld/COFF/MinGW.cpp (+12-13)
  • (modified) lld/COFF/MinGW.h (+2-10)
  • (modified) lld/COFF/SymbolTable.h (+10)
  • (added) lld/test/COFF/arm64x-wrap.s (+31)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 281510b7ac6ea6..4e31ebf315b04c 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2573,11 +2573,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     ctx.symtab.addUndefinedGlob(pat);
 
   // Create wrapped symbols for -wrap option.
-  std::vector<WrappedSymbol> wrapped = addWrappedSymbols(ctx, args);
-  // Load more object files that might be needed for wrapped symbols.
-  if (!wrapped.empty())
-    while (run())
-      ;
+  ctx.forEachSymtab([&](SymbolTable &symtab) {
+    addWrappedSymbols(symtab, args);
+    // Load more object files that might be needed for wrapped symbols.
+    if (!symtab.wrapped.empty())
+      while (run())
+        ;
+  });
 
   if (config->autoImport || config->stdcallFixup) {
     // MinGW specific.
@@ -2647,8 +2649,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   run();
 
   // Apply symbol renames for -wrap.
-  if (!wrapped.empty())
-    wrapSymbols(ctx, wrapped);
+  ctx.forEachSymtab([](SymbolTable &symtab) {
+    if (!symtab.wrapped.empty())
+      wrapSymbols(symtab);
+  });
 
   if (isArm64EC(config->machine))
     createECExportThunks();
diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp
index 0786353b06432a..818522b916d10e 100644
--- a/lld/COFF/MinGW.cpp
+++ b/lld/COFF/MinGW.cpp
@@ -207,8 +207,8 @@ static StringRef mangle(Twine sym, MachineTypes machine) {
 // This function instantiates wrapper symbols. At this point, they seem
 // like they are not being used at all, so we explicitly set some flags so
 // that LTO won't eliminate them.
-std::vector<WrappedSymbol>
-lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
+void lld::coff::addWrappedSymbols(SymbolTable &symtab,
+                                  opt::InputArgList &args) {
   std::vector<WrappedSymbol> v;
   DenseSet<StringRef> seen;
 
@@ -217,14 +217,14 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
     if (!seen.insert(name).second)
       continue;
 
-    Symbol *sym = ctx.symtab.findUnderscore(name);
+    Symbol *sym = symtab.findUnderscore(name);
     if (!sym)
       continue;
 
     Symbol *real =
-        ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine));
+        symtab.addUndefined(mangle("__real_" + name, symtab.machine));
     Symbol *wrap =
-        ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine));
+        symtab.addUndefined(mangle("__wrap_" + name, symtab.machine));
     v.push_back({sym, real, wrap});
 
     // These symbols may seem undefined initially, but don't bail out
@@ -243,7 +243,7 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
     if (!isa<Undefined>(wrap))
       wrap->isUsedInRegularObj = true;
   }
-  return v;
+  symtab.wrapped = std::move(v);
 }
 
 // Do renaming for -wrap by updating pointers to symbols.
@@ -251,29 +251,28 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
 // When this function is executed, only InputFiles and symbol table
 // contain pointers to symbol objects. We visit them to replace pointers,
 // so that wrapped symbols are swapped as instructed by the command line.
-void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
-                            ArrayRef<WrappedSymbol> wrapped) {
+void lld::coff::wrapSymbols(SymbolTable &symtab) {
   DenseMap<Symbol *, Symbol *> map;
-  for (const WrappedSymbol &w : wrapped) {
+  for (const WrappedSymbol &w : symtab.wrapped) {
     map[w.sym] = w.wrap;
     map[w.real] = w.sym;
     if (Defined *d = dyn_cast<Defined>(w.wrap)) {
-      Symbol *imp = ctx.symtab.find(("__imp_" + w.sym->getName()).str());
+      Symbol *imp = symtab.find(("__imp_" + w.sym->getName()).str());
       // Create a new defined local import for the wrap symbol. If
       // no imp prefixed symbol existed, there's no need for it.
       // (We can't easily distinguish whether any object file actually
       // referenced it or not, though.)
       if (imp) {
         DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
-            ctx, saver().save("__imp_" + w.wrap->getName()), d);
-        ctx.symtab.localImportChunks.push_back(wrapimp->getChunk());
+            symtab.ctx, saver().save("__imp_" + w.wrap->getName()), d);
+        symtab.localImportChunks.push_back(wrapimp->getChunk());
         map[imp] = wrapimp;
       }
     }
   }
 
   // Update pointers in input files.
-  parallelForEach(ctx.objFileInstances, [&](ObjFile *file) {
+  parallelForEach(symtab.ctx.objFileInstances, [&](ObjFile *file) {
     MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
     for (auto &sym : syms)
       if (Symbol *s = map.lookup(sym))
diff --git a/lld/COFF/MinGW.h b/lld/COFF/MinGW.h
index 265beb315c9a71..48bce74c616bc4 100644
--- a/lld/COFF/MinGW.h
+++ b/lld/COFF/MinGW.h
@@ -55,17 +55,9 @@ void writeDefFile(COFFLinkerContext &, StringRef name,
 // symbol becomes accessible as `__real_foo`, so you can call that from your
 // wrapper.
 //
-// This data structure is instantiated for each -wrap option.
-struct WrappedSymbol {
-  Symbol *sym;
-  Symbol *real;
-  Symbol *wrap;
-};
-
-std::vector<WrappedSymbol> addWrappedSymbols(COFFLinkerContext &ctx,
-                                             llvm::opt::InputArgList &args);
+void addWrappedSymbols(SymbolTable &symtab, llvm::opt::InputArgList &args);
 
-void wrapSymbols(COFFLinkerContext &ctx, ArrayRef<WrappedSymbol> wrapped);
+void wrapSymbols(SymbolTable &symtab);
 
 } // namespace lld::coff
 
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index ff6e8487f07344..b124998809e3ce 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -34,6 +34,13 @@ class LazyArchive;
 class SectionChunk;
 class Symbol;
 
+// This data structure is instantiated for each -wrap option.
+struct WrappedSymbol {
+  Symbol *sym;
+  Symbol *real;
+  Symbol *wrap;
+};
+
 // SymbolTable is a bucket of all known symbols, including defined,
 // undefined, or lazy symbols (the last one is symbols in archive
 // files whose archive members are not yet loaded).
@@ -161,6 +168,9 @@ class SymbolTable {
   Symbol *delayLoadHelper = nullptr;
   Chunk *tailMergeUnwindInfoChunk = nullptr;
 
+  // A list of wrapped symbols.
+  std::vector<WrappedSymbol> wrapped;
+
   void fixupExports();
   void assignExportOrdinals();
   void parseModuleDefs(StringRef path);
diff --git a/lld/test/COFF/arm64x-wrap.s b/lld/test/COFF/arm64x-wrap.s
new file mode 100644
index 00000000000000..4f600e38f7a830
--- /dev/null
+++ b/lld/test/COFF/arm64x-wrap.s
@@ -0,0 +1,31 @@
+// REQUIRES: aarch64
+// RUN: split-file %s %t.dir && cd %t.dir
+
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows test.s -o test-arm64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows other.s -o other-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows other.s -o other-arm64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
+
+// RUN: lld-link -machine:arm64x -dll -noentry test-arm64.obj test-arm64ec.obj other-arm64.obj other-arm64ec.obj \
+// RUN:          loadconfig-arm64.obj loadconfig-arm64ec.obj -out:out.dll -wrap:sym -wrap:nosuchsym
+
+// RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck %s
+// CHECK: 0x180004000 02000000 02000000 01000000 02000000
+// CHECK: 0x180004010 02000000 01000000
+
+#--- test.s
+        .section .test,"dr"
+        .word sym
+        .word __wrap_sym
+        .word __real_sym
+
+#--- other.s
+        .global sym
+        .global __wrap_sym
+        .global __real_sym
+
+sym = 1
+__wrap_sym = 2
+__real_sym = 3

@llvmbot
Copy link
Member

llvmbot commented Feb 7, 2025

@llvm/pr-subscribers-platform-windows

Author: Jacek Caban (cjacek)

Changes

Apply -wrap arguments to both symbol tables.


Full diff: https://github.com/llvm/llvm-project/pull/126296.diff

5 Files Affected:

  • (modified) lld/COFF/Driver.cpp (+11-7)
  • (modified) lld/COFF/MinGW.cpp (+12-13)
  • (modified) lld/COFF/MinGW.h (+2-10)
  • (modified) lld/COFF/SymbolTable.h (+10)
  • (added) lld/test/COFF/arm64x-wrap.s (+31)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 281510b7ac6ea6..4e31ebf315b04c 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2573,11 +2573,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     ctx.symtab.addUndefinedGlob(pat);
 
   // Create wrapped symbols for -wrap option.
-  std::vector<WrappedSymbol> wrapped = addWrappedSymbols(ctx, args);
-  // Load more object files that might be needed for wrapped symbols.
-  if (!wrapped.empty())
-    while (run())
-      ;
+  ctx.forEachSymtab([&](SymbolTable &symtab) {
+    addWrappedSymbols(symtab, args);
+    // Load more object files that might be needed for wrapped symbols.
+    if (!symtab.wrapped.empty())
+      while (run())
+        ;
+  });
 
   if (config->autoImport || config->stdcallFixup) {
     // MinGW specific.
@@ -2647,8 +2649,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   run();
 
   // Apply symbol renames for -wrap.
-  if (!wrapped.empty())
-    wrapSymbols(ctx, wrapped);
+  ctx.forEachSymtab([](SymbolTable &symtab) {
+    if (!symtab.wrapped.empty())
+      wrapSymbols(symtab);
+  });
 
   if (isArm64EC(config->machine))
     createECExportThunks();
diff --git a/lld/COFF/MinGW.cpp b/lld/COFF/MinGW.cpp
index 0786353b06432a..818522b916d10e 100644
--- a/lld/COFF/MinGW.cpp
+++ b/lld/COFF/MinGW.cpp
@@ -207,8 +207,8 @@ static StringRef mangle(Twine sym, MachineTypes machine) {
 // This function instantiates wrapper symbols. At this point, they seem
 // like they are not being used at all, so we explicitly set some flags so
 // that LTO won't eliminate them.
-std::vector<WrappedSymbol>
-lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
+void lld::coff::addWrappedSymbols(SymbolTable &symtab,
+                                  opt::InputArgList &args) {
   std::vector<WrappedSymbol> v;
   DenseSet<StringRef> seen;
 
@@ -217,14 +217,14 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
     if (!seen.insert(name).second)
       continue;
 
-    Symbol *sym = ctx.symtab.findUnderscore(name);
+    Symbol *sym = symtab.findUnderscore(name);
     if (!sym)
       continue;
 
     Symbol *real =
-        ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine));
+        symtab.addUndefined(mangle("__real_" + name, symtab.machine));
     Symbol *wrap =
-        ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine));
+        symtab.addUndefined(mangle("__wrap_" + name, symtab.machine));
     v.push_back({sym, real, wrap});
 
     // These symbols may seem undefined initially, but don't bail out
@@ -243,7 +243,7 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
     if (!isa<Undefined>(wrap))
       wrap->isUsedInRegularObj = true;
   }
-  return v;
+  symtab.wrapped = std::move(v);
 }
 
 // Do renaming for -wrap by updating pointers to symbols.
@@ -251,29 +251,28 @@ lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
 // When this function is executed, only InputFiles and symbol table
 // contain pointers to symbol objects. We visit them to replace pointers,
 // so that wrapped symbols are swapped as instructed by the command line.
-void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
-                            ArrayRef<WrappedSymbol> wrapped) {
+void lld::coff::wrapSymbols(SymbolTable &symtab) {
   DenseMap<Symbol *, Symbol *> map;
-  for (const WrappedSymbol &w : wrapped) {
+  for (const WrappedSymbol &w : symtab.wrapped) {
     map[w.sym] = w.wrap;
     map[w.real] = w.sym;
     if (Defined *d = dyn_cast<Defined>(w.wrap)) {
-      Symbol *imp = ctx.symtab.find(("__imp_" + w.sym->getName()).str());
+      Symbol *imp = symtab.find(("__imp_" + w.sym->getName()).str());
       // Create a new defined local import for the wrap symbol. If
       // no imp prefixed symbol existed, there's no need for it.
       // (We can't easily distinguish whether any object file actually
       // referenced it or not, though.)
       if (imp) {
         DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
-            ctx, saver().save("__imp_" + w.wrap->getName()), d);
-        ctx.symtab.localImportChunks.push_back(wrapimp->getChunk());
+            symtab.ctx, saver().save("__imp_" + w.wrap->getName()), d);
+        symtab.localImportChunks.push_back(wrapimp->getChunk());
         map[imp] = wrapimp;
       }
     }
   }
 
   // Update pointers in input files.
-  parallelForEach(ctx.objFileInstances, [&](ObjFile *file) {
+  parallelForEach(symtab.ctx.objFileInstances, [&](ObjFile *file) {
     MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
     for (auto &sym : syms)
       if (Symbol *s = map.lookup(sym))
diff --git a/lld/COFF/MinGW.h b/lld/COFF/MinGW.h
index 265beb315c9a71..48bce74c616bc4 100644
--- a/lld/COFF/MinGW.h
+++ b/lld/COFF/MinGW.h
@@ -55,17 +55,9 @@ void writeDefFile(COFFLinkerContext &, StringRef name,
 // symbol becomes accessible as `__real_foo`, so you can call that from your
 // wrapper.
 //
-// This data structure is instantiated for each -wrap option.
-struct WrappedSymbol {
-  Symbol *sym;
-  Symbol *real;
-  Symbol *wrap;
-};
-
-std::vector<WrappedSymbol> addWrappedSymbols(COFFLinkerContext &ctx,
-                                             llvm::opt::InputArgList &args);
+void addWrappedSymbols(SymbolTable &symtab, llvm::opt::InputArgList &args);
 
-void wrapSymbols(COFFLinkerContext &ctx, ArrayRef<WrappedSymbol> wrapped);
+void wrapSymbols(SymbolTable &symtab);
 
 } // namespace lld::coff
 
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index ff6e8487f07344..b124998809e3ce 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -34,6 +34,13 @@ class LazyArchive;
 class SectionChunk;
 class Symbol;
 
+// This data structure is instantiated for each -wrap option.
+struct WrappedSymbol {
+  Symbol *sym;
+  Symbol *real;
+  Symbol *wrap;
+};
+
 // SymbolTable is a bucket of all known symbols, including defined,
 // undefined, or lazy symbols (the last one is symbols in archive
 // files whose archive members are not yet loaded).
@@ -161,6 +168,9 @@ class SymbolTable {
   Symbol *delayLoadHelper = nullptr;
   Chunk *tailMergeUnwindInfoChunk = nullptr;
 
+  // A list of wrapped symbols.
+  std::vector<WrappedSymbol> wrapped;
+
   void fixupExports();
   void assignExportOrdinals();
   void parseModuleDefs(StringRef path);
diff --git a/lld/test/COFF/arm64x-wrap.s b/lld/test/COFF/arm64x-wrap.s
new file mode 100644
index 00000000000000..4f600e38f7a830
--- /dev/null
+++ b/lld/test/COFF/arm64x-wrap.s
@@ -0,0 +1,31 @@
+// REQUIRES: aarch64
+// RUN: split-file %s %t.dir && cd %t.dir
+
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows test.s -o test-arm64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows other.s -o other-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows other.s -o other-arm64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
+
+// RUN: lld-link -machine:arm64x -dll -noentry test-arm64.obj test-arm64ec.obj other-arm64.obj other-arm64ec.obj \
+// RUN:          loadconfig-arm64.obj loadconfig-arm64ec.obj -out:out.dll -wrap:sym -wrap:nosuchsym
+
+// RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck %s
+// CHECK: 0x180004000 02000000 02000000 01000000 02000000
+// CHECK: 0x180004010 02000000 01000000
+
+#--- test.s
+        .section .test,"dr"
+        .word sym
+        .word __wrap_sym
+        .word __real_sym
+
+#--- other.s
+        .global sym
+        .global __wrap_sym
+        .global __real_sym
+
+sym = 1
+__wrap_sym = 2
+__real_sym = 3

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

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

LGTM

This is a rather uncommon feature though; did you run into this in actual use somewhere, or did you just notice it while looking at the code, including bits that you see in the code that only is handled for one namespace right now?

@cjacek
Copy link
Contributor Author

cjacek commented Feb 10, 2025

I just noticed it in the code, no actual use. I generally went through the entire code looking for things to change. My thinking was that skipping such cases would make things less consistent, but I don't mind skipping them if you prefer a more pragmatic approach. I don't have any more such cases in my queue, remaining changes that are more practical.

@mstorsjo
Copy link
Member

I just noticed it in the code, no actual use. I generally went through the entire code looking for things to change. My thinking was that skipping such cases would make things less consistent, but I don't mind skipping them if you prefer a more pragmatic approach. I don't have any more such cases in my queue, remaining changes that are more practical.

No, I think it's probably fine to do; I agree that it's good to go through the code to catch cases like this, to make them consistent.

I'm trying to feel how one would be using wrap on arm64x and what the logical behaviour would be. Practically, doing it for both sides feels most logical. I think it's possible that one could want to do it for only one side though, but it'd be messy to express this (so if one wants to do that, it's possible that one would need to supply suitable dummy symbols for the other side).

So I think it's fine to merge this!

@cjacek cjacek merged commit 94d9563 into llvm:main Feb 10, 2025
12 checks passed
@cjacek cjacek deleted the arm64x-wrap branch February 10, 2025 21:52
@cjacek
Copy link
Contributor Author

cjacek commented Feb 10, 2025

Merged, thanks!

Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Feb 11, 2025
Apply `-wrap` arguments to both symbol tables.
joaosaffran pushed a commit to joaosaffran/llvm-project that referenced this pull request Feb 14, 2025
Apply `-wrap` arguments to both symbol tables.
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Feb 24, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Mar 8, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Mar 20, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Apr 2, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Apr 17, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Apr 30, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request May 15, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request May 29, 2025
Apply `-wrap` arguments to both symbol tables.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Jun 13, 2025
Apply `-wrap` arguments to both symbol tables.
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.

3 participants