1 | //===- Writer.cpp ---------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "Writer.h" |
10 | #include "COFFLinkerContext.h" |
11 | #include "CallGraphSort.h" |
12 | #include "Config.h" |
13 | #include "DLL.h" |
14 | #include "InputFiles.h" |
15 | #include "LLDMapFile.h" |
16 | #include "MapFile.h" |
17 | #include "PDB.h" |
18 | #include "SymbolTable.h" |
19 | #include "Symbols.h" |
20 | #include "lld/Common/ErrorHandler.h" |
21 | #include "lld/Common/Memory.h" |
22 | #include "lld/Common/Timer.h" |
23 | #include "llvm/ADT/DenseMap.h" |
24 | #include "llvm/ADT/STLExtras.h" |
25 | #include "llvm/ADT/StringSet.h" |
26 | #include "llvm/BinaryFormat/COFF.h" |
27 | #include "llvm/Support/BinaryStreamReader.h" |
28 | #include "llvm/Support/Debug.h" |
29 | #include "llvm/Support/Endian.h" |
30 | #include "llvm/Support/FileOutputBuffer.h" |
31 | #include "llvm/Support/Parallel.h" |
32 | #include "llvm/Support/Path.h" |
33 | #include "llvm/Support/RandomNumberGenerator.h" |
34 | #include "llvm/Support/TimeProfiler.h" |
35 | #include "llvm/Support/xxhash.h" |
36 | #include <algorithm> |
37 | #include <cstdio> |
38 | #include <map> |
39 | #include <memory> |
40 | #include <utility> |
41 | |
42 | using namespace llvm; |
43 | using namespace llvm::COFF; |
44 | using namespace llvm::object; |
45 | using namespace llvm::support; |
46 | using namespace llvm::support::endian; |
47 | using namespace lld; |
48 | using namespace lld::coff; |
49 | |
50 | /* To re-generate DOSProgram: |
51 | $ cat > /tmp/DOSProgram.asm |
52 | org 0 |
53 | ; Copy cs to ds. |
54 | push cs |
55 | pop ds |
56 | ; Point ds:dx at the $-terminated string. |
57 | mov dx, str |
58 | ; Int 21/AH=09h: Write string to standard output. |
59 | mov ah, 0x9 |
60 | int 0x21 |
61 | ; Int 21/AH=4Ch: Exit with return code (in AL). |
62 | mov ax, 0x4C01 |
63 | int 0x21 |
64 | str: |
65 | db 'This program cannot be run in DOS mode.$' |
66 | align 8, db 0 |
67 | $ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin |
68 | $ xxd -i /tmp/DOSProgram.bin |
69 | */ |
70 | static unsigned char dosProgram[] = { |
71 | 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, |
72 | 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, |
73 | 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, |
74 | 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, |
75 | 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 |
76 | }; |
77 | static_assert(sizeof(dosProgram) % 8 == 0, |
78 | "DOSProgram size must be multiple of 8" ); |
79 | |
80 | static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram); |
81 | static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8" ); |
82 | |
83 | static const int numberOfDataDirectory = 16; |
84 | |
85 | namespace { |
86 | |
87 | class DebugDirectoryChunk : public NonSectionChunk { |
88 | public: |
89 | DebugDirectoryChunk(const COFFLinkerContext &c, |
90 | const std::vector<std::pair<COFF::DebugType, Chunk *>> &r, |
91 | bool writeRepro) |
92 | : records(r), writeRepro(writeRepro), ctx(c) {} |
93 | |
94 | size_t getSize() const override { |
95 | return (records.size() + int(writeRepro)) * sizeof(debug_directory); |
96 | } |
97 | |
98 | void writeTo(uint8_t *b) const override { |
99 | auto *d = reinterpret_cast<debug_directory *>(b); |
100 | |
101 | for (const std::pair<COFF::DebugType, Chunk *>& record : records) { |
102 | Chunk *c = record.second; |
103 | const OutputSection *os = ctx.getOutputSection(c); |
104 | uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA()); |
105 | fillEntry(d, debugType: record.first, size: c->getSize(), rva: c->getRVA(), offs); |
106 | ++d; |
107 | } |
108 | |
109 | if (writeRepro) { |
110 | // FIXME: The COFF spec allows either a 0-sized entry to just say |
111 | // "the timestamp field is really a hash", or a 4-byte size field |
112 | // followed by that many bytes containing a longer hash (with the |
113 | // lowest 4 bytes usually being the timestamp in little-endian order). |
114 | // Consider storing the full 8 bytes computed by xxh3_64bits here. |
115 | fillEntry(d, debugType: COFF::IMAGE_DEBUG_TYPE_REPRO, size: 0, rva: 0, offs: 0); |
116 | } |
117 | } |
118 | |
119 | void setTimeDateStamp(uint32_t timeDateStamp) { |
120 | for (support::ulittle32_t *tds : timeDateStamps) |
121 | *tds = timeDateStamp; |
122 | } |
123 | |
124 | private: |
125 | void fillEntry(debug_directory *d, COFF::DebugType debugType, size_t size, |
126 | uint64_t rva, uint64_t offs) const { |
127 | d->Characteristics = 0; |
128 | d->TimeDateStamp = 0; |
129 | d->MajorVersion = 0; |
130 | d->MinorVersion = 0; |
131 | d->Type = debugType; |
132 | d->SizeOfData = size; |
133 | d->AddressOfRawData = rva; |
134 | d->PointerToRawData = offs; |
135 | |
136 | timeDateStamps.push_back(x: &d->TimeDateStamp); |
137 | } |
138 | |
139 | mutable std::vector<support::ulittle32_t *> timeDateStamps; |
140 | const std::vector<std::pair<COFF::DebugType, Chunk *>> &records; |
141 | bool writeRepro; |
142 | const COFFLinkerContext &ctx; |
143 | }; |
144 | |
145 | class CVDebugRecordChunk : public NonSectionChunk { |
146 | public: |
147 | CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {} |
148 | |
149 | size_t getSize() const override { |
150 | return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1; |
151 | } |
152 | |
153 | void writeTo(uint8_t *b) const override { |
154 | // Save off the DebugInfo entry to backfill the file signature (build id) |
155 | // in Writer::writeBuildId |
156 | buildId = reinterpret_cast<codeview::DebugInfo *>(b); |
157 | |
158 | // variable sized field (PDB Path) |
159 | char *p = reinterpret_cast<char *>(b + sizeof(*buildId)); |
160 | if (!ctx.config.pdbAltPath.empty()) |
161 | memcpy(dest: p, src: ctx.config.pdbAltPath.data(), n: ctx.config.pdbAltPath.size()); |
162 | p[ctx.config.pdbAltPath.size()] = '\0'; |
163 | } |
164 | |
165 | mutable codeview::DebugInfo *buildId = nullptr; |
166 | |
167 | private: |
168 | const COFFLinkerContext &ctx; |
169 | }; |
170 | |
171 | class ExtendedDllCharacteristicsChunk : public NonSectionChunk { |
172 | public: |
173 | ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {} |
174 | |
175 | size_t getSize() const override { return 4; } |
176 | |
177 | void writeTo(uint8_t *buf) const override { write32le(P: buf, V: characteristics); } |
178 | |
179 | uint32_t characteristics = 0; |
180 | }; |
181 | |
182 | // PartialSection represents a group of chunks that contribute to an |
183 | // OutputSection. Collating a collection of PartialSections of same name and |
184 | // characteristics constitutes the OutputSection. |
185 | class PartialSectionKey { |
186 | public: |
187 | StringRef name; |
188 | unsigned characteristics; |
189 | |
190 | bool operator<(const PartialSectionKey &other) const { |
191 | int c = name.compare(RHS: other.name); |
192 | if (c > 0) |
193 | return false; |
194 | if (c == 0) |
195 | return characteristics < other.characteristics; |
196 | return true; |
197 | } |
198 | }; |
199 | |
200 | struct ChunkRange { |
201 | Chunk *first = nullptr, *last; |
202 | }; |
203 | |
204 | // The writer writes a SymbolTable result to a file. |
205 | class Writer { |
206 | public: |
207 | Writer(COFFLinkerContext &c) |
208 | : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {} |
209 | void run(); |
210 | |
211 | private: |
212 | void createSections(); |
213 | void createMiscChunks(); |
214 | void createImportTables(); |
215 | void appendImportThunks(); |
216 | void locateImportTables(); |
217 | void createExportTable(); |
218 | void mergeSections(); |
219 | void sortECChunks(); |
220 | void removeUnusedSections(); |
221 | void assignAddresses(); |
222 | bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin); |
223 | std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks, |
224 | Defined *target, uint64_t p, |
225 | uint16_t type, int margin); |
226 | bool createThunks(OutputSection *os, int margin); |
227 | bool verifyRanges(const std::vector<Chunk *> chunks); |
228 | void createECCodeMap(); |
229 | void finalizeAddresses(); |
230 | void removeEmptySections(); |
231 | void assignOutputSectionIndices(); |
232 | void createSymbolAndStringTable(); |
233 | void openFile(StringRef outputPath); |
234 | template <typename PEHeaderTy> void writeHeader(); |
235 | void createSEHTable(); |
236 | void createRuntimePseudoRelocs(); |
237 | void createECChunks(); |
238 | void insertCtorDtorSymbols(); |
239 | void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols); |
240 | void createGuardCFTables(); |
241 | void markSymbolsForRVATable(ObjFile *file, |
242 | ArrayRef<SectionChunk *> symIdxChunks, |
243 | SymbolRVASet &tableSymbols); |
244 | void getSymbolsFromSections(ObjFile *file, |
245 | ArrayRef<SectionChunk *> symIdxChunks, |
246 | std::vector<Symbol *> &symbols); |
247 | void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, |
248 | StringRef countSym, bool hasFlag=false); |
249 | void setSectionPermissions(); |
250 | void setECSymbols(); |
251 | void writeSections(); |
252 | void writeBuildId(); |
253 | void writePEChecksum(); |
254 | void sortSections(); |
255 | template <typename T> void sortExceptionTable(ChunkRange &exceptionTable); |
256 | void sortExceptionTables(); |
257 | void sortCRTSectionChunks(std::vector<Chunk *> &chunks); |
258 | void addSyntheticIdata(); |
259 | void sortBySectionOrder(std::vector<Chunk *> &chunks); |
260 | void fixPartialSectionChars(StringRef name, uint32_t chars); |
261 | bool fixGnuImportChunks(); |
262 | void fixTlsAlignment(); |
263 | PartialSection *createPartialSection(StringRef name, uint32_t outChars); |
264 | PartialSection *findPartialSection(StringRef name, uint32_t outChars); |
265 | |
266 | std::optional<coff_symbol16> createSymbol(Defined *d); |
267 | size_t addEntryToStringTable(StringRef str); |
268 | |
269 | OutputSection *findSection(StringRef name); |
270 | void addBaserels(); |
271 | void addBaserelBlocks(std::vector<Baserel> &v); |
272 | |
273 | uint32_t getSizeOfInitializedData(); |
274 | |
275 | void prepareLoadConfig(); |
276 | template <typename T> void prepareLoadConfig(T *loadConfig); |
277 | template <typename T> void checkLoadConfigGuardData(const T *loadConfig); |
278 | |
279 | std::unique_ptr<FileOutputBuffer> &buffer; |
280 | std::map<PartialSectionKey, PartialSection *> partialSections; |
281 | std::vector<char> strtab; |
282 | std::vector<llvm::object::coff_symbol16> outputSymtab; |
283 | std::vector<ECCodeMapEntry> codeMap; |
284 | IdataContents idata; |
285 | Chunk *importTableStart = nullptr; |
286 | uint64_t importTableSize = 0; |
287 | Chunk *edataStart = nullptr; |
288 | Chunk *edataEnd = nullptr; |
289 | Chunk *iatStart = nullptr; |
290 | uint64_t iatSize = 0; |
291 | DelayLoadContents delayIdata; |
292 | EdataContents edata; |
293 | bool setNoSEHCharacteristic = false; |
294 | uint32_t tlsAlignment = 0; |
295 | |
296 | DebugDirectoryChunk *debugDirectory = nullptr; |
297 | std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords; |
298 | CVDebugRecordChunk *buildId = nullptr; |
299 | ArrayRef<uint8_t> sectionTable; |
300 | |
301 | uint64_t fileSize; |
302 | uint32_t pointerToSymbolTable = 0; |
303 | uint64_t sizeOfImage; |
304 | uint64_t ; |
305 | |
306 | OutputSection *textSec; |
307 | OutputSection *rdataSec; |
308 | OutputSection *buildidSec; |
309 | OutputSection *dataSec; |
310 | OutputSection *pdataSec; |
311 | OutputSection *idataSec; |
312 | OutputSection *edataSec; |
313 | OutputSection *didatSec; |
314 | OutputSection *rsrcSec; |
315 | OutputSection *relocSec; |
316 | OutputSection *; |
317 | OutputSection *; |
318 | // Either .rdata section or .buildid section. |
319 | OutputSection *debugInfoSec; |
320 | |
321 | // The range of .pdata sections in the output file. |
322 | // |
323 | // We need to keep track of the location of .pdata in whichever section it |
324 | // gets merged into so that we can sort its contents and emit a correct data |
325 | // directory entry for the exception table. This is also the case for some |
326 | // other sections (such as .edata) but because the contents of those sections |
327 | // are entirely linker-generated we can keep track of their locations using |
328 | // the chunks that the linker creates. All .pdata chunks come from input |
329 | // files, so we need to keep track of them separately. |
330 | ChunkRange pdata; |
331 | |
332 | // x86_64 .pdata sections on ARM64EC/ARM64X targets. |
333 | ChunkRange hybridPdata; |
334 | |
335 | COFFLinkerContext &ctx; |
336 | }; |
337 | } // anonymous namespace |
338 | |
339 | void lld::coff::writeResult(COFFLinkerContext &ctx) { |
340 | llvm::TimeTraceScope timeScope("Write output(s)" ); |
341 | Writer(ctx).run(); |
342 | } |
343 | |
344 | void OutputSection::addChunk(Chunk *c) { |
345 | chunks.push_back(x: c); |
346 | } |
347 | |
348 | void OutputSection::insertChunkAtStart(Chunk *c) { |
349 | chunks.insert(position: chunks.begin(), x: c); |
350 | } |
351 | |
352 | void OutputSection::setPermissions(uint32_t c) { |
353 | header.Characteristics &= ~permMask; |
354 | header.Characteristics |= c; |
355 | } |
356 | |
357 | void OutputSection::merge(OutputSection *other) { |
358 | chunks.insert(position: chunks.end(), first: other->chunks.begin(), last: other->chunks.end()); |
359 | other->chunks.clear(); |
360 | contribSections.insert(position: contribSections.end(), first: other->contribSections.begin(), |
361 | last: other->contribSections.end()); |
362 | other->contribSections.clear(); |
363 | |
364 | // MS link.exe compatibility: when merging a code section into a data section, |
365 | // mark the target section as a code section. |
366 | if (other->header.Characteristics & IMAGE_SCN_CNT_CODE) { |
367 | header.Characteristics |= IMAGE_SCN_CNT_CODE; |
368 | header.Characteristics &= |
369 | ~(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_CNT_UNINITIALIZED_DATA); |
370 | } |
371 | } |
372 | |
373 | // Write the section header to a given buffer. |
374 | void OutputSection::(uint8_t *buf, bool isDebug) { |
375 | auto *hdr = reinterpret_cast<coff_section *>(buf); |
376 | *hdr = header; |
377 | if (stringTableOff) { |
378 | // If name is too long, write offset into the string table as a name. |
379 | encodeSectionName(Out: hdr->Name, Offset: stringTableOff); |
380 | } else { |
381 | assert(!isDebug || name.size() <= COFF::NameSize || |
382 | (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0); |
383 | strncpy(dest: hdr->Name, src: name.data(), |
384 | n: std::min(a: name.size(), b: (size_t)COFF::NameSize)); |
385 | } |
386 | } |
387 | |
388 | void OutputSection::addContributingPartialSection(PartialSection *sec) { |
389 | contribSections.push_back(x: sec); |
390 | } |
391 | |
392 | // Check whether the target address S is in range from a relocation |
393 | // of type relType at address P. |
394 | bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { |
395 | if (ctx.config.machine == ARMNT) { |
396 | int64_t diff = AbsoluteDifference(X: s, Y: p + 4) + margin; |
397 | switch (relType) { |
398 | case IMAGE_REL_ARM_BRANCH20T: |
399 | return isInt<21>(x: diff); |
400 | case IMAGE_REL_ARM_BRANCH24T: |
401 | case IMAGE_REL_ARM_BLX23T: |
402 | return isInt<25>(x: diff); |
403 | default: |
404 | return true; |
405 | } |
406 | } else if (ctx.config.machine == ARM64) { |
407 | int64_t diff = AbsoluteDifference(X: s, Y: p) + margin; |
408 | switch (relType) { |
409 | case IMAGE_REL_ARM64_BRANCH26: |
410 | return isInt<28>(x: diff); |
411 | case IMAGE_REL_ARM64_BRANCH19: |
412 | return isInt<21>(x: diff); |
413 | case IMAGE_REL_ARM64_BRANCH14: |
414 | return isInt<16>(x: diff); |
415 | default: |
416 | return true; |
417 | } |
418 | } else { |
419 | llvm_unreachable("Unexpected architecture" ); |
420 | } |
421 | } |
422 | |
423 | // Return the last thunk for the given target if it is in range, |
424 | // or create a new one. |
425 | std::pair<Defined *, bool> |
426 | Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target, |
427 | uint64_t p, uint16_t type, int margin) { |
428 | Defined *&lastThunk = lastThunks[target->getRVA()]; |
429 | if (lastThunk && isInRange(relType: type, s: lastThunk->getRVA(), p, margin)) |
430 | return {lastThunk, false}; |
431 | Chunk *c; |
432 | switch (ctx.config.machine) { |
433 | case ARMNT: |
434 | c = make<RangeExtensionThunkARM>(args&: ctx, args&: target); |
435 | break; |
436 | case ARM64: |
437 | c = make<RangeExtensionThunkARM64>(args&: ctx, args&: target); |
438 | break; |
439 | default: |
440 | llvm_unreachable("Unexpected architecture" ); |
441 | } |
442 | Defined *d = make<DefinedSynthetic>(args: "range_extension_thunk" , args&: c); |
443 | lastThunk = d; |
444 | return {d, true}; |
445 | } |
446 | |
447 | // This checks all relocations, and for any relocation which isn't in range |
448 | // it adds a thunk after the section chunk that contains the relocation. |
449 | // If the latest thunk for the specific target is in range, that is used |
450 | // instead of creating a new thunk. All range checks are done with the |
451 | // specified margin, to make sure that relocations that originally are in |
452 | // range, but only barely, also get thunks - in case other added thunks makes |
453 | // the target go out of range. |
454 | // |
455 | // After adding thunks, we verify that all relocations are in range (with |
456 | // no extra margin requirements). If this failed, we restart (throwing away |
457 | // the previously created thunks) and retry with a wider margin. |
458 | bool Writer::createThunks(OutputSection *os, int margin) { |
459 | bool addressesChanged = false; |
460 | DenseMap<uint64_t, Defined *> lastThunks; |
461 | DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices; |
462 | size_t thunksSize = 0; |
463 | // Recheck Chunks.size() each iteration, since we can insert more |
464 | // elements into it. |
465 | for (size_t i = 0; i != os->chunks.size(); ++i) { |
466 | SectionChunk *sc = dyn_cast_or_null<SectionChunk>(Val: os->chunks[i]); |
467 | if (!sc) |
468 | continue; |
469 | size_t thunkInsertionSpot = i + 1; |
470 | |
471 | // Try to get a good enough estimate of where new thunks will be placed. |
472 | // Offset this by the size of the new thunks added so far, to make the |
473 | // estimate slightly better. |
474 | size_t thunkInsertionRVA = sc->getRVA() + sc->getSize() + thunksSize; |
475 | ObjFile *file = sc->file; |
476 | std::vector<std::pair<uint32_t, uint32_t>> relocReplacements; |
477 | ArrayRef<coff_relocation> originalRelocs = |
478 | file->getCOFFObj()->getRelocations(Sec: sc->header); |
479 | for (size_t j = 0, e = originalRelocs.size(); j < e; ++j) { |
480 | const coff_relocation &rel = originalRelocs[j]; |
481 | Symbol *relocTarget = file->getSymbol(symbolIndex: rel.SymbolTableIndex); |
482 | |
483 | // The estimate of the source address P should be pretty accurate, |
484 | // but we don't know whether the target Symbol address should be |
485 | // offset by thunksSize or not (or by some of thunksSize but not all of |
486 | // it), giving us some uncertainty once we have added one thunk. |
487 | uint64_t p = sc->getRVA() + rel.VirtualAddress + thunksSize; |
488 | |
489 | Defined *sym = dyn_cast_or_null<Defined>(Val: relocTarget); |
490 | if (!sym) |
491 | continue; |
492 | |
493 | uint64_t s = sym->getRVA(); |
494 | |
495 | if (isInRange(relType: rel.Type, s, p, margin)) |
496 | continue; |
497 | |
498 | // If the target isn't in range, hook it up to an existing or new thunk. |
499 | auto [thunk, wasNew] = getThunk(lastThunks, target: sym, p, type: rel.Type, margin); |
500 | if (wasNew) { |
501 | Chunk *thunkChunk = thunk->getChunk(); |
502 | thunkChunk->setRVA( |
503 | thunkInsertionRVA); // Estimate of where it will be located. |
504 | os->chunks.insert(position: os->chunks.begin() + thunkInsertionSpot, x: thunkChunk); |
505 | thunkInsertionSpot++; |
506 | thunksSize += thunkChunk->getSize(); |
507 | thunkInsertionRVA += thunkChunk->getSize(); |
508 | addressesChanged = true; |
509 | } |
510 | |
511 | // To redirect the relocation, add a symbol to the parent object file's |
512 | // symbol table, and replace the relocation symbol table index with the |
513 | // new index. |
514 | auto insertion = thunkSymtabIndices.insert(KV: {{file, thunk}, ~0U}); |
515 | uint32_t &thunkSymbolIndex = insertion.first->second; |
516 | if (insertion.second) |
517 | thunkSymbolIndex = file->addRangeThunkSymbol(thunk); |
518 | relocReplacements.emplace_back(args&: j, args&: thunkSymbolIndex); |
519 | } |
520 | |
521 | // Get a writable copy of this section's relocations so they can be |
522 | // modified. If the relocations point into the object file, allocate new |
523 | // memory. Otherwise, this must be previously allocated memory that can be |
524 | // modified in place. |
525 | ArrayRef<coff_relocation> curRelocs = sc->getRelocs(); |
526 | MutableArrayRef<coff_relocation> newRelocs; |
527 | if (originalRelocs.data() == curRelocs.data()) { |
528 | newRelocs = MutableArrayRef( |
529 | bAlloc().Allocate<coff_relocation>(Num: originalRelocs.size()), |
530 | originalRelocs.size()); |
531 | } else { |
532 | newRelocs = MutableArrayRef( |
533 | const_cast<coff_relocation *>(curRelocs.data()), curRelocs.size()); |
534 | } |
535 | |
536 | // Copy each relocation, but replace the symbol table indices which need |
537 | // thunks. |
538 | auto nextReplacement = relocReplacements.begin(); |
539 | auto endReplacement = relocReplacements.end(); |
540 | for (size_t i = 0, e = originalRelocs.size(); i != e; ++i) { |
541 | newRelocs[i] = originalRelocs[i]; |
542 | if (nextReplacement != endReplacement && nextReplacement->first == i) { |
543 | newRelocs[i].SymbolTableIndex = nextReplacement->second; |
544 | ++nextReplacement; |
545 | } |
546 | } |
547 | |
548 | sc->setRelocs(newRelocs); |
549 | } |
550 | return addressesChanged; |
551 | } |
552 | |
553 | // Create a code map for CHPE metadata. |
554 | void Writer::createECCodeMap() { |
555 | if (!isArm64EC(Machine: ctx.config.machine)) |
556 | return; |
557 | |
558 | // Clear the map in case we were're recomputing the map after adding |
559 | // a range extension thunk. |
560 | codeMap.clear(); |
561 | |
562 | std::optional<chpe_range_type> lastType; |
563 | Chunk *first, *last; |
564 | |
565 | auto closeRange = [&]() { |
566 | if (lastType) { |
567 | codeMap.push_back(x: {first, last, *lastType}); |
568 | lastType.reset(); |
569 | } |
570 | }; |
571 | |
572 | for (OutputSection *sec : ctx.outputSections) { |
573 | for (Chunk *c : sec->chunks) { |
574 | // Skip empty section chunks. MS link.exe does not seem to do that and |
575 | // generates empty code ranges in some cases. |
576 | if (isa<SectionChunk>(Val: c) && !c->getSize()) |
577 | continue; |
578 | |
579 | std::optional<chpe_range_type> chunkType = c->getArm64ECRangeType(); |
580 | if (chunkType != lastType) { |
581 | closeRange(); |
582 | first = c; |
583 | lastType = chunkType; |
584 | } |
585 | last = c; |
586 | } |
587 | } |
588 | |
589 | closeRange(); |
590 | |
591 | Symbol *tableCountSym = ctx.symtab.findUnderscore(name: "__hybrid_code_map_count" ); |
592 | cast<DefinedAbsolute>(Val: tableCountSym)->setVA(codeMap.size()); |
593 | } |
594 | |
595 | // Verify that all relocations are in range, with no extra margin requirements. |
596 | bool Writer::verifyRanges(const std::vector<Chunk *> chunks) { |
597 | for (Chunk *c : chunks) { |
598 | SectionChunk *sc = dyn_cast_or_null<SectionChunk>(Val: c); |
599 | if (!sc) |
600 | continue; |
601 | |
602 | ArrayRef<coff_relocation> relocs = sc->getRelocs(); |
603 | for (const coff_relocation &rel : relocs) { |
604 | Symbol *relocTarget = sc->file->getSymbol(symbolIndex: rel.SymbolTableIndex); |
605 | |
606 | Defined *sym = dyn_cast_or_null<Defined>(Val: relocTarget); |
607 | if (!sym) |
608 | continue; |
609 | |
610 | uint64_t p = sc->getRVA() + rel.VirtualAddress; |
611 | uint64_t s = sym->getRVA(); |
612 | |
613 | if (!isInRange(relType: rel.Type, s, p, margin: 0)) |
614 | return false; |
615 | } |
616 | } |
617 | return true; |
618 | } |
619 | |
620 | // Assign addresses and add thunks if necessary. |
621 | void Writer::finalizeAddresses() { |
622 | assignAddresses(); |
623 | if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64) |
624 | return; |
625 | |
626 | size_t origNumChunks = 0; |
627 | for (OutputSection *sec : ctx.outputSections) { |
628 | sec->origChunks = sec->chunks; |
629 | origNumChunks += sec->chunks.size(); |
630 | } |
631 | |
632 | int pass = 0; |
633 | int margin = 1024 * 100; |
634 | while (true) { |
635 | llvm::TimeTraceScope timeScope2("Add thunks pass" ); |
636 | |
637 | // First check whether we need thunks at all, or if the previous pass of |
638 | // adding them turned out ok. |
639 | bool rangesOk = true; |
640 | size_t numChunks = 0; |
641 | { |
642 | llvm::TimeTraceScope timeScope3("Verify ranges" ); |
643 | for (OutputSection *sec : ctx.outputSections) { |
644 | if (!verifyRanges(chunks: sec->chunks)) { |
645 | rangesOk = false; |
646 | break; |
647 | } |
648 | numChunks += sec->chunks.size(); |
649 | } |
650 | } |
651 | if (rangesOk) { |
652 | if (pass > 0) |
653 | log(msg: "Added " + Twine(numChunks - origNumChunks) + " thunks with " + |
654 | "margin " + Twine(margin) + " in " + Twine(pass) + " passes" ); |
655 | return; |
656 | } |
657 | |
658 | if (pass >= 10) |
659 | fatal(msg: "adding thunks hasn't converged after " + Twine(pass) + " passes" ); |
660 | |
661 | if (pass > 0) { |
662 | // If the previous pass didn't work out, reset everything back to the |
663 | // original conditions before retrying with a wider margin. This should |
664 | // ideally never happen under real circumstances. |
665 | for (OutputSection *sec : ctx.outputSections) |
666 | sec->chunks = sec->origChunks; |
667 | margin *= 2; |
668 | } |
669 | |
670 | // Try adding thunks everywhere where it is needed, with a margin |
671 | // to avoid things going out of range due to the added thunks. |
672 | bool addressesChanged = false; |
673 | { |
674 | llvm::TimeTraceScope timeScope3("Create thunks" ); |
675 | for (OutputSection *sec : ctx.outputSections) |
676 | addressesChanged |= createThunks(os: sec, margin); |
677 | } |
678 | // If the verification above thought we needed thunks, we should have |
679 | // added some. |
680 | assert(addressesChanged); |
681 | (void)addressesChanged; |
682 | |
683 | // Recalculate the layout for the whole image (and verify the ranges at |
684 | // the start of the next round). |
685 | assignAddresses(); |
686 | |
687 | pass++; |
688 | } |
689 | } |
690 | |
691 | void Writer::writePEChecksum() { |
692 | if (!ctx.config.writeCheckSum) { |
693 | return; |
694 | } |
695 | |
696 | llvm::TimeTraceScope timeScope("PE checksum" ); |
697 | |
698 | // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#checksum |
699 | uint32_t *buf = (uint32_t *)buffer->getBufferStart(); |
700 | uint32_t size = (uint32_t)(buffer->getBufferSize()); |
701 | |
702 | coff_file_header * = |
703 | (coff_file_header *)((uint8_t *)buf + dosStubSize + sizeof(PEMagic)); |
704 | pe32_header * = |
705 | (pe32_header *)((uint8_t *)coffHeader + sizeof(coff_file_header)); |
706 | |
707 | uint64_t sum = 0; |
708 | uint32_t count = size; |
709 | ulittle16_t *addr = (ulittle16_t *)buf; |
710 | |
711 | // The PE checksum algorithm, implemented as suggested in RFC1071 |
712 | while (count > 1) { |
713 | sum += *addr++; |
714 | count -= 2; |
715 | } |
716 | |
717 | // Add left-over byte, if any |
718 | if (count > 0) |
719 | sum += *(unsigned char *)addr; |
720 | |
721 | // Fold 32-bit sum to 16 bits |
722 | while (sum >> 16) { |
723 | sum = (sum & 0xffff) + (sum >> 16); |
724 | } |
725 | |
726 | sum += size; |
727 | peHeader->CheckSum = sum; |
728 | } |
729 | |
730 | // The main function of the writer. |
731 | void Writer::run() { |
732 | { |
733 | llvm::TimeTraceScope timeScope("Write PE" ); |
734 | ScopedTimer t1(ctx.codeLayoutTimer); |
735 | |
736 | createImportTables(); |
737 | createSections(); |
738 | appendImportThunks(); |
739 | // Import thunks must be added before the Control Flow Guard tables are |
740 | // added. |
741 | createMiscChunks(); |
742 | createExportTable(); |
743 | mergeSections(); |
744 | sortECChunks(); |
745 | removeUnusedSections(); |
746 | finalizeAddresses(); |
747 | removeEmptySections(); |
748 | assignOutputSectionIndices(); |
749 | setSectionPermissions(); |
750 | setECSymbols(); |
751 | createSymbolAndStringTable(); |
752 | |
753 | if (fileSize > UINT32_MAX) |
754 | fatal(msg: "image size (" + Twine(fileSize) + ") " + |
755 | "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")" ); |
756 | |
757 | openFile(outputPath: ctx.config.outputFile); |
758 | if (ctx.config.is64()) { |
759 | writeHeader<pe32plus_header>(); |
760 | } else { |
761 | writeHeader<pe32_header>(); |
762 | } |
763 | writeSections(); |
764 | prepareLoadConfig(); |
765 | sortExceptionTables(); |
766 | |
767 | // Fix up the alignment in the TLS Directory's characteristic field, |
768 | // if a specific alignment value is needed |
769 | if (tlsAlignment) |
770 | fixTlsAlignment(); |
771 | } |
772 | |
773 | if (!ctx.config.pdbPath.empty() && ctx.config.debug) { |
774 | assert(buildId); |
775 | createPDB(ctx, sectionTable, buildId: buildId->buildId); |
776 | } |
777 | writeBuildId(); |
778 | |
779 | writeLLDMapFile(ctx); |
780 | writeMapFile(ctx); |
781 | |
782 | writePEChecksum(); |
783 | |
784 | if (errorCount()) |
785 | return; |
786 | |
787 | llvm::TimeTraceScope timeScope("Commit PE to disk" ); |
788 | ScopedTimer t2(ctx.outputCommitTimer); |
789 | if (auto e = buffer->commit()) |
790 | fatal(msg: "failed to write output '" + buffer->getPath() + |
791 | "': " + toString(E: std::move(e))); |
792 | } |
793 | |
794 | static StringRef getOutputSectionName(StringRef name) { |
795 | StringRef s = name.split(Separator: '$').first; |
796 | |
797 | // Treat a later period as a separator for MinGW, for sections like |
798 | // ".ctors.01234". |
799 | return s.substr(Start: 0, N: s.find(C: '.', From: 1)); |
800 | } |
801 | |
802 | // For /order. |
803 | void Writer::sortBySectionOrder(std::vector<Chunk *> &chunks) { |
804 | auto getPriority = [&ctx = ctx](const Chunk *c) { |
805 | if (auto *sec = dyn_cast<SectionChunk>(Val: c)) |
806 | if (sec->sym) |
807 | return ctx.config.order.lookup(Key: sec->sym->getName()); |
808 | return 0; |
809 | }; |
810 | |
811 | llvm::stable_sort(Range&: chunks, C: [=](const Chunk *a, const Chunk *b) { |
812 | return getPriority(a) < getPriority(b); |
813 | }); |
814 | } |
815 | |
816 | // Change the characteristics of existing PartialSections that belong to the |
817 | // section Name to Chars. |
818 | void Writer::fixPartialSectionChars(StringRef name, uint32_t chars) { |
819 | for (auto it : partialSections) { |
820 | PartialSection *pSec = it.second; |
821 | StringRef curName = pSec->name; |
822 | if (!curName.consume_front(Prefix: name) || |
823 | (!curName.empty() && !curName.starts_with(Prefix: "$" ))) |
824 | continue; |
825 | if (pSec->characteristics == chars) |
826 | continue; |
827 | PartialSection *destSec = createPartialSection(name: pSec->name, outChars: chars); |
828 | destSec->chunks.insert(position: destSec->chunks.end(), first: pSec->chunks.begin(), |
829 | last: pSec->chunks.end()); |
830 | pSec->chunks.clear(); |
831 | } |
832 | } |
833 | |
834 | // Sort concrete section chunks from GNU import libraries. |
835 | // |
836 | // GNU binutils doesn't use short import files, but instead produces import |
837 | // libraries that consist of object files, with section chunks for the .idata$* |
838 | // sections. These are linked just as regular static libraries. Each import |
839 | // library consists of one header object, one object file for every imported |
840 | // symbol, and one trailer object. In order for the .idata tables/lists to |
841 | // be formed correctly, the section chunks within each .idata$* section need |
842 | // to be grouped by library, and sorted alphabetically within each library |
843 | // (which makes sure the header comes first and the trailer last). |
844 | bool Writer::fixGnuImportChunks() { |
845 | uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; |
846 | |
847 | // Make sure all .idata$* section chunks are mapped as RDATA in order to |
848 | // be sorted into the same sections as our own synthesized .idata chunks. |
849 | fixPartialSectionChars(name: ".idata" , chars: rdata); |
850 | |
851 | bool hasIdata = false; |
852 | // Sort all .idata$* chunks, grouping chunks from the same library, |
853 | // with alphabetical ordering of the object files within a library. |
854 | for (auto it : partialSections) { |
855 | PartialSection *pSec = it.second; |
856 | if (!pSec->name.starts_with(Prefix: ".idata" )) |
857 | continue; |
858 | |
859 | if (!pSec->chunks.empty()) |
860 | hasIdata = true; |
861 | llvm::stable_sort(Range&: pSec->chunks, C: [&](Chunk *s, Chunk *t) { |
862 | SectionChunk *sc1 = dyn_cast_or_null<SectionChunk>(Val: s); |
863 | SectionChunk *sc2 = dyn_cast_or_null<SectionChunk>(Val: t); |
864 | if (!sc1 || !sc2) { |
865 | // if SC1, order them ascending. If SC2 or both null, |
866 | // S is not less than T. |
867 | return sc1 != nullptr; |
868 | } |
869 | // Make a string with "libraryname/objectfile" for sorting, achieving |
870 | // both grouping by library and sorting of objects within a library, |
871 | // at once. |
872 | std::string key1 = |
873 | (sc1->file->parentName + "/" + sc1->file->getName()).str(); |
874 | std::string key2 = |
875 | (sc2->file->parentName + "/" + sc2->file->getName()).str(); |
876 | return key1 < key2; |
877 | }); |
878 | } |
879 | return hasIdata; |
880 | } |
881 | |
882 | // Add generated idata chunks, for imported symbols and DLLs, and a |
883 | // terminator in .idata$2. |
884 | void Writer::addSyntheticIdata() { |
885 | uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; |
886 | idata.create(ctx); |
887 | |
888 | // Add the .idata content in the right section groups, to allow |
889 | // chunks from other linked in object files to be grouped together. |
890 | // See Microsoft PE/COFF spec 5.4 for details. |
891 | auto add = [&](StringRef n, std::vector<Chunk *> &v) { |
892 | PartialSection *pSec = createPartialSection(name: n, outChars: rdata); |
893 | pSec->chunks.insert(position: pSec->chunks.end(), first: v.begin(), last: v.end()); |
894 | }; |
895 | |
896 | // The loader assumes a specific order of data. |
897 | // Add each type in the correct order. |
898 | add(".idata$2" , idata.dirs); |
899 | add(".idata$4" , idata.lookups); |
900 | add(".idata$5" , idata.addresses); |
901 | if (!idata.hints.empty()) |
902 | add(".idata$6" , idata.hints); |
903 | add(".idata$7" , idata.dllNames); |
904 | } |
905 | |
906 | // Locate the first Chunk and size of the import directory list and the |
907 | // IAT. |
908 | void Writer::locateImportTables() { |
909 | uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ; |
910 | |
911 | if (PartialSection *importDirs = findPartialSection(name: ".idata$2" , outChars: rdata)) { |
912 | if (!importDirs->chunks.empty()) |
913 | importTableStart = importDirs->chunks.front(); |
914 | for (Chunk *c : importDirs->chunks) |
915 | importTableSize += c->getSize(); |
916 | } |
917 | |
918 | if (PartialSection *importAddresses = findPartialSection(name: ".idata$5" , outChars: rdata)) { |
919 | if (!importAddresses->chunks.empty()) |
920 | iatStart = importAddresses->chunks.front(); |
921 | for (Chunk *c : importAddresses->chunks) |
922 | iatSize += c->getSize(); |
923 | } |
924 | } |
925 | |
926 | // Return whether a SectionChunk's suffix (the dollar and any trailing |
927 | // suffix) should be removed and sorted into the main suffixless |
928 | // PartialSection. |
929 | static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name, |
930 | bool isMinGW) { |
931 | // On MinGW, comdat groups are formed by putting the comdat group name |
932 | // after the '$' in the section name. For .eh_frame$<symbol>, that must |
933 | // still be sorted before the .eh_frame trailer from crtend.o, thus just |
934 | // strip the section name trailer. For other sections, such as |
935 | // .tls$$<symbol> (where non-comdat .tls symbols are otherwise stored in |
936 | // ".tls$"), they must be strictly sorted after .tls. And for the |
937 | // hypothetical case of comdat .CRT$XCU, we definitely need to keep the |
938 | // suffix for sorting. Thus, to play it safe, only strip the suffix for |
939 | // the standard sections. |
940 | if (!isMinGW) |
941 | return false; |
942 | if (!sc || !sc->isCOMDAT()) |
943 | return false; |
944 | return name.starts_with(Prefix: ".text$" ) || name.starts_with(Prefix: ".data$" ) || |
945 | name.starts_with(Prefix: ".rdata$" ) || name.starts_with(Prefix: ".pdata$" ) || |
946 | name.starts_with(Prefix: ".xdata$" ) || name.starts_with(Prefix: ".eh_frame$" ); |
947 | } |
948 | |
949 | void Writer::sortSections() { |
950 | if (!ctx.config.callGraphProfile.empty()) { |
951 | DenseMap<const SectionChunk *, int> order = |
952 | computeCallGraphProfileOrder(ctx); |
953 | for (auto it : order) { |
954 | if (DefinedRegular *sym = it.first->sym) |
955 | ctx.config.order[sym->getName()] = it.second; |
956 | } |
957 | } |
958 | if (!ctx.config.order.empty()) |
959 | for (auto it : partialSections) |
960 | sortBySectionOrder(chunks&: it.second->chunks); |
961 | } |
962 | |
963 | // Create output section objects and add them to OutputSections. |
964 | void Writer::createSections() { |
965 | llvm::TimeTraceScope timeScope("Output sections" ); |
966 | // First, create the builtin sections. |
967 | const uint32_t data = IMAGE_SCN_CNT_INITIALIZED_DATA; |
968 | const uint32_t bss = IMAGE_SCN_CNT_UNINITIALIZED_DATA; |
969 | const uint32_t code = IMAGE_SCN_CNT_CODE; |
970 | const uint32_t discardable = IMAGE_SCN_MEM_DISCARDABLE; |
971 | const uint32_t r = IMAGE_SCN_MEM_READ; |
972 | const uint32_t w = IMAGE_SCN_MEM_WRITE; |
973 | const uint32_t x = IMAGE_SCN_MEM_EXECUTE; |
974 | |
975 | SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> sections; |
976 | auto createSection = [&](StringRef name, uint32_t outChars) { |
977 | OutputSection *&sec = sections[{name, outChars}]; |
978 | if (!sec) { |
979 | sec = make<OutputSection>(args&: name, args&: outChars); |
980 | ctx.outputSections.push_back(x: sec); |
981 | } |
982 | return sec; |
983 | }; |
984 | |
985 | // Try to match the section order used by link.exe. |
986 | textSec = createSection(".text" , code | r | x); |
987 | createSection(".bss" , bss | r | w); |
988 | rdataSec = createSection(".rdata" , data | r); |
989 | buildidSec = createSection(".buildid" , data | r); |
990 | dataSec = createSection(".data" , data | r | w); |
991 | pdataSec = createSection(".pdata" , data | r); |
992 | idataSec = createSection(".idata" , data | r); |
993 | edataSec = createSection(".edata" , data | r); |
994 | didatSec = createSection(".didat" , data | r); |
995 | rsrcSec = createSection(".rsrc" , data | r); |
996 | relocSec = createSection(".reloc" , data | discardable | r); |
997 | ctorsSec = createSection(".ctors" , data | r | w); |
998 | dtorsSec = createSection(".dtors" , data | r | w); |
999 | |
1000 | // Then bin chunks by name and output characteristics. |
1001 | for (Chunk *c : ctx.symtab.getChunks()) { |
1002 | auto *sc = dyn_cast<SectionChunk>(Val: c); |
1003 | if (sc && !sc->live) { |
1004 | if (ctx.config.verbose) |
1005 | sc->printDiscardedMessage(); |
1006 | continue; |
1007 | } |
1008 | StringRef name = c->getSectionName(); |
1009 | if (shouldStripSectionSuffix(sc, name, isMinGW: ctx.config.mingw)) |
1010 | name = name.split(Separator: '$').first; |
1011 | |
1012 | if (name.starts_with(Prefix: ".tls" )) |
1013 | tlsAlignment = std::max(a: tlsAlignment, b: c->getAlignment()); |
1014 | |
1015 | PartialSection *pSec = createPartialSection(name, |
1016 | outChars: c->getOutputCharacteristics()); |
1017 | pSec->chunks.push_back(x: c); |
1018 | } |
1019 | |
1020 | fixPartialSectionChars(name: ".rsrc" , chars: data | r); |
1021 | fixPartialSectionChars(name: ".edata" , chars: data | r); |
1022 | // Even in non MinGW cases, we might need to link against GNU import |
1023 | // libraries. |
1024 | bool hasIdata = fixGnuImportChunks(); |
1025 | if (!idata.empty()) |
1026 | hasIdata = true; |
1027 | |
1028 | if (hasIdata) |
1029 | addSyntheticIdata(); |
1030 | |
1031 | sortSections(); |
1032 | |
1033 | if (hasIdata) |
1034 | locateImportTables(); |
1035 | |
1036 | // Then create an OutputSection for each section. |
1037 | // '$' and all following characters in input section names are |
1038 | // discarded when determining output section. So, .text$foo |
1039 | // contributes to .text, for example. See PE/COFF spec 3.2. |
1040 | for (auto it : partialSections) { |
1041 | PartialSection *pSec = it.second; |
1042 | StringRef name = getOutputSectionName(name: pSec->name); |
1043 | uint32_t outChars = pSec->characteristics; |
1044 | |
1045 | if (name == ".CRT" ) { |
1046 | // In link.exe, there is a special case for the I386 target where .CRT |
1047 | // sections are treated as if they have output characteristics DATA | R if |
1048 | // their characteristics are DATA | R | W. This implements the same |
1049 | // special case for all architectures. |
1050 | outChars = data | r; |
1051 | |
1052 | log(msg: "Processing section " + pSec->name + " -> " + name); |
1053 | |
1054 | sortCRTSectionChunks(chunks&: pSec->chunks); |
1055 | } |
1056 | |
1057 | OutputSection *sec = createSection(name, outChars); |
1058 | for (Chunk *c : pSec->chunks) |
1059 | sec->addChunk(c); |
1060 | |
1061 | sec->addContributingPartialSection(sec: pSec); |
1062 | } |
1063 | |
1064 | // Finally, move some output sections to the end. |
1065 | auto sectionOrder = [&](const OutputSection *s) { |
1066 | // Move DISCARDABLE (or non-memory-mapped) sections to the end of file |
1067 | // because the loader cannot handle holes. Stripping can remove other |
1068 | // discardable ones than .reloc, which is first of them (created early). |
1069 | if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) { |
1070 | // Move discardable sections named .debug_ to the end, after other |
1071 | // discardable sections. Stripping only removes the sections named |
1072 | // .debug_* - thus try to avoid leaving holes after stripping. |
1073 | if (s->name.starts_with(Prefix: ".debug_" )) |
1074 | return 3; |
1075 | return 2; |
1076 | } |
1077 | // .rsrc should come at the end of the non-discardable sections because its |
1078 | // size may change by the Win32 UpdateResources() function, causing |
1079 | // subsequent sections to move (see https://crbug.com/827082). |
1080 | if (s == rsrcSec) |
1081 | return 1; |
1082 | return 0; |
1083 | }; |
1084 | llvm::stable_sort(Range&: ctx.outputSections, |
1085 | C: [&](const OutputSection *s, const OutputSection *t) { |
1086 | return sectionOrder(s) < sectionOrder(t); |
1087 | }); |
1088 | } |
1089 | |
1090 | void Writer::createMiscChunks() { |
1091 | llvm::TimeTraceScope timeScope("Misc chunks" ); |
1092 | Configuration *config = &ctx.config; |
1093 | |
1094 | for (MergeChunk *p : ctx.mergeChunkInstances) { |
1095 | if (p) { |
1096 | p->finalizeContents(); |
1097 | rdataSec->addChunk(c: p); |
1098 | } |
1099 | } |
1100 | |
1101 | // Create thunks for locally-dllimported symbols. |
1102 | if (!ctx.symtab.localImportChunks.empty()) { |
1103 | for (Chunk *c : ctx.symtab.localImportChunks) |
1104 | rdataSec->addChunk(c); |
1105 | } |
1106 | |
1107 | // Create Debug Information Chunks |
1108 | debugInfoSec = config->mingw ? buildidSec : rdataSec; |
1109 | if (config->buildIDHash != BuildIDHash::None || config->debug || |
1110 | config->repro || config->cetCompat) { |
1111 | debugDirectory = |
1112 | make<DebugDirectoryChunk>(args&: ctx, args&: debugRecords, args&: config->repro); |
1113 | debugDirectory->setAlignment(4); |
1114 | debugInfoSec->addChunk(c: debugDirectory); |
1115 | } |
1116 | |
1117 | if (config->debug || config->buildIDHash != BuildIDHash::None) { |
1118 | // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We |
1119 | // output a PDB no matter what, and this chunk provides the only means of |
1120 | // allowing a debugger to match a PDB and an executable. So we need it even |
1121 | // if we're ultimately not going to write CodeView data to the PDB. |
1122 | buildId = make<CVDebugRecordChunk>(args&: ctx); |
1123 | debugRecords.emplace_back(args: COFF::IMAGE_DEBUG_TYPE_CODEVIEW, args&: buildId); |
1124 | if (Symbol *buildidSym = ctx.symtab.findUnderscore(name: "__buildid" )) |
1125 | replaceSymbol<DefinedSynthetic>(s: buildidSym, arg: buildidSym->getName(), |
1126 | arg&: buildId, arg: 4); |
1127 | } |
1128 | |
1129 | if (config->cetCompat) { |
1130 | debugRecords.emplace_back(args: COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, |
1131 | args: make<ExtendedDllCharacteristicsChunk>( |
1132 | args: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT)); |
1133 | } |
1134 | |
1135 | // Align and add each chunk referenced by the debug data directory. |
1136 | for (std::pair<COFF::DebugType, Chunk *> r : debugRecords) { |
1137 | r.second->setAlignment(4); |
1138 | debugInfoSec->addChunk(c: r.second); |
1139 | } |
1140 | |
1141 | // Create SEH table. x86-only. |
1142 | if (config->safeSEH) |
1143 | createSEHTable(); |
1144 | |
1145 | // Create /guard:cf tables if requested. |
1146 | if (config->guardCF != GuardCFLevel::Off) |
1147 | createGuardCFTables(); |
1148 | |
1149 | if (isArm64EC(Machine: config->machine)) |
1150 | createECChunks(); |
1151 | |
1152 | if (config->autoImport) |
1153 | createRuntimePseudoRelocs(); |
1154 | |
1155 | if (config->mingw) |
1156 | insertCtorDtorSymbols(); |
1157 | } |
1158 | |
1159 | // Create .idata section for the DLL-imported symbol table. |
1160 | // The format of this section is inherently Windows-specific. |
1161 | // IdataContents class abstracted away the details for us, |
1162 | // so we just let it create chunks and add them to the section. |
1163 | void Writer::createImportTables() { |
1164 | llvm::TimeTraceScope timeScope("Import tables" ); |
1165 | // Initialize DLLOrder so that import entries are ordered in |
1166 | // the same order as in the command line. (That affects DLL |
1167 | // initialization order, and this ordering is MSVC-compatible.) |
1168 | for (ImportFile *file : ctx.importFileInstances) { |
1169 | if (!file->live) |
1170 | continue; |
1171 | |
1172 | std::string dll = StringRef(file->dllName).lower(); |
1173 | if (ctx.config.dllOrder.count(x: dll) == 0) |
1174 | ctx.config.dllOrder[dll] = ctx.config.dllOrder.size(); |
1175 | |
1176 | if (file->impSym && !isa<DefinedImportData>(Val: file->impSym)) |
1177 | fatal(msg: toString(ctx, b&: *file->impSym) + " was replaced" ); |
1178 | DefinedImportData *impSym = cast_or_null<DefinedImportData>(Val: file->impSym); |
1179 | if (ctx.config.delayLoads.count(x: StringRef(file->dllName).lower())) { |
1180 | if (!file->thunkSym) |
1181 | fatal(msg: "cannot delay-load " + toString(file) + |
1182 | " due to import of data: " + toString(ctx, b&: *impSym)); |
1183 | delayIdata.add(sym: impSym); |
1184 | } else { |
1185 | idata.add(sym: impSym); |
1186 | } |
1187 | } |
1188 | } |
1189 | |
1190 | void Writer::appendImportThunks() { |
1191 | if (ctx.importFileInstances.empty()) |
1192 | return; |
1193 | |
1194 | llvm::TimeTraceScope timeScope("Import thunks" ); |
1195 | for (ImportFile *file : ctx.importFileInstances) { |
1196 | if (!file->live) |
1197 | continue; |
1198 | |
1199 | if (!file->thunkSym) |
1200 | continue; |
1201 | |
1202 | if (!isa<DefinedImportThunk>(Val: file->thunkSym)) |
1203 | fatal(msg: toString(ctx, b&: *file->thunkSym) + " was replaced" ); |
1204 | DefinedImportThunk *thunk = cast<DefinedImportThunk>(Val: file->thunkSym); |
1205 | if (file->thunkLive) |
1206 | textSec->addChunk(c: thunk->getChunk()); |
1207 | } |
1208 | |
1209 | if (!delayIdata.empty()) { |
1210 | Defined *helper = cast<Defined>(Val: ctx.config.delayLoadHelper); |
1211 | delayIdata.create(helper); |
1212 | for (Chunk *c : delayIdata.getChunks()) |
1213 | didatSec->addChunk(c); |
1214 | for (Chunk *c : delayIdata.getDataChunks()) |
1215 | dataSec->addChunk(c); |
1216 | for (Chunk *c : delayIdata.getCodeChunks()) |
1217 | textSec->addChunk(c); |
1218 | for (Chunk *c : delayIdata.getCodePData()) |
1219 | pdataSec->addChunk(c); |
1220 | for (Chunk *c : delayIdata.getCodeUnwindInfo()) |
1221 | rdataSec->addChunk(c); |
1222 | } |
1223 | } |
1224 | |
1225 | void Writer::createExportTable() { |
1226 | llvm::TimeTraceScope timeScope("Export table" ); |
1227 | if (!edataSec->chunks.empty()) { |
1228 | // Allow using a custom built export table from input object files, instead |
1229 | // of having the linker synthesize the tables. |
1230 | if (ctx.config.hadExplicitExports) |
1231 | warn(msg: "literal .edata sections override exports" ); |
1232 | } else if (!ctx.config.exports.empty()) { |
1233 | for (Chunk *c : edata.chunks) |
1234 | edataSec->addChunk(c); |
1235 | } |
1236 | if (!edataSec->chunks.empty()) { |
1237 | edataStart = edataSec->chunks.front(); |
1238 | edataEnd = edataSec->chunks.back(); |
1239 | } |
1240 | // Warn on exported deleting destructor. |
1241 | for (auto e : ctx.config.exports) |
1242 | if (e.sym && e.sym->getName().starts_with(Prefix: "??_G" )) |
1243 | warn(msg: "export of deleting dtor: " + toString(ctx, b&: *e.sym)); |
1244 | } |
1245 | |
1246 | void Writer::removeUnusedSections() { |
1247 | llvm::TimeTraceScope timeScope("Remove unused sections" ); |
1248 | // Remove sections that we can be sure won't get content, to avoid |
1249 | // allocating space for their section headers. |
1250 | auto isUnused = [this](OutputSection *s) { |
1251 | if (s == relocSec) |
1252 | return false; // This section is populated later. |
1253 | // MergeChunks have zero size at this point, as their size is finalized |
1254 | // later. Only remove sections that have no Chunks at all. |
1255 | return s->chunks.empty(); |
1256 | }; |
1257 | llvm::erase_if(C&: ctx.outputSections, P: isUnused); |
1258 | } |
1259 | |
1260 | // The Windows loader doesn't seem to like empty sections, |
1261 | // so we remove them if any. |
1262 | void Writer::removeEmptySections() { |
1263 | llvm::TimeTraceScope timeScope("Remove empty sections" ); |
1264 | auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; }; |
1265 | llvm::erase_if(C&: ctx.outputSections, P: isEmpty); |
1266 | } |
1267 | |
1268 | void Writer::assignOutputSectionIndices() { |
1269 | llvm::TimeTraceScope timeScope("Output sections indices" ); |
1270 | // Assign final output section indices, and assign each chunk to its output |
1271 | // section. |
1272 | uint32_t idx = 1; |
1273 | for (OutputSection *os : ctx.outputSections) { |
1274 | os->sectionIndex = idx; |
1275 | for (Chunk *c : os->chunks) |
1276 | c->setOutputSectionIdx(idx); |
1277 | ++idx; |
1278 | } |
1279 | |
1280 | // Merge chunks are containers of chunks, so assign those an output section |
1281 | // too. |
1282 | for (MergeChunk *mc : ctx.mergeChunkInstances) |
1283 | if (mc) |
1284 | for (SectionChunk *sc : mc->sections) |
1285 | if (sc && sc->live) |
1286 | sc->setOutputSectionIdx(mc->getOutputSectionIdx()); |
1287 | } |
1288 | |
1289 | size_t Writer::addEntryToStringTable(StringRef str) { |
1290 | assert(str.size() > COFF::NameSize); |
1291 | size_t offsetOfEntry = strtab.size() + 4; // +4 for the size field |
1292 | strtab.insert(position: strtab.end(), first: str.begin(), last: str.end()); |
1293 | strtab.push_back(x: '\0'); |
1294 | return offsetOfEntry; |
1295 | } |
1296 | |
1297 | std::optional<coff_symbol16> Writer::createSymbol(Defined *def) { |
1298 | coff_symbol16 sym; |
1299 | switch (def->kind()) { |
1300 | case Symbol::DefinedAbsoluteKind: { |
1301 | auto *da = dyn_cast<DefinedAbsolute>(Val: def); |
1302 | // Note: COFF symbol can only store 32-bit values, so 64-bit absolute |
1303 | // values will be truncated. |
1304 | sym.Value = da->getVA(); |
1305 | sym.SectionNumber = IMAGE_SYM_ABSOLUTE; |
1306 | break; |
1307 | } |
1308 | default: { |
1309 | // Don't write symbols that won't be written to the output to the symbol |
1310 | // table. |
1311 | // We also try to write DefinedSynthetic as a normal symbol. Some of these |
1312 | // symbols do point to an actual chunk, like __safe_se_handler_table. Others |
1313 | // like __ImageBase are outside of sections and thus cannot be represented. |
1314 | Chunk *c = def->getChunk(); |
1315 | if (!c) |
1316 | return std::nullopt; |
1317 | OutputSection *os = ctx.getOutputSection(c); |
1318 | if (!os) |
1319 | return std::nullopt; |
1320 | |
1321 | sym.Value = def->getRVA() - os->getRVA(); |
1322 | sym.SectionNumber = os->sectionIndex; |
1323 | break; |
1324 | } |
1325 | } |
1326 | |
1327 | // Symbols that are runtime pseudo relocations don't point to the actual |
1328 | // symbol data itself (as they are imported), but points to the IAT entry |
1329 | // instead. Avoid emitting them to the symbol table, as they can confuse |
1330 | // debuggers. |
1331 | if (def->isRuntimePseudoReloc) |
1332 | return std::nullopt; |
1333 | |
1334 | StringRef name = def->getName(); |
1335 | if (name.size() > COFF::NameSize) { |
1336 | sym.Name.Offset.Zeroes = 0; |
1337 | sym.Name.Offset.Offset = addEntryToStringTable(str: name); |
1338 | } else { |
1339 | memset(s: sym.Name.ShortName, c: 0, n: COFF::NameSize); |
1340 | memcpy(dest: sym.Name.ShortName, src: name.data(), n: name.size()); |
1341 | } |
1342 | |
1343 | if (auto *d = dyn_cast<DefinedCOFF>(Val: def)) { |
1344 | COFFSymbolRef ref = d->getCOFFSymbol(); |
1345 | sym.Type = ref.getType(); |
1346 | sym.StorageClass = ref.getStorageClass(); |
1347 | } else if (def->kind() == Symbol::DefinedImportThunkKind) { |
1348 | sym.Type = (IMAGE_SYM_DTYPE_FUNCTION << SCT_COMPLEX_TYPE_SHIFT) | |
1349 | IMAGE_SYM_TYPE_NULL; |
1350 | sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; |
1351 | } else { |
1352 | sym.Type = IMAGE_SYM_TYPE_NULL; |
1353 | sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; |
1354 | } |
1355 | sym.NumberOfAuxSymbols = 0; |
1356 | return sym; |
1357 | } |
1358 | |
1359 | void Writer::createSymbolAndStringTable() { |
1360 | llvm::TimeTraceScope timeScope("Symbol and string table" ); |
1361 | // PE/COFF images are limited to 8 byte section names. Longer names can be |
1362 | // supported by writing a non-standard string table, but this string table is |
1363 | // not mapped at runtime and the long names will therefore be inaccessible. |
1364 | // link.exe always truncates section names to 8 bytes, whereas binutils always |
1365 | // preserves long section names via the string table. LLD adopts a hybrid |
1366 | // solution where discardable sections have long names preserved and |
1367 | // non-discardable sections have their names truncated, to ensure that any |
1368 | // section which is mapped at runtime also has its name mapped at runtime. |
1369 | for (OutputSection *sec : ctx.outputSections) { |
1370 | if (sec->name.size() <= COFF::NameSize) |
1371 | continue; |
1372 | if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) |
1373 | continue; |
1374 | if (ctx.config.warnLongSectionNames) { |
1375 | warn(msg: "section name " + sec->name + |
1376 | " is longer than 8 characters and will use a non-standard string " |
1377 | "table" ); |
1378 | } |
1379 | sec->setStringTableOff(addEntryToStringTable(str: sec->name)); |
1380 | } |
1381 | |
1382 | if (ctx.config.writeSymtab) { |
1383 | for (ObjFile *file : ctx.objFileInstances) { |
1384 | for (Symbol *b : file->getSymbols()) { |
1385 | auto *d = dyn_cast_or_null<Defined>(Val: b); |
1386 | if (!d || d->writtenToSymtab) |
1387 | continue; |
1388 | d->writtenToSymtab = true; |
1389 | if (auto *dc = dyn_cast_or_null<DefinedCOFF>(Val: d)) { |
1390 | COFFSymbolRef symRef = dc->getCOFFSymbol(); |
1391 | if (symRef.isSectionDefinition() || |
1392 | symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL) |
1393 | continue; |
1394 | } |
1395 | |
1396 | if (std::optional<coff_symbol16> sym = createSymbol(def: d)) |
1397 | outputSymtab.push_back(x: *sym); |
1398 | |
1399 | if (auto *dthunk = dyn_cast<DefinedImportThunk>(Val: d)) { |
1400 | if (!dthunk->wrappedSym->writtenToSymtab) { |
1401 | dthunk->wrappedSym->writtenToSymtab = true; |
1402 | if (std::optional<coff_symbol16> sym = |
1403 | createSymbol(def: dthunk->wrappedSym)) |
1404 | outputSymtab.push_back(x: *sym); |
1405 | } |
1406 | } |
1407 | } |
1408 | } |
1409 | } |
1410 | |
1411 | if (outputSymtab.empty() && strtab.empty()) |
1412 | return; |
1413 | |
1414 | // We position the symbol table to be adjacent to the end of the last section. |
1415 | uint64_t fileOff = fileSize; |
1416 | pointerToSymbolTable = fileOff; |
1417 | fileOff += outputSymtab.size() * sizeof(coff_symbol16); |
1418 | fileOff += 4 + strtab.size(); |
1419 | fileSize = alignTo(Value: fileOff, Align: ctx.config.fileAlign); |
1420 | } |
1421 | |
1422 | void Writer::mergeSections() { |
1423 | llvm::TimeTraceScope timeScope("Merge sections" ); |
1424 | if (!pdataSec->chunks.empty()) { |
1425 | if (isArm64EC(Machine: ctx.config.machine)) { |
1426 | // On ARM64EC .pdata may contain both ARM64 and X64 data. Split them by |
1427 | // sorting and store their regions separately. |
1428 | llvm::stable_sort(Range&: pdataSec->chunks, C: [=](const Chunk *a, const Chunk *b) { |
1429 | return (a->getMachine() == AMD64) < (b->getMachine() == AMD64); |
1430 | }); |
1431 | |
1432 | for (auto chunk : pdataSec->chunks) { |
1433 | if (chunk->getMachine() == AMD64) { |
1434 | hybridPdata.first = chunk; |
1435 | hybridPdata.last = pdataSec->chunks.back(); |
1436 | break; |
1437 | } |
1438 | |
1439 | if (!pdata.first) |
1440 | pdata.first = chunk; |
1441 | pdata.last = chunk; |
1442 | } |
1443 | } else { |
1444 | pdata.first = pdataSec->chunks.front(); |
1445 | pdata.last = pdataSec->chunks.back(); |
1446 | } |
1447 | } |
1448 | |
1449 | for (auto &p : ctx.config.merge) { |
1450 | StringRef toName = p.second; |
1451 | if (p.first == toName) |
1452 | continue; |
1453 | StringSet<> names; |
1454 | while (true) { |
1455 | if (!names.insert(key: toName).second) |
1456 | fatal(msg: "/merge: cycle found for section '" + p.first + "'" ); |
1457 | auto i = ctx.config.merge.find(x: toName); |
1458 | if (i == ctx.config.merge.end()) |
1459 | break; |
1460 | toName = i->second; |
1461 | } |
1462 | OutputSection *from = findSection(name: p.first); |
1463 | OutputSection *to = findSection(name: toName); |
1464 | if (!from) |
1465 | continue; |
1466 | if (!to) { |
1467 | from->name = toName; |
1468 | continue; |
1469 | } |
1470 | to->merge(other: from); |
1471 | } |
1472 | } |
1473 | |
1474 | // EC targets may have chunks of various architectures mixed together at this |
1475 | // point. Group code chunks of the same architecture together by sorting chunks |
1476 | // by their EC range type. |
1477 | void Writer::sortECChunks() { |
1478 | if (!isArm64EC(Machine: ctx.config.machine)) |
1479 | return; |
1480 | |
1481 | for (OutputSection *sec : ctx.outputSections) { |
1482 | if (sec->isCodeSection()) |
1483 | llvm::stable_sort(Range&: sec->chunks, C: [=](const Chunk *a, const Chunk *b) { |
1484 | std::optional<chpe_range_type> aType = a->getArm64ECRangeType(), |
1485 | bType = b->getArm64ECRangeType(); |
1486 | return bType && (!aType || *aType < *bType); |
1487 | }); |
1488 | } |
1489 | } |
1490 | |
1491 | // Visits all sections to assign incremental, non-overlapping RVAs and |
1492 | // file offsets. |
1493 | void Writer::assignAddresses() { |
1494 | llvm::TimeTraceScope timeScope("Assign addresses" ); |
1495 | Configuration *config = &ctx.config; |
1496 | |
1497 | // We need to create EC code map so that ECCodeMapChunk knows its size. |
1498 | // We do it here to make sure that we account for range extension chunks. |
1499 | createECCodeMap(); |
1500 | |
1501 | sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) + |
1502 | sizeof(data_directory) * numberOfDataDirectory + |
1503 | sizeof(coff_section) * ctx.outputSections.size(); |
1504 | sizeOfHeaders += |
1505 | config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); |
1506 | sizeOfHeaders = alignTo(Value: sizeOfHeaders, Align: config->fileAlign); |
1507 | fileSize = sizeOfHeaders; |
1508 | |
1509 | // The first page is kept unmapped. |
1510 | uint64_t rva = alignTo(Value: sizeOfHeaders, Align: config->align); |
1511 | |
1512 | for (OutputSection *sec : ctx.outputSections) { |
1513 | llvm::TimeTraceScope timeScope("Section: " , sec->name); |
1514 | if (sec == relocSec) |
1515 | addBaserels(); |
1516 | uint64_t rawSize = 0, virtualSize = 0; |
1517 | sec->header.VirtualAddress = rva; |
1518 | |
1519 | // If /FUNCTIONPADMIN is used, functions are padded in order to create a |
1520 | // hotpatchable image. |
1521 | uint32_t padding = sec->isCodeSection() ? config->functionPadMin : 0; |
1522 | std::optional<chpe_range_type> prevECRange; |
1523 | |
1524 | for (Chunk *c : sec->chunks) { |
1525 | // Alignment EC code range baudaries. |
1526 | if (isArm64EC(Machine: ctx.config.machine) && sec->isCodeSection()) { |
1527 | std::optional<chpe_range_type> rangeType = c->getArm64ECRangeType(); |
1528 | if (rangeType != prevECRange) { |
1529 | virtualSize = alignTo(Value: virtualSize, Align: 4096); |
1530 | prevECRange = rangeType; |
1531 | } |
1532 | } |
1533 | if (padding && c->isHotPatchable()) |
1534 | virtualSize += padding; |
1535 | // If chunk has EC entry thunk, reserve a space for an offset to the |
1536 | // thunk. |
1537 | if (c->getEntryThunk()) |
1538 | virtualSize += sizeof(uint32_t); |
1539 | virtualSize = alignTo(Value: virtualSize, Align: c->getAlignment()); |
1540 | c->setRVA(rva + virtualSize); |
1541 | virtualSize += c->getSize(); |
1542 | if (c->hasData) |
1543 | rawSize = alignTo(Value: virtualSize, Align: config->fileAlign); |
1544 | } |
1545 | if (virtualSize > UINT32_MAX) |
1546 | error(msg: "section larger than 4 GiB: " + sec->name); |
1547 | sec->header.VirtualSize = virtualSize; |
1548 | sec->header.SizeOfRawData = rawSize; |
1549 | if (rawSize != 0) |
1550 | sec->header.PointerToRawData = fileSize; |
1551 | rva += alignTo(Value: virtualSize, Align: config->align); |
1552 | fileSize += alignTo(Value: rawSize, Align: config->fileAlign); |
1553 | } |
1554 | sizeOfImage = alignTo(Value: rva, Align: config->align); |
1555 | |
1556 | // Assign addresses to sections in MergeChunks. |
1557 | for (MergeChunk *mc : ctx.mergeChunkInstances) |
1558 | if (mc) |
1559 | mc->assignSubsectionRVAs(); |
1560 | } |
1561 | |
1562 | template <typename PEHeaderTy> void Writer::() { |
1563 | // Write DOS header. For backwards compatibility, the first part of a PE/COFF |
1564 | // executable consists of an MS-DOS MZ executable. If the executable is run |
1565 | // under DOS, that program gets run (usually to just print an error message). |
1566 | // When run under Windows, the loader looks at AddressOfNewExeHeader and uses |
1567 | // the PE header instead. |
1568 | Configuration *config = &ctx.config; |
1569 | uint8_t *buf = buffer->getBufferStart(); |
1570 | auto *dos = reinterpret_cast<dos_header *>(buf); |
1571 | buf += sizeof(dos_header); |
1572 | dos->Magic[0] = 'M'; |
1573 | dos->Magic[1] = 'Z'; |
1574 | dos->UsedBytesInTheLastPage = dosStubSize % 512; |
1575 | dos->FileSizeInPages = divideCeil(Numerator: dosStubSize, Denominator: 512); |
1576 | dos->HeaderSizeInParagraphs = sizeof(dos_header) / 16; |
1577 | |
1578 | dos->AddressOfRelocationTable = sizeof(dos_header); |
1579 | dos->AddressOfNewExeHeader = dosStubSize; |
1580 | |
1581 | // Write DOS program. |
1582 | memcpy(dest: buf, src: dosProgram, n: sizeof(dosProgram)); |
1583 | buf += sizeof(dosProgram); |
1584 | |
1585 | // Write PE magic |
1586 | memcpy(dest: buf, src: PEMagic, n: sizeof(PEMagic)); |
1587 | buf += sizeof(PEMagic); |
1588 | |
1589 | // Write COFF header |
1590 | auto *coff = reinterpret_cast<coff_file_header *>(buf); |
1591 | buf += sizeof(*coff); |
1592 | switch (config->machine) { |
1593 | case ARM64EC: |
1594 | coff->Machine = AMD64; |
1595 | break; |
1596 | case ARM64X: |
1597 | coff->Machine = ARM64; |
1598 | break; |
1599 | default: |
1600 | coff->Machine = config->machine; |
1601 | } |
1602 | coff->NumberOfSections = ctx.outputSections.size(); |
1603 | coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE; |
1604 | if (config->largeAddressAware) |
1605 | coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE; |
1606 | if (!config->is64()) |
1607 | coff->Characteristics |= IMAGE_FILE_32BIT_MACHINE; |
1608 | if (config->dll) |
1609 | coff->Characteristics |= IMAGE_FILE_DLL; |
1610 | if (config->driverUponly) |
1611 | coff->Characteristics |= IMAGE_FILE_UP_SYSTEM_ONLY; |
1612 | if (!config->relocatable) |
1613 | coff->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; |
1614 | if (config->swaprunCD) |
1615 | coff->Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; |
1616 | if (config->swaprunNet) |
1617 | coff->Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP; |
1618 | coff->SizeOfOptionalHeader = |
1619 | sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory; |
1620 | |
1621 | // Write PE header |
1622 | auto *pe = reinterpret_cast<PEHeaderTy *>(buf); |
1623 | buf += sizeof(*pe); |
1624 | pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; |
1625 | |
1626 | // If {Major,Minor}LinkerVersion is left at 0.0, then for some |
1627 | // reason signing the resulting PE file with Authenticode produces a |
1628 | // signature that fails to validate on Windows 7 (but is OK on 10). |
1629 | // Set it to 14.0, which is what VS2015 outputs, and which avoids |
1630 | // that problem. |
1631 | pe->MajorLinkerVersion = 14; |
1632 | pe->MinorLinkerVersion = 0; |
1633 | |
1634 | pe->ImageBase = config->imageBase; |
1635 | pe->SectionAlignment = config->align; |
1636 | pe->FileAlignment = config->fileAlign; |
1637 | pe->MajorImageVersion = config->majorImageVersion; |
1638 | pe->MinorImageVersion = config->minorImageVersion; |
1639 | pe->MajorOperatingSystemVersion = config->majorOSVersion; |
1640 | pe->MinorOperatingSystemVersion = config->minorOSVersion; |
1641 | pe->MajorSubsystemVersion = config->majorSubsystemVersion; |
1642 | pe->MinorSubsystemVersion = config->minorSubsystemVersion; |
1643 | pe->Subsystem = config->subsystem; |
1644 | pe->SizeOfImage = sizeOfImage; |
1645 | pe->SizeOfHeaders = sizeOfHeaders; |
1646 | if (!config->noEntry) { |
1647 | Defined *entry = cast<Defined>(Val: config->entry); |
1648 | pe->AddressOfEntryPoint = entry->getRVA(); |
1649 | // Pointer to thumb code must have the LSB set, so adjust it. |
1650 | if (config->machine == ARMNT) |
1651 | pe->AddressOfEntryPoint |= 1; |
1652 | } |
1653 | pe->SizeOfStackReserve = config->stackReserve; |
1654 | pe->SizeOfStackCommit = config->stackCommit; |
1655 | pe->SizeOfHeapReserve = config->heapReserve; |
1656 | pe->SizeOfHeapCommit = config->heapCommit; |
1657 | if (config->appContainer) |
1658 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; |
1659 | if (config->driverWdm) |
1660 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER; |
1661 | if (config->dynamicBase) |
1662 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; |
1663 | if (config->highEntropyVA) |
1664 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; |
1665 | if (!config->allowBind) |
1666 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND; |
1667 | if (config->nxCompat) |
1668 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; |
1669 | if (!config->allowIsolation) |
1670 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; |
1671 | if (config->guardCF != GuardCFLevel::Off) |
1672 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; |
1673 | if (config->integrityCheck) |
1674 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; |
1675 | if (setNoSEHCharacteristic || config->noSEH) |
1676 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; |
1677 | if (config->terminalServerAware) |
1678 | pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; |
1679 | pe->NumberOfRvaAndSize = numberOfDataDirectory; |
1680 | if (textSec->getVirtualSize()) { |
1681 | pe->BaseOfCode = textSec->getRVA(); |
1682 | pe->SizeOfCode = textSec->getRawSize(); |
1683 | } |
1684 | pe->SizeOfInitializedData = getSizeOfInitializedData(); |
1685 | |
1686 | // Write data directory |
1687 | auto *dir = reinterpret_cast<data_directory *>(buf); |
1688 | buf += sizeof(*dir) * numberOfDataDirectory; |
1689 | if (edataStart) { |
1690 | dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA(); |
1691 | dir[EXPORT_TABLE].Size = |
1692 | edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA(); |
1693 | } |
1694 | if (importTableStart) { |
1695 | dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); |
1696 | dir[IMPORT_TABLE].Size = importTableSize; |
1697 | } |
1698 | if (iatStart) { |
1699 | dir[IAT].RelativeVirtualAddress = iatStart->getRVA(); |
1700 | dir[IAT].Size = iatSize; |
1701 | } |
1702 | if (rsrcSec->getVirtualSize()) { |
1703 | dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA(); |
1704 | dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize(); |
1705 | } |
1706 | // ARM64EC (but not ARM64X) contains x86_64 exception table in data directory. |
1707 | ChunkRange &exceptionTable = |
1708 | ctx.config.machine == ARM64EC ? hybridPdata : pdata; |
1709 | if (exceptionTable.first) { |
1710 | dir[EXCEPTION_TABLE].RelativeVirtualAddress = |
1711 | exceptionTable.first->getRVA(); |
1712 | dir[EXCEPTION_TABLE].Size = exceptionTable.last->getRVA() + |
1713 | exceptionTable.last->getSize() - |
1714 | exceptionTable.first->getRVA(); |
1715 | } |
1716 | if (relocSec->getVirtualSize()) { |
1717 | dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA(); |
1718 | dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize(); |
1719 | } |
1720 | if (Symbol *sym = ctx.symtab.findUnderscore(name: "_tls_used" )) { |
1721 | if (Defined *b = dyn_cast<Defined>(Val: sym)) { |
1722 | dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA(); |
1723 | dir[TLS_TABLE].Size = config->is64() |
1724 | ? sizeof(object::coff_tls_directory64) |
1725 | : sizeof(object::coff_tls_directory32); |
1726 | } |
1727 | } |
1728 | if (debugDirectory) { |
1729 | dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA(); |
1730 | dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize(); |
1731 | } |
1732 | if (Symbol *sym = ctx.symtab.findUnderscore(name: "_load_config_used" )) { |
1733 | if (auto *b = dyn_cast<DefinedRegular>(Val: sym)) { |
1734 | SectionChunk *sc = b->getChunk(); |
1735 | assert(b->getRVA() >= sc->getRVA()); |
1736 | uint64_t offsetInChunk = b->getRVA() - sc->getRVA(); |
1737 | if (!sc->hasData || offsetInChunk + 4 > sc->getSize()) |
1738 | fatal(msg: "_load_config_used is malformed" ); |
1739 | |
1740 | ArrayRef<uint8_t> secContents = sc->getContents(); |
1741 | uint32_t loadConfigSize = |
1742 | *reinterpret_cast<const ulittle32_t *>(&secContents[offsetInChunk]); |
1743 | if (offsetInChunk + loadConfigSize > sc->getSize()) |
1744 | fatal(msg: "_load_config_used is too large" ); |
1745 | dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = b->getRVA(); |
1746 | dir[LOAD_CONFIG_TABLE].Size = loadConfigSize; |
1747 | } |
1748 | } |
1749 | if (!delayIdata.empty()) { |
1750 | dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = |
1751 | delayIdata.getDirRVA(); |
1752 | dir[DELAY_IMPORT_DESCRIPTOR].Size = delayIdata.getDirSize(); |
1753 | } |
1754 | |
1755 | // Write section table |
1756 | for (OutputSection *sec : ctx.outputSections) { |
1757 | sec->writeHeaderTo(buf, isDebug: config->debug); |
1758 | buf += sizeof(coff_section); |
1759 | } |
1760 | sectionTable = ArrayRef<uint8_t>( |
1761 | buf - ctx.outputSections.size() * sizeof(coff_section), buf); |
1762 | |
1763 | if (outputSymtab.empty() && strtab.empty()) |
1764 | return; |
1765 | |
1766 | coff->PointerToSymbolTable = pointerToSymbolTable; |
1767 | uint32_t numberOfSymbols = outputSymtab.size(); |
1768 | coff->NumberOfSymbols = numberOfSymbols; |
1769 | auto *symbolTable = reinterpret_cast<coff_symbol16 *>( |
1770 | buffer->getBufferStart() + coff->PointerToSymbolTable); |
1771 | for (size_t i = 0; i != numberOfSymbols; ++i) |
1772 | symbolTable[i] = outputSymtab[i]; |
1773 | // Create the string table, it follows immediately after the symbol table. |
1774 | // The first 4 bytes is length including itself. |
1775 | buf = reinterpret_cast<uint8_t *>(&symbolTable[numberOfSymbols]); |
1776 | write32le(P: buf, V: strtab.size() + 4); |
1777 | if (!strtab.empty()) |
1778 | memcpy(dest: buf + 4, src: strtab.data(), n: strtab.size()); |
1779 | } |
1780 | |
1781 | void Writer::openFile(StringRef path) { |
1782 | buffer = CHECK( |
1783 | FileOutputBuffer::create(path, fileSize, FileOutputBuffer::F_executable), |
1784 | "failed to open " + path); |
1785 | } |
1786 | |
1787 | void Writer::createSEHTable() { |
1788 | SymbolRVASet handlers; |
1789 | for (ObjFile *file : ctx.objFileInstances) { |
1790 | if (!file->hasSafeSEH()) |
1791 | error(msg: "/safeseh: " + file->getName() + " is not compatible with SEH" ); |
1792 | markSymbolsForRVATable(file, symIdxChunks: file->getSXDataChunks(), tableSymbols&: handlers); |
1793 | } |
1794 | |
1795 | // Set the "no SEH" characteristic if there really were no handlers, or if |
1796 | // there is no load config object to point to the table of handlers. |
1797 | setNoSEHCharacteristic = |
1798 | handlers.empty() || !ctx.symtab.findUnderscore(name: "_load_config_used" ); |
1799 | |
1800 | maybeAddRVATable(tableSymbols: std::move(handlers), tableSym: "__safe_se_handler_table" , |
1801 | countSym: "__safe_se_handler_count" ); |
1802 | } |
1803 | |
1804 | // Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set |
1805 | // cannot contain duplicates. Therefore, the set is uniqued by Chunk and the |
1806 | // symbol's offset into that Chunk. |
1807 | static void addSymbolToRVASet(SymbolRVASet &rvaSet, Defined *s) { |
1808 | Chunk *c = s->getChunk(); |
1809 | if (!c) |
1810 | return; |
1811 | if (auto *sc = dyn_cast<SectionChunk>(Val: c)) |
1812 | c = sc->repl; // Look through ICF replacement. |
1813 | uint32_t off = s->getRVA() - (c ? c->getRVA() : 0); |
1814 | rvaSet.insert(V: {.inputChunk: c, .offset: off}); |
1815 | } |
1816 | |
1817 | // Given a symbol, add it to the GFIDs table if it is a live, defined, function |
1818 | // symbol in an executable section. |
1819 | static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms, |
1820 | Symbol *s) { |
1821 | if (!s) |
1822 | return; |
1823 | |
1824 | switch (s->kind()) { |
1825 | case Symbol::DefinedLocalImportKind: |
1826 | case Symbol::DefinedImportDataKind: |
1827 | // Defines an __imp_ pointer, so it is data, so it is ignored. |
1828 | break; |
1829 | case Symbol::DefinedCommonKind: |
1830 | // Common is always data, so it is ignored. |
1831 | break; |
1832 | case Symbol::DefinedAbsoluteKind: |
1833 | case Symbol::DefinedSyntheticKind: |
1834 | // Absolute is never code, synthetic generally isn't and usually isn't |
1835 | // determinable. |
1836 | break; |
1837 | case Symbol::LazyArchiveKind: |
1838 | case Symbol::LazyObjectKind: |
1839 | case Symbol::LazyDLLSymbolKind: |
1840 | case Symbol::UndefinedKind: |
1841 | // Undefined symbols resolve to zero, so they don't have an RVA. Lazy |
1842 | // symbols shouldn't have relocations. |
1843 | break; |
1844 | |
1845 | case Symbol::DefinedImportThunkKind: |
1846 | // Thunks are always code, include them. |
1847 | addSymbolToRVASet(rvaSet&: addressTakenSyms, s: cast<Defined>(Val: s)); |
1848 | break; |
1849 | |
1850 | case Symbol::DefinedRegularKind: { |
1851 | // This is a regular, defined, symbol from a COFF file. Mark the symbol as |
1852 | // address taken if the symbol type is function and it's in an executable |
1853 | // section. |
1854 | auto *d = cast<DefinedRegular>(Val: s); |
1855 | if (d->getCOFFSymbol().getComplexType() == COFF::IMAGE_SYM_DTYPE_FUNCTION) { |
1856 | SectionChunk *sc = dyn_cast<SectionChunk>(Val: d->getChunk()); |
1857 | if (sc && sc->live && |
1858 | sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) |
1859 | addSymbolToRVASet(rvaSet&: addressTakenSyms, s: d); |
1860 | } |
1861 | break; |
1862 | } |
1863 | } |
1864 | } |
1865 | |
1866 | // Visit all relocations from all section contributions of this object file and |
1867 | // mark the relocation target as address-taken. |
1868 | void Writer::markSymbolsWithRelocations(ObjFile *file, |
1869 | SymbolRVASet &usedSymbols) { |
1870 | for (Chunk *c : file->getChunks()) { |
1871 | // We only care about live section chunks. Common chunks and other chunks |
1872 | // don't generally contain relocations. |
1873 | SectionChunk *sc = dyn_cast<SectionChunk>(Val: c); |
1874 | if (!sc || !sc->live) |
1875 | continue; |
1876 | |
1877 | for (const coff_relocation &reloc : sc->getRelocs()) { |
1878 | if (ctx.config.machine == I386 && |
1879 | reloc.Type == COFF::IMAGE_REL_I386_REL32) |
1880 | // Ignore relative relocations on x86. On x86_64 they can't be ignored |
1881 | // since they're also used to compute absolute addresses. |
1882 | continue; |
1883 | |
1884 | Symbol *ref = sc->file->getSymbol(symbolIndex: reloc.SymbolTableIndex); |
1885 | maybeAddAddressTakenFunction(addressTakenSyms&: usedSymbols, s: ref); |
1886 | } |
1887 | } |
1888 | } |
1889 | |
1890 | // Create the guard function id table. This is a table of RVAs of all |
1891 | // address-taken functions. It is sorted and uniqued, just like the safe SEH |
1892 | // table. |
1893 | void Writer::createGuardCFTables() { |
1894 | Configuration *config = &ctx.config; |
1895 | |
1896 | SymbolRVASet addressTakenSyms; |
1897 | SymbolRVASet giatsRVASet; |
1898 | std::vector<Symbol *> giatsSymbols; |
1899 | SymbolRVASet longJmpTargets; |
1900 | SymbolRVASet ehContTargets; |
1901 | for (ObjFile *file : ctx.objFileInstances) { |
1902 | // If the object was compiled with /guard:cf, the address taken symbols |
1903 | // are in .gfids$y sections, and the longjmp targets are in .gljmp$y |
1904 | // sections. If the object was not compiled with /guard:cf, we assume there |
1905 | // were no setjmp targets, and that all code symbols with relocations are |
1906 | // possibly address-taken. |
1907 | if (file->hasGuardCF()) { |
1908 | markSymbolsForRVATable(file, symIdxChunks: file->getGuardFidChunks(), tableSymbols&: addressTakenSyms); |
1909 | markSymbolsForRVATable(file, symIdxChunks: file->getGuardIATChunks(), tableSymbols&: giatsRVASet); |
1910 | getSymbolsFromSections(file, symIdxChunks: file->getGuardIATChunks(), symbols&: giatsSymbols); |
1911 | markSymbolsForRVATable(file, symIdxChunks: file->getGuardLJmpChunks(), tableSymbols&: longJmpTargets); |
1912 | } else { |
1913 | markSymbolsWithRelocations(file, usedSymbols&: addressTakenSyms); |
1914 | } |
1915 | // If the object was compiled with /guard:ehcont, the ehcont targets are in |
1916 | // .gehcont$y sections. |
1917 | if (file->hasGuardEHCont()) |
1918 | markSymbolsForRVATable(file, symIdxChunks: file->getGuardEHContChunks(), tableSymbols&: ehContTargets); |
1919 | } |
1920 | |
1921 | // Mark the image entry as address-taken. |
1922 | if (config->entry) |
1923 | maybeAddAddressTakenFunction(addressTakenSyms, s: config->entry); |
1924 | |
1925 | // Mark exported symbols in executable sections as address-taken. |
1926 | for (Export &e : config->exports) |
1927 | maybeAddAddressTakenFunction(addressTakenSyms, s: e.sym); |
1928 | |
1929 | // For each entry in the .giats table, check if it has a corresponding load |
1930 | // thunk (e.g. because the DLL that defines it will be delay-loaded) and, if |
1931 | // so, add the load thunk to the address taken (.gfids) table. |
1932 | for (Symbol *s : giatsSymbols) { |
1933 | if (auto *di = dyn_cast<DefinedImportData>(Val: s)) { |
1934 | if (di->loadThunkSym) |
1935 | addSymbolToRVASet(rvaSet&: addressTakenSyms, s: di->loadThunkSym); |
1936 | } |
1937 | } |
1938 | |
1939 | // Ensure sections referenced in the gfid table are 16-byte aligned. |
1940 | for (const ChunkAndOffset &c : addressTakenSyms) |
1941 | if (c.inputChunk->getAlignment() < 16) |
1942 | c.inputChunk->setAlignment(16); |
1943 | |
1944 | maybeAddRVATable(tableSymbols: std::move(addressTakenSyms), tableSym: "__guard_fids_table" , |
1945 | countSym: "__guard_fids_count" ); |
1946 | |
1947 | // Add the Guard Address Taken IAT Entry Table (.giats). |
1948 | maybeAddRVATable(tableSymbols: std::move(giatsRVASet), tableSym: "__guard_iat_table" , |
1949 | countSym: "__guard_iat_count" ); |
1950 | |
1951 | // Add the longjmp target table unless the user told us not to. |
1952 | if (config->guardCF & GuardCFLevel::LongJmp) |
1953 | maybeAddRVATable(tableSymbols: std::move(longJmpTargets), tableSym: "__guard_longjmp_table" , |
1954 | countSym: "__guard_longjmp_count" ); |
1955 | |
1956 | // Add the ehcont target table unless the user told us not to. |
1957 | if (config->guardCF & GuardCFLevel::EHCont) |
1958 | maybeAddRVATable(tableSymbols: std::move(ehContTargets), tableSym: "__guard_eh_cont_table" , |
1959 | countSym: "__guard_eh_cont_count" ); |
1960 | |
1961 | // Set __guard_flags, which will be used in the load config to indicate that |
1962 | // /guard:cf was enabled. |
1963 | uint32_t guardFlags = uint32_t(GuardFlags::CF_INSTRUMENTED) | |
1964 | uint32_t(GuardFlags::CF_FUNCTION_TABLE_PRESENT); |
1965 | if (config->guardCF & GuardCFLevel::LongJmp) |
1966 | guardFlags |= uint32_t(GuardFlags::CF_LONGJUMP_TABLE_PRESENT); |
1967 | if (config->guardCF & GuardCFLevel::EHCont) |
1968 | guardFlags |= uint32_t(GuardFlags::EH_CONTINUATION_TABLE_PRESENT); |
1969 | Symbol *flagSym = ctx.symtab.findUnderscore(name: "__guard_flags" ); |
1970 | cast<DefinedAbsolute>(Val: flagSym)->setVA(guardFlags); |
1971 | } |
1972 | |
1973 | // Take a list of input sections containing symbol table indices and add those |
1974 | // symbols to a vector. The challenge is that symbol RVAs are not known and |
1975 | // depend on the table size, so we can't directly build a set of integers. |
1976 | void Writer::getSymbolsFromSections(ObjFile *file, |
1977 | ArrayRef<SectionChunk *> symIdxChunks, |
1978 | std::vector<Symbol *> &symbols) { |
1979 | for (SectionChunk *c : symIdxChunks) { |
1980 | // Skip sections discarded by linker GC. This comes up when a .gfids section |
1981 | // is associated with something like a vtable and the vtable is discarded. |
1982 | // In this case, the associated gfids section is discarded, and we don't |
1983 | // mark the virtual member functions as address-taken by the vtable. |
1984 | if (!c->live) |
1985 | continue; |
1986 | |
1987 | // Validate that the contents look like symbol table indices. |
1988 | ArrayRef<uint8_t> data = c->getContents(); |
1989 | if (data.size() % 4 != 0) { |
1990 | warn(msg: "ignoring " + c->getSectionName() + |
1991 | " symbol table index section in object " + toString(file)); |
1992 | continue; |
1993 | } |
1994 | |
1995 | // Read each symbol table index and check if that symbol was included in the |
1996 | // final link. If so, add it to the vector of symbols. |
1997 | ArrayRef<ulittle32_t> symIndices( |
1998 | reinterpret_cast<const ulittle32_t *>(data.data()), data.size() / 4); |
1999 | ArrayRef<Symbol *> objSymbols = file->getSymbols(); |
2000 | for (uint32_t symIndex : symIndices) { |
2001 | if (symIndex >= objSymbols.size()) { |
2002 | warn(msg: "ignoring invalid symbol table index in section " + |
2003 | c->getSectionName() + " in object " + toString(file)); |
2004 | continue; |
2005 | } |
2006 | if (Symbol *s = objSymbols[symIndex]) { |
2007 | if (s->isLive()) |
2008 | symbols.push_back(x: cast<Symbol>(Val: s)); |
2009 | } |
2010 | } |
2011 | } |
2012 | } |
2013 | |
2014 | // Take a list of input sections containing symbol table indices and add those |
2015 | // symbols to an RVA table. |
2016 | void Writer::markSymbolsForRVATable(ObjFile *file, |
2017 | ArrayRef<SectionChunk *> symIdxChunks, |
2018 | SymbolRVASet &tableSymbols) { |
2019 | std::vector<Symbol *> syms; |
2020 | getSymbolsFromSections(file, symIdxChunks, symbols&: syms); |
2021 | |
2022 | for (Symbol *s : syms) |
2023 | addSymbolToRVASet(rvaSet&: tableSymbols, s: cast<Defined>(Val: s)); |
2024 | } |
2025 | |
2026 | // Replace the absolute table symbol with a synthetic symbol pointing to |
2027 | // tableChunk so that we can emit base relocations for it and resolve section |
2028 | // relative relocations. |
2029 | void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym, |
2030 | StringRef countSym, bool hasFlag) { |
2031 | if (tableSymbols.empty()) |
2032 | return; |
2033 | |
2034 | NonSectionChunk *tableChunk; |
2035 | if (hasFlag) |
2036 | tableChunk = make<RVAFlagTableChunk>(args: std::move(tableSymbols)); |
2037 | else |
2038 | tableChunk = make<RVATableChunk>(args: std::move(tableSymbols)); |
2039 | rdataSec->addChunk(c: tableChunk); |
2040 | |
2041 | Symbol *t = ctx.symtab.findUnderscore(name: tableSym); |
2042 | Symbol *c = ctx.symtab.findUnderscore(name: countSym); |
2043 | replaceSymbol<DefinedSynthetic>(s: t, arg: t->getName(), arg&: tableChunk); |
2044 | cast<DefinedAbsolute>(Val: c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4)); |
2045 | } |
2046 | |
2047 | // Create CHPE metadata chunks. |
2048 | void Writer::createECChunks() { |
2049 | auto codeMapChunk = make<ECCodeMapChunk>(args&: codeMap); |
2050 | rdataSec->addChunk(c: codeMapChunk); |
2051 | Symbol *codeMapSym = ctx.symtab.findUnderscore(name: "__hybrid_code_map" ); |
2052 | replaceSymbol<DefinedSynthetic>(s: codeMapSym, arg: codeMapSym->getName(), |
2053 | arg&: codeMapChunk); |
2054 | } |
2055 | |
2056 | // MinGW specific. Gather all relocations that are imported from a DLL even |
2057 | // though the code didn't expect it to, produce the table that the runtime |
2058 | // uses for fixing them up, and provide the synthetic symbols that the |
2059 | // runtime uses for finding the table. |
2060 | void Writer::createRuntimePseudoRelocs() { |
2061 | std::vector<RuntimePseudoReloc> rels; |
2062 | |
2063 | for (Chunk *c : ctx.symtab.getChunks()) { |
2064 | auto *sc = dyn_cast<SectionChunk>(Val: c); |
2065 | if (!sc || !sc->live) |
2066 | continue; |
2067 | // Don't create pseudo relocations for sections that won't be |
2068 | // mapped at runtime. |
2069 | if (sc->header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) |
2070 | continue; |
2071 | sc->getRuntimePseudoRelocs(res&: rels); |
2072 | } |
2073 | |
2074 | if (!ctx.config.pseudoRelocs) { |
2075 | // Not writing any pseudo relocs; if some were needed, error out and |
2076 | // indicate what required them. |
2077 | for (const RuntimePseudoReloc &rpr : rels) |
2078 | error(msg: "automatic dllimport of " + rpr.sym->getName() + " in " + |
2079 | toString(file: rpr.target->file) + " requires pseudo relocations" ); |
2080 | return; |
2081 | } |
2082 | |
2083 | if (!rels.empty()) { |
2084 | log(msg: "Writing " + Twine(rels.size()) + " runtime pseudo relocations" ); |
2085 | const char *symbolName = "_pei386_runtime_relocator" ; |
2086 | Symbol *relocator = ctx.symtab.findUnderscore(name: symbolName); |
2087 | if (!relocator) |
2088 | error(msg: "output image has runtime pseudo relocations, but the function " + |
2089 | Twine(symbolName) + |
2090 | " is missing; it is needed for fixing the relocations at runtime" ); |
2091 | } |
2092 | |
2093 | PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(args&: rels); |
2094 | rdataSec->addChunk(c: table); |
2095 | EmptyChunk *endOfList = make<EmptyChunk>(); |
2096 | rdataSec->addChunk(c: endOfList); |
2097 | |
2098 | Symbol *headSym = ctx.symtab.findUnderscore(name: "__RUNTIME_PSEUDO_RELOC_LIST__" ); |
2099 | Symbol *endSym = |
2100 | ctx.symtab.findUnderscore(name: "__RUNTIME_PSEUDO_RELOC_LIST_END__" ); |
2101 | replaceSymbol<DefinedSynthetic>(s: headSym, arg: headSym->getName(), arg&: table); |
2102 | replaceSymbol<DefinedSynthetic>(s: endSym, arg: endSym->getName(), arg&: endOfList); |
2103 | } |
2104 | |
2105 | // MinGW specific. |
2106 | // The MinGW .ctors and .dtors lists have sentinels at each end; |
2107 | // a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end. |
2108 | // There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__ |
2109 | // and __DTOR_LIST__ respectively. |
2110 | void Writer::insertCtorDtorSymbols() { |
2111 | AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(args&: ctx, args: -1); |
2112 | AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(args&: ctx, args: 0); |
2113 | AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(args&: ctx, args: -1); |
2114 | AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(args&: ctx, args: 0); |
2115 | ctorsSec->insertChunkAtStart(c: ctorListHead); |
2116 | ctorsSec->addChunk(c: ctorListEnd); |
2117 | dtorsSec->insertChunkAtStart(c: dtorListHead); |
2118 | dtorsSec->addChunk(c: dtorListEnd); |
2119 | |
2120 | Symbol *ctorListSym = ctx.symtab.findUnderscore(name: "__CTOR_LIST__" ); |
2121 | Symbol *dtorListSym = ctx.symtab.findUnderscore(name: "__DTOR_LIST__" ); |
2122 | replaceSymbol<DefinedSynthetic>(s: ctorListSym, arg: ctorListSym->getName(), |
2123 | arg&: ctorListHead); |
2124 | replaceSymbol<DefinedSynthetic>(s: dtorListSym, arg: dtorListSym->getName(), |
2125 | arg&: dtorListHead); |
2126 | } |
2127 | |
2128 | // Handles /section options to allow users to overwrite |
2129 | // section attributes. |
2130 | void Writer::setSectionPermissions() { |
2131 | llvm::TimeTraceScope timeScope("Sections permissions" ); |
2132 | for (auto &p : ctx.config.section) { |
2133 | StringRef name = p.first; |
2134 | uint32_t perm = p.second; |
2135 | for (OutputSection *sec : ctx.outputSections) |
2136 | if (sec->name == name) |
2137 | sec->setPermissions(perm); |
2138 | } |
2139 | } |
2140 | |
2141 | // Set symbols used by ARM64EC metadata. |
2142 | void Writer::setECSymbols() { |
2143 | if (!isArm64EC(Machine: ctx.config.machine)) |
2144 | return; |
2145 | |
2146 | Symbol *rfeTableSym = ctx.symtab.findUnderscore(name: "__arm64x_extra_rfe_table" ); |
2147 | replaceSymbol<DefinedSynthetic>(s: rfeTableSym, arg: "__arm64x_extra_rfe_table" , |
2148 | arg&: pdata.first); |
2149 | |
2150 | if (pdata.first) { |
2151 | Symbol *rfeSizeSym = |
2152 | ctx.symtab.findUnderscore(name: "__arm64x_extra_rfe_table_size" ); |
2153 | cast<DefinedAbsolute>(Val: rfeSizeSym) |
2154 | ->setVA(pdata.last->getRVA() + pdata.last->getSize() - |
2155 | pdata.first->getRVA()); |
2156 | } |
2157 | } |
2158 | |
2159 | // Write section contents to a mmap'ed file. |
2160 | void Writer::writeSections() { |
2161 | llvm::TimeTraceScope timeScope("Write sections" ); |
2162 | uint8_t *buf = buffer->getBufferStart(); |
2163 | for (OutputSection *sec : ctx.outputSections) { |
2164 | uint8_t *secBuf = buf + sec->getFileOff(); |
2165 | // Fill gaps between functions in .text with INT3 instructions |
2166 | // instead of leaving as NUL bytes (which can be interpreted as |
2167 | // ADD instructions). Only fill the gaps between chunks. Most |
2168 | // chunks overwrite it anyway, but uninitialized data chunks |
2169 | // merged into a code section don't. |
2170 | if ((sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && |
2171 | (ctx.config.machine == AMD64 || ctx.config.machine == I386)) { |
2172 | uint32_t prevEnd = 0; |
2173 | for (Chunk *c : sec->chunks) { |
2174 | uint32_t off = c->getRVA() - sec->getRVA(); |
2175 | memset(s: secBuf + prevEnd, c: 0xCC, n: off - prevEnd); |
2176 | prevEnd = off + c->getSize(); |
2177 | } |
2178 | memset(s: secBuf + prevEnd, c: 0xCC, n: sec->getRawSize() - prevEnd); |
2179 | } |
2180 | |
2181 | parallelForEach(R&: sec->chunks, Fn: [&](Chunk *c) { |
2182 | c->writeTo(buf: secBuf + c->getRVA() - sec->getRVA()); |
2183 | }); |
2184 | } |
2185 | } |
2186 | |
2187 | void Writer::writeBuildId() { |
2188 | llvm::TimeTraceScope timeScope("Write build ID" ); |
2189 | |
2190 | // There are two important parts to the build ID. |
2191 | // 1) If building with debug info, the COFF debug directory contains a |
2192 | // timestamp as well as a Guid and Age of the PDB. |
2193 | // 2) In all cases, the PE COFF file header also contains a timestamp. |
2194 | // For reproducibility, instead of a timestamp we want to use a hash of the |
2195 | // PE contents. |
2196 | Configuration *config = &ctx.config; |
2197 | bool generateSyntheticBuildId = config->buildIDHash == BuildIDHash::Binary; |
2198 | if (generateSyntheticBuildId) { |
2199 | assert(buildId && "BuildId is not set!" ); |
2200 | // BuildId->BuildId was filled in when the PDB was written. |
2201 | } |
2202 | |
2203 | // At this point the only fields in the COFF file which remain unset are the |
2204 | // "timestamp" in the COFF file header, and the ones in the coff debug |
2205 | // directory. Now we can hash the file and write that hash to the various |
2206 | // timestamp fields in the file. |
2207 | StringRef outputFileData( |
2208 | reinterpret_cast<const char *>(buffer->getBufferStart()), |
2209 | buffer->getBufferSize()); |
2210 | |
2211 | uint32_t timestamp = config->timestamp; |
2212 | uint64_t hash = 0; |
2213 | |
2214 | if (config->repro || generateSyntheticBuildId) |
2215 | hash = xxh3_64bits(data: outputFileData); |
2216 | |
2217 | if (config->repro) |
2218 | timestamp = static_cast<uint32_t>(hash); |
2219 | |
2220 | if (generateSyntheticBuildId) { |
2221 | buildId->buildId->PDB70.CVSignature = OMF::Signature::PDB70; |
2222 | buildId->buildId->PDB70.Age = 1; |
2223 | memcpy(dest: buildId->buildId->PDB70.Signature, src: &hash, n: 8); |
2224 | // xxhash only gives us 8 bytes, so put some fixed data in the other half. |
2225 | memcpy(dest: &buildId->buildId->PDB70.Signature[8], src: "LLD PDB." , n: 8); |
2226 | } |
2227 | |
2228 | if (debugDirectory) |
2229 | debugDirectory->setTimeDateStamp(timestamp); |
2230 | |
2231 | uint8_t *buf = buffer->getBufferStart(); |
2232 | buf += dosStubSize + sizeof(PEMagic); |
2233 | object::coff_file_header * = |
2234 | reinterpret_cast<coff_file_header *>(buf); |
2235 | coffHeader->TimeDateStamp = timestamp; |
2236 | } |
2237 | |
2238 | // Sort .pdata section contents according to PE/COFF spec 5.5. |
2239 | template <typename T> |
2240 | void Writer::sortExceptionTable(ChunkRange &exceptionTable) { |
2241 | if (!exceptionTable.first) |
2242 | return; |
2243 | |
2244 | // We assume .pdata contains function table entries only. |
2245 | auto bufAddr = [&](Chunk *c) { |
2246 | OutputSection *os = ctx.getOutputSection(c); |
2247 | return buffer->getBufferStart() + os->getFileOff() + c->getRVA() - |
2248 | os->getRVA(); |
2249 | }; |
2250 | uint8_t *begin = bufAddr(exceptionTable.first); |
2251 | uint8_t *end = bufAddr(exceptionTable.last) + exceptionTable.last->getSize(); |
2252 | if ((end - begin) % sizeof(T) != 0) { |
2253 | fatal(msg: "unexpected .pdata size: " + Twine(end - begin) + |
2254 | " is not a multiple of " + Twine(sizeof(T))); |
2255 | } |
2256 | |
2257 | parallelSort(MutableArrayRef<T>(reinterpret_cast<T *>(begin), |
2258 | reinterpret_cast<T *>(end)), |
2259 | [](const T &a, const T &b) { return a.begin < b.begin; }); |
2260 | } |
2261 | |
2262 | // Sort .pdata section contents according to PE/COFF spec 5.5. |
2263 | void Writer::sortExceptionTables() { |
2264 | llvm::TimeTraceScope timeScope("Sort exception table" ); |
2265 | |
2266 | struct EntryX64 { |
2267 | ulittle32_t begin, end, unwind; |
2268 | }; |
2269 | struct EntryArm { |
2270 | ulittle32_t begin, unwind; |
2271 | }; |
2272 | |
2273 | switch (ctx.config.machine) { |
2274 | case AMD64: |
2275 | sortExceptionTable<EntryX64>(exceptionTable&: pdata); |
2276 | break; |
2277 | case ARM64EC: |
2278 | case ARM64X: |
2279 | sortExceptionTable<EntryX64>(exceptionTable&: hybridPdata); |
2280 | [[fallthrough]]; |
2281 | case ARMNT: |
2282 | case ARM64: |
2283 | sortExceptionTable<EntryArm>(exceptionTable&: pdata); |
2284 | break; |
2285 | default: |
2286 | if (pdata.first) |
2287 | lld::errs() << "warning: don't know how to handle .pdata.\n" ; |
2288 | break; |
2289 | } |
2290 | } |
2291 | |
2292 | // The CRT section contains, among other things, the array of function |
2293 | // pointers that initialize every global variable that is not trivially |
2294 | // constructed. The CRT calls them one after the other prior to invoking |
2295 | // main(). |
2296 | // |
2297 | // As per C++ spec, 3.6.2/2.3, |
2298 | // "Variables with ordered initialization defined within a single |
2299 | // translation unit shall be initialized in the order of their definitions |
2300 | // in the translation unit" |
2301 | // |
2302 | // It is therefore critical to sort the chunks containing the function |
2303 | // pointers in the order that they are listed in the object file (top to |
2304 | // bottom), otherwise global objects might not be initialized in the |
2305 | // correct order. |
2306 | void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) { |
2307 | auto sectionChunkOrder = [](const Chunk *a, const Chunk *b) { |
2308 | auto sa = dyn_cast<SectionChunk>(Val: a); |
2309 | auto sb = dyn_cast<SectionChunk>(Val: b); |
2310 | assert(sa && sb && "Non-section chunks in CRT section!" ); |
2311 | |
2312 | StringRef sAObj = sa->file->mb.getBufferIdentifier(); |
2313 | StringRef sBObj = sb->file->mb.getBufferIdentifier(); |
2314 | |
2315 | return sAObj == sBObj && sa->getSectionNumber() < sb->getSectionNumber(); |
2316 | }; |
2317 | llvm::stable_sort(Range&: chunks, C: sectionChunkOrder); |
2318 | |
2319 | if (ctx.config.verbose) { |
2320 | for (auto &c : chunks) { |
2321 | auto sc = dyn_cast<SectionChunk>(Val: c); |
2322 | log(msg: " " + sc->file->mb.getBufferIdentifier().str() + |
2323 | ", SectionID: " + Twine(sc->getSectionNumber())); |
2324 | } |
2325 | } |
2326 | } |
2327 | |
2328 | OutputSection *Writer::findSection(StringRef name) { |
2329 | for (OutputSection *sec : ctx.outputSections) |
2330 | if (sec->name == name) |
2331 | return sec; |
2332 | return nullptr; |
2333 | } |
2334 | |
2335 | uint32_t Writer::getSizeOfInitializedData() { |
2336 | uint32_t res = 0; |
2337 | for (OutputSection *s : ctx.outputSections) |
2338 | if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) |
2339 | res += s->getRawSize(); |
2340 | return res; |
2341 | } |
2342 | |
2343 | // Add base relocations to .reloc section. |
2344 | void Writer::addBaserels() { |
2345 | if (!ctx.config.relocatable) |
2346 | return; |
2347 | relocSec->chunks.clear(); |
2348 | std::vector<Baserel> v; |
2349 | for (OutputSection *sec : ctx.outputSections) { |
2350 | if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) |
2351 | continue; |
2352 | llvm::TimeTraceScope timeScope("Base relocations: " , sec->name); |
2353 | // Collect all locations for base relocations. |
2354 | for (Chunk *c : sec->chunks) |
2355 | c->getBaserels(res: &v); |
2356 | // Add the addresses to .reloc section. |
2357 | if (!v.empty()) |
2358 | addBaserelBlocks(v); |
2359 | v.clear(); |
2360 | } |
2361 | } |
2362 | |
2363 | // Add addresses to .reloc section. Note that addresses are grouped by page. |
2364 | void Writer::addBaserelBlocks(std::vector<Baserel> &v) { |
2365 | const uint32_t mask = ~uint32_t(pageSize - 1); |
2366 | uint32_t page = v[0].rva & mask; |
2367 | size_t i = 0, j = 1; |
2368 | for (size_t e = v.size(); j < e; ++j) { |
2369 | uint32_t p = v[j].rva & mask; |
2370 | if (p == page) |
2371 | continue; |
2372 | relocSec->addChunk(c: make<BaserelChunk>(args&: page, args: &v[i], args: &v[0] + j)); |
2373 | i = j; |
2374 | page = p; |
2375 | } |
2376 | if (i == j) |
2377 | return; |
2378 | relocSec->addChunk(c: make<BaserelChunk>(args&: page, args: &v[i], args: &v[0] + j)); |
2379 | } |
2380 | |
2381 | PartialSection *Writer::createPartialSection(StringRef name, |
2382 | uint32_t outChars) { |
2383 | PartialSection *&pSec = partialSections[{.name: name, .characteristics: outChars}]; |
2384 | if (pSec) |
2385 | return pSec; |
2386 | pSec = make<PartialSection>(args&: name, args&: outChars); |
2387 | return pSec; |
2388 | } |
2389 | |
2390 | PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) { |
2391 | auto it = partialSections.find(x: {.name: name, .characteristics: outChars}); |
2392 | if (it != partialSections.end()) |
2393 | return it->second; |
2394 | return nullptr; |
2395 | } |
2396 | |
2397 | void Writer::fixTlsAlignment() { |
2398 | Defined *tlsSym = |
2399 | dyn_cast_or_null<Defined>(Val: ctx.symtab.findUnderscore(name: "_tls_used" )); |
2400 | if (!tlsSym) |
2401 | return; |
2402 | |
2403 | OutputSection *sec = ctx.getOutputSection(c: tlsSym->getChunk()); |
2404 | assert(sec && tlsSym->getRVA() >= sec->getRVA() && |
2405 | "no output section for _tls_used" ); |
2406 | |
2407 | uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff(); |
2408 | uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA(); |
2409 | uint64_t directorySize = ctx.config.is64() |
2410 | ? sizeof(object::coff_tls_directory64) |
2411 | : sizeof(object::coff_tls_directory32); |
2412 | |
2413 | if (tlsOffset + directorySize > sec->getRawSize()) |
2414 | fatal(msg: "_tls_used sym is malformed" ); |
2415 | |
2416 | if (ctx.config.is64()) { |
2417 | object::coff_tls_directory64 *tlsDir = |
2418 | reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]); |
2419 | tlsDir->setAlignment(tlsAlignment); |
2420 | } else { |
2421 | object::coff_tls_directory32 *tlsDir = |
2422 | reinterpret_cast<object::coff_tls_directory32 *>(&secBuf[tlsOffset]); |
2423 | tlsDir->setAlignment(tlsAlignment); |
2424 | } |
2425 | } |
2426 | |
2427 | void Writer::prepareLoadConfig() { |
2428 | Symbol *sym = ctx.symtab.findUnderscore(name: "_load_config_used" ); |
2429 | auto *b = cast_if_present<DefinedRegular>(Val: sym); |
2430 | if (!b) { |
2431 | if (ctx.config.guardCF != GuardCFLevel::Off) |
2432 | warn(msg: "Control Flow Guard is enabled but '_load_config_used' is missing" ); |
2433 | return; |
2434 | } |
2435 | |
2436 | OutputSection *sec = ctx.getOutputSection(c: b->getChunk()); |
2437 | uint8_t *buf = buffer->getBufferStart(); |
2438 | uint8_t *secBuf = buf + sec->getFileOff(); |
2439 | uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA()); |
2440 | uint32_t expectedAlign = ctx.config.is64() ? 8 : 4; |
2441 | if (b->getChunk()->getAlignment() < expectedAlign) |
2442 | warn(msg: "'_load_config_used' is misaligned (expected alignment to be " + |
2443 | Twine(expectedAlign) + " bytes, got " + |
2444 | Twine(b->getChunk()->getAlignment()) + " instead)" ); |
2445 | else if (!isAligned(Lhs: Align(expectedAlign), SizeInBytes: b->getRVA())) |
2446 | warn(msg: "'_load_config_used' is misaligned (RVA is 0x" + |
2447 | Twine::utohexstr(Val: b->getRVA()) + " not aligned to " + |
2448 | Twine(expectedAlign) + " bytes)" ); |
2449 | |
2450 | if (ctx.config.is64()) |
2451 | prepareLoadConfig(loadConfig: reinterpret_cast<coff_load_configuration64 *>(symBuf)); |
2452 | else |
2453 | prepareLoadConfig(loadConfig: reinterpret_cast<coff_load_configuration32 *>(symBuf)); |
2454 | } |
2455 | |
2456 | template <typename T> void Writer::prepareLoadConfig(T *loadConfig) { |
2457 | if (ctx.config.dependentLoadFlags) |
2458 | loadConfig->DependentLoadFlags = ctx.config.dependentLoadFlags; |
2459 | |
2460 | checkLoadConfigGuardData(loadConfig); |
2461 | } |
2462 | |
2463 | template <typename T> |
2464 | void Writer::checkLoadConfigGuardData(const T *loadConfig) { |
2465 | size_t loadConfigSize = loadConfig->Size; |
2466 | |
2467 | #define RETURN_IF_NOT_CONTAINS(field) \ |
2468 | if (loadConfigSize < offsetof(T, field) + sizeof(T::field)) { \ |
2469 | warn("'_load_config_used' structure too small to include " #field); \ |
2470 | return; \ |
2471 | } |
2472 | |
2473 | #define IF_CONTAINS(field) \ |
2474 | if (loadConfigSize >= offsetof(T, field) + sizeof(T::field)) |
2475 | |
2476 | #define CHECK_VA(field, sym) \ |
2477 | if (auto *s = dyn_cast<DefinedSynthetic>(ctx.symtab.findUnderscore(sym))) \ |
2478 | if (loadConfig->field != ctx.config.imageBase + s->getRVA()) \ |
2479 | warn(#field " not set correctly in '_load_config_used'"); |
2480 | |
2481 | #define CHECK_ABSOLUTE(field, sym) \ |
2482 | if (auto *s = dyn_cast<DefinedAbsolute>(ctx.symtab.findUnderscore(sym))) \ |
2483 | if (loadConfig->field != s->getVA()) \ |
2484 | warn(#field " not set correctly in '_load_config_used'"); |
2485 | |
2486 | if (ctx.config.guardCF == GuardCFLevel::Off) |
2487 | return; |
2488 | RETURN_IF_NOT_CONTAINS(GuardFlags) |
2489 | CHECK_VA(GuardCFFunctionTable, "__guard_fids_table" ) |
2490 | CHECK_ABSOLUTE(GuardCFFunctionCount, "__guard_fids_count" ) |
2491 | CHECK_ABSOLUTE(GuardFlags, "__guard_flags" ) |
2492 | IF_CONTAINS(GuardAddressTakenIatEntryCount) { |
2493 | CHECK_VA(GuardAddressTakenIatEntryTable, "__guard_iat_table" ) |
2494 | CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count" ) |
2495 | } |
2496 | |
2497 | if (!(ctx.config.guardCF & GuardCFLevel::LongJmp)) |
2498 | return; |
2499 | RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount) |
2500 | CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table" ) |
2501 | CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count" ) |
2502 | |
2503 | if (!(ctx.config.guardCF & GuardCFLevel::EHCont)) |
2504 | return; |
2505 | RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount) |
2506 | CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table" ) |
2507 | CHECK_ABSOLUTE(GuardEHContinuationCount, "__guard_eh_cont_count" ) |
2508 | |
2509 | #undef RETURN_IF_NOT_CONTAINS |
2510 | #undef IF_CONTAINS |
2511 | #undef CHECK_VA |
2512 | #undef CHECK_ABSOLUTE |
2513 | } |
2514 | |