From 4ecc59afce0c7004d149e1a25a89bd0372731404 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 2 Aug 2013 21:26:14 +0000 Subject: [PATCH] [PECOFF] Handle .drectve section. Summary: The .drectve section contains linker command line options, and the linker is expected to interpret them as if they were given via the command line. In this patch, the command line parser in the driver is called from the object file reader to parse the string. I think this patch is important, because this is the first step towards mutable TargetInfo. We had a discussion about that on llvm-commits mailing list before. I haven't removed "const" from the function signature yet. Instead, I just use cast to remove "const". This is a temporary aid for an experiment. If we don't see any issue with this mutable TargetInfo appraoch, I'll change the function signature, and rename the class LinkerContext from TargetInfo. Reviewers: kledzik CC: llvm-commits Differential Revision: http://llvm-reviews.chandlerc.com/D1246 llvm-svn: 187677 --- lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp | 132 ++++++++++++++++++++++++++++- lld/test/pecoff/Inputs/drectve.obj.yaml | 49 +++++++++++ lld/test/pecoff/drectve.test | 11 +++ 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 lld/test/pecoff/Inputs/drectve.obj.yaml create mode 100644 lld/test/pecoff/drectve.test diff --git a/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp b/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp index 7621685..4212857 100644 --- a/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp +++ b/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp @@ -13,12 +13,14 @@ #include "ReaderImportHeader.h" #include "lld/Core/File.h" +#include "lld/Driver/Driver.h" #include "lld/ReaderWriter/Reader.h" #include "lld/ReaderWriter/ReaderArchive.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Memory.h" @@ -80,7 +82,11 @@ public: if ((ec = createDefinedSymbols(symbols, _definedAtoms._atoms))) return; - ec = addRelocationReferenceToAtoms(); + if ((ec = addRelocationReferenceToAtoms())) + return; + + // Read .drectve section if exists. + ec = maybeReadLinkerDirectives(); } virtual const atom_collection &defined() const { @@ -101,6 +107,8 @@ public: virtual const TargetInfo &getTargetInfo() const { return _targetInfo; } + StringRef getLinkerDirectives() const { return _directives; } + private: /// Iterate over the symbol table to retrieve all symbols. error_code readSymbolTable(vector &result) { @@ -389,12 +397,69 @@ private: return error_code::success(); } + /// Find a section by name. + error_code findSection(StringRef name, const coff_section *&result) { + error_code ec; + for (auto si = _obj->begin_sections(), se = _obj->end_sections(); si != se; + si.increment(ec)) { + const coff_section *section = _obj->getCOFFSection(si); + StringRef sectionName; + if ((ec = _obj->getSectionName(section, sectionName))) + return ec; + if (sectionName == name) { + result = section; + return error_code::success(); + } + } + // Section was not found, but it's not an error. This method returns an error + // only when there's a read error. + return error_code::success(); + } + + // Convert ArrayRef to std::string. The array contains a string which + // may not be terminated by NUL. + std::string ArrayRefToString(ArrayRef array) { + // Skip the UTF-8 byte marker if exists. The contents of .drectve section + // is, according to the Microsoft PE/COFF spec, encoded as ANSI or UTF-8 + // with the BOM marker. + // + // FIXME: I think "ANSI" in the spec means Windows-1252 encoding, which is a + // superset of ASCII. We need to convert it to UTF-8. + if (array.size() >= 3 && array[0] == 0xEF && array[1] == 0xBB && + array[2] == 0xBF) { + array = array.slice(3); + } + + size_t len = 0; + size_t e = array.size(); + while (len < e && array[len] != '\0') + ++len; + return std::string(reinterpret_cast(&array[0]), len); + } + + // Read .drectve section contents if exists, and store it to _directives. + error_code maybeReadLinkerDirectives() { + const coff_section *section = nullptr; + if (error_code ec = findSection(".drectve", section)) + return ec; + if (section != nullptr) { + ArrayRef contents; + if (error_code ec = _obj->getSectionContents(section, contents)) + return ec; + _directives = std::move(ArrayRefToString(contents)); + } + return error_code::success(); + } + std::unique_ptr _obj; atom_collection_vector _definedAtoms; atom_collection_vector _undefinedAtoms; atom_collection_vector _sharedLibraryAtoms; atom_collection_vector _absoluteAtoms; + // The contents of .drectve section. + std::string _directives; + // A map from symbol to its name. All symbols should be in this map except // unnamed ones. std::map _symbolName; @@ -409,6 +474,19 @@ private: const TargetInfo &_targetInfo; }; +class BumpPtrStringSaver : public llvm::cl::StringSaver { +public: + virtual const char *SaveString(const char *str) { + size_t len = strlen(str); + char *copy = _alloc.Allocate(len + 1); + memcpy(copy, str, len + 1); + return copy; + } + +private: + llvm::BumpPtrAllocator _alloc; +}; + class ReaderCOFF : public Reader { public: explicit ReaderCOFF(const TargetInfo &ti) @@ -426,10 +504,54 @@ public: } private: + // Interpret the contents of .drectve section. If exists, the section contains + // a string containing command line options. The linker is expected to + // interpret the options as if they were given via the command line. + // + // The section mainly contains /defaultlib (-l in Unix), but can contain any + // options as long as they are valid. + void handleDirectiveSection(StringRef directives) const { + DEBUG({ + llvm::dbgs() << ".drectve: " << directives << "\n"; + }); + + // Remove const from _targetInfo. + // FIXME: Rename TargetInfo -> LinkingContext and treat it a mutable object + // in the core linker. + PECOFFTargetInfo *targetInfo = (PECOFFTargetInfo *)&_targetInfo; + + // Split the string into tokens, as the shell would do for argv. + SmallVector tokens; + tokens.push_back("link"); // argv[0] is the command name. Will be ignored. + llvm::cl::TokenizeWindowsCommandLine(directives, _stringSaver, tokens); + tokens.push_back(nullptr); + + // Calls the command line parser to interpret the token string as if they + // were given via the command line. + int argc = tokens.size() - 1; + const char **argv = &tokens[0]; + std::string errorMessage; + llvm::raw_string_ostream stream(errorMessage); + bool parseFailed = WinLinkDriver::parse(argc, argv, *targetInfo, stream); + stream.flush(); + + // Print error message if error. + if (parseFailed) { + auto msg = Twine("Failed to parse '") + directives + "': " + + errorMessage + "\n"; + llvm::report_fatal_error(msg); + } + if (!errorMessage.empty()) { + llvm::errs() << "lld warning: " << errorMessage << "\n"; + } + } + error_code parseCOFFFile(std::unique_ptr &mb, std::vector > &result) const { + // Parse the memory buffer as PECOFF file. error_code ec; - std::unique_ptr file(new FileCOFF(_targetInfo, std::move(mb), ec)); + std::unique_ptr file( + new FileCOFF(_targetInfo, std::move(mb), ec)); if (ec) return ec; @@ -443,11 +565,17 @@ private: } }); + // Interpret .drectve section if the section has contents. + StringRef directives = file->getLinkerDirectives(); + if (!directives.empty()) + handleDirectiveSection(directives); + result.push_back(std::move(file)); return error_code::success(); } ReaderArchive _readerArchive; + mutable BumpPtrStringSaver _stringSaver; }; } // end namespace anonymous diff --git a/lld/test/pecoff/Inputs/drectve.obj.yaml b/lld/test/pecoff/Inputs/drectve.obj.yaml new file mode 100644 index 0000000..69b1cf0 --- /dev/null +++ b/lld/test/pecoff/Inputs/drectve.obj.yaml @@ -0,0 +1,49 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B82A000000C3 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: "" + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2f73756273797374656d3a636f6e736f6c652c34322e31393500 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + NumberOfAuxSymbols: 1 + AuxiliaryData: 060000000000000000000000000000000000 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + NumberOfAuxSymbols: 1 + AuxiliaryData: 000000000000000000000000000000000000 + - Name: _start + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .drectve + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + NumberOfAuxSymbols: 1 + AuxiliaryData: 0D0000000000000000000000000000000000 +... diff --git a/lld/test/pecoff/drectve.test b/lld/test/pecoff/drectve.test new file mode 100644 index 0000000..100a710 --- /dev/null +++ b/lld/test/pecoff/drectve.test @@ -0,0 +1,11 @@ +# Test if the linker can properly parse the .drectve section contents. +# "drectve.obj" contains "/subsystem:console,42.195" in its .drectve +# section. + +# RUN: yaml2obj %p/Inputs/drectve.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1 /entry:start -- %t.obj \ +# RUN: && llvm-readobj -file-headers %t1 | FileCheck %s + +CHECK: MajorOperatingSystemVersion: 42 +CHECK: MinorOperatingSystemVersion: 195 -- 2.7.4