| 1 | //===-- WindowsResource.cpp -------------------------------------*- C++ -*-===// |
| 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 | // This file implements the .res file class. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "llvm/Object/WindowsResource.h" |
| 14 | #include "llvm/Object/COFF.h" |
| 15 | #include "llvm/Object/WindowsMachineFlag.h" |
| 16 | #include "llvm/Support/FormatVariadic.h" |
| 17 | #include "llvm/Support/MathExtras.h" |
| 18 | #include "llvm/Support/ScopedPrinter.h" |
| 19 | #include <ctime> |
| 20 | #include <queue> |
| 21 | |
| 22 | using namespace llvm; |
| 23 | using namespace object; |
| 24 | |
| 25 | namespace llvm { |
| 26 | namespace object { |
| 27 | |
| 28 | #define RETURN_IF_ERROR(X) \ |
| 29 | if (auto EC = X) \ |
| 30 | return EC; |
| 31 | |
| 32 | #define UNWRAP_REF_OR_RETURN(Name, Expr) \ |
| 33 | auto Name##OrErr = Expr; \ |
| 34 | if (!Name##OrErr) \ |
| 35 | return Name##OrErr.takeError(); \ |
| 36 | const auto &Name = *Name##OrErr; |
| 37 | |
| 38 | #define UNWRAP_OR_RETURN(Name, Expr) \ |
| 39 | auto Name##OrErr = Expr; \ |
| 40 | if (!Name##OrErr) \ |
| 41 | return Name##OrErr.takeError(); \ |
| 42 | auto Name = *Name##OrErr; |
| 43 | |
| 44 | const uint32_t = 7 * sizeof(uint32_t) + 2 * sizeof(uint16_t); |
| 45 | |
| 46 | // COFF files seem to be inconsistent with alignment between sections, just use |
| 47 | // 8-byte because it makes everyone happy. |
| 48 | const uint32_t SECTION_ALIGNMENT = sizeof(uint64_t); |
| 49 | |
| 50 | WindowsResource::WindowsResource(MemoryBufferRef Source) |
| 51 | : Binary(Binary::ID_WinRes, Source) { |
| 52 | size_t LeadingSize = WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE; |
| 53 | BBS = BinaryByteStream(Data.getBuffer().drop_front(N: LeadingSize), |
| 54 | llvm::endianness::little); |
| 55 | } |
| 56 | |
| 57 | // static |
| 58 | Expected<std::unique_ptr<WindowsResource>> |
| 59 | WindowsResource::createWindowsResource(MemoryBufferRef Source) { |
| 60 | if (Source.getBufferSize() < WIN_RES_MAGIC_SIZE + WIN_RES_NULL_ENTRY_SIZE) |
| 61 | return make_error<GenericBinaryError>( |
| 62 | Args: Source.getBufferIdentifier() + ": too small to be a resource file" , |
| 63 | Args: object_error::invalid_file_type); |
| 64 | std::unique_ptr<WindowsResource> Ret(new WindowsResource(Source)); |
| 65 | return std::move(Ret); |
| 66 | } |
| 67 | |
| 68 | Expected<ResourceEntryRef> WindowsResource::getHeadEntry() { |
| 69 | if (BBS.getLength() < sizeof(WinResHeaderPrefix) + sizeof(WinResHeaderSuffix)) |
| 70 | return make_error<EmptyResError>(Args: getFileName() + " contains no entries" , |
| 71 | Args: object_error::unexpected_eof); |
| 72 | return ResourceEntryRef::create(Ref: BinaryStreamRef(BBS), Owner: this); |
| 73 | } |
| 74 | |
| 75 | ResourceEntryRef::ResourceEntryRef(BinaryStreamRef Ref, |
| 76 | const WindowsResource *Owner) |
| 77 | : Reader(Ref), Owner(Owner) {} |
| 78 | |
| 79 | Expected<ResourceEntryRef> |
| 80 | ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { |
| 81 | auto Ref = ResourceEntryRef(BSR, Owner); |
| 82 | if (auto E = Ref.loadNext()) |
| 83 | return E; |
| 84 | return Ref; |
| 85 | } |
| 86 | |
| 87 | Error ResourceEntryRef::moveNext(bool &End) { |
| 88 | // Reached end of all the entries. |
| 89 | if (Reader.bytesRemaining() == 0) { |
| 90 | End = true; |
| 91 | return Error::success(); |
| 92 | } |
| 93 | RETURN_IF_ERROR(loadNext()); |
| 94 | |
| 95 | return Error::success(); |
| 96 | } |
| 97 | |
| 98 | static Error readStringOrId(BinaryStreamReader &Reader, uint16_t &ID, |
| 99 | ArrayRef<UTF16> &Str, bool &IsString) { |
| 100 | uint16_t IDFlag; |
| 101 | RETURN_IF_ERROR(Reader.readInteger(IDFlag)); |
| 102 | IsString = IDFlag != 0xffff; |
| 103 | |
| 104 | if (IsString) { |
| 105 | Reader.setOffset( |
| 106 | Reader.getOffset() - |
| 107 | sizeof(uint16_t)); // Re-read the bytes which we used to check the flag. |
| 108 | RETURN_IF_ERROR(Reader.readWideString(Str)); |
| 109 | } else |
| 110 | RETURN_IF_ERROR(Reader.readInteger(ID)); |
| 111 | |
| 112 | return Error::success(); |
| 113 | } |
| 114 | |
| 115 | Error ResourceEntryRef::loadNext() { |
| 116 | const WinResHeaderPrefix *Prefix; |
| 117 | RETURN_IF_ERROR(Reader.readObject(Prefix)); |
| 118 | |
| 119 | if (Prefix->HeaderSize < MIN_HEADER_SIZE) |
| 120 | return make_error<GenericBinaryError>(Args: Owner->getFileName() + |
| 121 | ": header size too small" , |
| 122 | Args: object_error::parse_failed); |
| 123 | |
| 124 | RETURN_IF_ERROR(readStringOrId(Reader, TypeID, Type, IsStringType)); |
| 125 | |
| 126 | RETURN_IF_ERROR(readStringOrId(Reader, NameID, Name, IsStringName)); |
| 127 | |
| 128 | RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_HEADER_ALIGNMENT)); |
| 129 | |
| 130 | RETURN_IF_ERROR(Reader.readObject(Suffix)); |
| 131 | |
| 132 | RETURN_IF_ERROR(Reader.readArray(Data, Prefix->DataSize)); |
| 133 | |
| 134 | RETURN_IF_ERROR(Reader.padToAlignment(WIN_RES_DATA_ALIGNMENT)); |
| 135 | |
| 136 | return Error::success(); |
| 137 | } |
| 138 | |
| 139 | WindowsResourceParser::WindowsResourceParser(bool MinGW) |
| 140 | : Root(false), MinGW(MinGW) {} |
| 141 | |
| 142 | void printResourceTypeName(uint16_t TypeID, raw_ostream &OS) { |
| 143 | switch (TypeID) { |
| 144 | case 1: OS << "CURSOR (ID 1)" ; break; |
| 145 | case 2: OS << "BITMAP (ID 2)" ; break; |
| 146 | case 3: OS << "ICON (ID 3)" ; break; |
| 147 | case 4: OS << "MENU (ID 4)" ; break; |
| 148 | case 5: OS << "DIALOG (ID 5)" ; break; |
| 149 | case 6: OS << "STRINGTABLE (ID 6)" ; break; |
| 150 | case 7: OS << "FONTDIR (ID 7)" ; break; |
| 151 | case 8: OS << "FONT (ID 8)" ; break; |
| 152 | case 9: OS << "ACCELERATOR (ID 9)" ; break; |
| 153 | case 10: OS << "RCDATA (ID 10)" ; break; |
| 154 | case 11: OS << "MESSAGETABLE (ID 11)" ; break; |
| 155 | case 12: OS << "GROUP_CURSOR (ID 12)" ; break; |
| 156 | case 14: OS << "GROUP_ICON (ID 14)" ; break; |
| 157 | case 16: OS << "VERSIONINFO (ID 16)" ; break; |
| 158 | case 17: OS << "DLGINCLUDE (ID 17)" ; break; |
| 159 | case 19: OS << "PLUGPLAY (ID 19)" ; break; |
| 160 | case 20: OS << "VXD (ID 20)" ; break; |
| 161 | case 21: OS << "ANICURSOR (ID 21)" ; break; |
| 162 | case 22: OS << "ANIICON (ID 22)" ; break; |
| 163 | case 23: OS << "HTML (ID 23)" ; break; |
| 164 | case 24: OS << "MANIFEST (ID 24)" ; break; |
| 165 | default: OS << "ID " << TypeID; break; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | static bool convertUTF16LEToUTF8String(ArrayRef<UTF16> Src, std::string &Out) { |
| 170 | if (!sys::IsBigEndianHost) |
| 171 | return convertUTF16ToUTF8String(Src, Out); |
| 172 | |
| 173 | std::vector<UTF16> EndianCorrectedSrc; |
| 174 | EndianCorrectedSrc.resize(new_size: Src.size() + 1); |
| 175 | llvm::copy(Range&: Src, Out: EndianCorrectedSrc.begin() + 1); |
| 176 | EndianCorrectedSrc[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED; |
| 177 | return convertUTF16ToUTF8String(Src: ArrayRef(EndianCorrectedSrc), Out); |
| 178 | } |
| 179 | |
| 180 | static std::string makeDuplicateResourceError( |
| 181 | const ResourceEntryRef &Entry, StringRef File1, StringRef File2) { |
| 182 | std::string Ret; |
| 183 | raw_string_ostream OS(Ret); |
| 184 | |
| 185 | OS << "duplicate resource:" ; |
| 186 | |
| 187 | OS << " type " ; |
| 188 | if (Entry.checkTypeString()) { |
| 189 | std::string UTF8; |
| 190 | if (!convertUTF16LEToUTF8String(Src: Entry.getTypeString(), Out&: UTF8)) |
| 191 | UTF8 = "(failed conversion from UTF16)" ; |
| 192 | OS << '\"' << UTF8 << '\"'; |
| 193 | } else |
| 194 | printResourceTypeName(TypeID: Entry.getTypeID(), OS); |
| 195 | |
| 196 | OS << "/name " ; |
| 197 | if (Entry.checkNameString()) { |
| 198 | std::string UTF8; |
| 199 | if (!convertUTF16LEToUTF8String(Src: Entry.getNameString(), Out&: UTF8)) |
| 200 | UTF8 = "(failed conversion from UTF16)" ; |
| 201 | OS << '\"' << UTF8 << '\"'; |
| 202 | } else { |
| 203 | OS << "ID " << Entry.getNameID(); |
| 204 | } |
| 205 | |
| 206 | OS << "/language " << Entry.getLanguage() << ", in " << File1 << " and in " |
| 207 | << File2; |
| 208 | |
| 209 | return OS.str(); |
| 210 | } |
| 211 | |
| 212 | static void printStringOrID(const WindowsResourceParser::StringOrID &S, |
| 213 | raw_string_ostream &OS, bool IsType, bool IsID) { |
| 214 | if (S.IsString) { |
| 215 | std::string UTF8; |
| 216 | if (!convertUTF16LEToUTF8String(Src: S.String, Out&: UTF8)) |
| 217 | UTF8 = "(failed conversion from UTF16)" ; |
| 218 | OS << '\"' << UTF8 << '\"'; |
| 219 | } else if (IsType) |
| 220 | printResourceTypeName(TypeID: S.ID, OS); |
| 221 | else if (IsID) |
| 222 | OS << "ID " << S.ID; |
| 223 | else |
| 224 | OS << S.ID; |
| 225 | } |
| 226 | |
| 227 | static std::string makeDuplicateResourceError( |
| 228 | const std::vector<WindowsResourceParser::StringOrID> &Context, |
| 229 | StringRef File1, StringRef File2) { |
| 230 | std::string Ret; |
| 231 | raw_string_ostream OS(Ret); |
| 232 | |
| 233 | OS << "duplicate resource:" ; |
| 234 | |
| 235 | if (Context.size() >= 1) { |
| 236 | OS << " type " ; |
| 237 | printStringOrID(S: Context[0], OS, /* IsType */ true, /* IsID */ true); |
| 238 | } |
| 239 | |
| 240 | if (Context.size() >= 2) { |
| 241 | OS << "/name " ; |
| 242 | printStringOrID(S: Context[1], OS, /* IsType */ false, /* IsID */ true); |
| 243 | } |
| 244 | |
| 245 | if (Context.size() >= 3) { |
| 246 | OS << "/language " ; |
| 247 | printStringOrID(S: Context[2], OS, /* IsType */ false, /* IsID */ false); |
| 248 | } |
| 249 | OS << ", in " << File1 << " and in " << File2; |
| 250 | |
| 251 | return OS.str(); |
| 252 | } |
| 253 | |
| 254 | // MinGW specific. Remove default manifests (with language zero) if there are |
| 255 | // other manifests present, and report an error if there are more than one |
| 256 | // manifest with a non-zero language code. |
| 257 | // GCC has the concept of a default manifest resource object, which gets |
| 258 | // linked in implicitly if present. This default manifest has got language |
| 259 | // id zero, and should be dropped silently if there's another manifest present. |
| 260 | // If the user resources surprisignly had a manifest with language id zero, |
| 261 | // we should also ignore the duplicate default manifest. |
| 262 | void WindowsResourceParser::cleanUpManifests( |
| 263 | std::vector<std::string> &Duplicates) { |
| 264 | auto TypeIt = Root.IDChildren.find(/* RT_MANIFEST */ x: 24); |
| 265 | if (TypeIt == Root.IDChildren.end()) |
| 266 | return; |
| 267 | |
| 268 | TreeNode *TypeNode = TypeIt->second.get(); |
| 269 | auto NameIt = |
| 270 | TypeNode->IDChildren.find(/* CREATEPROCESS_MANIFEST_RESOURCE_ID */ x: 1); |
| 271 | if (NameIt == TypeNode->IDChildren.end()) |
| 272 | return; |
| 273 | |
| 274 | TreeNode *NameNode = NameIt->second.get(); |
| 275 | if (NameNode->IDChildren.size() <= 1) |
| 276 | return; // None or one manifest present, all good. |
| 277 | |
| 278 | // If we have more than one manifest, drop the language zero one if present, |
| 279 | // and check again. |
| 280 | auto LangZeroIt = NameNode->IDChildren.find(x: 0); |
| 281 | if (LangZeroIt != NameNode->IDChildren.end() && |
| 282 | LangZeroIt->second->IsDataNode) { |
| 283 | uint32_t RemovedIndex = LangZeroIt->second->DataIndex; |
| 284 | NameNode->IDChildren.erase(position: LangZeroIt); |
| 285 | Data.erase(position: Data.begin() + RemovedIndex); |
| 286 | Root.shiftDataIndexDown(Index: RemovedIndex); |
| 287 | |
| 288 | // If we're now down to one manifest, all is good. |
| 289 | if (NameNode->IDChildren.size() <= 1) |
| 290 | return; |
| 291 | } |
| 292 | |
| 293 | // More than one non-language-zero manifest |
| 294 | auto FirstIt = NameNode->IDChildren.begin(); |
| 295 | uint32_t FirstLang = FirstIt->first; |
| 296 | TreeNode *FirstNode = FirstIt->second.get(); |
| 297 | auto LastIt = NameNode->IDChildren.rbegin(); |
| 298 | uint32_t LastLang = LastIt->first; |
| 299 | TreeNode *LastNode = LastIt->second.get(); |
| 300 | Duplicates.push_back( |
| 301 | x: ("duplicate non-default manifests with languages " + Twine(FirstLang) + |
| 302 | " in " + InputFilenames[FirstNode->Origin] + " and " + Twine(LastLang) + |
| 303 | " in " + InputFilenames[LastNode->Origin]) |
| 304 | .str()); |
| 305 | } |
| 306 | |
| 307 | // Ignore duplicates of manifests with language zero (the default manifest), |
| 308 | // in case the user has provided a manifest with that language id. See |
| 309 | // the function comment above for context. Only returns true if MinGW is set |
| 310 | // to true. |
| 311 | bool WindowsResourceParser::shouldIgnoreDuplicate( |
| 312 | const ResourceEntryRef &Entry) const { |
| 313 | return MinGW && !Entry.checkTypeString() && |
| 314 | Entry.getTypeID() == /* RT_MANIFEST */ 24 && |
| 315 | !Entry.checkNameString() && |
| 316 | Entry.getNameID() == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && |
| 317 | Entry.getLanguage() == 0; |
| 318 | } |
| 319 | |
| 320 | bool WindowsResourceParser::shouldIgnoreDuplicate( |
| 321 | const std::vector<StringOrID> &Context) const { |
| 322 | return MinGW && Context.size() == 3 && !Context[0].IsString && |
| 323 | Context[0].ID == /* RT_MANIFEST */ 24 && !Context[1].IsString && |
| 324 | Context[1].ID == /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ 1 && |
| 325 | !Context[2].IsString && Context[2].ID == 0; |
| 326 | } |
| 327 | |
| 328 | Error WindowsResourceParser::parse(WindowsResource *WR, |
| 329 | std::vector<std::string> &Duplicates) { |
| 330 | auto EntryOrErr = WR->getHeadEntry(); |
| 331 | if (!EntryOrErr) { |
| 332 | auto E = EntryOrErr.takeError(); |
| 333 | if (E.isA<EmptyResError>()) { |
| 334 | // Check if the .res file contains no entries. In this case we don't have |
| 335 | // to throw an error but can rather just return without parsing anything. |
| 336 | // This applies for files which have a valid PE header magic and the |
| 337 | // mandatory empty null resource entry. Files which do not fit this |
| 338 | // criteria would have already been filtered out by |
| 339 | // WindowsResource::createWindowsResource(). |
| 340 | consumeError(Err: std::move(E)); |
| 341 | return Error::success(); |
| 342 | } |
| 343 | return E; |
| 344 | } |
| 345 | |
| 346 | ResourceEntryRef Entry = EntryOrErr.get(); |
| 347 | uint32_t Origin = InputFilenames.size(); |
| 348 | InputFilenames.push_back(x: std::string(WR->getFileName())); |
| 349 | bool End = false; |
| 350 | while (!End) { |
| 351 | |
| 352 | TreeNode *Node; |
| 353 | bool IsNewNode = Root.addEntry(Entry, Origin, Data, StringTable, Result&: Node); |
| 354 | if (!IsNewNode) { |
| 355 | if (!shouldIgnoreDuplicate(Entry)) |
| 356 | Duplicates.push_back(x: makeDuplicateResourceError( |
| 357 | Entry, File1: InputFilenames[Node->Origin], File2: WR->getFileName())); |
| 358 | } |
| 359 | |
| 360 | RETURN_IF_ERROR(Entry.moveNext(End)); |
| 361 | } |
| 362 | |
| 363 | return Error::success(); |
| 364 | } |
| 365 | |
| 366 | Error WindowsResourceParser::parse(ResourceSectionRef &RSR, StringRef Filename, |
| 367 | std::vector<std::string> &Duplicates) { |
| 368 | UNWRAP_REF_OR_RETURN(BaseTable, RSR.getBaseTable()); |
| 369 | uint32_t Origin = InputFilenames.size(); |
| 370 | InputFilenames.push_back(x: std::string(Filename)); |
| 371 | std::vector<StringOrID> Context; |
| 372 | return addChildren(Node&: Root, RSR, Table: BaseTable, Origin, Context, Duplicates); |
| 373 | } |
| 374 | |
| 375 | void WindowsResourceParser::printTree(raw_ostream &OS) const { |
| 376 | ScopedPrinter Writer(OS); |
| 377 | Root.print(Writer, Name: "Resource Tree" ); |
| 378 | } |
| 379 | |
| 380 | bool WindowsResourceParser::TreeNode::addEntry( |
| 381 | const ResourceEntryRef &Entry, uint32_t Origin, |
| 382 | std::vector<std::vector<uint8_t>> &Data, |
| 383 | std::vector<std::vector<UTF16>> &StringTable, TreeNode *&Result) { |
| 384 | TreeNode &TypeNode = addTypeNode(Entry, StringTable); |
| 385 | TreeNode &NameNode = TypeNode.addNameNode(Entry, StringTable); |
| 386 | return NameNode.addLanguageNode(Entry, Origin, Data, Result); |
| 387 | } |
| 388 | |
| 389 | Error WindowsResourceParser::addChildren(TreeNode &Node, |
| 390 | ResourceSectionRef &RSR, |
| 391 | const coff_resource_dir_table &Table, |
| 392 | uint32_t Origin, |
| 393 | std::vector<StringOrID> &Context, |
| 394 | std::vector<std::string> &Duplicates) { |
| 395 | |
| 396 | for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; |
| 397 | i++) { |
| 398 | UNWRAP_REF_OR_RETURN(Entry, RSR.getTableEntry(Table, i)); |
| 399 | TreeNode *Child; |
| 400 | |
| 401 | if (Entry.Offset.isSubDir()) { |
| 402 | |
| 403 | // Create a new subdirectory and recurse |
| 404 | if (i < Table.NumberOfNameEntries) { |
| 405 | UNWRAP_OR_RETURN(NameString, RSR.getEntryNameString(Entry)); |
| 406 | Child = &Node.addNameChild(NameRef: NameString, StringTable); |
| 407 | Context.push_back(x: StringOrID(NameString)); |
| 408 | } else { |
| 409 | Child = &Node.addIDChild(ID: Entry.Identifier.ID); |
| 410 | Context.push_back(x: StringOrID(Entry.Identifier.ID)); |
| 411 | } |
| 412 | |
| 413 | UNWRAP_REF_OR_RETURN(NextTable, RSR.getEntrySubDir(Entry)); |
| 414 | Error E = |
| 415 | addChildren(Node&: *Child, RSR, Table: NextTable, Origin, Context, Duplicates); |
| 416 | if (E) |
| 417 | return E; |
| 418 | Context.pop_back(); |
| 419 | |
| 420 | } else { |
| 421 | |
| 422 | // Data leaves are supposed to have a numeric ID as identifier (language). |
| 423 | if (Table.NumberOfNameEntries > 0) |
| 424 | return createStringError(EC: object_error::parse_failed, |
| 425 | S: "unexpected string key for data object" ); |
| 426 | |
| 427 | // Try adding a data leaf |
| 428 | UNWRAP_REF_OR_RETURN(DataEntry, RSR.getEntryData(Entry)); |
| 429 | TreeNode *Child; |
| 430 | Context.push_back(x: StringOrID(Entry.Identifier.ID)); |
| 431 | bool Added = Node.addDataChild(ID: Entry.Identifier.ID, MajorVersion: Table.MajorVersion, |
| 432 | MinorVersion: Table.MinorVersion, Characteristics: Table.Characteristics, |
| 433 | Origin, DataIndex: Data.size(), Result&: Child); |
| 434 | if (Added) { |
| 435 | UNWRAP_OR_RETURN(Contents, RSR.getContents(DataEntry)); |
| 436 | Data.push_back(x: ArrayRef<uint8_t>( |
| 437 | reinterpret_cast<const uint8_t *>(Contents.data()), |
| 438 | Contents.size())); |
| 439 | } else { |
| 440 | if (!shouldIgnoreDuplicate(Context)) |
| 441 | Duplicates.push_back(x: makeDuplicateResourceError( |
| 442 | Context, File1: InputFilenames[Child->Origin], File2: InputFilenames.back())); |
| 443 | } |
| 444 | Context.pop_back(); |
| 445 | |
| 446 | } |
| 447 | } |
| 448 | return Error::success(); |
| 449 | } |
| 450 | |
| 451 | WindowsResourceParser::TreeNode::TreeNode(uint32_t StringIndex) |
| 452 | : StringIndex(StringIndex) {} |
| 453 | |
| 454 | WindowsResourceParser::TreeNode::TreeNode(uint16_t MajorVersion, |
| 455 | uint16_t MinorVersion, |
| 456 | uint32_t Characteristics, |
| 457 | uint32_t Origin, uint32_t DataIndex) |
| 458 | : IsDataNode(true), DataIndex(DataIndex), MajorVersion(MajorVersion), |
| 459 | MinorVersion(MinorVersion), Characteristics(Characteristics), |
| 460 | Origin(Origin) {} |
| 461 | |
| 462 | std::unique_ptr<WindowsResourceParser::TreeNode> |
| 463 | WindowsResourceParser::TreeNode::createStringNode(uint32_t Index) { |
| 464 | return std::unique_ptr<TreeNode>(new TreeNode(Index)); |
| 465 | } |
| 466 | |
| 467 | std::unique_ptr<WindowsResourceParser::TreeNode> |
| 468 | WindowsResourceParser::TreeNode::createIDNode() { |
| 469 | return std::unique_ptr<TreeNode>(new TreeNode(0)); |
| 470 | } |
| 471 | |
| 472 | std::unique_ptr<WindowsResourceParser::TreeNode> |
| 473 | WindowsResourceParser::TreeNode::createDataNode(uint16_t MajorVersion, |
| 474 | uint16_t MinorVersion, |
| 475 | uint32_t Characteristics, |
| 476 | uint32_t Origin, |
| 477 | uint32_t DataIndex) { |
| 478 | return std::unique_ptr<TreeNode>(new TreeNode( |
| 479 | MajorVersion, MinorVersion, Characteristics, Origin, DataIndex)); |
| 480 | } |
| 481 | |
| 482 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addTypeNode( |
| 483 | const ResourceEntryRef &Entry, |
| 484 | std::vector<std::vector<UTF16>> &StringTable) { |
| 485 | if (Entry.checkTypeString()) |
| 486 | return addNameChild(NameRef: Entry.getTypeString(), StringTable); |
| 487 | else |
| 488 | return addIDChild(ID: Entry.getTypeID()); |
| 489 | } |
| 490 | |
| 491 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameNode( |
| 492 | const ResourceEntryRef &Entry, |
| 493 | std::vector<std::vector<UTF16>> &StringTable) { |
| 494 | if (Entry.checkNameString()) |
| 495 | return addNameChild(NameRef: Entry.getNameString(), StringTable); |
| 496 | else |
| 497 | return addIDChild(ID: Entry.getNameID()); |
| 498 | } |
| 499 | |
| 500 | bool WindowsResourceParser::TreeNode::addLanguageNode( |
| 501 | const ResourceEntryRef &Entry, uint32_t Origin, |
| 502 | std::vector<std::vector<uint8_t>> &Data, TreeNode *&Result) { |
| 503 | bool Added = addDataChild(ID: Entry.getLanguage(), MajorVersion: Entry.getMajorVersion(), |
| 504 | MinorVersion: Entry.getMinorVersion(), Characteristics: Entry.getCharacteristics(), |
| 505 | Origin, DataIndex: Data.size(), Result); |
| 506 | if (Added) |
| 507 | Data.push_back(x: Entry.getData()); |
| 508 | return Added; |
| 509 | } |
| 510 | |
| 511 | bool WindowsResourceParser::TreeNode::addDataChild( |
| 512 | uint32_t ID, uint16_t MajorVersion, uint16_t MinorVersion, |
| 513 | uint32_t Characteristics, uint32_t Origin, uint32_t DataIndex, |
| 514 | TreeNode *&Result) { |
| 515 | auto NewChild = createDataNode(MajorVersion, MinorVersion, Characteristics, |
| 516 | Origin, DataIndex); |
| 517 | auto ElementInserted = IDChildren.emplace(args&: ID, args: std::move(NewChild)); |
| 518 | Result = ElementInserted.first->second.get(); |
| 519 | return ElementInserted.second; |
| 520 | } |
| 521 | |
| 522 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addIDChild( |
| 523 | uint32_t ID) { |
| 524 | auto Child = IDChildren.find(x: ID); |
| 525 | if (Child == IDChildren.end()) { |
| 526 | auto NewChild = createIDNode(); |
| 527 | WindowsResourceParser::TreeNode &Node = *NewChild; |
| 528 | IDChildren.emplace(args&: ID, args: std::move(NewChild)); |
| 529 | return Node; |
| 530 | } else |
| 531 | return *(Child->second); |
| 532 | } |
| 533 | |
| 534 | WindowsResourceParser::TreeNode &WindowsResourceParser::TreeNode::addNameChild( |
| 535 | ArrayRef<UTF16> NameRef, std::vector<std::vector<UTF16>> &StringTable) { |
| 536 | std::string NameString; |
| 537 | convertUTF16LEToUTF8String(Src: NameRef, Out&: NameString); |
| 538 | |
| 539 | auto Child = StringChildren.find(x: NameString); |
| 540 | if (Child == StringChildren.end()) { |
| 541 | auto NewChild = createStringNode(Index: StringTable.size()); |
| 542 | StringTable.push_back(x: NameRef); |
| 543 | WindowsResourceParser::TreeNode &Node = *NewChild; |
| 544 | StringChildren.emplace(args&: NameString, args: std::move(NewChild)); |
| 545 | return Node; |
| 546 | } else |
| 547 | return *(Child->second); |
| 548 | } |
| 549 | |
| 550 | void WindowsResourceParser::TreeNode::print(ScopedPrinter &Writer, |
| 551 | StringRef Name) const { |
| 552 | ListScope NodeScope(Writer, Name); |
| 553 | for (auto const &Child : StringChildren) { |
| 554 | Child.second->print(Writer, Name: Child.first); |
| 555 | } |
| 556 | for (auto const &Child : IDChildren) { |
| 557 | Child.second->print(Writer, Name: to_string(Value: Child.first)); |
| 558 | } |
| 559 | } |
| 560 | |
| 561 | // This function returns the size of the entire resource tree, including |
| 562 | // directory tables, directory entries, and data entries. It does not include |
| 563 | // the directory strings or the relocations of the .rsrc section. |
| 564 | uint32_t WindowsResourceParser::TreeNode::getTreeSize() const { |
| 565 | uint32_t Size = (IDChildren.size() + StringChildren.size()) * |
| 566 | sizeof(coff_resource_dir_entry); |
| 567 | |
| 568 | // Reached a node pointing to a data entry. |
| 569 | if (IsDataNode) { |
| 570 | Size += sizeof(coff_resource_data_entry); |
| 571 | return Size; |
| 572 | } |
| 573 | |
| 574 | // If the node does not point to data, it must have a directory table pointing |
| 575 | // to other nodes. |
| 576 | Size += sizeof(coff_resource_dir_table); |
| 577 | |
| 578 | for (auto const &Child : StringChildren) { |
| 579 | Size += Child.second->getTreeSize(); |
| 580 | } |
| 581 | for (auto const &Child : IDChildren) { |
| 582 | Size += Child.second->getTreeSize(); |
| 583 | } |
| 584 | return Size; |
| 585 | } |
| 586 | |
| 587 | // Shift DataIndex of all data children with an Index greater or equal to the |
| 588 | // given one, to fill a gap from removing an entry from the Data vector. |
| 589 | void WindowsResourceParser::TreeNode::shiftDataIndexDown(uint32_t Index) { |
| 590 | if (IsDataNode && DataIndex >= Index) { |
| 591 | DataIndex--; |
| 592 | } else { |
| 593 | for (auto &Child : IDChildren) |
| 594 | Child.second->shiftDataIndexDown(Index); |
| 595 | for (auto &Child : StringChildren) |
| 596 | Child.second->shiftDataIndexDown(Index); |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | class WindowsResourceCOFFWriter { |
| 601 | public: |
| 602 | WindowsResourceCOFFWriter(COFF::MachineTypes MachineType, |
| 603 | const WindowsResourceParser &Parser, Error &E); |
| 604 | std::unique_ptr<MemoryBuffer> write(uint32_t TimeDateStamp); |
| 605 | |
| 606 | private: |
| 607 | void performFileLayout(); |
| 608 | void performSectionOneLayout(); |
| 609 | void performSectionTwoLayout(); |
| 610 | void writeCOFFHeader(uint32_t TimeDateStamp); |
| 611 | void writeFirstSectionHeader(); |
| 612 | void writeSecondSectionHeader(); |
| 613 | void writeFirstSection(); |
| 614 | void writeSecondSection(); |
| 615 | void writeSymbolTable(); |
| 616 | void writeStringTable(); |
| 617 | void writeDirectoryTree(); |
| 618 | void writeDirectoryStringTable(); |
| 619 | void writeFirstSectionRelocations(); |
| 620 | std::unique_ptr<WritableMemoryBuffer> OutputBuffer; |
| 621 | char *BufferStart; |
| 622 | uint64_t CurrentOffset = 0; |
| 623 | COFF::MachineTypes MachineType; |
| 624 | const WindowsResourceParser::TreeNode &Resources; |
| 625 | const ArrayRef<std::vector<uint8_t>> Data; |
| 626 | uint64_t FileSize; |
| 627 | uint32_t SymbolTableOffset; |
| 628 | uint32_t SectionOneSize; |
| 629 | uint32_t SectionOneOffset; |
| 630 | uint32_t SectionOneRelocations; |
| 631 | uint32_t SectionTwoSize; |
| 632 | uint32_t SectionTwoOffset; |
| 633 | const ArrayRef<std::vector<UTF16>> StringTable; |
| 634 | std::vector<uint32_t> StringTableOffsets; |
| 635 | std::vector<uint32_t> DataOffsets; |
| 636 | std::vector<uint32_t> RelocationAddresses; |
| 637 | }; |
| 638 | |
| 639 | WindowsResourceCOFFWriter::WindowsResourceCOFFWriter( |
| 640 | COFF::MachineTypes MachineType, const WindowsResourceParser &Parser, |
| 641 | Error &E) |
| 642 | : MachineType(MachineType), Resources(Parser.getTree()), |
| 643 | Data(Parser.getData()), StringTable(Parser.getStringTable()) { |
| 644 | performFileLayout(); |
| 645 | |
| 646 | OutputBuffer = WritableMemoryBuffer::getNewMemBuffer( |
| 647 | Size: FileSize, BufferName: "internal .obj file created from .res files" ); |
| 648 | } |
| 649 | |
| 650 | void WindowsResourceCOFFWriter::performFileLayout() { |
| 651 | // Add size of COFF header. |
| 652 | FileSize = COFF::Header16Size; |
| 653 | |
| 654 | // one .rsrc section header for directory tree, another for resource data. |
| 655 | FileSize += 2 * COFF::SectionSize; |
| 656 | |
| 657 | performSectionOneLayout(); |
| 658 | performSectionTwoLayout(); |
| 659 | |
| 660 | // We have reached the address of the symbol table. |
| 661 | SymbolTableOffset = FileSize; |
| 662 | |
| 663 | FileSize += COFF::Symbol16Size; // size of the @feat.00 symbol. |
| 664 | FileSize += 4 * COFF::Symbol16Size; // symbol + aux for each section. |
| 665 | FileSize += Data.size() * COFF::Symbol16Size; // 1 symbol per resource. |
| 666 | FileSize += 4; // four null bytes for the string table. |
| 667 | } |
| 668 | |
| 669 | void WindowsResourceCOFFWriter::performSectionOneLayout() { |
| 670 | SectionOneOffset = FileSize; |
| 671 | |
| 672 | SectionOneSize = Resources.getTreeSize(); |
| 673 | uint32_t CurrentStringOffset = SectionOneSize; |
| 674 | uint32_t TotalStringTableSize = 0; |
| 675 | for (auto const &String : StringTable) { |
| 676 | StringTableOffsets.push_back(x: CurrentStringOffset); |
| 677 | uint32_t StringSize = String.size() * sizeof(UTF16) + sizeof(uint16_t); |
| 678 | CurrentStringOffset += StringSize; |
| 679 | TotalStringTableSize += StringSize; |
| 680 | } |
| 681 | SectionOneSize += alignTo(Value: TotalStringTableSize, Align: sizeof(uint32_t)); |
| 682 | |
| 683 | // account for the relocations of section one. |
| 684 | SectionOneRelocations = FileSize + SectionOneSize; |
| 685 | FileSize += SectionOneSize; |
| 686 | FileSize += |
| 687 | Data.size() * COFF::RelocationSize; // one relocation for each resource. |
| 688 | FileSize = alignTo(Value: FileSize, Align: SECTION_ALIGNMENT); |
| 689 | } |
| 690 | |
| 691 | void WindowsResourceCOFFWriter::performSectionTwoLayout() { |
| 692 | // add size of .rsrc$2 section, which contains all resource data on 8-byte |
| 693 | // alignment. |
| 694 | SectionTwoOffset = FileSize; |
| 695 | SectionTwoSize = 0; |
| 696 | for (auto const &Entry : Data) { |
| 697 | DataOffsets.push_back(x: SectionTwoSize); |
| 698 | SectionTwoSize += alignTo(Value: Entry.size(), Align: sizeof(uint64_t)); |
| 699 | } |
| 700 | FileSize += SectionTwoSize; |
| 701 | FileSize = alignTo(Value: FileSize, Align: SECTION_ALIGNMENT); |
| 702 | } |
| 703 | |
| 704 | std::unique_ptr<MemoryBuffer> |
| 705 | WindowsResourceCOFFWriter::write(uint32_t TimeDateStamp) { |
| 706 | BufferStart = OutputBuffer->getBufferStart(); |
| 707 | |
| 708 | writeCOFFHeader(TimeDateStamp); |
| 709 | writeFirstSectionHeader(); |
| 710 | writeSecondSectionHeader(); |
| 711 | writeFirstSection(); |
| 712 | writeSecondSection(); |
| 713 | writeSymbolTable(); |
| 714 | writeStringTable(); |
| 715 | |
| 716 | return std::move(OutputBuffer); |
| 717 | } |
| 718 | |
| 719 | // According to COFF specification, if the Src has a size equal to Dest, |
| 720 | // it's okay to *not* copy the trailing zero. |
| 721 | static void coffnamecpy(char (&Dest)[COFF::NameSize], StringRef Src) { |
| 722 | assert(Src.size() <= COFF::NameSize && |
| 723 | "Src is larger than COFF::NameSize" ); |
| 724 | assert((Src.size() == COFF::NameSize || Dest[Src.size()] == '\0') && |
| 725 | "Dest not zeroed upon initialization" ); |
| 726 | memcpy(dest: Dest, src: Src.data(), n: Src.size()); |
| 727 | } |
| 728 | |
| 729 | void WindowsResourceCOFFWriter::(uint32_t TimeDateStamp) { |
| 730 | // Write the COFF header. |
| 731 | auto * = reinterpret_cast<coff_file_header *>(BufferStart); |
| 732 | Header->Machine = MachineType; |
| 733 | Header->NumberOfSections = 2; |
| 734 | Header->TimeDateStamp = TimeDateStamp; |
| 735 | Header->PointerToSymbolTable = SymbolTableOffset; |
| 736 | // One symbol for every resource plus 2 for each section and 1 for @feat.00 |
| 737 | Header->NumberOfSymbols = Data.size() + 5; |
| 738 | Header->SizeOfOptionalHeader = 0; |
| 739 | // cvtres.exe sets 32BIT_MACHINE even for 64-bit machine types. Match it. |
| 740 | Header->Characteristics = COFF::IMAGE_FILE_32BIT_MACHINE; |
| 741 | } |
| 742 | |
| 743 | void WindowsResourceCOFFWriter::() { |
| 744 | // Write the first section header. |
| 745 | CurrentOffset += sizeof(coff_file_header); |
| 746 | auto * = |
| 747 | reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); |
| 748 | coffnamecpy(Dest&: SectionOneHeader->Name, Src: ".rsrc$01" ); |
| 749 | SectionOneHeader->VirtualSize = 0; |
| 750 | SectionOneHeader->VirtualAddress = 0; |
| 751 | SectionOneHeader->SizeOfRawData = SectionOneSize; |
| 752 | SectionOneHeader->PointerToRawData = SectionOneOffset; |
| 753 | SectionOneHeader->PointerToRelocations = SectionOneRelocations; |
| 754 | SectionOneHeader->PointerToLinenumbers = 0; |
| 755 | SectionOneHeader->NumberOfRelocations = Data.size(); |
| 756 | SectionOneHeader->NumberOfLinenumbers = 0; |
| 757 | SectionOneHeader->Characteristics += COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; |
| 758 | SectionOneHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; |
| 759 | } |
| 760 | |
| 761 | void WindowsResourceCOFFWriter::() { |
| 762 | // Write the second section header. |
| 763 | CurrentOffset += sizeof(coff_section); |
| 764 | auto * = |
| 765 | reinterpret_cast<coff_section *>(BufferStart + CurrentOffset); |
| 766 | coffnamecpy(Dest&: SectionTwoHeader->Name, Src: ".rsrc$02" ); |
| 767 | SectionTwoHeader->VirtualSize = 0; |
| 768 | SectionTwoHeader->VirtualAddress = 0; |
| 769 | SectionTwoHeader->SizeOfRawData = SectionTwoSize; |
| 770 | SectionTwoHeader->PointerToRawData = SectionTwoOffset; |
| 771 | SectionTwoHeader->PointerToRelocations = 0; |
| 772 | SectionTwoHeader->PointerToLinenumbers = 0; |
| 773 | SectionTwoHeader->NumberOfRelocations = 0; |
| 774 | SectionTwoHeader->NumberOfLinenumbers = 0; |
| 775 | SectionTwoHeader->Characteristics = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; |
| 776 | SectionTwoHeader->Characteristics += COFF::IMAGE_SCN_MEM_READ; |
| 777 | } |
| 778 | |
| 779 | void WindowsResourceCOFFWriter::writeFirstSection() { |
| 780 | // Write section one. |
| 781 | CurrentOffset += sizeof(coff_section); |
| 782 | |
| 783 | writeDirectoryTree(); |
| 784 | writeDirectoryStringTable(); |
| 785 | writeFirstSectionRelocations(); |
| 786 | |
| 787 | CurrentOffset = alignTo(Value: CurrentOffset, Align: SECTION_ALIGNMENT); |
| 788 | } |
| 789 | |
| 790 | void WindowsResourceCOFFWriter::writeSecondSection() { |
| 791 | // Now write the .rsrc$02 section. |
| 792 | for (auto const &RawDataEntry : Data) { |
| 793 | llvm::copy(Range: RawDataEntry, Out: BufferStart + CurrentOffset); |
| 794 | CurrentOffset += alignTo(Value: RawDataEntry.size(), Align: sizeof(uint64_t)); |
| 795 | } |
| 796 | |
| 797 | CurrentOffset = alignTo(Value: CurrentOffset, Align: SECTION_ALIGNMENT); |
| 798 | } |
| 799 | |
| 800 | void WindowsResourceCOFFWriter::writeSymbolTable() { |
| 801 | // Now write the symbol table. |
| 802 | // First, the feat symbol. |
| 803 | auto *Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); |
| 804 | coffnamecpy(Dest&: Symbol->Name.ShortName, Src: "@feat.00" ); |
| 805 | Symbol->Value = 0x11; |
| 806 | Symbol->SectionNumber = 0xffff; |
| 807 | Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; |
| 808 | Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; |
| 809 | Symbol->NumberOfAuxSymbols = 0; |
| 810 | CurrentOffset += sizeof(coff_symbol16); |
| 811 | |
| 812 | // Now write the .rsrc1 symbol + aux. |
| 813 | Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); |
| 814 | coffnamecpy(Dest&: Symbol->Name.ShortName, Src: ".rsrc$01" ); |
| 815 | Symbol->Value = 0; |
| 816 | Symbol->SectionNumber = 1; |
| 817 | Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; |
| 818 | Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; |
| 819 | Symbol->NumberOfAuxSymbols = 1; |
| 820 | CurrentOffset += sizeof(coff_symbol16); |
| 821 | auto *Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + |
| 822 | CurrentOffset); |
| 823 | Aux->Length = SectionOneSize; |
| 824 | Aux->NumberOfRelocations = Data.size(); |
| 825 | Aux->NumberOfLinenumbers = 0; |
| 826 | Aux->CheckSum = 0; |
| 827 | Aux->NumberLowPart = 0; |
| 828 | Aux->Selection = 0; |
| 829 | CurrentOffset += sizeof(coff_aux_section_definition); |
| 830 | |
| 831 | // Now write the .rsrc2 symbol + aux. |
| 832 | Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); |
| 833 | coffnamecpy(Dest&: Symbol->Name.ShortName, Src: ".rsrc$02" ); |
| 834 | Symbol->Value = 0; |
| 835 | Symbol->SectionNumber = 2; |
| 836 | Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; |
| 837 | Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; |
| 838 | Symbol->NumberOfAuxSymbols = 1; |
| 839 | CurrentOffset += sizeof(coff_symbol16); |
| 840 | Aux = reinterpret_cast<coff_aux_section_definition *>(BufferStart + |
| 841 | CurrentOffset); |
| 842 | Aux->Length = SectionTwoSize; |
| 843 | Aux->NumberOfRelocations = 0; |
| 844 | Aux->NumberOfLinenumbers = 0; |
| 845 | Aux->CheckSum = 0; |
| 846 | Aux->NumberLowPart = 0; |
| 847 | Aux->Selection = 0; |
| 848 | CurrentOffset += sizeof(coff_aux_section_definition); |
| 849 | |
| 850 | // Now write a symbol for each relocation. |
| 851 | for (unsigned i = 0; i < Data.size(); i++) { |
| 852 | auto RelocationName = formatv(Fmt: "$R{0:X-6}" , Vals: i & 0xffffff).sstr<COFF::NameSize>(); |
| 853 | Symbol = reinterpret_cast<coff_symbol16 *>(BufferStart + CurrentOffset); |
| 854 | coffnamecpy(Dest&: Symbol->Name.ShortName, Src: RelocationName); |
| 855 | Symbol->Value = DataOffsets[i]; |
| 856 | Symbol->SectionNumber = 2; |
| 857 | Symbol->Type = COFF::IMAGE_SYM_DTYPE_NULL; |
| 858 | Symbol->StorageClass = COFF::IMAGE_SYM_CLASS_STATIC; |
| 859 | Symbol->NumberOfAuxSymbols = 0; |
| 860 | CurrentOffset += sizeof(coff_symbol16); |
| 861 | } |
| 862 | } |
| 863 | |
| 864 | void WindowsResourceCOFFWriter::writeStringTable() { |
| 865 | // Just 4 null bytes for the string table. |
| 866 | auto COFFStringTable = reinterpret_cast<void *>(BufferStart + CurrentOffset); |
| 867 | memset(s: COFFStringTable, c: 0, n: 4); |
| 868 | } |
| 869 | |
| 870 | void WindowsResourceCOFFWriter::writeDirectoryTree() { |
| 871 | // Traverse parsed resource tree breadth-first and write the corresponding |
| 872 | // COFF objects. |
| 873 | std::queue<const WindowsResourceParser::TreeNode *> Queue; |
| 874 | Queue.push(x: &Resources); |
| 875 | uint32_t NextLevelOffset = |
| 876 | sizeof(coff_resource_dir_table) + (Resources.getStringChildren().size() + |
| 877 | Resources.getIDChildren().size()) * |
| 878 | sizeof(coff_resource_dir_entry); |
| 879 | std::vector<const WindowsResourceParser::TreeNode *> DataEntriesTreeOrder; |
| 880 | uint32_t CurrentRelativeOffset = 0; |
| 881 | |
| 882 | while (!Queue.empty()) { |
| 883 | auto CurrentNode = Queue.front(); |
| 884 | Queue.pop(); |
| 885 | auto *Table = reinterpret_cast<coff_resource_dir_table *>(BufferStart + |
| 886 | CurrentOffset); |
| 887 | Table->Characteristics = CurrentNode->getCharacteristics(); |
| 888 | Table->TimeDateStamp = 0; |
| 889 | Table->MajorVersion = CurrentNode->getMajorVersion(); |
| 890 | Table->MinorVersion = CurrentNode->getMinorVersion(); |
| 891 | auto &IDChildren = CurrentNode->getIDChildren(); |
| 892 | auto &StringChildren = CurrentNode->getStringChildren(); |
| 893 | Table->NumberOfNameEntries = StringChildren.size(); |
| 894 | Table->NumberOfIDEntries = IDChildren.size(); |
| 895 | CurrentOffset += sizeof(coff_resource_dir_table); |
| 896 | CurrentRelativeOffset += sizeof(coff_resource_dir_table); |
| 897 | |
| 898 | // Write the directory entries immediately following each directory table. |
| 899 | for (auto const &Child : StringChildren) { |
| 900 | auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + |
| 901 | CurrentOffset); |
| 902 | Entry->Identifier.setNameOffset( |
| 903 | StringTableOffsets[Child.second->getStringIndex()]); |
| 904 | if (Child.second->checkIsDataNode()) { |
| 905 | Entry->Offset.DataEntryOffset = NextLevelOffset; |
| 906 | NextLevelOffset += sizeof(coff_resource_data_entry); |
| 907 | DataEntriesTreeOrder.push_back(x: Child.second.get()); |
| 908 | } else { |
| 909 | Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); |
| 910 | NextLevelOffset += sizeof(coff_resource_dir_table) + |
| 911 | (Child.second->getStringChildren().size() + |
| 912 | Child.second->getIDChildren().size()) * |
| 913 | sizeof(coff_resource_dir_entry); |
| 914 | Queue.push(x: Child.second.get()); |
| 915 | } |
| 916 | CurrentOffset += sizeof(coff_resource_dir_entry); |
| 917 | CurrentRelativeOffset += sizeof(coff_resource_dir_entry); |
| 918 | } |
| 919 | for (auto const &Child : IDChildren) { |
| 920 | auto *Entry = reinterpret_cast<coff_resource_dir_entry *>(BufferStart + |
| 921 | CurrentOffset); |
| 922 | Entry->Identifier.ID = Child.first; |
| 923 | if (Child.second->checkIsDataNode()) { |
| 924 | Entry->Offset.DataEntryOffset = NextLevelOffset; |
| 925 | NextLevelOffset += sizeof(coff_resource_data_entry); |
| 926 | DataEntriesTreeOrder.push_back(x: Child.second.get()); |
| 927 | } else { |
| 928 | Entry->Offset.SubdirOffset = NextLevelOffset + (1 << 31); |
| 929 | NextLevelOffset += sizeof(coff_resource_dir_table) + |
| 930 | (Child.second->getStringChildren().size() + |
| 931 | Child.second->getIDChildren().size()) * |
| 932 | sizeof(coff_resource_dir_entry); |
| 933 | Queue.push(x: Child.second.get()); |
| 934 | } |
| 935 | CurrentOffset += sizeof(coff_resource_dir_entry); |
| 936 | CurrentRelativeOffset += sizeof(coff_resource_dir_entry); |
| 937 | } |
| 938 | } |
| 939 | |
| 940 | RelocationAddresses.resize(new_size: Data.size()); |
| 941 | // Now write all the resource data entries. |
| 942 | for (const auto *DataNodes : DataEntriesTreeOrder) { |
| 943 | auto *Entry = reinterpret_cast<coff_resource_data_entry *>(BufferStart + |
| 944 | CurrentOffset); |
| 945 | RelocationAddresses[DataNodes->getDataIndex()] = CurrentRelativeOffset; |
| 946 | Entry->DataRVA = 0; // Set to zero because it is a relocation. |
| 947 | Entry->DataSize = Data[DataNodes->getDataIndex()].size(); |
| 948 | Entry->Codepage = 0; |
| 949 | Entry->Reserved = 0; |
| 950 | CurrentOffset += sizeof(coff_resource_data_entry); |
| 951 | CurrentRelativeOffset += sizeof(coff_resource_data_entry); |
| 952 | } |
| 953 | } |
| 954 | |
| 955 | void WindowsResourceCOFFWriter::writeDirectoryStringTable() { |
| 956 | // Now write the directory string table for .rsrc$01 |
| 957 | uint32_t TotalStringTableSize = 0; |
| 958 | for (auto &String : StringTable) { |
| 959 | uint16_t Length = String.size(); |
| 960 | support::endian::write16le(P: BufferStart + CurrentOffset, V: Length); |
| 961 | CurrentOffset += sizeof(uint16_t); |
| 962 | auto *Start = reinterpret_cast<UTF16 *>(BufferStart + CurrentOffset); |
| 963 | llvm::copy(Range: String, Out: Start); |
| 964 | CurrentOffset += Length * sizeof(UTF16); |
| 965 | TotalStringTableSize += Length * sizeof(UTF16) + sizeof(uint16_t); |
| 966 | } |
| 967 | CurrentOffset += |
| 968 | alignTo(Value: TotalStringTableSize, Align: sizeof(uint32_t)) - TotalStringTableSize; |
| 969 | } |
| 970 | |
| 971 | void WindowsResourceCOFFWriter::writeFirstSectionRelocations() { |
| 972 | |
| 973 | // Now write the relocations for .rsrc$01 |
| 974 | // Five symbols already in table before we start, @feat.00 and 2 for each |
| 975 | // .rsrc section. |
| 976 | uint32_t NextSymbolIndex = 5; |
| 977 | for (unsigned i = 0; i < Data.size(); i++) { |
| 978 | auto *Reloc = |
| 979 | reinterpret_cast<coff_relocation *>(BufferStart + CurrentOffset); |
| 980 | Reloc->VirtualAddress = RelocationAddresses[i]; |
| 981 | Reloc->SymbolTableIndex = NextSymbolIndex++; |
| 982 | switch (getMachineArchType(machine: MachineType)) { |
| 983 | case Triple::thumb: |
| 984 | Reloc->Type = COFF::IMAGE_REL_ARM_ADDR32NB; |
| 985 | break; |
| 986 | case Triple::x86_64: |
| 987 | Reloc->Type = COFF::IMAGE_REL_AMD64_ADDR32NB; |
| 988 | break; |
| 989 | case Triple::x86: |
| 990 | Reloc->Type = COFF::IMAGE_REL_I386_DIR32NB; |
| 991 | break; |
| 992 | case Triple::aarch64: |
| 993 | Reloc->Type = COFF::IMAGE_REL_ARM64_ADDR32NB; |
| 994 | break; |
| 995 | default: |
| 996 | llvm_unreachable("unknown machine type" ); |
| 997 | } |
| 998 | CurrentOffset += sizeof(coff_relocation); |
| 999 | } |
| 1000 | } |
| 1001 | |
| 1002 | Expected<std::unique_ptr<MemoryBuffer>> |
| 1003 | writeWindowsResourceCOFF(COFF::MachineTypes MachineType, |
| 1004 | const WindowsResourceParser &Parser, |
| 1005 | uint32_t TimeDateStamp) { |
| 1006 | Error E = Error::success(); |
| 1007 | WindowsResourceCOFFWriter Writer(MachineType, Parser, E); |
| 1008 | if (E) |
| 1009 | return E; |
| 1010 | return Writer.write(TimeDateStamp); |
| 1011 | } |
| 1012 | |
| 1013 | } // namespace object |
| 1014 | } // namespace llvm |
| 1015 | |