From d2c8f20d6700932a7c073ef742f70849b88f19b7 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sat, 9 Aug 2025 03:56:53 +0900 Subject: [PATCH 01/15] [LLD][COFF] Add support for custom section layout --- lld/COFF/Config.h | 2 + lld/COFF/Driver.cpp | 3 + lld/COFF/Driver.h | 1 + lld/COFF/DriverUtils.cpp | 32 ++++++ lld/COFF/Options.td | 1 + lld/COFF/Writer.cpp | 30 ++++++ lld/test/COFF/Inputs/sectionlayout.yaml | 25 +++++ lld/test/COFF/sectionlayout.test | 126 ++++++++++++++++++++++++ 8 files changed, 220 insertions(+) create mode 100644 lld/test/COFF/Inputs/sectionlayout.yaml create mode 100644 lld/test/COFF/sectionlayout.test diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index a03bb57641670..bb9bcb957cab5 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -212,6 +212,8 @@ struct Configuration { // Used for /section=.name,{DEKPRSW} to set section attributes. std::map section; + // Used for /sectionlayout:@ to layout sections in specified order. + std::map sectionLayout; // Options for manifest files. ManifestKind manifest = Default; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index ff616d0ce2bff..852c509a5c77d 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -2049,6 +2049,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle /section for (auto *arg : args.filtered(OPT_section)) parseSection(arg->getValue()); + // Handle /sectionlayout + if (auto *arg = args.getLastArg(OPT_sectionlayout)) + parseSectionLayout(arg->getValue()); // Handle /align if (auto *arg = args.getLastArg(OPT_align)) { diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index b500ac8bba569..14710d5853bcf 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -213,6 +213,7 @@ class LinkerDriver { void parseMerge(StringRef); void parsePDBPageSize(StringRef); void parseSection(StringRef); + void parseSectionLayout(StringRef); void parseSameAddress(StringRef); diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index dc4039f116f25..a31521eccbb0b 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -214,6 +214,38 @@ void LinkerDriver::parseSection(StringRef s) { ctx.config.section[name] = parseSectionAttributes(ctx, attrs); } +// Parses /sectionlayout:@ option argument. +void LinkerDriver::parseSectionLayout(StringRef path) { + if (path.starts_with("@")) + path = path.substr(1); + std::unique_ptr layoutFile = + CHECK(MemoryBuffer::getFile(path), "could not open " + path); + StringRef content = layoutFile->getBuffer(); + int index = 0; + + while (!content.empty()) { + size_t pos = content.find_first_of("\r\n"); + StringRef line; + + if (pos == StringRef::npos) { + line = content; + content = StringRef(); + } else { + line = content.substr(0, pos); + content = content.substr(pos); + while (!content.empty() && (content[0] == '\r' || content[0] == '\n')) + content = content.substr(1); + } + + line = line.trim(); + if (line.empty()) + continue; + + StringRef sectionName = line.split(' ').first; + ctx.config.sectionLayout[sectionName.str()] = index++; + } +} + void LinkerDriver::parseDosStub(StringRef path) { std::unique_ptr stub = CHECK(MemoryBuffer::getFile(path), "could not open " + path); diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index 2c393cc94b5e3..26f6acaa3b7ad 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -102,6 +102,7 @@ def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">, MetaVarName<"=">, HelpText<"Embed the contents of in the PDB as named stream ">; def section : P<"section", "Specify section attributes">; +def sectionlayout : P<"sectionlayout", "Specifies the layout strategy for output sections">; def stack : P<"stack", "Size of the stack">; def stub : P<"stub", "Specify DOS stub file">; def subsystem : P<"subsystem", "Specify subsystem">; diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 21ab9d17a26f9..f777dcb4a6dac 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -219,6 +219,7 @@ class Writer { void sortECChunks(); void appendECImportTables(); void removeUnusedSections(); + void layoutSections(); void assignAddresses(); bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin, MachineTypes machine); @@ -783,6 +784,7 @@ void Writer::run() { appendECImportTables(); createDynamicRelocs(); removeUnusedSections(); + layoutSections(); finalizeAddresses(); removeEmptySections(); assignOutputSectionIndices(); @@ -1413,6 +1415,34 @@ void Writer::removeUnusedSections() { llvm::erase_if(ctx.outputSections, isUnused); } +void Writer::layoutSections() { + llvm::TimeTraceScope timeScope("Layout sections"); + if (ctx.config.sectionLayout.empty()) + return; + + std::unordered_map originalOrder; + for (size_t i = 0; i < ctx.outputSections.size(); ++i) + originalOrder[ctx.outputSections[i]] = i; + + std::stable_sort( + ctx.outputSections.begin(), ctx.outputSections.end(), + [this, &originalOrder](const OutputSection *a, const OutputSection *b) { + auto itA = ctx.config.sectionLayout.find(a->name.str()); + auto itB = ctx.config.sectionLayout.find(b->name.str()); + + if (itA != ctx.config.sectionLayout.end() && + itB != ctx.config.sectionLayout.end()) + return itA->second < itB->second; + + if (itA == ctx.config.sectionLayout.end() && + itB == ctx.config.sectionLayout.end()) + return originalOrder[a] < originalOrder[b]; + + // Not found in layout file; respect the original order + return originalOrder[a] < originalOrder[b]; + }); +} + // The Windows loader doesn't seem to like empty sections, // so we remove them if any. void Writer::removeEmptySections() { diff --git a/lld/test/COFF/Inputs/sectionlayout.yaml b/lld/test/COFF/Inputs/sectionlayout.yaml new file mode 100644 index 0000000000000..f5a2a5e333b95 --- /dev/null +++ b/lld/test/COFF/Inputs/sectionlayout.yaml @@ -0,0 +1,25 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: '.text1' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: B82A000000C3 + - Name: '.text2' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: CC + - Name: '.text3' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: CC +symbols: + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/lld/test/COFF/sectionlayout.test b/lld/test/COFF/sectionlayout.test new file mode 100644 index 0000000000000..24590ee4e26e7 --- /dev/null +++ b/lld/test/COFF/sectionlayout.test @@ -0,0 +1,126 @@ +RUN: yaml2obj %p/Inputs/sectionlayout.yaml -o %t.obj + +## Error on non-exist input layout file +RUN: not lld-link /entry:main /sectionlayout:doesnotexist.txt %t.obj + +## Order in 1 -> 3 -> 2 +RUN: echo ".text1" > %t.layout.txt +RUN: echo ".text3" >> %t.layout.txt +RUN: echo ".text2" >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s + +## While /sectionlayout:abc is valid, /sectionlayout:@abc is also accepted (to align with MS link.exe) +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:@%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s + +CHECK1: Sections [ +CHECK1: Section { +CHECK1: Number: 1 +CHECK1: Name: .text1 +CHECK1: } +CHECK1: Section { +CHECK1: Number: 2 +CHECK1: Name: .text3 +CHECK1: } +CHECK1: Section { +CHECK1: Number: 3 +CHECK1: Name: .text2 +CHECK1: } +CHECK1: ] + +## Order in 3 -> 2 -> 1 +RUN: echo ".text3" > %t.layout.txt +RUN: echo ".text2" >> %t.layout.txt +RUN: echo ".text1" >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK2 %s + +CHECK2: Sections [ +CHECK2: Section { +CHECK2: Number: 1 +CHECK2: Name: .text3 +CHECK2: } +CHECK2: Section { +CHECK2: Number: 2 +CHECK2: Name: .text2 +CHECK2: } +CHECK2: Section { +CHECK2: Number: 3 +CHECK2: Name: .text1 +CHECK2: } +CHECK2: ] + +## Put non-exisist section in layout file has no effect; original order is respected +RUN: echo "notexist" > %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK3 %s + +## Empty layout file has no effect +RUN: echo "" > %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK3 %s + +## Empty layout file has no effect +RUN: echo " " > %t.layout.txt +RUN: echo " " >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK3 %s + +CHECK3: Sections [ +CHECK3: Section { +CHECK3: Number: 1 +CHECK3: Name: .text1 +CHECK3: } +CHECK3: Section { +CHECK3: Number: 2 +CHECK3: Name: .text2 +CHECK3: } +CHECK3: Section { +CHECK3: Number: 3 +CHECK3: Name: .text3 +CHECK3: } +CHECK3: ] + +## Order in 3 -> 1, but 2 remains unspecified +RUN: echo ".text3" > %t.layout.txt +RUN: echo ".text1" >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK4 %s + +CHECK4: Sections [ +CHECK4: Section { +CHECK4: Number: 1 +CHECK4: Name: .text3 +CHECK4: } +CHECK4: Section { +CHECK4: Number: 2 +CHECK4: Name: .text1 +CHECK4: } +CHECK4: Section { +CHECK4: Number: 3 +CHECK4: Name: .text2 +CHECK4: } +CHECK4: ] + +## Order in 3 -> 2, but 1 remains unspecified. +## 1 should be the first, as the original order (1 -> 2 -> 3) is respected +RUN: echo ".text3" > %t.layout.txt +RUN: echo ".text2" >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK5 %s + +CHECK5: Sections [ +CHECK5: Section { +CHECK5: Number: 1 +CHECK5: Name: .text1 +CHECK5: } +CHECK5: Section { +CHECK5: Number: 2 +CHECK5: Name: .text3 +CHECK5: } +CHECK5: Section { +CHECK5: Number: 3 +CHECK5: Name: .text2 +CHECK5: } +CHECK5: ] From 8fe1010d7c7038a88db7979e4a980e33bfe788c7 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sat, 9 Aug 2025 05:46:06 +0900 Subject: [PATCH 02/15] use `llvm::stable_sort` instead of std --- 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 f777dcb4a6dac..59b1322427ff3 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1424,8 +1424,8 @@ void Writer::layoutSections() { for (size_t i = 0; i < ctx.outputSections.size(); ++i) originalOrder[ctx.outputSections[i]] = i; - std::stable_sort( - ctx.outputSections.begin(), ctx.outputSections.end(), + llvm::stable_sort( + ctx.outputSections, [this, &originalOrder](const OutputSection *a, const OutputSection *b) { auto itA = ctx.config.sectionLayout.find(a->name.str()); auto itB = ctx.config.sectionLayout.find(b->name.str()); From 0e9b1ef2e4a80e810d0b57481040511c85be8890 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sat, 9 Aug 2025 05:52:10 +0900 Subject: [PATCH 03/15] add check/test for multiple specification in layout file --- lld/COFF/DriverUtils.cpp | 11 ++++++++++- lld/test/COFF/sectionlayout.test | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index a31521eccbb0b..39ce3c50aa7ac 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -222,6 +222,7 @@ void LinkerDriver::parseSectionLayout(StringRef path) { CHECK(MemoryBuffer::getFile(path), "could not open " + path); StringRef content = layoutFile->getBuffer(); int index = 0; + std::set seenSections; while (!content.empty()) { size_t pos = content.find_first_of("\r\n"); @@ -242,7 +243,15 @@ void LinkerDriver::parseSectionLayout(StringRef path) { continue; StringRef sectionName = line.split(' ').first; - ctx.config.sectionLayout[sectionName.str()] = index++; + std::string sectionNameStr = sectionName.str(); + + if (seenSections.count(sectionNameStr)) { + Warn(ctx) << "duplicate section '" << sectionNameStr << "' in section layout file, ignoring"; + continue; + } + + seenSections.insert(sectionNameStr); + ctx.config.sectionLayout[sectionNameStr] = index++; } } diff --git a/lld/test/COFF/sectionlayout.test b/lld/test/COFF/sectionlayout.test index 24590ee4e26e7..e2254bbb2fe49 100644 --- a/lld/test/COFF/sectionlayout.test +++ b/lld/test/COFF/sectionlayout.test @@ -124,3 +124,14 @@ CHECK5: Number: 3 CHECK5: Name: .text2 CHECK5: } CHECK5: ] + +## Order in 3 -> 2 -> 1, multiple specification has no effect (the first one is used) +RUN: echo ".text3" > %t.layout.txt +RUN: echo ".text3" >> %t.layout.txt +RUN: echo ".text3" >> %t.layout.txt +RUN: echo ".text2" >> %t.layout.txt +RUN: echo ".text2" >> %t.layout.txt +RUN: echo ".text1" >> %t.layout.txt +RUN: echo ".text3" >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK2 %s From e1bb9c6f55267434db216e0e13e04e0c236224b7 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sat, 9 Aug 2025 05:54:03 +0900 Subject: [PATCH 04/15] fmt --- lld/COFF/DriverUtils.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 39ce3c50aa7ac..a6ac43928879f 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -244,12 +244,13 @@ void LinkerDriver::parseSectionLayout(StringRef path) { StringRef sectionName = line.split(' ').first; std::string sectionNameStr = sectionName.str(); - + if (seenSections.count(sectionNameStr)) { - Warn(ctx) << "duplicate section '" << sectionNameStr << "' in section layout file, ignoring"; + Warn(ctx) << "duplicate section '" << sectionNameStr + << "' in section layout file, ignoring"; continue; } - + seenSections.insert(sectionNameStr); ctx.config.sectionLayout[sectionNameStr] = index++; } From a0f622644f2fdb61ce189965597b5ae02805ef52 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Mon, 11 Aug 2025 01:41:44 +0900 Subject: [PATCH 05/15] fmt --- lld/COFF/Config.h | 2 +- lld/COFF/DriverUtils.cpp | 2 +- lld/COFF/Writer.cpp | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index bb9bcb957cab5..feb7395cd5303 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -213,7 +213,7 @@ struct Configuration { // Used for /section=.name,{DEKPRSW} to set section attributes. std::map section; // Used for /sectionlayout:@ to layout sections in specified order. - std::map sectionLayout; + std::map sectionOrder; // Options for manifest files. ManifestKind manifest = Default; diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index a6ac43928879f..216f4837f4bfa 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -252,7 +252,7 @@ void LinkerDriver::parseSectionLayout(StringRef path) { } seenSections.insert(sectionNameStr); - ctx.config.sectionLayout[sectionNameStr] = index++; + ctx.config.sectionOrder[sectionNameStr] = index++; } } diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 59b1322427ff3..1d6b70375bbc6 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1417,7 +1417,7 @@ void Writer::removeUnusedSections() { void Writer::layoutSections() { llvm::TimeTraceScope timeScope("Layout sections"); - if (ctx.config.sectionLayout.empty()) + if (ctx.config.sectionOrder.empty()) return; std::unordered_map originalOrder; @@ -1427,15 +1427,15 @@ void Writer::layoutSections() { llvm::stable_sort( ctx.outputSections, [this, &originalOrder](const OutputSection *a, const OutputSection *b) { - auto itA = ctx.config.sectionLayout.find(a->name.str()); - auto itB = ctx.config.sectionLayout.find(b->name.str()); + auto itA = ctx.config.sectionOrder.find(a->name.str()); + auto itB = ctx.config.sectionOrder.find(b->name.str()); - if (itA != ctx.config.sectionLayout.end() && - itB != ctx.config.sectionLayout.end()) + if (itA != ctx.config.sectionOrder.end() && + itB != ctx.config.sectionOrder.end()) return itA->second < itB->second; - if (itA == ctx.config.sectionLayout.end() && - itB == ctx.config.sectionLayout.end()) + if (itA == ctx.config.sectionOrder.end() && + itB == ctx.config.sectionOrder.end()) return originalOrder[a] < originalOrder[b]; // Not found in layout file; respect the original order From 81d8d835d4cf7325ddf88b06baa74fc9b951bc76 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 15 Aug 2025 19:34:35 +0900 Subject: [PATCH 06/15] squash into the same CHECK --- lld/test/COFF/sectionlayout.test | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/lld/test/COFF/sectionlayout.test b/lld/test/COFF/sectionlayout.test index e2254bbb2fe49..5e362ee92ea25 100644 --- a/lld/test/COFF/sectionlayout.test +++ b/lld/test/COFF/sectionlayout.test @@ -108,22 +108,7 @@ CHECK4: ] RUN: echo ".text3" > %t.layout.txt RUN: echo ".text2" >> %t.layout.txt RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj -RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK5 %s - -CHECK5: Sections [ -CHECK5: Section { -CHECK5: Number: 1 -CHECK5: Name: .text1 -CHECK5: } -CHECK5: Section { -CHECK5: Number: 2 -CHECK5: Name: .text3 -CHECK5: } -CHECK5: Section { -CHECK5: Number: 3 -CHECK5: Name: .text2 -CHECK5: } -CHECK5: ] +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s ## Order in 3 -> 2 -> 1, multiple specification has no effect (the first one is used) RUN: echo ".text3" > %t.layout.txt From 968b1c88e664ee2065c6db33e7f54e1697b02c23 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Tue, 26 Aug 2025 02:06:32 +0900 Subject: [PATCH 07/15] remove seenSections and redundant if block --- lld/COFF/DriverUtils.cpp | 4 +--- lld/COFF/Writer.cpp | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 216f4837f4bfa..917a5ba06a50e 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -222,7 +222,6 @@ void LinkerDriver::parseSectionLayout(StringRef path) { CHECK(MemoryBuffer::getFile(path), "could not open " + path); StringRef content = layoutFile->getBuffer(); int index = 0; - std::set seenSections; while (!content.empty()) { size_t pos = content.find_first_of("\r\n"); @@ -245,13 +244,12 @@ void LinkerDriver::parseSectionLayout(StringRef path) { StringRef sectionName = line.split(' ').first; std::string sectionNameStr = sectionName.str(); - if (seenSections.count(sectionNameStr)) { + if (ctx.config.sectionOrder.count(sectionNameStr)) { Warn(ctx) << "duplicate section '" << sectionNameStr << "' in section layout file, ignoring"; continue; } - seenSections.insert(sectionNameStr); ctx.config.sectionOrder[sectionNameStr] = index++; } } diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 1d6b70375bbc6..fbc654d2201ad 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1434,10 +1434,6 @@ void Writer::layoutSections() { itB != ctx.config.sectionOrder.end()) return itA->second < itB->second; - if (itA == ctx.config.sectionOrder.end() && - itB == ctx.config.sectionOrder.end()) - return originalOrder[a] < originalOrder[b]; - // Not found in layout file; respect the original order return originalOrder[a] < originalOrder[b]; }); From 41b76ab51a9f6a9ce2d7b706b90ada2bb2df3798 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 29 Aug 2025 17:24:01 +0900 Subject: [PATCH 08/15] remove `@` from `/sectionlayout:` comments --- lld/COFF/Config.h | 2 +- lld/COFF/DriverUtils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index feb7395cd5303..6754e303599f4 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -212,7 +212,7 @@ struct Configuration { // Used for /section=.name,{DEKPRSW} to set section attributes. std::map section; - // Used for /sectionlayout:@ to layout sections in specified order. + // Used for /sectionlayout: to layout sections in specified order. std::map sectionOrder; // Options for manifest files. diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 917a5ba06a50e..d0b407f5f8429 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -214,7 +214,7 @@ void LinkerDriver::parseSection(StringRef s) { ctx.config.section[name] = parseSectionAttributes(ctx, attrs); } -// Parses /sectionlayout:@ option argument. +// Parses /sectionlayout: option argument. void LinkerDriver::parseSectionLayout(StringRef path) { if (path.starts_with("@")) path = path.substr(1); From 7c924433d36ca84c6890803be0a3c363655c6d57 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 29 Aug 2025 17:56:56 +0900 Subject: [PATCH 09/15] parseSectionLayout cleanup --- lld/COFF/DriverUtils.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index d0b407f5f8429..96ae2f0ddef6f 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -232,9 +232,7 @@ void LinkerDriver::parseSectionLayout(StringRef path) { content = StringRef(); } else { line = content.substr(0, pos); - content = content.substr(pos); - while (!content.empty() && (content[0] == '\r' || content[0] == '\n')) - content = content.substr(1); + content = content.substr(pos).ltrim("\r\n"); } line = line.trim(); @@ -242,15 +240,14 @@ void LinkerDriver::parseSectionLayout(StringRef path) { continue; StringRef sectionName = line.split(' ').first; - std::string sectionNameStr = sectionName.str(); - if (ctx.config.sectionOrder.count(sectionNameStr)) { - Warn(ctx) << "duplicate section '" << sectionNameStr + if (ctx.config.sectionOrder.count(sectionName.str())) { + Warn(ctx) << "duplicate section '" << sectionName.str() << "' in section layout file, ignoring"; continue; } - ctx.config.sectionOrder[sectionNameStr] = index++; + ctx.config.sectionOrder[sectionName.str()] = index++; } } From 33b92f20aad86d94cff8c2d2532c44f4a934c459 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Fri, 29 Aug 2025 17:59:43 +0900 Subject: [PATCH 10/15] add test for ignored tokens in a line --- lld/test/COFF/sectionlayout.test | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lld/test/COFF/sectionlayout.test b/lld/test/COFF/sectionlayout.test index 5e362ee92ea25..af419a4d3f545 100644 --- a/lld/test/COFF/sectionlayout.test +++ b/lld/test/COFF/sectionlayout.test @@ -14,6 +14,13 @@ RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s RUN: lld-link /out:%t.exe /entry:main /sectionlayout:@%t.layout.txt %t.obj RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s +## Ensure tokens after section name is ignored (for now, to align with MS link.exe) +RUN: echo ".text1 ALIGN=1" > %t.layout.txt +RUN: echo ".text3" >> %t.layout.txt +RUN: echo ".text2" >> %t.layout.txt +RUN: lld-link /out:%t.exe /entry:main /sectionlayout:@%t.layout.txt %t.obj +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s + CHECK1: Sections [ CHECK1: Section { CHECK1: Number: 1 From d7b531e7769fecca7296e372aae23c07db253f11 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Sat, 30 Aug 2025 17:21:44 +0900 Subject: [PATCH 11/15] put unspecified sections after all specified sections --- lld/COFF/Writer.cpp | 16 +++++++++++----- lld/test/COFF/sectionlayout.test | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index fbc654d2201ad..dcd600cae26d4 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1429,13 +1429,19 @@ void Writer::layoutSections() { [this, &originalOrder](const OutputSection *a, const OutputSection *b) { auto itA = ctx.config.sectionOrder.find(a->name.str()); auto itB = ctx.config.sectionOrder.find(b->name.str()); + bool aInOrder = itA != ctx.config.sectionOrder.end(); + bool bInOrder = itB != ctx.config.sectionOrder.end(); - if (itA != ctx.config.sectionOrder.end() && - itB != ctx.config.sectionOrder.end()) + // Put unspecified sections after all specified sections + if (aInOrder && bInOrder) { return itA->second < itB->second; - - // Not found in layout file; respect the original order - return originalOrder[a] < originalOrder[b]; + } else if (aInOrder && !bInOrder) { + return true; + } else if (!aInOrder && bInOrder) { + return false; + } else { + return originalOrder[a] < originalOrder[b]; + } }); } diff --git a/lld/test/COFF/sectionlayout.test b/lld/test/COFF/sectionlayout.test index af419a4d3f545..109f35d992b1b 100644 --- a/lld/test/COFF/sectionlayout.test +++ b/lld/test/COFF/sectionlayout.test @@ -115,7 +115,7 @@ CHECK4: ] RUN: echo ".text3" > %t.layout.txt RUN: echo ".text2" >> %t.layout.txt RUN: lld-link /out:%t.exe /entry:main /sectionlayout:%t.layout.txt %t.obj -RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK1 %s +RUN: llvm-readobj --sections %t.exe | FileCheck -check-prefix=CHECK2 %s ## Order in 3 -> 2 -> 1, multiple specification has no effect (the first one is used) RUN: echo ".text3" > %t.layout.txt From bac6debd87de8cce40d086075536aabb01b8dda1 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Wed, 3 Sep 2025 22:55:01 +0900 Subject: [PATCH 12/15] simplify the comparison --- lld/COFF/Writer.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index dcd600cae26d4..2e771c7ad32fb 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1420,13 +1420,9 @@ void Writer::layoutSections() { if (ctx.config.sectionOrder.empty()) return; - std::unordered_map originalOrder; - for (size_t i = 0; i < ctx.outputSections.size(); ++i) - originalOrder[ctx.outputSections[i]] = i; - llvm::stable_sort( ctx.outputSections, - [this, &originalOrder](const OutputSection *a, const OutputSection *b) { + [this](const OutputSection *a, const OutputSection *b) { auto itA = ctx.config.sectionOrder.find(a->name.str()); auto itB = ctx.config.sectionOrder.find(b->name.str()); bool aInOrder = itA != ctx.config.sectionOrder.end(); @@ -1437,10 +1433,8 @@ void Writer::layoutSections() { return itA->second < itB->second; } else if (aInOrder && !bInOrder) { return true; - } else if (!aInOrder && bInOrder) { - return false; } else { - return originalOrder[a] < originalOrder[b]; + return false; } }); } From 01af3b7f36a803cc9030db8fdffbc34a9d685ea1 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Wed, 3 Sep 2025 23:05:49 +0900 Subject: [PATCH 13/15] fmt --- lld/COFF/Writer.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 2e771c7ad32fb..7d87f009a0829 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1420,23 +1420,22 @@ void Writer::layoutSections() { if (ctx.config.sectionOrder.empty()) return; - llvm::stable_sort( - ctx.outputSections, - [this](const OutputSection *a, const OutputSection *b) { - auto itA = ctx.config.sectionOrder.find(a->name.str()); - auto itB = ctx.config.sectionOrder.find(b->name.str()); - bool aInOrder = itA != ctx.config.sectionOrder.end(); - bool bInOrder = itB != ctx.config.sectionOrder.end(); - - // Put unspecified sections after all specified sections - if (aInOrder && bInOrder) { - return itA->second < itB->second; - } else if (aInOrder && !bInOrder) { - return true; - } else { - return false; - } - }); + llvm::stable_sort(ctx.outputSections, + [this](const OutputSection *a, const OutputSection *b) { + auto itA = ctx.config.sectionOrder.find(a->name.str()); + auto itB = ctx.config.sectionOrder.find(b->name.str()); + bool aInOrder = itA != ctx.config.sectionOrder.end(); + bool bInOrder = itB != ctx.config.sectionOrder.end(); + + // Put unspecified sections after all specified sections + if (aInOrder && bInOrder) { + return itA->second < itB->second; + } else if (aInOrder && !bInOrder) { + return true; + } else { + return false; + } + }); } // The Windows loader doesn't seem to like empty sections, From 752ca98e81614369fa34989491e54b11cbbc8b99 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Thu, 4 Sep 2025 18:15:48 +0900 Subject: [PATCH 14/15] add comments in the comparison --- lld/COFF/Writer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 7d87f009a0829..8d387c4b0c393 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1431,8 +1431,11 @@ void Writer::layoutSections() { if (aInOrder && bInOrder) { return itA->second < itB->second; } else if (aInOrder && !bInOrder) { - return true; + return true; // ordered sections come before unordered } else { + // (!aInOrder && bInOrder): unordered comes after ordered + // (!aInOrder && !bInOrder): both unspecified, preserve + // the original order return false; } }); From 7ad63ee974e864047034cf13dedc421824fc0206 Mon Sep 17 00:00:00 2001 From: kkent030315 Date: Thu, 4 Sep 2025 18:30:55 +0900 Subject: [PATCH 15/15] fmt --- lld/COFF/Writer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 8d387c4b0c393..930673ef6c5e3 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1433,7 +1433,8 @@ void Writer::layoutSections() { } else if (aInOrder && !bInOrder) { return true; // ordered sections come before unordered } else { - // (!aInOrder && bInOrder): unordered comes after ordered + // (!aInOrder && bInOrder): unordered comes after + // ordered // (!aInOrder && !bInOrder): both unspecified, preserve // the original order return false;