Skip to content

Commit c8dba24

Browse files
author
Eric Beckmann
committed
Revert "Revert "Revert "Revert "Switch external cvtres.exe for llvm's own resource library.""""
This reverts commit 147f45f. Revert "Revert "Revert "Revert "Replace trivial use of external rc.exe by writing our own .res file."""" This reverts commit 61a90a6. The patches were intially reverted because they were causing a failure on CrWinClangLLD. Unfortunately, this was done haphazardly and didn't compile, so the revert was reverted again quickly to fix this. One that was done, the revert of the revert was itself reverted. This allowed me to finally fix the actual bug in r307452. This patch re-enables the code path that had originally been causing the bug, now that it (should) be fixed. llvm-svn: 307460
1 parent 41f6a45 commit c8dba24

File tree

16 files changed

+159
-127
lines changed

16 files changed

+159
-127
lines changed

lld/COFF/DriverUtils.cpp

Lines changed: 71 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@
2020
#include "Symbols.h"
2121
#include "llvm/ADT/Optional.h"
2222
#include "llvm/ADT/StringSwitch.h"
23+
#include "llvm/BinaryFormat/COFF.h"
2324
#include "llvm/Object/COFF.h"
25+
#include "llvm/Object/WindowsResource.h"
2426
#include "llvm/Option/Arg.h"
2527
#include "llvm/Option/ArgList.h"
2628
#include "llvm/Option/Option.h"
2729
#include "llvm/Support/CommandLine.h"
2830
#include "llvm/Support/FileUtilities.h"
31+
#include "llvm/Support/MathExtras.h"
2932
#include "llvm/Support/Process.h"
3033
#include "llvm/Support/Program.h"
3134
#include "llvm/Support/raw_ostream.h"
@@ -41,6 +44,9 @@ namespace lld {
4144
namespace coff {
4245
namespace {
4346

47+
const uint16_t SUBLANG_ENGLISH_US = 0x0409;
48+
const uint16_t RT_MANIFEST = 24;
49+
4450
class Executor {
4551
public:
4652
explicit Executor(StringRef S) : Prog(Saver.save(S)) {}
@@ -260,26 +266,6 @@ void parseManifestUAC(StringRef Arg) {
260266
}
261267
}
262268

263-
// Quote each line with "". Existing double-quote is converted
264-
// to two double-quotes.
265-
static void quoteAndPrint(raw_ostream &Out, StringRef S) {
266-
while (!S.empty()) {
267-
StringRef Line;
268-
std::tie(Line, S) = S.split("\n");
269-
if (Line.empty())
270-
continue;
271-
Out << '\"';
272-
for (int I = 0, E = Line.size(); I != E; ++I) {
273-
if (Line[I] == '\"') {
274-
Out << "\"\"";
275-
} else {
276-
Out << Line[I];
277-
}
278-
}
279-
Out << "\"\n";
280-
}
281-
}
282-
283269
// An RAII temporary file class that automatically removes a temporary file.
284270
namespace {
285271
class TemporaryFile {
@@ -393,38 +379,62 @@ static std::string createManifestXml() {
393379
return readFile(File2.Path);
394380
}
395381

382+
static std::unique_ptr<MemoryBuffer>
383+
createMemoryBufferForManifestRes(size_t ManifestSize) {
384+
size_t ResSize = alignTo(
385+
object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
386+
sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
387+
sizeof(object::WinResHeaderSuffix) + ManifestSize,
388+
object::WIN_RES_DATA_ALIGNMENT);
389+
return MemoryBuffer::getNewMemBuffer(ResSize);
390+
}
391+
392+
static void writeResFileHeader(char *&Buf) {
393+
memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
394+
Buf += sizeof(COFF::WinResMagic);
395+
memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
396+
Buf += object::WIN_RES_NULL_ENTRY_SIZE;
397+
}
398+
399+
static void writeResEntryHeader(char *&Buf, size_t ManifestSize) {
400+
// Write the prefix.
401+
auto *Prefix = reinterpret_cast<object::WinResHeaderPrefix *>(Buf);
402+
Prefix->DataSize = ManifestSize;
403+
Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
404+
sizeof(object::WinResIDs) +
405+
sizeof(object::WinResHeaderSuffix);
406+
Buf += sizeof(object::WinResHeaderPrefix);
407+
408+
// Write the Type/Name IDs.
409+
auto *IDs = reinterpret_cast<object::WinResIDs *>(Buf);
410+
IDs->setType(RT_MANIFEST);
411+
IDs->setName(Config->ManifestID);
412+
Buf += sizeof(object::WinResIDs);
413+
414+
// Write the suffix.
415+
auto *Suffix = reinterpret_cast<object::WinResHeaderSuffix *>(Buf);
416+
Suffix->DataVersion = 0;
417+
Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
418+
Suffix->Language = SUBLANG_ENGLISH_US;
419+
Suffix->Version = 0;
420+
Suffix->Characteristics = 0;
421+
Buf += sizeof(object::WinResHeaderSuffix);
422+
}
423+
396424
// Create a resource file containing a manifest XML.
397425
std::unique_ptr<MemoryBuffer> createManifestRes() {
398-
// Create a temporary file for the resource script file.
399-
TemporaryFile RCFile("manifest", "rc");
426+
std::string Manifest = createManifestXml();
400427

401-
// Open the temporary file for writing.
402-
std::error_code EC;
403-
raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text);
404-
if (EC)
405-
fatal(EC, "failed to open " + RCFile.Path);
406-
407-
// Write resource script to the RC file.
408-
Out << "#define LANG_ENGLISH 9\n"
409-
<< "#define SUBLANG_DEFAULT 1\n"
410-
<< "#define APP_MANIFEST " << Config->ManifestID << "\n"
411-
<< "#define RT_MANIFEST 24\n"
412-
<< "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"
413-
<< "APP_MANIFEST RT_MANIFEST {\n";
414-
quoteAndPrint(Out, createManifestXml());
415-
Out << "}\n";
416-
Out.close();
417-
418-
// Create output resource file.
419-
TemporaryFile ResFile("output-resource", "res");
420-
421-
Executor E("rc.exe");
422-
E.add("/fo");
423-
E.add(ResFile.Path);
424-
E.add("/nologo");
425-
E.add(RCFile.Path);
426-
E.run();
427-
return ResFile.getMemoryBuffer();
428+
std::unique_ptr<MemoryBuffer> Res =
429+
createMemoryBufferForManifestRes(Manifest.size());
430+
431+
char *Buf = const_cast<char *>(Res->getBufferStart());
432+
writeResFileHeader(Buf);
433+
writeResEntryHeader(Buf, Manifest.size());
434+
435+
// Copy the manifest data into the .res file.
436+
std::copy(Manifest.begin(), Manifest.end(), Buf);
437+
return Res;
428438
}
429439

430440
void createSideBySideManifest() {
@@ -595,40 +605,22 @@ void checkFailIfMismatch(StringRef Arg) {
595605
// using cvtres.exe.
596606
std::unique_ptr<MemoryBuffer>
597607
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
598-
// Create an output file path.
599-
TemporaryFile File("resource-file", "obj");
608+
object::WindowsResourceParser Parser;
600609

601-
// Execute cvtres.exe.
602-
Executor E("cvtres.exe");
603-
E.add("/machine:" + machineToStr(Config->Machine));
604-
E.add("/readonly");
605-
E.add("/nologo");
606-
E.add("/out:" + Twine(File.Path));
607-
608-
// We must create new files because the memory buffers we have may have no
609-
// underlying file still existing on the disk.
610-
// It happens if it was created from a TemporaryFile, which usually delete
611-
// the file just after creating the MemoryBuffer.
612-
std::vector<TemporaryFile> ResFiles;
613-
ResFiles.reserve(MBs.size());
614610
for (MemoryBufferRef MB : MBs) {
615-
// We store the temporary file in a vector to avoid deletion
616-
// before running cvtres
617-
ResFiles.emplace_back("resource-file", "res");
618-
TemporaryFile& ResFile = ResFiles.back();
619-
// Write the content of the resource in a temporary file
620-
std::error_code EC;
621-
raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None);
622-
if (EC)
623-
fatal(EC, "failed to open " + ResFile.Path);
624-
OS << MB.getBuffer();
625-
OS.close();
626-
627-
E.add(ResFile.Path);
611+
std::unique_ptr<object::Binary> Bin = check(object::createBinary(MB));
612+
object::WindowsResource *RF = dyn_cast<object::WindowsResource>(Bin.get());
613+
if (!RF)
614+
fatal("cannot compile non-resource file as resource");
615+
if (auto EC = Parser.parse(RF))
616+
fatal(EC, "failed to parse .res file");
628617
}
629618

630-
E.run();
631-
return File.getMemoryBuffer();
619+
Expected<std::unique_ptr<MemoryBuffer>> E =
620+
llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
621+
if (!E)
622+
fatal(errorToErrorCode(E.takeError()), "failed to write .res to COFF");
623+
return std::move(E.get());
632624
}
633625

634626
// Run MSVC link.exe for given in-memory object files.

lld/test/COFF/combined-resources.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
// > rc /fo combined-resources.res /nologo combined-resources.rc
55
// > rc /fo combined-resources-2.res /nologo combined-resources-2.rc
66

7-
# REQUIRES: winres
8-
97
# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
108
# RUN: lld-link /out:%t.exe /entry:main %t.obj %p/Inputs/resource.res \
119
# RUN: %p/Inputs/combined-resources.res %p/Inputs/combined-resources-2.res

lld/test/COFF/def-name.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# REQUIRES: winres
2-
31
# RUN: rm -rf %t
42
# RUN: mkdir -p %t
53
# RUN: cd %t

lld/test/COFF/dll.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# REQUIRES: winres
2-
31
# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
42
# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2 \
53
# RUN: /export:mangled

lld/test/COFF/dllimport-gc.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# REQUIRES: winres
2-
31
# RUN: yaml2obj < %p/Inputs/export.yaml > %t-lib.obj
42
# RUN: lld-link /out:%t.dll /dll %t-lib.obj /implib:%t.lib /export:exportfn1
53

lld/test/COFF/manifestinput.test

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# REQUIRES: winres
1+
# REQUIRES: win_mt
22

33
# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
44
# RUN: lld-link /out:%t.exe /entry:main \
@@ -8,3 +8,28 @@
88

99
CHECK: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1010
CHECK: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity></dependentAssembly></dependency><trustInfo><security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security></trustInfo></assembly>
11+
12+
# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
13+
# RUN: lld-link /out:%t.exe /entry:main \
14+
# RUN: /manifest:embed \
15+
# RUN: /manifestuac:"level='requireAdministrator'" \
16+
# RUN: /manifestinput:%p/Inputs/manifestinput.test %t.obj
17+
# RUN: llvm-readobj -coff-resources -file-headers %t.exe | FileCheck %s \
18+
# RUN: -check-prefix TEST_EMBED
19+
20+
TEST_EMBED: ResourceTableRVA: 0x1000
21+
TEST_EMBED-NEXT: ResourceTableSize: 0x298
22+
TEST_EMBED-DAG: Resources [
23+
TEST_EMBED-NEXT: Total Number of Resources: 1
24+
TEST_EMBED-DAG: Number of String Entries: 0
25+
TEST_EMBED-NEXT: Number of ID Entries: 1
26+
TEST_EMBED-NEXT: Type: kRT_MANIFEST (ID 24) [
27+
TEST_EMBED-NEXT: Table Offset: 0x18
28+
TEST_EMBED-NEXT: Number of String Entries: 0
29+
TEST_EMBED-NEXT: Number of ID Entries: 1
30+
TEST_EMBED-NEXT: Name: (ID 1) [
31+
TEST_EMBED-NEXT: Table Offset: 0x30
32+
TEST_EMBED-NEXT: Number of String Entries: 0
33+
TEST_EMBED-NEXT: Number of ID Entries: 1
34+
TEST_EMBED-NEXT: Language: (ID 1033) [
35+
TEST_EMBED-NEXT: Entry Offset: 0x48

lld/test/COFF/noentry.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# REQUIRES: winres
2-
31
# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
42
# RUN: lld-link /out:%t.dll /dll %t.obj
53
# RUN: llvm-readobj -file-headers %t.dll | FileCheck -check-prefix=ENTRY %s

lld/test/COFF/out.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# REQUIRES: winres
21
# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
32

43
# RUN: mkdir -p %T/out/tmp

lld/test/COFF/resource.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# REQUIRES: winres
2-
31
# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
42
# RUN: lld-link /out:%t.exe /entry:main %t.obj %p/Inputs/resource.res
53

lld/test/lit.cfg

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,7 @@ llvm_config_cmd.wait()
264264
# Set a fake constant version so that we get consitent output.
265265
config.environment['LLD_VERSION'] = 'LLD 1.0'
266266

267-
# Check if Windows resource file compiler exists.
268-
cvtres = lit.util.which('cvtres', config.environment['PATH'])
269-
rc = lit.util.which('rc', config.environment['PATH'])
270-
if cvtres and rc:
271-
config.available_features.add('winres')
267+
# Indirectly check if the mt.exe Microsoft utility exists by searching for
268+
# cvtres, which always accompanies it.
269+
if lit.util.which('cvtres', config.environment['PATH']):
270+
config.available_features.add('win_mt')

llvm/include/llvm/BinaryFormat/COFF.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ static const char ClGlObjMagic[] = {
4646
'\xac', '\x9b', '\xd6', '\xb6', '\x22', '\x26', '\x53', '\xc2',
4747
};
4848

49+
// The signature bytes that start a .res file.
50+
static const char WinResMagic[] = {
51+
'\x00', '\x00', '\x00', '\x00', '\x20', '\x00', '\x00', '\x00',
52+
'\xff', '\xff', '\x00', '\x00', '\xff', '\xff', '\x00', '\x00',
53+
};
54+
4955
// Sizes in bytes of various things in the COFF format.
5056
enum {
5157
Header16Size = 20,

llvm/include/llvm/Object/WindowsResource.h

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,47 @@
4343
#include <map>
4444

4545
namespace llvm {
46-
4746
namespace object {
4847

4948
class WindowsResource;
5049

51-
enum class Machine { UNKNOWN, ARM, X64, X86 };
50+
const size_t WIN_RES_MAGIC_SIZE = 16;
51+
const size_t WIN_RES_NULL_ENTRY_SIZE = 16;
52+
const uint32_t WIN_RES_HEADER_ALIGNMENT = 4;
53+
const uint32_t WIN_RES_DATA_ALIGNMENT = 4;
54+
const uint16_t WIN_RES_PURE_MOVEABLE = 0x0030;
55+
56+
struct WinResHeaderPrefix {
57+
support::ulittle32_t DataSize;
58+
support::ulittle32_t HeaderSize;
59+
};
60+
61+
// Type and Name may each either be an integer ID or a string. This struct is
62+
// only used in the case where they are both IDs.
63+
struct WinResIDs {
64+
uint16_t TypeFlag;
65+
support::ulittle16_t TypeID;
66+
uint16_t NameFlag;
67+
support::ulittle16_t NameID;
68+
69+
void setType(uint16_t ID) {
70+
TypeFlag = 0xffff;
71+
TypeID = ID;
72+
}
73+
74+
void setName(uint16_t ID) {
75+
NameFlag = 0xffff;
76+
NameID = ID;
77+
}
78+
};
79+
80+
struct WinResHeaderSuffix {
81+
support::ulittle32_t DataVersion;
82+
support::ulittle16_t MemoryFlags;
83+
support::ulittle16_t Language;
84+
support::ulittle32_t Version;
85+
support::ulittle32_t Characteristics;
86+
};
5287

5388
class ResourceEntryRef {
5489
public:
@@ -73,22 +108,14 @@ class ResourceEntryRef {
73108

74109
Error loadNext();
75110

76-
struct HeaderSuffix {
77-
support::ulittle32_t DataVersion;
78-
support::ulittle16_t MemoryFlags;
79-
support::ulittle16_t Language;
80-
support::ulittle32_t Version;
81-
support::ulittle32_t Characteristics;
82-
};
83-
84111
BinaryStreamReader Reader;
85112
bool IsStringType;
86113
ArrayRef<UTF16> Type;
87114
uint16_t TypeID;
88115
bool IsStringName;
89116
ArrayRef<UTF16> Name;
90117
uint16_t NameID;
91-
const HeaderSuffix *Suffix = nullptr;
118+
const WinResHeaderSuffix *Suffix = nullptr;
92119
ArrayRef<uint8_t> Data;
93120
const WindowsResource *OwningRes = nullptr;
94121
};

llvm/lib/BinaryFormat/Magic.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ file_magic llvm::identify_magic(StringRef Magic) {
5151
return file_magic::coff_import_library;
5252
}
5353
// Windows resource file
54-
if (startswith(Magic, "\0\0\0\0\x20\0\0\0\xFF"))
54+
if (Magic.size() >= sizeof(COFF::WinResMagic) &&
55+
memcmp(Magic.data(), COFF::WinResMagic, sizeof(COFF::WinResMagic)) == 0)
5556
return file_magic::windows_resource;
5657
// 0x0000 = COFF unknown machine type
5758
if (Magic[1] == 0)

0 commit comments

Comments
 (0)