Skip to content

Commit 64a362e

Browse files
committed
[llvm-lib] Correctly handle .lib input files
If archive files are passed as input files, llvm-lib needs to append the members of the input archive files to the output file. This patch implements that behavior. This patch splits an existing function into smaller functions. Effectively, the new code is only `if (Magic == file_magic::archive) { ... }` part. Fixes https://bugs.llvm.org/show_bug.cgi?id=32674 Differential Revision: https://reviews.llvm.org/D68204 llvm-svn: 373424
1 parent 8d6a863 commit 64a362e

File tree

3 files changed

+148
-89
lines changed

3 files changed

+148
-89
lines changed

llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp

Lines changed: 132 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,125 @@ static void doList(opt::InputArgList& Args) {
141141
fatalOpenError(std::move(Err), B->getBufferIdentifier());
142142
}
143143

144+
static COFF::MachineTypes getCOFFFileMachine(MemoryBufferRef MB) {
145+
std::error_code EC;
146+
object::COFFObjectFile Obj(MB, EC);
147+
if (EC) {
148+
llvm::errs() << MB.getBufferIdentifier()
149+
<< ": failed to open: " << EC.message() << '\n';
150+
exit(1);
151+
}
152+
153+
uint16_t Machine = Obj.getMachine();
154+
if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&
155+
Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&
156+
Machine != COFF::IMAGE_FILE_MACHINE_ARMNT &&
157+
Machine != COFF::IMAGE_FILE_MACHINE_ARM64) {
158+
llvm::errs() << MB.getBufferIdentifier() << ": unknown machine: " << Machine
159+
<< '\n';
160+
exit(1);
161+
}
162+
163+
return static_cast<COFF::MachineTypes>(Machine);
164+
}
165+
166+
static COFF::MachineTypes getBitcodeFileMachine(MemoryBufferRef MB) {
167+
Expected<std::string> TripleStr = getBitcodeTargetTriple(MB);
168+
if (!TripleStr) {
169+
llvm::errs() << MB.getBufferIdentifier()
170+
<< ": failed to get target triple from bitcode\n";
171+
exit(1);
172+
}
173+
174+
switch (Triple(*TripleStr).getArch()) {
175+
case Triple::x86:
176+
return COFF::IMAGE_FILE_MACHINE_I386;
177+
case Triple::x86_64:
178+
return COFF::IMAGE_FILE_MACHINE_AMD64;
179+
case Triple::arm:
180+
return COFF::IMAGE_FILE_MACHINE_ARMNT;
181+
case Triple::aarch64:
182+
return COFF::IMAGE_FILE_MACHINE_ARM64;
183+
default:
184+
llvm::errs() << MB.getBufferIdentifier()
185+
<< ": unknown arch in target triple " << *TripleStr << '\n';
186+
exit(1);
187+
}
188+
}
189+
190+
static void appendFile(std::vector<NewArchiveMember> &Members,
191+
COFF::MachineTypes &LibMachine,
192+
std::string &LibMachineSource, MemoryBufferRef MB) {
193+
file_magic Magic = identify_magic(MB.getBuffer());
194+
195+
if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&
196+
Magic != file_magic::archive && Magic != file_magic::windows_resource) {
197+
llvm::errs() << MB.getBufferIdentifier()
198+
<< ": not a COFF object, bitcode, archive or resource file\n";
199+
exit(1);
200+
}
201+
202+
// If a user attempts to add an archive to another archive, llvm-lib doesn't
203+
// handle the first archive file as a single file. Instead, it extracts all
204+
// members from the archive and add them to the second archive. This beahvior
205+
// is for compatibility with Microsoft's lib command.
206+
if (Magic == file_magic::archive) {
207+
Error Err = Error::success();
208+
object::Archive Archive(MB, Err);
209+
fatalOpenError(std::move(Err), MB.getBufferIdentifier());
210+
211+
for (auto &C : Archive.children(Err)) {
212+
Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();
213+
if (!ChildMB) {
214+
handleAllErrors(ChildMB.takeError(), [&](const ErrorInfoBase &EIB) {
215+
llvm::errs() << MB.getBufferIdentifier() << ": " << EIB.message()
216+
<< "\n";
217+
});
218+
exit(1);
219+
}
220+
221+
appendFile(Members, LibMachine, LibMachineSource, *ChildMB);
222+
}
223+
224+
fatalOpenError(std::move(Err), MB.getBufferIdentifier());
225+
return;
226+
}
227+
228+
// Check that all input files have the same machine type.
229+
// Mixing normal objects and LTO bitcode files is fine as long as they
230+
// have the same machine type.
231+
// Doing this here duplicates the header parsing work that writeArchive()
232+
// below does, but it's not a lot of work and it's a bit awkward to do
233+
// in writeArchive() which needs to support many tools, can't assume the
234+
// input is COFF, and doesn't have a good way to report errors.
235+
if (Magic == file_magic::coff_object || Magic == file_magic::bitcode) {
236+
COFF::MachineTypes FileMachine = (Magic == file_magic::coff_object)
237+
? getCOFFFileMachine(MB)
238+
: getBitcodeFileMachine(MB);
239+
240+
// FIXME: Once lld-link rejects multiple resource .obj files:
241+
// Call convertResToCOFF() on .res files and add the resulting
242+
// COFF file to the .lib output instead of adding the .res file, and remove
243+
// this check. See PR42180.
244+
if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
245+
if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
246+
LibMachine = FileMachine;
247+
LibMachineSource =
248+
(" (inferred from earlier file '" + MB.getBufferIdentifier() + "')")
249+
.str();
250+
} else if (LibMachine != FileMachine) {
251+
llvm::errs() << MB.getBufferIdentifier() << ": file machine type "
252+
<< machineToStr(FileMachine)
253+
<< " conflicts with library machine type "
254+
<< machineToStr(LibMachine) << LibMachineSource << '\n';
255+
exit(1);
256+
}
257+
}
258+
}
259+
260+
Members.emplace_back(MB);
261+
}
262+
144263
int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
145264
BumpPtrAllocator Alloc;
146265
StringSaver Saver(Alloc);
@@ -195,104 +314,29 @@ int llvm::libDriverMain(ArrayRef<const char *> ArgsArr) {
195314
std::string(" (from '/machine:") + Arg->getValue() + "' flag)";
196315
}
197316

198-
// Create a NewArchiveMember for each input file.
317+
std::vector<std::unique_ptr<MemoryBuffer>> MBs;
199318
std::vector<NewArchiveMember> Members;
319+
320+
// Create a NewArchiveMember for each input file.
200321
for (auto *Arg : Args.filtered(OPT_INPUT)) {
322+
// Find a file
201323
std::string Path = findInputFile(Arg->getValue(), SearchPaths);
202324
if (Path.empty()) {
203325
llvm::errs() << Arg->getValue() << ": no such file or directory\n";
204326
return 1;
205327
}
206328

207-
Expected<NewArchiveMember> MOrErr =
208-
NewArchiveMember::getFile(Saver.save(Path), /*Deterministic=*/true);
209-
if (!MOrErr) {
210-
handleAllErrors(MOrErr.takeError(), [&](const ErrorInfoBase &EIB) {
211-
llvm::errs() << Arg->getValue() << ": " << EIB.message() << "\n";
212-
});
213-
return 1;
214-
}
329+
// Open a file.
330+
ErrorOr<std::unique_ptr<MemoryBuffer>> MOrErr =
331+
MemoryBuffer::getFile(Path, -1, false);
332+
fatalOpenError(errorCodeToError(MOrErr.getError()), Path);
333+
MemoryBufferRef MBRef = (*MOrErr)->getMemBufferRef();
215334

216-
file_magic Magic = identify_magic(MOrErr->Buf->getBuffer());
217-
if (Magic != file_magic::coff_object && Magic != file_magic::bitcode &&
218-
Magic != file_magic::windows_resource) {
219-
llvm::errs() << Arg->getValue()
220-
<< ": not a COFF object, bitcode or resource file\n";
221-
return 1;
222-
}
223-
224-
// Check that all input files have the same machine type.
225-
// Mixing normal objects and LTO bitcode files is fine as long as they
226-
// have the same machine type.
227-
// Doing this here duplicates the header parsing work that writeArchive()
228-
// below does, but it's not a lot of work and it's a bit awkward to do
229-
// in writeArchive() which needs to support many tools, can't assume the
230-
// input is COFF, and doesn't have a good way to report errors.
231-
COFF::MachineTypes FileMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN;
232-
if (Magic == file_magic::coff_object) {
233-
std::error_code EC;
234-
object::COFFObjectFile Obj(*MOrErr->Buf, EC);
235-
if (EC) {
236-
llvm::errs() << Arg->getValue() << ": failed to open: " << EC.message()
237-
<< '\n';
238-
return 1;
239-
}
240-
uint16_t Machine = Obj.getMachine();
241-
if (Machine != COFF::IMAGE_FILE_MACHINE_I386 &&
242-
Machine != COFF::IMAGE_FILE_MACHINE_AMD64 &&
243-
Machine != COFF::IMAGE_FILE_MACHINE_ARMNT &&
244-
Machine != COFF::IMAGE_FILE_MACHINE_ARM64) {
245-
llvm::errs() << Arg->getValue() << ": unknown machine: " << Machine
246-
<< '\n';
247-
return 1;
248-
}
249-
FileMachine = static_cast<COFF::MachineTypes>(Machine);
250-
} else if (Magic == file_magic::bitcode) {
251-
Expected<std::string> TripleStr = getBitcodeTargetTriple(*MOrErr->Buf);
252-
if (!TripleStr) {
253-
llvm::errs() << Arg->getValue()
254-
<< ": failed to get target triple from bitcode\n";
255-
return 1;
256-
}
257-
switch (Triple(*TripleStr).getArch()) {
258-
case Triple::x86:
259-
FileMachine = COFF::IMAGE_FILE_MACHINE_I386;
260-
break;
261-
case Triple::x86_64:
262-
FileMachine = COFF::IMAGE_FILE_MACHINE_AMD64;
263-
break;
264-
case Triple::arm:
265-
FileMachine = COFF::IMAGE_FILE_MACHINE_ARMNT;
266-
break;
267-
case Triple::aarch64:
268-
FileMachine = COFF::IMAGE_FILE_MACHINE_ARM64;
269-
break;
270-
default:
271-
llvm::errs() << Arg->getValue() << ": unknown arch in target triple "
272-
<< *TripleStr << '\n';
273-
return 1;
274-
}
275-
}
276-
277-
// FIXME: Once lld-link rejects multiple resource .obj files:
278-
// Call convertResToCOFF() on .res files and add the resulting
279-
// COFF file to the .lib output instead of adding the .res file, and remove
280-
// this check. See PR42180.
281-
if (FileMachine != COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
282-
if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) {
283-
LibMachine = FileMachine;
284-
LibMachineSource = std::string(" (inferred from earlier file '") +
285-
Arg->getValue() + "')";
286-
} else if (LibMachine != FileMachine) {
287-
llvm::errs() << Arg->getValue() << ": file machine type "
288-
<< machineToStr(FileMachine)
289-
<< " conflicts with library machine type "
290-
<< machineToStr(LibMachine) << LibMachineSource << '\n';
291-
return 1;
292-
}
293-
}
335+
// Append a file.
336+
appendFile(Members, LibMachine, LibMachineSource, MBRef);
294337

295-
Members.emplace_back(std::move(*MOrErr));
338+
// Take the ownership of the file buffer to keep the file open.
339+
MBs.push_back(std::move(*MOrErr));
296340
}
297341

298342
// Create an archive file.

llvm/test/tools/llvm-lib/invalid.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
RUN: not llvm-lib %S/Inputs/cl-gl.obj 2>&1 | FileCheck %s
2-
CHECK: not a COFF object, bitcode or resource file
2+
CHECK: not a COFF object, bitcode, archive or resource file

llvm/test/tools/llvm-lib/nest.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
If an archive file is specified as an input file, its members
2+
are added to an output file. This test verifies that beahvior.
3+
4+
RUN: rm -rf %t
5+
RUN: mkdir -p %t
6+
7+
RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o %t/foo.o %S/Inputs/a.s
8+
RUN: llvm-lib -out:%t/foo.lib %t/foo.o
9+
10+
RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o %t/bar.o %S/Inputs/b.s
11+
RUN: llvm-lib -out:%t/bar.lib %t/foo.lib %t/bar.o
12+
13+
RUN: llvm-ar t %t/bar.lib | FileCheck %s
14+
CHECK: foo.o
15+
CHECK: bar.o

0 commit comments

Comments
 (0)