diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
commit | e6d1592492a3a379186bfb02bd0f4eda0669c0d5 (patch) | |
tree | 599ab169a01f1c86eda9adc774edaedde2f2db5b /lib/Object/WindowsResource.cpp | |
parent | 1a56a5ead7a2e84bee8240f5f6b033b5f1707154 (diff) |
Diffstat (limited to 'lib/Object/WindowsResource.cpp')
-rw-r--r-- | lib/Object/WindowsResource.cpp | 203 |
1 files changed, 141 insertions, 62 deletions
diff --git a/lib/Object/WindowsResource.cpp b/lib/Object/WindowsResource.cpp index 65413dd8bea1..d76e1231684c 100644 --- a/lib/Object/WindowsResource.cpp +++ b/lib/Object/WindowsResource.cpp @@ -1,9 +1,8 @@ //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -16,6 +15,7 @@ #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/ScopedPrinter.h" #include <ctime> #include <queue> #include <system_error> @@ -46,11 +46,12 @@ WindowsResource::WindowsResource(MemoryBufferRef Source) support::little); } +// static Expected<std::unique_ptr<WindowsResource>> WindowsResource::createWindowsResource(MemoryBufferRef Source) { if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) return make_error<GenericBinaryError>( - "File too small to be a resource file", + Source.getBufferIdentifier() + ": too small to be a resource file", object_error::invalid_file_type); std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); return std::move(Ret); @@ -58,14 +59,14 @@ WindowsResource::createWindowsResource(MemoryBufferRef Source) { Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) - return make_error<EmptyResError>(".res contains no entries", + return make_error<EmptyResError>(getFileName() + " contains no entries", object_error::unexpected_eof); return ResourceEntryRef::create(BinaryStreamRef(BBS), this); } ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, const WindowsResource *Owner) - : Reader(Ref) {} + : Reader(Ref), Owner(Owner) {} Expected<ResourceEntryRef> ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { @@ -108,7 +109,8 @@ Error ResourceEntryRef::loadNext() { RETURN_IF_ERROR(Reader.readObject(Prefix)); if (Prefix->HeaderSize < MIN_HEADER_SIZE) - return make_error<GenericBinaryError>("Header size is too small.", + return make_error<GenericBinaryError>(Owner->getFileName() + + ": header size too small", object_error::parse_failed); RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); @@ -128,7 +130,78 @@ Error ResourceEntryRef::loadNext() { WindowsResourceParser::WindowsResourceParser() : Root(false) {} -Error WindowsResourceParser::parse(WindowsResource *WR) { +void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) { + switch (TypeID) { + case 1: OS << "CURSOR (ID 1)"; break; + case 2: OS << "BITMAP (ID 2)"; break; + case 3: OS << "ICON (ID 3)"; break; + case 4: OS << "MENU (ID 4)"; break; + case 5: OS << "DIALOG (ID 5)"; break; + case 6: OS << "STRINGTABLE (ID 6)"; break; + case 7: OS << "FONTDIR (ID 7)"; break; + case 8: OS << "FONT (ID 8)"; break; + case 9: OS << "ACCELERATOR (ID 9)"; break; + case 10: OS << "RCDATA (ID 10)"; break; + case 11: OS << "MESSAGETABLE (ID 11)"; break; + case 12: OS << "GROUP_CURSOR (ID 12)"; break; + case 14: OS << "GROUP_ICON (ID 14)"; break; + case 16: OS << "VERSIONINFO (ID 16)"; break; + case 17: OS << "DLGINCLUDE (ID 17)"; break; + case 19: OS << "PLUGPLAY (ID 19)"; break; + case 20: OS << "VXD (ID 20)"; break; + case 21: OS << "ANICURSOR (ID 21)"; break; + case 22: OS << "ANIICON (ID 22)"; break; + case 23: OS << "HTML (ID 23)"; break; + case 24: OS << "MANIFEST (ID 24)"; break; + default: OS << "ID " << TypeID; break; + } +} + +static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) { + if (!sys::IsBigEndianHost) + return convertUTF16ToUTF8String(Src, Out); + + std::vector<UTF16> EndianCorrectedSrc; + EndianCorrectedSrc.resize(Src.size() + 1); + llvm::copy(Src, EndianCorrectedSrc.begin() + 1); + EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; + return convertUTF16ToUTF8String(makeArrayRef(EndianCorrectedSrc), Out); +} + +static std::string makeDuplicateResourceError( + const ResourceEntryRef &Entry, StringRef File1, StringRef File2) { + std::string Ret; + raw_string_ostream OS(Ret); + + OS << "duplicate resource:"; + + OS << " type "; + if (Entry.checkTypeString()) { + std::string UTF8; + if (!convertUTF16LEToUTF8String(Entry.getTypeString(), UTF8)) + UTF8 = "(failed conversion from UTF16)"; + OS << '\"' << UTF8 << '\"'; + } else + printResourceTypeName(Entry.getTypeID(), OS); + + OS << "/name "; + if (Entry.checkNameString()) { + std::string UTF8; + if (!convertUTF16LEToUTF8String(Entry.getNameString(), UTF8)) + UTF8 = "(failed conversion from UTF16)"; + OS << '\"' << UTF8 << '\"'; + } else { + OS << "ID " << Entry.getNameID(); + } + + OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " + << File2; + + return OS.str(); +} + +Error WindowsResourceParser::parse(WindowsResource *WR, + std::vector<std::string> &Duplicates) { auto EntryOrErr = WR->getHeadEntry(); if (!EntryOrErr) { auto E = EntryOrErr.takeError(); @@ -153,7 +226,14 @@ Error WindowsResourceParser::parse(WindowsResource *WR) { bool IsNewTypeString = false; bool IsNewNameString = false; - Root.addEntry(Entry, IsNewTypeString, IsNewNameString); + TreeNode* Node; + bool IsNewNode = Root.addEntry(Entry, InputFilenames.size(), + IsNewTypeString, IsNewNameString, Node); + InputFilenames.push_back(WR->getFileName()); + if (!IsNewNode) { + Duplicates.push_back(makeDuplicateResourceError( + Entry, InputFilenames[Node->Origin], WR->getFileName())); + } if (IsNewTypeString) StringTable.push_back(Entry.getTypeString()); @@ -172,12 +252,14 @@ void WindowsResourceParser::printTree(raw_ostream &OS) const { Root.print(Writer, "Resource Tree"); } -void WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, +bool WindowsResourceParser::TreeNode::addEntry(const ResourceEntryRef &Entry, + uint32_t Origin, bool &IsNewTypeString, - bool &IsNewNameString) { + bool &IsNewNameString, + TreeNode *&Result) { TreeNode &TypeNode = addTypeNode(Entry, IsNewTypeString); TreeNode &NameNode = TypeNode.addNameNode(Entry, IsNewNameString); - NameNode.addLanguageNode(Entry); + return NameNode.addLanguageNode(Entry, Origin, Result); } WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { @@ -187,10 +269,11 @@ WindowsResourceParser::TreeNode::TreeNode(bool IsStringNode) { WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics) + uint32_t Characteristics, + uint32_t Origin) : IsDataNode(true), MajorVersion(MajorVersion), MinorVersion(MinorVersion), - Characteristics(Characteristics) { - DataIndex = DataCount++; + Characteristics(Characteristics), Origin(Origin) { + DataIndex = DataCount++; } std::unique_ptr<WindowsResourceParser::TreeNode> @@ -206,44 +289,52 @@ WindowsResourceParser::TreeNode::createIDNode() { std::unique_ptr<WindowsResourceParser::TreeNode> WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics) { + uint32_t Characteristics, + uint32_t Origin) { return std::unique_ptr<TreeNode>( - new TreeNode(MajorVersion, MinorVersion, Characteristics)); + new TreeNode(MajorVersion, MinorVersion, Characteristics, Origin)); } WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addTypeNode(const ResourceEntryRef &Entry, bool &IsNewTypeString) { if (Entry.checkTypeString()) - return addChild(Entry.getTypeString(), IsNewTypeString); + return addNameChild(Entry.getTypeString(), IsNewTypeString); else - return addChild(Entry.getTypeID()); + return addIDChild(Entry.getTypeID()); } WindowsResourceParser::TreeNode & WindowsResourceParser::TreeNode::addNameNode(const ResourceEntryRef &Entry, bool &IsNewNameString) { if (Entry.checkNameString()) - return addChild(Entry.getNameString(), IsNewNameString); + return addNameChild(Entry.getNameString(), IsNewNameString); else - return addChild(Entry.getNameID()); + return addIDChild(Entry.getNameID()); } -WindowsResourceParser::TreeNode & -WindowsResourceParser::TreeNode::addLanguageNode( - const ResourceEntryRef &Entry) { - return addChild(Entry.getLanguage(), true, Entry.getMajorVersion(), - Entry.getMinorVersion(), Entry.getCharacteristics()); +bool WindowsResourceParser::TreeNode::addLanguageNode( + const ResourceEntryRef &Entry, uint32_t Origin, TreeNode *&Result) { + return addDataChild(Entry.getLanguage(), Entry.getMajorVersion(), + Entry.getMinorVersion(), Entry.getCharacteristics(), + Origin, Result); } -WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild( - uint32_t ID, bool IsDataNode, uint16_t MajorVersion, uint16_t MinorVersion, - uint32_t Characteristics) { +bool WindowsResourceParser::TreeNode::addDataChild( + uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, + uint32_t Characteristics, uint32_t Origin, TreeNode *&Result) { + auto NewChild = + createDataNode(MajorVersion, MinorVersion, Characteristics, Origin); + auto ElementInserted = IDChildren.emplace(ID, std::move(NewChild)); + Result = ElementInserted.first->second.get(); + return ElementInserted.second; +} + +WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( + uint32_t ID) { auto Child = IDChildren.find(ID); if (Child == IDChildren.end()) { - auto NewChild = - IsDataNode ? createDataNode(MajorVersion, MinorVersion, Characteristics) - : createIDNode(); + auto NewChild = createIDNode(); WindowsResourceParser::TreeNode &Node = *NewChild; IDChildren.emplace(ID, std::move(NewChild)); return Node; @@ -252,19 +343,10 @@ WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addChild( } WindowsResourceParser::TreeNode & -WindowsResourceParser::TreeNode::addChild(ArrayRef<UTF16> NameRef, - bool &IsNewString) { +WindowsResourceParser::TreeNode::addNameChild(ArrayRef<UTF16> NameRef, + bool &IsNewString) { std::string NameString; - ArrayRef<UTF16> CorrectedName; - std::vector<UTF16> EndianCorrectedName; - if (sys::IsBigEndianHost) { - EndianCorrectedName.resize(NameRef.size() + 1); - llvm::copy(NameRef, EndianCorrectedName.begin() + 1); - EndianCorrectedName[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; - CorrectedName = makeArrayRef(EndianCorrectedName); - } else - CorrectedName = NameRef; - convertUTF16ToUTF8String(CorrectedName, NameString); + convertUTF16LEToUTF8String(NameRef, NameString); auto Child = StringChildren.find(NameString); if (Child == StringChildren.end()) { @@ -318,13 +400,13 @@ class WindowsResourceCOFFWriter { public: WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, Error &E); - std::unique_ptr<MemoryBuffer> write(); + std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); private: void performFileLayout(); void performSectionOneLayout(); void performSectionTwoLayout(); - void writeCOFFHeader(); + void writeCOFFHeader(uint32_t TimeDateStamp); void writeFirstSectionHeader(); void writeSecondSectionHeader(); void writeFirstSection(); @@ -360,7 +442,8 @@ WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( Data(Parser.getData()), StringTable(Parser.getStringTable()) { performFileLayout(); - OutputBuffer = WritableMemoryBuffer::getNewMemBuffer(FileSize); + OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( + FileSize, "internal .obj file created from .res files"); } void WindowsResourceCOFFWriter::performFileLayout() { @@ -417,17 +500,11 @@ void WindowsResourceCOFFWriter::performSectionTwoLayout() { FileSize = alignTo(FileSize, SECTION_ALIGNMENT); } -static std::time_t getTime() { - std::time_t Now = time(nullptr); - if (Now < 0 || !isUInt<32>(Now)) - return UINT32_MAX; - return Now; -} - -std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() { +std::unique_ptr<MemoryBuffer> +WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { BufferStart = OutputBuffer->getBufferStart(); - writeCOFFHeader(); + writeCOFFHeader(TimeDateStamp); writeFirstSectionHeader(); writeSecondSectionHeader(); writeFirstSection(); @@ -438,16 +515,17 @@ std::unique_ptr<MemoryBuffer> WindowsResourceCOFFWriter::write() { return std::move(OutputBuffer); } -void WindowsResourceCOFFWriter::writeCOFFHeader() { +void WindowsResourceCOFFWriter::writeCOFFHeader(uint32_t TimeDateStamp) { // Write the COFF header. auto *Header = reinterpret_cast<coff_file_header *>(BufferStart); Header->Machine = MachineType; Header->NumberOfSections = 2; - Header->TimeDateStamp = getTime(); + Header->TimeDateStamp = TimeDateStamp; Header->PointerToSymbolTable = SymbolTableOffset; - // One symbol for every resource plus 2 for each section and @feat.00 + // One symbol for every resource plus 2 for each section and 1 for @feat.00 Header->NumberOfSymbols = Data.size() + 5; Header->SizeOfOptionalHeader = 0; + // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; } @@ -712,12 +790,13 @@ void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { Expected<std::unique_ptr<MemoryBuffer>> writeWindowsResourceCOFF(COFF::MachineTypes MachineType, - const WindowsResourceParser &Parser) { + const WindowsResourceParser &Parser, + uint32_t TimeDateStamp) { Error E = Error::success(); WindowsResourceCOFFWriter Writer(MachineType, Parser, E); if (E) return std::move(E); - return Writer.write(); + return Writer.write(TimeDateStamp); } } // namespace object |