From 446d70ab9ccab29f7cc61734ba79db08b98e4da7 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sat, 11 Jan 2025 09:53:21 +0900 Subject: [PATCH 01/18] [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 9e6b17e87c9e7..294df1912ff5a 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 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 791382fd9bdd4..14619e7af236b 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -2418,6 +2418,10 @@ void LinkerDriver::linkerMain(ArrayRef 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 5132568904298..9afff23a7ed25 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 "[:]" void parseFunctionPadMin(llvm::opt::Arg *a); diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 1148be09fb10c..fdee5dcbc26d1 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 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 eb82a9cc01593..29543da541983 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(); 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 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(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@djvuN literal 0 HcmV?d00001 diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test new file mode 100644 index 0000000000000..2d5ad351d407d --- /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 Date: Tue, 14 Jan 2025 09:00:47 +0900 Subject: [PATCH 02/18] 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 29543da541983..b19dbbac7cf6d 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1687,8 +1687,8 @@ template 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 Date: Tue, 14 Jan 2025 09:05:04 +0900 Subject: [PATCH 03/18] 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 2d5ad351d407d..a5ab2657f0af4 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 Date: Tue, 14 Jan 2025 09:11:43 +0900 Subject: [PATCH 04/18] 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 a5ab2657f0af4..11b820c86cf87 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 Date: Tue, 14 Jan 2025 19:13:43 +0900 Subject: [PATCH 05/18] 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 294df1912ff5a..69c79efddabf8 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 stub; + std::unique_ptr 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 fdee5dcbc26d1..d6a5127daac31 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 b19dbbac7cf6d..388347738a22a 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 void Writer::writeHeader() { auto *dos = reinterpret_cast(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 Date: Tue, 14 Jan 2025 19:55:51 +0900 Subject: [PATCH 06/18] 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 d6a5127daac31..85db485d1fd9c 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 Date: Tue, 14 Jan 2025 20:07:12 +0900 Subject: [PATCH 07/18] 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 Date: Tue, 14 Jan 2025 20:33:34 +0900 Subject: [PATCH 08/18] 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 85db485d1fd9c..2dacd67f02399 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 11b820c86cf87..630be3c4479f5 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 Date: Tue, 14 Jan 2025 20:38:41 +0900 Subject: [PATCH 09/18] 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 69c79efddabf8..687c7b9529251 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 stub; + std::unique_ptr 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 14619e7af236b..2ee04a16b9815 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -2420,7 +2420,7 @@ void LinkerDriver::linkerMain(ArrayRef 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 9afff23a7ed25..9ac7f5dd96d27 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 "[:]" void parseFunctionPadMin(llvm::opt::Arg *a); diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 2dacd67f02399..404c8b907b893 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 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 388347738a22a..0f2430a67e1ef 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 void Writer::writeHeader() { auto *dos = reinterpret_cast(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 Date: Thu, 16 Jan 2025 01:52:47 +0900 Subject: [PATCH 10/18] 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 Date: Thu, 16 Jan 2025 01:53:17 +0900 Subject: [PATCH 11/18] 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 404c8b907b893..61b65fc074bce 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 0f2430a67e1ef..1ec6f04f9b2a5 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1692,6 +1692,9 @@ template 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 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 630be3c4479f5..bb3843b6d41b3 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: {{.*}} From 6a8a59b5355fbf2d6e82ed105e871b1c096b880c Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Thu, 16 Jan 2025 02:06:47 +0900 Subject: [PATCH 12/18] update comments as well --- lld/COFF/DriverUtils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 61b65fc074bce..813528fc98c2e 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -253,8 +253,7 @@ void LinkerDriver::parseDosStub(StringRef path) { const char *bufferStart = stub->getBufferStart(); // MS link.exe compatibility: // 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' + // 2. stub must be start with a valid dos signature 'MZ' if (bufferSize < 0x40) Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: " << path; From e1e9b435c7615ccad973a5da422cbcd63d17a3b7 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 17 Jan 2025 00:39:17 +0900 Subject: [PATCH 13/18] adjust comments --- lld/COFF/DriverUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 813528fc98c2e..308459b1239fc 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -252,8 +252,8 @@ void LinkerDriver::parseDosStub(StringRef path) { size_t bufferSize = stub->getBufferSize(); const char *bufferStart = stub->getBufferStart(); // MS link.exe compatibility: - // 1. stub must be greater than or equal to 64 bytes - // 2. stub must be start with a valid dos signature 'MZ' + // 1. stub must greater than or equal to 64 bytes + // 2. stub must start with a valid dos signature 'MZ' if (bufferSize < 0x40) Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: " << path; From 27affd6642d52521f3d233a257fad687084c8134 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 17 Jan 2025 00:40:26 +0900 Subject: [PATCH 14/18] use raw expr instead of 0x40 hexdecimal --- 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 308459b1239fc..d42899e06ffbc 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -254,7 +254,7 @@ void LinkerDriver::parseDosStub(StringRef path) { // MS link.exe compatibility: // 1. stub must greater than or equal to 64 bytes // 2. stub must start with a valid dos signature 'MZ' - if (bufferSize < 0x40) + if (bufferSize < 64) Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: " << path; if (bufferStart[0] != 'M' || bufferStart[1] != 'Z') From 8bac7cdaa0dc62b29c4c03ee3f0edd17bf7d2000 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 17 Jan 2025 00:50:57 +0900 Subject: [PATCH 15/18] fix unaligned test --- lld/test/COFF/stub.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test index bb3843b6d41b3..4fb8ef6fdb248 100644 --- a/lld/test/COFF/stub.test +++ b/lld/test/COFF/stub.test @@ -27,7 +27,7 @@ CHECK1: AddressOfNewExeHeader: 64 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: lld-link /out:%t.exe /entry:main /stub:%p/Inputs/stub68mz %t.obj # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=CHECK3 %s CHECK3: Magic: MZ From 639e14f119d5808cf43e922aaa3f78abf17c3563 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 17 Jan 2025 01:23:27 +0900 Subject: [PATCH 16/18] fix dosStubSize align and coffHeaderOffset respectively --- lld/COFF/Writer.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 1ec6f04f9b2a5..907c291aa37e6 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -728,10 +728,8 @@ void Writer::writePEChecksum() { uint32_t *buf = (uint32_t *)buffer->getBufferStart(); uint32_t size = (uint32_t)(buffer->getBufferSize()); - coff_file_header *coffHeader = - (coff_file_header *)((uint8_t *)buf + dosStubSize + sizeof(PEMagic)); - pe32_header *peHeader = - (pe32_header *)((uint8_t *)coffHeader + sizeof(coff_file_header)); + pe32_header *peHeader = (pe32_header *)((uint8_t *)buf + coffHeaderOffset + + sizeof(coff_file_header)); uint64_t sum = 0; uint32_t count = size; @@ -1038,7 +1036,7 @@ void Writer::sortSections() { void Writer::calculateStubDependentSizes() { if (ctx.config.dosStub) - dosStubSize = ctx.config.dosStub->getBufferSize(); + dosStubSize = alignTo(ctx.config.dosStub->getBufferSize(), 8); else dosStubSize = sizeof(dos_header) + sizeof(dosProgram); @@ -1690,11 +1688,10 @@ template void Writer::writeHeader() { config->dosStub->getBufferSize()); // MS link.exe accepts an invalid `e_lfanew` (AddressOfNewExeHeader) and // updates it automatically. Replicate the same behaviour. - dos->AddressOfNewExeHeader = config->dosStub->getBufferSize(); - buf += config->dosStub->getBufferSize(); + dos->AddressOfNewExeHeader = alignTo(config->dosStub->getBufferSize(), 8); // 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; + buf += alignTo(config->dosStub->getBufferSize(), 8); } else { buf += sizeof(dos_header); dos->Magic[0] = 'M'; From d241e33ee531fce0b1a40123198652e9c11bdf59 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 17 Jan 2025 01:25:05 +0900 Subject: [PATCH 17/18] fix unaligned test --- lld/test/COFF/stub.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lld/test/COFF/stub.test b/lld/test/COFF/stub.test index 4fb8ef6fdb248..84de6ed84c957 100644 --- a/lld/test/COFF/stub.test +++ b/lld/test/COFF/stub.test @@ -46,7 +46,8 @@ CHECK3: AddressOfRelocationTable: 64 CHECK3: OverlayNumber: 0 CHECK3: OEMid: 0 CHECK3: OEMinfo: 0 -CHECK3: AddressOfNewExeHeader: 64 +## 68 is unaligned and rounded up to 72 by LLD +CHECK3: AddressOfNewExeHeader: 72 ## 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 From 6c2e58ce70ef99f35aebabc0519aa559c0d54933 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sun, 19 Jan 2025 01:12:31 +0900 Subject: [PATCH 18/18] adjust comments (again) --- 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 d42899e06ffbc..19abd4806d53f 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -252,7 +252,7 @@ void LinkerDriver::parseDosStub(StringRef path) { size_t bufferSize = stub->getBufferSize(); const char *bufferStart = stub->getBufferStart(); // MS link.exe compatibility: - // 1. stub must greater than or equal to 64 bytes + // 1. stub must be greater than or equal to 64 bytes // 2. stub must start with a valid dos signature 'MZ' if (bufferSize < 64) Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: "