[lld] [LLD][COFF] Add support for the -defArm64Native argument (PR #123850)

Jacek Caban via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 22 08:02:22 PST 2025


https://github.com/cjacek updated https://github.com/llvm/llvm-project/pull/123850

>From 6e040952235d2c00198fa6637dc0025d970f86ed Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Sat, 18 Jan 2025 22:24:13 +0100
Subject: [PATCH 1/2] [LLD][COFF] Use EC symbol table for exports defined in
 module definition files

---
 lld/COFF/Driver.cpp              | 64 +-------------------------------
 lld/COFF/Driver.h                |  2 -
 lld/COFF/SymbolTable.cpp         | 63 +++++++++++++++++++++++++++++++
 lld/COFF/SymbolTable.h           |  1 +
 lld/test/COFF/arm64x-export.test | 12 ++++++
 5 files changed, 77 insertions(+), 65 deletions(-)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 4e0678282eed01..3fde9c84d977db 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -30,7 +30,6 @@
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/ArchiveWriter.h"
 #include "llvm/Object/COFFImportFile.h"
-#include "llvm/Object/COFFModuleDefinition.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/Option.h"
@@ -1012,67 +1011,6 @@ void LinkerDriver::createImportLibrary(bool asLib) {
   }
 }
 
-void LinkerDriver::parseModuleDefs(StringRef path) {
-  llvm::TimeTraceScope timeScope("Parse def file");
-  std::unique_ptr<MemoryBuffer> mb =
-      CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
-                                  /*RequiresNullTerminator=*/false,
-                                  /*IsVolatile=*/true),
-            "could not open " + path);
-  COFFModuleDefinition m = check(parseCOFFModuleDefinition(
-      mb->getMemBufferRef(), ctx.config.machine, ctx.config.mingw));
-
-  // Include in /reproduce: output if applicable.
-  ctx.driver.takeBuffer(std::move(mb));
-
-  if (ctx.config.outputFile.empty())
-    ctx.config.outputFile = std::string(saver().save(m.OutputFile));
-  ctx.config.importName = std::string(saver().save(m.ImportName));
-  if (m.ImageBase)
-    ctx.config.imageBase = m.ImageBase;
-  if (m.StackReserve)
-    ctx.config.stackReserve = m.StackReserve;
-  if (m.StackCommit)
-    ctx.config.stackCommit = m.StackCommit;
-  if (m.HeapReserve)
-    ctx.config.heapReserve = m.HeapReserve;
-  if (m.HeapCommit)
-    ctx.config.heapCommit = m.HeapCommit;
-  if (m.MajorImageVersion)
-    ctx.config.majorImageVersion = m.MajorImageVersion;
-  if (m.MinorImageVersion)
-    ctx.config.minorImageVersion = m.MinorImageVersion;
-  if (m.MajorOSVersion)
-    ctx.config.majorOSVersion = m.MajorOSVersion;
-  if (m.MinorOSVersion)
-    ctx.config.minorOSVersion = m.MinorOSVersion;
-
-  for (COFFShortExport e1 : m.Exports) {
-    Export e2;
-    // Renamed exports are parsed and set as "ExtName = Name". If Name has
-    // the form "OtherDll.Func", it shouldn't be a normal exported
-    // function but a forward to another DLL instead. This is supported
-    // by both MS and GNU linkers.
-    if (!e1.ExtName.empty() && e1.ExtName != e1.Name &&
-        StringRef(e1.Name).contains('.')) {
-      e2.name = saver().save(e1.ExtName);
-      e2.forwardTo = saver().save(e1.Name);
-    } else {
-      e2.name = saver().save(e1.Name);
-      e2.extName = saver().save(e1.ExtName);
-    }
-    e2.exportAs = saver().save(e1.ExportAs);
-    e2.importName = saver().save(e1.ImportName);
-    e2.ordinal = e1.Ordinal;
-    e2.noname = e1.Noname;
-    e2.data = e1.Data;
-    e2.isPrivate = e1.Private;
-    e2.constant = e1.Constant;
-    e2.source = ExportSource::ModuleDefinition;
-    ctx.symtab.exports.push_back(e2);
-  }
-}
-
 void LinkerDriver::enqueueTask(std::function<void()> task) {
   taskQueue.push_back(std::move(task));
 }
@@ -2352,7 +2290,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Handle /def
   if (auto *arg = args.getLastArg(OPT_deffile)) {
     // parseModuleDefs mutates Config object.
-    parseModuleDefs(arg->getValue());
+    mainSymtab.parseModuleDefs(arg->getValue());
   }
 
   // Handle generation of import library from a def file.
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 12724cbd1eef49..58dc5458e9a544 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -143,8 +143,6 @@ class LinkerDriver {
   // Used by the resolver to parse .drectve section contents.
   void parseDirectives(InputFile *file);
 
-  void parseModuleDefs(StringRef path);
-
   // Parse an /order file. If an option is given, the linker places COMDAT
   // sections int he same order as their names appear in the given file.
   void parseOrderFile(StringRef arg);
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index ecccc7d6ed70c7..32ea4a5b2e1fc3 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -20,6 +20,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/LTO/LTO.h"
+#include "llvm/Object/COFFModuleDefinition.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/GlobPattern.h"
 #include "llvm/Support/Parallel.h"
@@ -29,6 +30,7 @@
 
 using namespace llvm;
 using namespace llvm::COFF;
+using namespace llvm::object;
 using namespace llvm::support;
 
 namespace lld::coff {
@@ -1253,6 +1255,67 @@ void SymbolTable::assignExportOrdinals() {
                << Twine(std::numeric_limits<uint16_t>::max()) << ")";
 }
 
+void SymbolTable::parseModuleDefs(StringRef path) {
+  llvm::TimeTraceScope timeScope("Parse def file");
+  std::unique_ptr<MemoryBuffer> mb =
+      CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
+                                  /*RequiresNullTerminator=*/false,
+                                  /*IsVolatile=*/true),
+            "could not open " + path);
+  COFFModuleDefinition m = check(parseCOFFModuleDefinition(
+      mb->getMemBufferRef(), machine, ctx.config.mingw));
+
+  // Include in /reproduce: output if applicable.
+  ctx.driver.takeBuffer(std::move(mb));
+
+  if (ctx.config.outputFile.empty())
+    ctx.config.outputFile = std::string(saver().save(m.OutputFile));
+  ctx.config.importName = std::string(saver().save(m.ImportName));
+  if (m.ImageBase)
+    ctx.config.imageBase = m.ImageBase;
+  if (m.StackReserve)
+    ctx.config.stackReserve = m.StackReserve;
+  if (m.StackCommit)
+    ctx.config.stackCommit = m.StackCommit;
+  if (m.HeapReserve)
+    ctx.config.heapReserve = m.HeapReserve;
+  if (m.HeapCommit)
+    ctx.config.heapCommit = m.HeapCommit;
+  if (m.MajorImageVersion)
+    ctx.config.majorImageVersion = m.MajorImageVersion;
+  if (m.MinorImageVersion)
+    ctx.config.minorImageVersion = m.MinorImageVersion;
+  if (m.MajorOSVersion)
+    ctx.config.majorOSVersion = m.MajorOSVersion;
+  if (m.MinorOSVersion)
+    ctx.config.minorOSVersion = m.MinorOSVersion;
+
+  for (COFFShortExport e1 : m.Exports) {
+    Export e2;
+    // Renamed exports are parsed and set as "ExtName = Name". If Name has
+    // the form "OtherDll.Func", it shouldn't be a normal exported
+    // function but a forward to another DLL instead. This is supported
+    // by both MS and GNU linkers.
+    if (!e1.ExtName.empty() && e1.ExtName != e1.Name &&
+        StringRef(e1.Name).contains('.')) {
+      e2.name = saver().save(e1.ExtName);
+      e2.forwardTo = saver().save(e1.Name);
+    } else {
+      e2.name = saver().save(e1.Name);
+      e2.extName = saver().save(e1.ExtName);
+    }
+    e2.exportAs = saver().save(e1.ExportAs);
+    e2.importName = saver().save(e1.ImportName);
+    e2.ordinal = e1.Ordinal;
+    e2.noname = e1.Noname;
+    e2.data = e1.Data;
+    e2.isPrivate = e1.Private;
+    e2.constant = e1.Constant;
+    e2.source = ExportSource::ModuleDefinition;
+    exports.push_back(e2);
+  }
+}
+
 Symbol *SymbolTable::addUndefined(StringRef name) {
   return addUndefined(name, nullptr, false);
 }
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index e5b02ce5904c49..c8d7251838842f 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -160,6 +160,7 @@ class SymbolTable {
 
   void fixupExports();
   void assignExportOrdinals();
+  void parseModuleDefs(StringRef path);
 
   // Iterates symbols in non-determinstic hash table order.
   template <typename T> void forEachSymbol(T callback) {
diff --git a/lld/test/COFF/arm64x-export.test b/lld/test/COFF/arm64x-export.test
index 526be633973581..a78b291cedbe10 100644
--- a/lld/test/COFF/arm64x-export.test
+++ b/lld/test/COFF/arm64x-export.test
@@ -55,6 +55,13 @@ RUN:          loadconfig-arm64.obj loadconfig-arm64ec.obj arm64ec-drectve.obj -n
 RUN: llvm-objdump -d out-drectve-ec.dll | FileCheck --check-prefix=DISASM-EC %s
 RUN: llvm-readobj --headers --coff-exports out-drectve-ec.dll | FileCheck --check-prefix=EXPORTS-EC %s
 
+# A command-line def file applies only to EC exports.
+
+RUN: lld-link -machine:arm64x -dll -out:out-def-ec.dll arm64ec-func.obj arm64-func.obj \
+RUN:          loadconfig-arm64.obj loadconfig-arm64ec.obj -def:func.def -noentry
+RUN: llvm-objdump -d out-def-ec.dll | FileCheck --check-prefix=DISASM-EC %s
+RUN: llvm-readobj --headers --coff-exports out-def-ec.dll | FileCheck --check-prefix=EXPORTS-EC %s
+
 # Export using the EC .edata section.
 
 RUN: lld-link -machine:arm64x -dll -out:out-edata-ec.dll arm64ec-func.obj arm64-func.obj \
@@ -227,3 +234,8 @@ funcname_func:
 
 name:
     .asciz "out-edata.dll"
+
+#--- func.def
+LIBRARY out.dll
+EXPORTS
+        func

>From 53be74f468b6e05babc9a84aa2a304bbad7b2bb1 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Sat, 18 Jan 2025 22:47:15 +0100
Subject: [PATCH 2/2] [LLD][COFF] Add support for the -defArm64Native argument

MSVC ignores the /defArm64Native argument on non-ARM64X targets.
It is also ignored if the /def option is not specified.
---
 lld/COFF/Driver.cpp              |  9 +++++++++
 lld/COFF/Options.td              |  3 +++
 lld/test/COFF/arm64x-export.test | 23 +++++++++++++++++++++++
 3 files changed, 35 insertions(+)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 3fde9c84d977db..6eea11f5f451fd 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1795,6 +1795,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       setMachine(machine);
     }
   }
+
+  // Most of main arguments apply either to both or only to EC symbol table on
+  // ARM64X target.
   SymbolTable &mainSymtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
 
   // Handle /nodefaultlib:<filename>
@@ -2291,6 +2294,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (auto *arg = args.getLastArg(OPT_deffile)) {
     // parseModuleDefs mutates Config object.
     mainSymtab.parseModuleDefs(arg->getValue());
+    if (ctx.hybridSymtab) {
+      // MSVC ignores the /defArm64Native argument on non-ARM64X targets.
+      // It is also ignored if the /def option is not specified.
+      if (auto *arg = args.getLastArg(OPT_defarm64native))
+        ctx.symtab.parseModuleDefs(arg->getValue());
+    }
   }
 
   // Handle generation of import library from a def file.
diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td
index c7ceb51f70b70a..b6fd3d0daaef99 100644
--- a/lld/COFF/Options.td
+++ b/lld/COFF/Options.td
@@ -140,6 +140,9 @@ def incl : Joined<["/", "-", "/?", "-?"], "include:">,
 def deffile : Joined<["/", "-", "/?", "-?"], "def:">,
     HelpText<"Use module-definition file">;
 
+def defarm64native
+    : P<"defarm64native",
+        "Use a module-definition file for the native view in a hybrid image.">;
 def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
 def debug_opt : P<"debug", "Embed a symbol table in the image with option">;
 def debugtype : P<"debugtype", "Debug Info Options">;
diff --git a/lld/test/COFF/arm64x-export.test b/lld/test/COFF/arm64x-export.test
index a78b291cedbe10..3ae0725a67089f 100644
--- a/lld/test/COFF/arm64x-export.test
+++ b/lld/test/COFF/arm64x-export.test
@@ -162,6 +162,29 @@ EXPORTS-BOTH-NEXT:     RVA: 0x3000
 EXPORTS-BOTH-NEXT:   }
 EXPORTS-BOTH-NEXT: }
 
+# Export using both the -def and -defarm64native arguments.
+
+RUN: lld-link -machine:arm64x -dll -out:out-def-both.dll arm64ec-func.obj arm64-func.obj \
+RUN:          loadconfig-arm64.obj loadconfig-arm64ec.obj -def:func.def -defarm64native:func.def -noentry
+RUN: llvm-objdump -d out-def-both.dll | FileCheck --check-prefix=DISASM-BOTH %s
+RUN: llvm-readobj --headers --coff-exports out-def-both.dll | FileCheck --check-prefix=EXPORTS-BOTH %s
+
+# -defarm64native is ignored if -def is not specified.
+
+RUN: lld-link -machine:arm64x -dll -out:out-def-native.dll arm64ec-func.obj arm64-func.obj \
+RUN:          loadconfig-arm64.obj loadconfig-arm64ec.obj -defarm64native:func.def -noentry
+RUN: llvm-readobj --headers --coff-exports out-def-native.dll | FileCheck --check-prefix=NO-EXPORT %s
+NO-EXPORT:  ExportTableRVA: 0x0
+NO-EXPORT:  ExportTableSize: 0x0
+NO-EXPORT:  HybridObject {
+NO-EXPORT:    ExportTableRVA: 0x0
+NO-EXPORT:    ExportTableSize: 0x0
+NO-EXPORT:  }
+
+# -defarm64native is ignored on ARM64 target.
+
+RUN: lld-link -machine:arm64 -dll -out:out-arm64-def.dll arm64-func.obj -defarm64native:invalid.def -def:func.def -noentry 2>&1 | count 0
+
 # Export using both the native and EC .edata sections.
 
 RUN: lld-link -machine:arm64x -dll -out:out-edata-both.dll arm64ec-func.obj arm64-func.obj \



More information about the llvm-commits mailing list