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 | |