[lld] [LLD][COFF] Add support for custom DOS stub (PR #122561)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 15 08:53:42 PST 2025


https://github.com/kkent030315 updated https://github.com/llvm/llvm-project/pull/122561

>From 446d70ab9ccab29f7cc61734ba79db08b98e4da7 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Sat, 11 Jan 2025 09:53:21 +0900
Subject: [PATCH 01/11] [LLD][COFF] Add support for custom DOS stub

This change implements support for the /stub flag to align with MS link.exe. This option is useful when a program needs to optimize the DOS program that executes when the PE runs on DOS, avoiding the traditional hardcoded DOS program in LLD.
---
 lld/COFF/Config.h              |   1 +
 lld/COFF/Driver.cpp            |   4 +++
 lld/COFF/Driver.h              |   3 ++
 lld/COFF/DriverUtils.cpp       |  15 +++++++++
 lld/COFF/Writer.cpp            |  60 ++++++++++++++++++++++-----------
 lld/test/COFF/Inputs/stub511mz | Bin 0 -> 63 bytes
 lld/test/COFF/Inputs/stub512mz | Bin 0 -> 64 bytes
 lld/test/COFF/Inputs/stub512zz | Bin 0 -> 64 bytes
 lld/test/COFF/Inputs/stub516mz | Bin 0 -> 68 bytes
 lld/test/COFF/stub.test        |  31 +++++++++++++++++
 10 files changed, 95 insertions(+), 19 deletions(-)
 create mode 100644 lld/test/COFF/Inputs/stub511mz
 create mode 100644 lld/test/COFF/Inputs/stub512mz
 create mode 100644 lld/test/COFF/Inputs/stub512zz
 create mode 100644 lld/test/COFF/Inputs/stub516mz
 create mode 100644 lld/test/COFF/stub.test

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 9e6b17e87c9e70..294df1912ff5ae 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -115,6 +115,7 @@ struct Configuration {
   enum ManifestKind { Default, SideBySide, Embed, No };
   bool is64() const { return llvm::COFF::is64Bit(machine); }
 
+  llvm::SmallVector<uint8_t> stub;
   llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
   bool machineInferred = false;
   size_t wordsize;
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 791382fd9bdd4a..14619e7af236bb 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2418,6 +2418,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     config->noSEH = args.hasArg(OPT_noseh);
   }
 
+  // Handle /stub
+  if (auto *arg = args.getLastArg(OPT_stub))
+    parseStub(arg->getValue());
+
   // Handle /functionpadmin
   for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
     parseFunctionPadMin(arg);
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 51325689042981..9afff23a7ed255 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -236,6 +236,9 @@ class LinkerDriver {
   void parseSection(StringRef);
   void parseAligncomm(StringRef);
 
+  // Parses a MS-DOS stub file
+  void parseStub(StringRef path);
+
   // Parses a string in the form of "[:<integer>]"
   void parseFunctionPadMin(llvm::opt::Arg *a);
 
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index 1148be09fb10cc..fdee5dcbc26d14 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -246,6 +246,21 @@ void LinkerDriver::parseAligncomm(StringRef s) {
       std::max(ctx.config.alignComm[std::string(name)], 1 << v);
 }
 
+void LinkerDriver::parseStub(StringRef path) {
+  std::unique_ptr<MemoryBuffer> stub =
+      CHECK(MemoryBuffer::getFile(path), "could not open " + path);
+  size_t bufferSize = stub->getBufferSize();
+  const char *bufferStart = stub->getBufferStart();
+  // MS link.exe compatibility:
+  // 1. stub must be greater or equal than 64 bytes
+  // 2. stub must be 8-byte aligned
+  // 3. stub must be start with a valid dos signature 'MZ'
+  if (bufferSize < 0x40 || bufferSize % 8 != 0 ||
+      (bufferStart[0] != 'M' || bufferStart[1] != 'Z'))
+    Err(ctx) << "/stub: invalid format for MS-DOS stub file: " << path;
+  ctx.config.stub.append(bufferStart, bufferStart + bufferSize);
+}
+
 // Parses /functionpadmin option argument.
 void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) {
   StringRef arg = a->getNumValues() ? a->getValue() : "";
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index eb82a9cc015933..29543da5419833 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -76,14 +76,8 @@ static unsigned char dosProgram[] = {
 };
 static_assert(sizeof(dosProgram) % 8 == 0,
               "DOSProgram size must be multiple of 8");
-
-static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram);
-static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8");
-static const uint32_t coffHeaderOffset = dosStubSize + sizeof(PEMagic);
-static const uint32_t peHeaderOffset =
-    coffHeaderOffset + sizeof(coff_file_header);
-static const uint32_t dataDirOffset64 =
-    peHeaderOffset + sizeof(pe32plus_header);
+static_assert((sizeof(dos_header) + sizeof(dosProgram)) % 8 == 0,
+              "DOSStub size must be multiple of 8");
 
 static const int numberOfDataDirectory = 16;
 
@@ -214,6 +208,7 @@ class Writer {
   void run();
 
 private:
+  void calculateStubDependentSizes();
   void createSections();
   void createMiscChunks();
   void createImportTables();
@@ -315,6 +310,11 @@ class Writer {
   uint64_t sizeOfImage;
   uint64_t sizeOfHeaders;
 
+  uint32_t dosStubSize;
+  uint32_t coffHeaderOffset;
+  uint32_t peHeaderOffset;
+  uint32_t dataDirOffset64;
+
   OutputSection *textSec;
   OutputSection *hexpthkSec;
   OutputSection *rdataSec;
@@ -762,6 +762,7 @@ void Writer::run() {
     llvm::TimeTraceScope timeScope("Write PE");
     ScopedTimer t1(ctx.codeLayoutTimer);
 
+    calculateStubDependentSizes();
     if (ctx.config.machine == ARM64X)
       ctx.dynamicRelocs = make<DynamicRelocsChunk>();
     createImportTables();
@@ -1035,6 +1036,17 @@ void Writer::sortSections() {
       sortBySectionOrder(it.second->chunks);
 }
 
+void Writer::calculateStubDependentSizes() {
+  if (ctx.config.stub.size())
+    dosStubSize = ctx.config.stub.size();
+  else
+    dosStubSize = sizeof(dos_header) + sizeof(dosProgram);
+
+  coffHeaderOffset = dosStubSize + sizeof(PEMagic);
+  peHeaderOffset = coffHeaderOffset + sizeof(coff_file_header);
+  dataDirOffset64 = peHeaderOffset + sizeof(pe32plus_header);
+}
+
 // Create output section objects and add them to OutputSections.
 void Writer::createSections() {
   llvm::TimeTraceScope timeScope("Output sections");
@@ -1668,21 +1680,31 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   // When run under Windows, the loader looks at AddressOfNewExeHeader and uses
   // the PE header instead.
   Configuration *config = &ctx.config;
+
   uint8_t *buf = buffer->getBufferStart();
   auto *dos = reinterpret_cast<dos_header *>(buf);
-  buf += sizeof(dos_header);
-  dos->Magic[0] = 'M';
-  dos->Magic[1] = 'Z';
-  dos->UsedBytesInTheLastPage = dosStubSize % 512;
-  dos->FileSizeInPages = divideCeil(dosStubSize, 512);
-  dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16;
-
-  dos->AddressOfRelocationTable = sizeof(dos_header);
-  dos->AddressOfNewExeHeader = dosStubSize;
 
   // Write DOS program.
-  memcpy(buf, dosProgram, sizeof(dosProgram));
-  buf += sizeof(dosProgram);
+  if (config->stub.size()) {
+    memcpy(buf, config->stub.data(), config->stub.size());
+    // MS link.exe accepts an invalid `e_lfanew` and updates it automatically.
+    // Replicate the same behaviour.
+    dos->AddressOfNewExeHeader = config->stub.size();
+    buf += config->stub.size();
+  } else {
+    buf += sizeof(dos_header);
+    dos->Magic[0] = 'M';
+    dos->Magic[1] = 'Z';
+    dos->UsedBytesInTheLastPage = dosStubSize % 512;
+    dos->FileSizeInPages = divideCeil(dosStubSize, 512);
+    dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16;
+
+    dos->AddressOfRelocationTable = sizeof(dos_header);
+    dos->AddressOfNewExeHeader = dosStubSize;
+
+    memcpy(buf, dosProgram, sizeof(dosProgram));
+    buf += sizeof(dosProgram);
+  }
 
   // Write PE magic
   memcpy(buf, PEMagic, sizeof(PEMagic));
diff --git a/lld/test/COFF/Inputs/stub511mz b/lld/test/COFF/Inputs/stub511mz
new file mode 100644
index 0000000000000000000000000000000000000000..2a8954d2d6917fc39b8f285a84b470d19cd775c1
GIT binary patch
literal 63
ecmeZ`n!v!!z`(!)#Q*;@Fzf)*Am9Kd2@(L@{sc7u

literal 0
HcmV?d00001

diff --git a/lld/test/COFF/Inputs/stub512mz b/lld/test/COFF/Inputs/stub512mz
new file mode 100644
index 0000000000000000000000000000000000000000..aaeb005adb54cb6bf5a89f8490884eea07f898d0
GIT binary patch
literal 64
ecmeZ`n!v!!z`(!)#Q*;@Fzf)*Am9Kd2@?R}GXymN

literal 0
HcmV?d00001

diff --git a/lld/test/COFF/Inputs/stub512zz b/lld/test/COFF/Inputs/stub512zz
new file mode 100644
index 0000000000000000000000000000000000000000..fa58df18aabe77009483019e8dee9d72fb051a50
GIT binary patch
literal 64
ecma!wn!v!!z`(!)#Q*;@Fzf)*Am9Kd2@?S1a|A*F

literal 0
HcmV?d00001

diff --git a/lld/test/COFF/Inputs/stub516mz b/lld/test/COFF/Inputs/stub516mz
new file mode 100644
index 0000000000000000000000000000000000000000..a4ee6b2291e67284e2b5bf1587aed06111af5d60
GIT binary patch
literal 68
ecma!wn!v!!z`(!)#Q*;@Fzf)*Am9Kdi6a2 at djvuN

literal 0
HcmV?d00001

diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test
new file mode 100644
index 00000000000000..2d5ad351d407d2
--- /dev/null
+++ b/lld/test/COFF/stub.test
@@ -0,0 +1,31 @@
+# RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
+
+# RUN: lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512mz %t.obj
+# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=CHECK1 %s
+
+CHECK1: Magic: MZ
+CHECK1: UsedBytesInTheLastPage: 144
+CHECK1: FileSizeInPages: 3
+CHECK1: NumberOfRelocationItems: 0
+CHECK1: HeaderSizeInParagraphs: 4
+CHECK1: MinimumExtraParagraphs: 0
+CHECK1: MaximumExtraParagraphs: 65535
+CHECK1: InitialRelativeSS: 0
+CHECK1: InitialSP: 184
+CHECK1: Checksum: 0
+CHECK1: InitialIP: 0
+CHECK1: InitialRelativeCS: 0
+CHECK1: AddressOfRelocationTable: 64
+CHECK1: OverlayNumber: 0
+CHECK1: OEMid: 0
+CHECK1: OEMinfo: 0
+CHECK1: AddressOfNewExeHeader: 64
+
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK2 %s
+# CHECK2: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
+
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub516mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK3 %s
+# CHECK3: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
+
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub511mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK4 %s
+# CHECK4: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}

>From 7cb0b007c1f0680119f81a621e7d57811cc450cb Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 09:00:47 +0900
Subject: [PATCH 02/11] mention that `e_lfanew` = `AddressOfNewExeHeader`

---
 lld/COFF/Writer.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 29543da5419833..b19dbbac7cf6de 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1687,8 +1687,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   // Write DOS program.
   if (config->stub.size()) {
     memcpy(buf, config->stub.data(), config->stub.size());
-    // MS link.exe accepts an invalid `e_lfanew` and updates it automatically.
-    // Replicate the same behaviour.
+    // MS link.exe accepts an invalid `e_lfanew` (AddressOfNewExeHeader) and
+    // updates it automatically. Replicate the same behaviour.
     dos->AddressOfNewExeHeader = config->stub.size();
     buf += config->stub.size();
   } else {

>From 2fa13d683183c7363c5df45ea3dfd0e0cf722487 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 09:05:04 +0900
Subject: [PATCH 03/11] Add comment for each tests

---
 lld/test/COFF/stub.test | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test
index 2d5ad351d407d2..a5ab2657f0af4e 100644
--- a/lld/test/COFF/stub.test
+++ b/lld/test/COFF/stub.test
@@ -21,11 +21,14 @@ CHECK1: OEMid: 0
 CHECK1: OEMinfo: 0
 CHECK1: AddressOfNewExeHeader: 64
 
+## Invalid DOS signature (must be `MZ`)
 # RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK2 %s
 # CHECK2: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
 
+## Unaligned stub (must be aligned to 8)
 # RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub516mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK3 %s
 # CHECK3: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
 
+## Too-small stub (must be at least 64 bytes long)
 # RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub511mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK4 %s
 # CHECK4: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}

>From 202a08d172d402805d668072832faec39c583c9d Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 09:11:43 +0900
Subject: [PATCH 04/11] combine checks to `CHECK-INVALID`

---
 lld/test/COFF/stub.test | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test
index a5ab2657f0af4e..11b820c86cf87c 100644
--- a/lld/test/COFF/stub.test
+++ b/lld/test/COFF/stub.test
@@ -22,13 +22,12 @@ CHECK1: OEMinfo: 0
 CHECK1: AddressOfNewExeHeader: 64
 
 ## Invalid DOS signature (must be `MZ`)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK2 %s
-# CHECK2: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
 
 ## Unaligned stub (must be aligned to 8)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub516mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK3 %s
-# CHECK3: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub516mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
 
 ## Too-small stub (must be at least 64 bytes long)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub511mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK4 %s
-# CHECK4: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub511mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
+
+# CHECK-INVALID: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}

>From 9f97f3ab19baefc294e9a54cc836c4fb1dba099f Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 19:13:43 +0900
Subject: [PATCH 05/11] Use mmap file for stub

---
 lld/COFF/Config.h        |  2 +-
 lld/COFF/DriverUtils.cpp |  2 +-
 lld/COFF/Writer.cpp      | 12 ++++++------
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 294df1912ff5ae..69c79efddabf85 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -115,7 +115,7 @@ struct Configuration {
   enum ManifestKind { Default, SideBySide, Embed, No };
   bool is64() const { return llvm::COFF::is64Bit(machine); }
 
-  llvm::SmallVector<uint8_t> stub;
+  std::unique_ptr<MemoryBuffer> stub;
   llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
   bool machineInferred = false;
   size_t wordsize;
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index fdee5dcbc26d14..d6a5127daac319 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -258,7 +258,7 @@ void LinkerDriver::parseStub(StringRef path) {
   if (bufferSize < 0x40 || bufferSize % 8 != 0 ||
       (bufferStart[0] != 'M' || bufferStart[1] != 'Z'))
     Err(ctx) << "/stub: invalid format for MS-DOS stub file: " << path;
-  ctx.config.stub.append(bufferStart, bufferStart + bufferSize);
+  ctx.config.stub = std::move(stub);
 }
 
 // Parses /functionpadmin option argument.
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index b19dbbac7cf6de..388347738a22ad 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1037,8 +1037,8 @@ void Writer::sortSections() {
 }
 
 void Writer::calculateStubDependentSizes() {
-  if (ctx.config.stub.size())
-    dosStubSize = ctx.config.stub.size();
+  if (ctx.config.stub)
+    dosStubSize = ctx.config.stub->getBufferSize();
   else
     dosStubSize = sizeof(dos_header) + sizeof(dosProgram);
 
@@ -1685,12 +1685,12 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   auto *dos = reinterpret_cast<dos_header *>(buf);
 
   // Write DOS program.
-  if (config->stub.size()) {
-    memcpy(buf, config->stub.data(), config->stub.size());
+  if (config->stub) {
+    memcpy(buf, config->stub->getBufferStart(), config->stub->getBufferSize());
     // MS link.exe accepts an invalid `e_lfanew` (AddressOfNewExeHeader) and
     // updates it automatically. Replicate the same behaviour.
-    dos->AddressOfNewExeHeader = config->stub.size();
-    buf += config->stub.size();
+    dos->AddressOfNewExeHeader = config->stub->getBufferSize();
+    buf += config->stub->getBufferSize();
   } else {
     buf += sizeof(dos_header);
     dos->Magic[0] = 'M';

>From a890923dee06bcbd0f6a04ad4754fc30cdfb8d4d Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 19:55:51 +0900
Subject: [PATCH 06/11] fix typo in comment

---
 lld/COFF/DriverUtils.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index d6a5127daac319..85db485d1fd9c0 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -252,7 +252,7 @@ void LinkerDriver::parseStub(StringRef path) {
   size_t bufferSize = stub->getBufferSize();
   const char *bufferStart = stub->getBufferStart();
   // MS link.exe compatibility:
-  // 1. stub must be greater or equal than 64 bytes
+  // 1. stub must be greater than or equal to 64 bytes
   // 2. stub must be 8-byte aligned
   // 3. stub must be start with a valid dos signature 'MZ'
   if (bufferSize < 0x40 || bufferSize % 8 != 0 ||

>From d20afac317e7389ad8e4287275a99b9908ba8fb3 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 20:07:12 +0900
Subject: [PATCH 07/11] fix filenames

---
 lld/test/COFF/Inputs/{stub511mz => stub63mz} | Bin
 lld/test/COFF/Inputs/{stub512mz => stub64mz} | Bin
 lld/test/COFF/Inputs/{stub512zz => stub64zz} | Bin
 lld/test/COFF/Inputs/{stub516mz => stub68mz} | Bin
 4 files changed, 0 insertions(+), 0 deletions(-)
 rename lld/test/COFF/Inputs/{stub511mz => stub63mz} (100%)
 rename lld/test/COFF/Inputs/{stub512mz => stub64mz} (100%)
 rename lld/test/COFF/Inputs/{stub512zz => stub64zz} (100%)
 rename lld/test/COFF/Inputs/{stub516mz => stub68mz} (100%)

diff --git a/lld/test/COFF/Inputs/stub511mz b/lld/test/COFF/Inputs/stub63mz
similarity index 100%
rename from lld/test/COFF/Inputs/stub511mz
rename to lld/test/COFF/Inputs/stub63mz
diff --git a/lld/test/COFF/Inputs/stub512mz b/lld/test/COFF/Inputs/stub64mz
similarity index 100%
rename from lld/test/COFF/Inputs/stub512mz
rename to lld/test/COFF/Inputs/stub64mz
diff --git a/lld/test/COFF/Inputs/stub512zz b/lld/test/COFF/Inputs/stub64zz
similarity index 100%
rename from lld/test/COFF/Inputs/stub512zz
rename to lld/test/COFF/Inputs/stub64zz
diff --git a/lld/test/COFF/Inputs/stub516mz b/lld/test/COFF/Inputs/stub68mz
similarity index 100%
rename from lld/test/COFF/Inputs/stub516mz
rename to lld/test/COFF/Inputs/stub68mz

>From 0d1b1ccba8af2f1b17f2f6b5e349d6b2093e89b6 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 20:33:34 +0900
Subject: [PATCH 08/11] improve error messages

---
 lld/COFF/DriverUtils.cpp | 10 +++++++---
 lld/test/COFF/stub.test  | 15 ++++++++-------
 2 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index 85db485d1fd9c0..2dacd67f02399f 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -255,9 +255,13 @@ void LinkerDriver::parseStub(StringRef path) {
   // 1. stub must be greater than or equal to 64 bytes
   // 2. stub must be 8-byte aligned
   // 3. stub must be start with a valid dos signature 'MZ'
-  if (bufferSize < 0x40 || bufferSize % 8 != 0 ||
-      (bufferStart[0] != 'M' || bufferStart[1] != 'Z'))
-    Err(ctx) << "/stub: invalid format for MS-DOS stub file: " << path;
+  if (bufferSize < 0x40)
+    Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: "
+             << path;
+  if (bufferStart[0] != 'M' || bufferStart[1] != 'Z')
+    Err(ctx) << "/stub: invalid DOS signature: " << path;
+  if (bufferSize % 8 != 0)
+    Err(ctx) << "/stub: stub must be aligned to 8 bytes: " << path;
   ctx.config.stub = std::move(stub);
 }
 
diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test
index 11b820c86cf87c..630be3c4479f54 100644
--- a/lld/test/COFF/stub.test
+++ b/lld/test/COFF/stub.test
@@ -1,6 +1,6 @@
 # RUN: yaml2obj %p/Inputs/ret42.yaml -o %t.obj
 
-# RUN: lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512mz %t.obj
+# RUN: lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub64mz %t.obj
 # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=CHECK1 %s
 
 CHECK1: Magic: MZ
@@ -22,12 +22,13 @@ CHECK1: OEMinfo: 0
 CHECK1: AddressOfNewExeHeader: 64
 
 ## Invalid DOS signature (must be `MZ`)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub512zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub64zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK2 %s
+# CHECK2: lld-link: error: /stub: invalid DOS signature: {{.*}}
 
 ## Unaligned stub (must be aligned to 8)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub516mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub68mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK3 %s
+# CHECK3: lld-link: error: /stub: stub must be aligned to 8 bytes: {{.*}}
 
-## Too-small stub (must be at least 64 bytes long)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub511mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK-INVALID %s
-
-# CHECK-INVALID: lld-link: error: /stub: invalid format for MS-DOS stub file: {{.*}}
+## Too-small stub (must be at least 64 bytes long) && Unaligned
+# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub63mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK4 %s
+# CHECK4: lld-link: error: /stub: stub must be greater than or equal to 64 bytes: {{.*}}

>From 8ba3ea463e7f7e8695bf74bf7f1d9ea71e613479 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Tue, 14 Jan 2025 20:38:41 +0900
Subject: [PATCH 09/11] stub -> dosStub and parseStub -> parseDosStub

---
 lld/COFF/Config.h        |  2 +-
 lld/COFF/Driver.cpp      |  2 +-
 lld/COFF/Driver.h        |  2 +-
 lld/COFF/DriverUtils.cpp |  4 ++--
 lld/COFF/Writer.cpp      | 13 +++++++------
 5 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 69c79efddabf85..687c7b95292514 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -115,7 +115,7 @@ struct Configuration {
   enum ManifestKind { Default, SideBySide, Embed, No };
   bool is64() const { return llvm::COFF::is64Bit(machine); }
 
-  std::unique_ptr<MemoryBuffer> stub;
+  std::unique_ptr<MemoryBuffer> dosStub;
   llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
   bool machineInferred = false;
   size_t wordsize;
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 14619e7af236bb..2ee04a16b9815f 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2420,7 +2420,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Handle /stub
   if (auto *arg = args.getLastArg(OPT_stub))
-    parseStub(arg->getValue());
+    parseDosStub(arg->getValue());
 
   // Handle /functionpadmin
   for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 9afff23a7ed255..9ac7f5dd96d270 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -237,7 +237,7 @@ class LinkerDriver {
   void parseAligncomm(StringRef);
 
   // Parses a MS-DOS stub file
-  void parseStub(StringRef path);
+  void parseDosStub(StringRef path);
 
   // Parses a string in the form of "[:<integer>]"
   void parseFunctionPadMin(llvm::opt::Arg *a);
diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index 2dacd67f02399f..404c8b907b8939 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -246,7 +246,7 @@ void LinkerDriver::parseAligncomm(StringRef s) {
       std::max(ctx.config.alignComm[std::string(name)], 1 << v);
 }
 
-void LinkerDriver::parseStub(StringRef path) {
+void LinkerDriver::parseDosStub(StringRef path) {
   std::unique_ptr<MemoryBuffer> stub =
       CHECK(MemoryBuffer::getFile(path), "could not open " + path);
   size_t bufferSize = stub->getBufferSize();
@@ -262,7 +262,7 @@ void LinkerDriver::parseStub(StringRef path) {
     Err(ctx) << "/stub: invalid DOS signature: " << path;
   if (bufferSize % 8 != 0)
     Err(ctx) << "/stub: stub must be aligned to 8 bytes: " << path;
-  ctx.config.stub = std::move(stub);
+  ctx.config.dosStub = std::move(stub);
 }
 
 // Parses /functionpadmin option argument.
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 388347738a22ad..0f2430a67e1ef1 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1037,8 +1037,8 @@ void Writer::sortSections() {
 }
 
 void Writer::calculateStubDependentSizes() {
-  if (ctx.config.stub)
-    dosStubSize = ctx.config.stub->getBufferSize();
+  if (ctx.config.dosStub)
+    dosStubSize = ctx.config.dosStub->getBufferSize();
   else
     dosStubSize = sizeof(dos_header) + sizeof(dosProgram);
 
@@ -1685,12 +1685,13 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   auto *dos = reinterpret_cast<dos_header *>(buf);
 
   // Write DOS program.
-  if (config->stub) {
-    memcpy(buf, config->stub->getBufferStart(), config->stub->getBufferSize());
+  if (config->dosStub) {
+    memcpy(buf, config->dosStub->getBufferStart(),
+           config->dosStub->getBufferSize());
     // MS link.exe accepts an invalid `e_lfanew` (AddressOfNewExeHeader) and
     // updates it automatically. Replicate the same behaviour.
-    dos->AddressOfNewExeHeader = config->stub->getBufferSize();
-    buf += config->stub->getBufferSize();
+    dos->AddressOfNewExeHeader = config->dosStub->getBufferSize();
+    buf += config->dosStub->getBufferSize();
   } else {
     buf += sizeof(dos_header);
     dos->Magic[0] = 'M';

>From d715929d9d918a774374db4c7aac26590a4c2084 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Thu, 16 Jan 2025 01:52:47 +0900
Subject: [PATCH 10/11] fix stub68mz not MZ (was ZZ accidentally)

---
 lld/test/COFF/Inputs/stub68mz | Bin 68 -> 68 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/lld/test/COFF/Inputs/stub68mz b/lld/test/COFF/Inputs/stub68mz
index a4ee6b2291e67284e2b5bf1587aed06111af5d60..42b72259465363bb3d519a9fe917f73cdeadaeee 100644
GIT binary patch
delta 7
OcmZ>9Vf3BI=nMb`tO2Y5

delta 7
OcmZ>9VT_u{=nMb`-~qV+


>From 94ac28f183d994e0ba5f61a9fcc788369be95970 Mon Sep 17 00:00:00 2001
From: kkent030315 <hrn832 at protonmail.com>
Date: Thu, 16 Jan 2025 01:53:17 +0900
Subject: [PATCH 11/11] Accept unaligned stubs

---
 lld/COFF/DriverUtils.cpp |  2 --
 lld/COFF/Writer.cpp      |  6 ++++++
 lld/test/COFF/stub.test  | 30 +++++++++++++++++++++++++-----
 3 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp
index 404c8b907b8939..61b65fc074bce4 100644
--- a/lld/COFF/DriverUtils.cpp
+++ b/lld/COFF/DriverUtils.cpp
@@ -260,8 +260,6 @@ void LinkerDriver::parseDosStub(StringRef path) {
              << path;
   if (bufferStart[0] != 'M' || bufferStart[1] != 'Z')
     Err(ctx) << "/stub: invalid DOS signature: " << path;
-  if (bufferSize % 8 != 0)
-    Err(ctx) << "/stub: stub must be aligned to 8 bytes: " << path;
   ctx.config.dosStub = std::move(stub);
 }
 
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 0f2430a67e1ef1..1ec6f04f9b2a5b 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1692,6 +1692,9 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
     // updates it automatically. Replicate the same behaviour.
     dos->AddressOfNewExeHeader = config->dosStub->getBufferSize();
     buf += config->dosStub->getBufferSize();
+    // Unlike MS link.exe, LLD accepts non-8-byte-aligned stubs.
+    // In that case, we add zero paddings ourselves.
+    buf += (8 - (config->dosStub->getBufferSize() % 8)) % 8;
   } else {
     buf += sizeof(dos_header);
     dos->Magic[0] = 'M';
@@ -1707,6 +1710,9 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
     buf += sizeof(dosProgram);
   }
 
+  // Make sure DOS stub is aligned to 8 bytes at this point
+  assert((buf - buffer->getBufferStart()) % 8 == 0);
+
   // Write PE magic
   memcpy(buf, PEMagic, sizeof(PEMagic));
   buf += sizeof(PEMagic);
diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test
index 630be3c4479f54..bb3843b6d41b38 100644
--- a/lld/test/COFF/stub.test
+++ b/lld/test/COFF/stub.test
@@ -23,12 +23,32 @@ CHECK1: AddressOfNewExeHeader: 64
 
 ## Invalid DOS signature (must be `MZ`)
 # RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub64zz %t.obj 2>&1 | FileCheck -check-prefix=CHECK2 %s
-# CHECK2: lld-link: error: /stub: invalid DOS signature: {{.*}}
 
-## Unaligned stub (must be aligned to 8)
-# RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub68mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK3 %s
-# CHECK3: lld-link: error: /stub: stub must be aligned to 8 bytes: {{.*}}
+CHECK2: lld-link: error: /stub: invalid DOS signature: {{.*}}
+
+## Unlike MS linker, we accept non-8byte-aligned stubs and we add paddings ourselves
+# RUN: lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub64mz %t.obj
+# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=CHECK3 %s
+
+CHECK3: Magic: MZ
+CHECK3: UsedBytesInTheLastPage: 144
+CHECK3: FileSizeInPages: 3
+CHECK3: NumberOfRelocationItems: 0
+CHECK3: HeaderSizeInParagraphs: 4
+CHECK3: MinimumExtraParagraphs: 0
+CHECK3: MaximumExtraParagraphs: 65535
+CHECK3: InitialRelativeSS: 0
+CHECK3: InitialSP: 184
+CHECK3: Checksum: 0
+CHECK3: InitialIP: 0
+CHECK3: InitialRelativeCS: 0
+CHECK3: AddressOfRelocationTable: 64
+CHECK3: OverlayNumber: 0
+CHECK3: OEMid: 0
+CHECK3: OEMinfo: 0
+CHECK3: AddressOfNewExeHeader: 64
 
 ## Too-small stub (must be at least 64 bytes long) && Unaligned
 # RUN: not lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub63mz %t.obj 2>&1 | FileCheck -check-prefix=CHECK4 %s
-# CHECK4: lld-link: error: /stub: stub must be greater than or equal to 64 bytes: {{.*}}
+
+CHECK4: lld-link: error: /stub: stub must be greater than or equal to 64 bytes: {{.*}}



More information about the llvm-commits mailing list