Index: COFF/Chunks.h =================================================================== --- COFF/Chunks.h +++ COFF/Chunks.h @@ -108,6 +108,8 @@ // The alignment of this chunk. The writer uses the value. uint32_t Alignment = 1; + virtual bool isHotPatchable() const { return false; } + protected: Chunk(Kind K = OtherKind) : ChunkKind(K) {} const Kind ChunkKind; @@ -205,6 +207,16 @@ // The section ID this chunk belongs to in its Obj. uint32_t getSectionNumber() const; + ArrayRef consumeDebugMagic(); + + static ArrayRef consumeDebugMagic(ArrayRef Data, + StringRef SectionName); + + static SectionChunk *findByName(ArrayRef Sections, + StringRef Name); + + bool isHotPatchable() const override { return File->HotPatchable; } + // A pointer pointing to a replacement for this chunk. // Initially it points to "this" object. If this chunk is merged // with other chunk by ICF, it points to another chunk, @@ -321,6 +333,8 @@ size_t getSize() const override { return sizeof(ImportThunkX86); } void writeTo(uint8_t *Buf) const override; + bool isHotPatchable() const override { return true; } + private: Defined *ImpSymbol; }; @@ -332,6 +346,8 @@ void getBaserels(std::vector *Res) override; void writeTo(uint8_t *Buf) const override; + bool isHotPatchable() const override { return true; } + private: Defined *ImpSymbol; }; @@ -343,6 +359,8 @@ void getBaserels(std::vector *Res) override; void writeTo(uint8_t *Buf) const override; + bool isHotPatchable() const override { return true; } + private: Defined *ImpSymbol; }; @@ -353,6 +371,8 @@ size_t getSize() const override { return sizeof(ImportThunkARM64); } void writeTo(uint8_t *Buf) const override; + bool isHotPatchable() const override { return true; } + private: Defined *ImpSymbol; }; Index: COFF/Chunks.cpp =================================================================== --- COFF/Chunks.cpp +++ COFF/Chunks.cpp @@ -594,6 +594,44 @@ return A; } +ArrayRef SectionChunk::consumeDebugMagic() { + assert(isCodeView()); + return consumeDebugMagic(getContents(), SectionName); +} + +ArrayRef SectionChunk::consumeDebugMagic(ArrayRef Data, + StringRef SectionName) { + if (Data.empty()) + return {}; + + // First 4 bytes are section magic. + if (Data.size() < 4) + fatal("The section is too short. " + SectionName); + + if (!SectionName.startswith(".debug$")) + fatal("Invalid section. " + SectionName); + + unsigned Magic = support::endian::read32le(Data.data()); + + if (SectionName == ".debug$H") { + if (Magic != DEBUG_HASHES_SECTION_MAGIC) + fatal("Section has an invalid magic. " + SectionName); + } else { + if (Magic != DEBUG_SECTION_MAGIC) + fatal("Section has an invalid magic. " + SectionName + + " magic: " + Twine(Magic)); + } + return Data.slice(4); +} + +SectionChunk *SectionChunk::findByName(ArrayRef Sections, + StringRef Name) { + for (SectionChunk *C : Sections) + if (C->getSectionName() == Name) + return C; + return nullptr; +} + void SectionChunk::replace(SectionChunk *Other) { Alignment = std::max(Alignment, Other->Alignment); Other->Repl = Repl; Index: COFF/Config.h =================================================================== --- COFF/Config.h +++ COFF/Config.h @@ -185,6 +185,7 @@ uint32_t MajorOSVersion = 6; uint32_t MinorOSVersion = 0; uint32_t Timestamp = 0; + uint32_t FunctionPadMin = 0; bool DynamicBase = true; bool AllowBind = true; bool NxCompat = true; Index: COFF/Driver.h =================================================================== --- COFF/Driver.h +++ COFF/Driver.h @@ -156,6 +156,9 @@ void parseSection(StringRef); void parseAligncomm(StringRef); +// Parses a string in the form of "[:]" +void parseFunctionPadMin(StringRef Arg, llvm::COFF::MachineTypes Machine); + // Parses a string in the form of "EMBED[,=]|NO". void parseManifest(StringRef Arg); Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -1384,6 +1384,10 @@ } Config->Wordsize = Config->is64() ? 8 : 4; + // Handle /functionpadmin + for (auto *Arg : Args.filtered(OPT_functionpadmin)) + parseFunctionPadMin(Arg->getValue(), Config->Machine); + // Input files can be Windows resource files (.res files). We use // WindowsResource to convert resource files to a regular COFF file, // then link the resulting file normally. Index: COFF/DriverUtils.cpp =================================================================== --- COFF/DriverUtils.cpp +++ COFF/DriverUtils.cpp @@ -249,6 +249,28 @@ Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V); } +// Parses /functionpadmin option argument. +void parseFunctionPadMin(StringRef Arg, llvm::COFF::MachineTypes Machine) { + StringRef Name, Padding; + std::tie(Name, Padding) = Arg.split(':'); + if (!Padding.empty()) { + // Optional padding in bytes is given. + if (Padding.getAsInteger(0, Config->FunctionPadMin)) + error("/functionpadmin: invalid argument: " + Arg); + return; + } + // No optional argument given. + // Set default padding based on machine, similar to link.exe. + // There is no default padding for ARM platforms. + if (Machine == I386) { + Config->FunctionPadMin = 5; + } else if (Machine == AMD64) { + Config->FunctionPadMin = 6; + } else { + error("/functionpadmin: invalid argument for this machine: " + Arg); + } +} + // Parses a string in the form of "EMBED[,=]|NO". // Results are directly written to Config. void parseManifest(StringRef Arg) { Index: COFF/InputFiles.h =================================================================== --- COFF/InputFiles.h +++ COFF/InputFiles.h @@ -116,6 +116,8 @@ ArrayRef getGuardLJmpChunks() { return GuardLJmpChunks; } ArrayRef getSymbols() { return Symbols; } + ArrayRef getDebugSection(StringRef SecName); + // Returns a Symbol object for the SymbolIndex'th symbol in the // underlying object file. Symbol *getSymbol(uint32_t SymbolIndex) { @@ -155,11 +157,15 @@ // precompiled object. Any difference indicates out-of-date objects. llvm::Optional PCHSignature; + // Tells whether this file was compiled with /hotpatch + bool HotPatchable = false; + private: const coff_section* getSection(uint32_t I); void initializeChunks(); void initializeSymbols(); + void initializeFlags(); SectionChunk * readSection(uint32_t SectionNumber, Index: COFF/InputFiles.cpp =================================================================== --- COFF/InputFiles.cpp +++ COFF/InputFiles.cpp @@ -19,6 +19,9 @@ #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Casting.h" @@ -34,6 +37,7 @@ using namespace llvm; using namespace llvm::COFF; +using namespace llvm::codeview; using namespace llvm::object; using namespace llvm::support::endian; @@ -124,6 +128,7 @@ // Read section and symbol tables. initializeChunks(); initializeSymbols(); + initializeFlags(); } const coff_section* ObjFile::getSection(uint32_t I) { @@ -593,6 +598,61 @@ return IMAGE_FILE_MACHINE_UNKNOWN; } +ArrayRef ObjFile::getDebugSection(StringRef SecName) { + if (SectionChunk *Sec = SectionChunk::findByName(DebugChunks, SecName)) { + return Sec->consumeDebugMagic(); + } + return {}; +} + +// OBJ files systematically store critical informations in a .debug$S stream, +// even if the TU was compiled with no debug info. At least two records are +// always there: S_OBJNAME and S_COMPILE3. The first one, S_OBJNAME, stores a +// 32-bit signature. The second, S_COMPILE3, stores information about flags that +// were provided at compile-time. The linker will use this information to take +// decisions on how the final binary should be produced. +void ObjFile::initializeFlags() { + ArrayRef Data = getDebugSection(".debug$S"); + if (Data.empty()) + return; + + DebugSubsectionArray Subsections; + + BinaryStreamReader Reader(Data, support::little); + ExitOnError ExitOnErr; + ExitOnErr(Reader.readArray(Subsections, Data.size())); + + for (const DebugSubsectionRecord &SS : Subsections) { + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + unsigned Offset = 0; + + // Only parse the first two records. We are only looking for S_OBJNAME + // and S_COMPILE3, and they usually appear at the beginning of the + // stream. + for (unsigned I = 0; I < 2; ++I) { + Expected Sym = readSymbolFromStream(SS.getRecordData(), Offset); + if (!Sym) { + consumeError(Sym.takeError()); + return; + } + if (Sym->kind() == SymbolKind::S_COMPILE3) { + auto CS = + cantFail(SymbolDeserializer::deserializeAs(Sym.get())); + HotPatchable = + (CS.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None; + } + if (Sym->kind() == SymbolKind::S_OBJNAME) { + auto ObjName = cantFail(SymbolDeserializer::deserializeAs( + Sym.get())); + PCHSignature = ObjName.Signature; + } + Offset += Sym->length(); + } + } +} + StringRef ltrim1(StringRef S, const char *Chars) { if (!S.empty() && strchr(Chars, S[0])) return S.substr(1); Index: COFF/Options.td =================================================================== --- COFF/Options.td +++ COFF/Options.td @@ -32,6 +32,7 @@ def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; +def functionpadmin : Joined<["/", "-", "-?"], "functionpadmin">, HelpText<"Prepares an image for hotpatching">; def guard : P<"guard", "Control flow guard">; def heap : P<"heap", "Size of the heap">; def ignore : P<"ignore", "Specify warning codes to ignore">; @@ -177,7 +178,6 @@ def _no : F; } -def functionpadmin : F<"functionpadmin">; def ignoreidl : F<"ignoreidl">; def nologo : F<"nologo">; def throwingnew : F<"throwingnew">; Index: COFF/PDB.cpp =================================================================== --- COFF/PDB.cpp +++ COFF/PDB.cpp @@ -308,30 +308,6 @@ FileName = std::move(AbsoluteFileName); } -static SectionChunk *findByName(ArrayRef Sections, - StringRef Name) { - for (SectionChunk *C : Sections) - if (C->getSectionName() == Name) - return C; - return nullptr; -} - -static ArrayRef consumeDebugMagic(ArrayRef Data, - StringRef SecName) { - // First 4 bytes are section magic. - if (Data.size() < 4) - fatal(SecName + " too short"); - if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) - fatal(SecName + " has an invalid magic"); - return Data.slice(4); -} - -static ArrayRef getDebugSection(ObjFile *File, StringRef SecName) { - if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName)) - return consumeDebugMagic(Sec->getContents(), SecName); - return {}; -} - // A COFF .debug$H section is currently a clang extension. This function checks // if a .debug$H section is in a format that we expect / understand, so that we // can ignore any sections which are coincidentally also named .debug$H but do @@ -349,7 +325,8 @@ } static Optional> getDebugH(ObjFile *File) { - SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H"); + SectionChunk *Sec = + SectionChunk::findByName(File->getDebugChunks(), ".debug$H"); if (!Sec) return llvm::None; ArrayRef Contents = Sec->getContents(); @@ -381,51 +358,17 @@ }); } -// OBJs usually start their symbol stream with a S_OBJNAME record. This record -// also contains the signature/key of the current PCH session. The signature -// must be same for all objects which depend on the precompiled object. -// Recompiling the precompiled headers will generate a new PCH key and thus -// invalidate all the dependent objects. -static uint32_t extractPCHSignature(ObjFile *File) { - auto DbgIt = find_if(File->getDebugChunks(), [](SectionChunk *C) { - return C->getSectionName() == ".debug$S"; - }); - if (!DbgIt) - return 0; - - ArrayRef Contents = - consumeDebugMagic((*DbgIt)->getContents(), ".debug$S"); - DebugSubsectionArray Subsections; - BinaryStreamReader Reader(Contents, support::little); - ExitOnErr(Reader.readArray(Subsections, Contents.size())); - - for (const DebugSubsectionRecord &SS : Subsections) { - if (SS.kind() != DebugSubsectionKind::Symbols) - continue; - - // If it's there, the S_OBJNAME record shall come first in the stream. - Expected Sym = readSymbolFromStream(SS.getRecordData(), 0); - if (!Sym) { - consumeError(Sym.takeError()); - continue; - } - if (auto ObjName = SymbolDeserializer::deserializeAs(Sym.get())) - return ObjName->Signature; - } - return 0; -} - Expected PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) { ScopedTimer T(TypeMergingTimer); bool IsPrecompiledHeader = false; - ArrayRef Data = getDebugSection(File, ".debug$T"); + ArrayRef Data = File->getDebugSection(".debug$T"); if (Data.empty()) { // Try again, Microsoft precompiled headers use .debug$P instead of // .debug$T - Data = getDebugSection(File, ".debug$P"); + Data = File->getDebugSection(".debug$P"); IsPrecompiledHeader = true; } if (Data.empty()) @@ -434,7 +377,7 @@ // Precompiled headers objects need to save the index map for further // reference by other objects which use the precompiled headers. if (IsPrecompiledHeader) { - uint32_t PCHSignature = extractPCHSignature(File); + uint32_t PCHSignature = File->PCHSignature.getValueOr(0); if (PCHSignature == 0) fatal("No signature found for the precompiled headers OBJ (" + File->getName() + ")"); @@ -1176,7 +1119,7 @@ void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) { DebugSubsectionArray Subsections; - ArrayRef RelocatedDebugContents = consumeDebugMagic( + ArrayRef RelocatedDebugContents = SectionChunk::consumeDebugMagic( relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName()); BinaryStreamReader Reader(RelocatedDebugContents, support::little); @@ -1676,7 +1619,7 @@ } ArrayRef Contents = - consumeDebugMagic(DbgC->getContents(), ".debug$S"); + SectionChunk::consumeDebugMagic(DbgC->getContents(), ".debug$S"); DebugSubsectionArray Subsections; BinaryStreamReader Reader(Contents, support::little); ExitOnErr(Reader.readArray(Subsections, Contents.size())); Index: COFF/Writer.cpp =================================================================== --- COFF/Writer.cpp +++ COFF/Writer.cpp @@ -1117,7 +1117,18 @@ addBaserels(); uint64_t RawSize = 0, VirtualSize = 0; Sec->Header.VirtualAddress = RVA; + + // If /FUNCTIONPADMIN is used, functions are padded in order to create a + // hotpatchable image. + const bool IsCodeSection = + (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) && + (Sec->Header.Characteristics & IMAGE_SCN_MEM_READ) && + (Sec->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE); + uint32_t Padding = IsCodeSection ? Config->FunctionPadMin : 0; + for (Chunk *C : Sec->Chunks) { + if (Padding && C->isHotPatchable()) + VirtualSize += Padding; VirtualSize = alignTo(VirtualSize, C->Alignment); C->setRVA(RVA + VirtualSize); C->OutputSectionOff = VirtualSize; Index: test/COFF/functionpadmin.test =================================================================== --- test/COFF/functionpadmin.test +++ test/COFF/functionpadmin.test @@ -0,0 +1,139 @@ + +// ---- precomp-a.obj - x86_64, hotpatch +RUN: lld-link %S/Inputs/precomp-a.obj /out:%t.exe /nodefaultlib /force +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix BASE + +RUN: lld-link %S/Inputs/precomp-a.obj /out:%t.exe /nodefaultlib /force /functionpadmin +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix PADDED + +RUN: lld-link %S/Inputs/precomp-a.obj /out:%t.exe /nodefaultlib /force /functionpadmin:17 +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix PADDED-MORE + +// ---- precomp-a.obj, precomp-b.obj - x86_64, hotpatch - Ensure several functions are properly padded +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /out:%t.exe /nodefaultlib /force +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix MULTI-BASE + +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /out:%t.exe /nodefaultlib /force /functionpadmin +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix MULTI-BASE-PADDED + +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /out:%t.exe /nodefaultlib /force /functionpadmin:17 +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix MULTI-BASE-PADDED-MORE + +// ---- pdb-diff.obj - x86, no hotpatch - No padding is applied +RUN: lld-link %S/Inputs/pdb-diff.obj /out:%t.exe /nodefaultlib /force /functionpadmin +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix NO-HOTPATCH + +// ---- hello64.obj - MASM, x86_64, no hotpatch - No padding is applied +RUN: lld-link %S/Inputs/hello64.obj /out:%t.exe /nodefaultlib /force /functionpadmin +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix HELLO + +// ---- std64.lib - Import library, x86_64 - Ensure thunks are padded +RUN: lld-link %S/Inputs/std64.lib /entry:ExitProcess /out:%t.exe /nodefaultlib /force /subsystem:console +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix THUNKS-NO-PAD +RUN: llvm-readobj --file-headers %t.exe | FileCheck %s --check-prefix THUNKS-NO-PAD-ENTRY + +RUN: lld-link %S/Inputs/std64.lib /entry:ExitProcess /out:%t.exe /nodefaultlib /force /functionpadmin /subsystem:console +RUN: llvm-objdump -s %t.exe | FileCheck %s --check-prefix THUNKS-PAD +RUN: llvm-readobj --file-headers %t.exe | FileCheck %s --check-prefix THUNKS-PAD-ENTRY + + +BASE: Contents of section .text: +BASE-NEXT: 140001000 4883ec28 b161e8f5 efffbf33 c04883c4 H..(.a.....3.H.. +BASE-NEXT: 140001010 28c3 (. +BASE-NEXT: Contents of section .rdata: +BASE-NEXT: 140002000 01040100 04420000 .....B.. +BASE-NEXT: Contents of section .pdata: +BASE-NEXT: 140003000 00100000 12100000 00200000 ......... .. + +PADDED: Contents of section .text: +PADDED-NEXT: 140001000 cccccccc cccccccc cccccccc cccccccc ................ +PADDED-NEXT: 140001010 4883ec28 b161e8e5 efffbf33 c04883c4 H..(.a.....3.H.. +PADDED-NEXT: 140001020 28c3 (. +PADDED-NEXT: Contents of section .rdata: +PADDED-NEXT: 140002000 01040100 04420000 .....B.. +PADDED-NEXT: Contents of section .pdata: +PADDED-NEXT: 140003000 10100000 22100000 00200000 ....".... .. + +PADDED-MORE: Contents of section .text: +PADDED-MORE-NEXT: 140001000 cccccccc cccccccc cccccccc cccccccc ................ +PADDED-MORE-NEXT: 140001010 cccccccc cccccccc cccccccc cccccccc ................ +PADDED-MORE-NEXT: 140001020 4883ec28 b161e8d5 efffbf33 c04883c4 H..(.a.....3.H.. +PADDED-MORE-NEXT: 140001030 28c3 (. +PADDED-MORE-NEXT: Contents of section .rdata: +PADDED-MORE-NEXT: 140002000 01040100 04420000 .....B.. +PADDED-MORE-NEXT: Contents of section .pdata: +PADDED-MORE-NEXT: 140003000 20100000 32100000 00200000 ...2.... .. + + +MULTI-BASE: Contents of section .text: +MULTI-BASE-NEXT: 140001000 4883ec28 b161e815 00000033 c04883c4 H..(.a.....3.H.. +MULTI-BASE-NEXT: 140001010 28c3cccc cccccccc cccccccc cccccccc (............... +MULTI-BASE-NEXT: 140001020 884c2408 0fbe4424 08c3 .L$...D$.. +MULTI-BASE-NEXT: Contents of section .rdata: +MULTI-BASE-NEXT: 140002000 01040100 04420000 .....B.. +MULTI-BASE-NEXT: Contents of section .pdata: +MULTI-BASE-NEXT: 140004000 00100000 12100000 00200000 ......... .. + +MULTI-BASE-PADDED: Contents of section .text: +MULTI-BASE-PADDED-NEXT: 140001000 cccccccc cccccccc cccccccc cccccccc ................ +MULTI-BASE-PADDED-NEXT: 140001010 4883ec28 b161e815 00000033 c04883c4 H..(.a.....3.H.. +MULTI-BASE-PADDED-NEXT: 140001020 28c3cccc cccccccc cccccccc cccccccc (............... +MULTI-BASE-PADDED-NEXT: 140001030 884c2408 0fbe4424 08c3 .L$...D$.. +MULTI-BASE-PADDED-NEXT: Contents of section .rdata: +MULTI-BASE-PADDED-NEXT: 140002000 01040100 04420000 .....B.. +MULTI-BASE-PADDED-NEXT: Contents of section .pdata: +MULTI-BASE-PADDED-NEXT: 140004000 10100000 22100000 00200000 ....".... .. + +MULTI-BASE-PADDED-MORE: Contents of section .text: +MULTI-BASE-PADDED-MORE-NEXT: 140001000 cccccccc cccccccc cccccccc cccccccc ................ +MULTI-BASE-PADDED-MORE-NEXT: 140001010 cccccccc cccccccc cccccccc cccccccc ................ +MULTI-BASE-PADDED-MORE-NEXT: 140001020 4883ec28 b161e825 00000033 c04883c4 H..(.a.%...3.H.. +MULTI-BASE-PADDED-MORE-NEXT: 140001030 28c3cccc cccccccc cccccccc cccccccc (............... +MULTI-BASE-PADDED-MORE-NEXT: 140001040 cccccccc cccccccc cccccccc cccccccc ................ +MULTI-BASE-PADDED-MORE-NEXT: 140001050 884c2408 0fbe4424 08c3 .L$...D$.. +MULTI-BASE-PADDED-MORE-NEXT: Contents of section .rdata: +MULTI-BASE-PADDED-MORE-NEXT: 140002000 01040100 04420000 .....B.. +MULTI-BASE-PADDED-MORE-NEXT: Contents of section .pdata: +MULTI-BASE-PADDED-MORE-NEXT: 140004000 20100000 32100000 00200000 ...2.... .. + + +NO-HOTPATCH: Contents of section .text: +NO-HOTPATCH-NEXT: 401000 558becb8 2a000000 5dc3 U...*...]. + +HELLO: Contents of section .text: +HELLO-NEXT: 140001000 4883ec28 48c7c100 00000048 8d15f40f H..(H......H.... +HELLO-NEXT: 140001010 00004c8d 05e70f00 0041b900 000000e8 ..L......A...... +HELLO-NEXT: 140001020 dcefffbf b9000000 00e8d2ef ffbfe8cd ................ +HELLO-NEXT: 140001030 efffbf ... +HELLO-NEXT: Contents of section .data: +HELLO-NEXT: 140002000 48656c6c 6f004865 6c6c6f20 576f726c Hello.Hello Worl +HELLO-NEXT: 140002010 642100 d!. + +THUNKS-NO-PAD: Contents of section .text: +THUNKS-NO-PAD-NEXT: 140001000 ff253210 0000 .%2... +THUNKS-NO-PAD-NEXT: Contents of section .rdata: +THUNKS-NO-PAD-NEXT: 140002000 28200000 00000000 00000000 56200000 ( ..........V .. +THUNKS-NO-PAD-NEXT: 140002010 38200000 00000000 00000000 00000000 8 .............. +THUNKS-NO-PAD-NEXT: 140002020 00000000 00000000 48200000 00000000 ........H ...... +THUNKS-NO-PAD-NEXT: 140002030 00000000 00000000 48200000 00000000 ........H ...... +THUNKS-NO-PAD-NEXT: 140002040 00000000 00000000 00004578 69745072 ..........ExitPr +THUNKS-NO-PAD-NEXT: 140002050 6f636573 73007374 6436342e 646c6c00 ocess.std64.dll. + +THUNKS-NO-PAD-ENTRY-LABEL: ImageOptionalHeader { +THUNKS-NO-PAD-ENTRY: AddressOfEntryPoint: 0x1000 +THUNKS-NO-PAD-ENTRY-NEXT: BaseOfCode: 0x1000 + +THUNKS-PAD: Contents of section .text: +THUNKS-PAD-NEXT: 140001000 cccccccc cccccccc cccccccc cccccccc ................ +THUNKS-PAD-NEXT: 140001010 ff252210 0000 .%"... +THUNKS-PAD-NEXT: Contents of section .rdata: +THUNKS-PAD-NEXT: 140002000 28200000 00000000 00000000 56200000 ( ..........V .. +THUNKS-PAD-NEXT: 140002010 38200000 00000000 00000000 00000000 8 .............. +THUNKS-PAD-NEXT: 140002020 00000000 00000000 48200000 00000000 ........H ...... +THUNKS-PAD-NEXT: 140002030 00000000 00000000 48200000 00000000 ........H ...... +THUNKS-PAD-NEXT: 140002040 00000000 00000000 00004578 69745072 ..........ExitPr +THUNKS-PAD-NEXT: 140002050 6f636573 73007374 6436342e 646c6c00 ocess.std64.dll. + +THUNKS-PAD-ENTRY-LABEL: ImageOptionalHeader { +THUNKS-PAD-ENTRY: AddressOfEntryPoint: 0x1010 +THUNKS-PAD-ENTRY-NEXT: BaseOfCode: 0x1000