Skip to content

Commit

Permalink
[Archive] Don't throw away errors for malformed archive members
Browse files Browse the repository at this point in the history
When adding an archive member with a problem, e.g. a new bitcode with an
old archiver, containing an unsupported attribute, or an ELF file with a
malformed symbol table, the archiver would throw away the error and
simply add the member to the archive without any symbol entries. This
meant that the resultant archive could be silently unusable when not
using --whole-archive, and result in unexpected undefined symbols.

This change fixes this issue by addressing two FIXMEs and only throwing
away not-an-object errors. However, this meant that some LLD tests which
didn't need symbol tables and were using invalid members deliberately to
test the linker's malformed input handling no longer worked, so this
patch also stops the archiver from looking for symbols in an object if
it doesn't require a symbol table, and updates the tests accordingly.

Differential Revision: https://reviews.llvm.org/D88288

Reviewed by: grimar, rupprecht, MaskRay
  • Loading branch information
jh7370 committed Oct 1, 2020
1 parent 5101e7e commit a20168d
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 34 deletions.
2 changes: 1 addition & 1 deletion lld/test/ELF/invalid/data-encoding.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Check we report this.

# RUN: yaml2obj %s -o %t.o
# RUN: llvm-ar rcs %t.a %t.o
# RUN: llvm-ar rcS %t.a %t.o

# RUN: not ld.lld --whole-archive %t.a -o /dev/null 2>&1 | FileCheck %s
# CHECK: {{.*}}.a({{.*}}.o): corrupted ELF file: invalid data encoding
Expand Down
2 changes: 1 addition & 1 deletion lld/test/ELF/invalid/invalid-file-class.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
## EV_CURRENT(1), ELFOSABI_LINUX(3), <padding zero bytes>, ET_REL(1), EM_NONE(0)
# RUN: echo -e -n "\x7f\x45\x4c\x46\x00\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" > %t/invalid.o

# RUN: llvm-ar --format=gnu cr %t/invalid-class.a %t/invalid.o
# RUN: llvm-ar --format=gnu crS %t/invalid-class.a %t/invalid.o
# RUN: not ld.lld -whole-archive %t/invalid-class.a -o /dev/null 2>&1 | FileCheck %s
# CHECK: invalid-class.a(invalid.o): corrupted ELF file: invalid file class

Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Object/SymbolicFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ class SymbolicFile : public Binary {
static bool classof(const Binary *v) {
return v->isSymbolic();
}

static bool isSymbolicFile(file_magic Type, const LLVMContext *Context);
};

inline BasicSymbolRef::BasicSymbolRef(DataRefImpl SymbolP,
Expand Down
42 changes: 23 additions & 19 deletions llvm/lib/Object/ArchiveWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,22 +359,21 @@ getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) {
// reference to it, thus SymbolicFile should be destroyed first.
LLVMContext Context;
std::unique_ptr<object::SymbolicFile> Obj;
if (identify_magic(Buf.getBuffer()) == file_magic::bitcode) {

const file_magic Type = identify_magic(Buf.getBuffer());
// Treat unsupported file types as having no symbols.
if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
return Ret;
if (Type == file_magic::bitcode) {
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
Buf, file_magic::bitcode, &Context);
if (!ObjOrErr) {
// FIXME: check only for "not an object file" errors.
consumeError(ObjOrErr.takeError());
return Ret;
}
if (!ObjOrErr)
return ObjOrErr.takeError();
Obj = std::move(*ObjOrErr);
} else {
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
if (!ObjOrErr) {
// FIXME: check only for "not an object file" errors.
consumeError(ObjOrErr.takeError());
return Ret;
}
if (!ObjOrErr)
return ObjOrErr.takeError();
Obj = std::move(*ObjOrErr);
}

Expand All @@ -393,7 +392,7 @@ getSymbols(MemoryBufferRef Buf, raw_ostream &SymNames, bool &HasObject) {
static Expected<std::vector<MemberData>>
computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
object::Archive::Kind Kind, bool Thin, bool Deterministic,
ArrayRef<NewArchiveMember> NewMembers) {
bool NeedSymbols, ArrayRef<NewArchiveMember> NewMembers) {
static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};

// This ignores the symbol table, but we only need the value mod 8 and the
Expand Down Expand Up @@ -494,13 +493,17 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
ModTime, Size);
Out.flush();

Expected<std::vector<unsigned>> Symbols =
getSymbols(Buf, SymNames, HasObject);
if (auto E = Symbols.takeError())
return std::move(E);
std::vector<unsigned> Symbols;
if (NeedSymbols) {
Expected<std::vector<unsigned>> SymbolsOrErr =
getSymbols(Buf, SymNames, HasObject);
if (auto E = SymbolsOrErr.takeError())
return std::move(E);
Symbols = std::move(*SymbolsOrErr);
}

Pos += Header.size() + Data.size() + Padding.size();
Ret.push_back({std::move(*Symbols), std::move(Header), Data, Padding});
Ret.push_back({std::move(Symbols), std::move(Header), Data, Padding});
}
// If there are no symbols, emit an empty symbol table, to satisfy Solaris
// tools, older versions of which expect a symbol table in a non-empty
Expand Down Expand Up @@ -564,8 +567,9 @@ static Error writeArchiveToStream(raw_ostream &Out,
SmallString<0> StringTableBuf;
raw_svector_ostream StringTable(StringTableBuf);

Expected<std::vector<MemberData>> DataOrErr = computeMemberData(
StringTable, SymNames, Kind, Thin, Deterministic, NewMembers);
Expected<std::vector<MemberData>> DataOrErr =
computeMemberData(StringTable, SymNames, Kind, Thin, Deterministic,
WriteSymtab, NewMembers);
if (Error E = DataOrErr.takeError())
return E;
std::vector<MemberData> &Data = *DataOrErr;
Expand Down
53 changes: 40 additions & 13 deletions llvm/lib/Object/SymbolicFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,14 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type,
if (Type == file_magic::unknown)
Type = identify_magic(Data);

if (!isSymbolicFile(Type, Context))
return errorCodeToError(object_error::invalid_file_type);

switch (Type) {
case file_magic::bitcode:
if (Context)
return IRObjectFile::create(Object, *Context);
LLVM_FALLTHROUGH;
case file_magic::unknown:
case file_magic::archive:
case file_magic::coff_cl_gl_object:
case file_magic::macho_universal_binary:
case file_magic::windows_resource:
case file_magic::pdb:
case file_magic::minidump:
case file_magic::tapi_file:
return errorCodeToError(object_error::invalid_file_type);
// Context is guaranteed to be non-null here, because bitcode magic only
// indicates a symbolic file when Context is non-null.
return IRObjectFile::create(Object, *Context);
case file_magic::elf:
case file_magic::elf_executable:
case file_magic::elf_shared_object:
Expand Down Expand Up @@ -95,6 +89,39 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type,
MemoryBufferRef(BCData->getBuffer(), Object.getBufferIdentifier()),
*Context);
}
default:
llvm_unreachable("Unexpected Binary File Type");
}
}

bool SymbolicFile::isSymbolicFile(file_magic Type, const LLVMContext *Context) {
switch (Type) {
case file_magic::bitcode:
return Context != nullptr;
case file_magic::elf:
case file_magic::elf_executable:
case file_magic::elf_shared_object:
case file_magic::elf_core:
case file_magic::macho_executable:
case file_magic::macho_fixed_virtual_memory_shared_lib:
case file_magic::macho_core:
case file_magic::macho_preload_executable:
case file_magic::macho_dynamically_linked_shared_lib:
case file_magic::macho_dynamic_linker:
case file_magic::macho_bundle:
case file_magic::macho_dynamically_linked_shared_lib_stub:
case file_magic::macho_dsym_companion:
case file_magic::macho_kext_bundle:
case file_magic::pecoff_executable:
case file_magic::xcoff_object_32:
case file_magic::xcoff_object_64:
case file_magic::wasm_object:
case file_magic::coff_import_library:
case file_magic::elf_relocatable:
case file_magic::macho_object:
case file_magic::coff_object:
return true;
default:
return false;
}
llvm_unreachable("Unexpected Binary File Type");
}
38 changes: 38 additions & 0 deletions llvm/test/Object/archive-malformed-object.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Show that the archive library emits error messages when adding malformed
## objects.

# RUN: rm -rf %t.dir
# RUN: split-file %s %t.dir
# RUN: cd %t.dir

## Malformed bitcode object.
# RUN: llvm-as input.ll -o input.bc
# RUN: %python -c "with open('input.bc', 'a') as f: f.truncate(10)"
# RUN: not llvm-ar rc bad.a input.bc 2>&1 | FileCheck %s --check-prefix=ERR1

# ERR1: error: bad.a: Invalid bitcode signature

## Non-bitcode malformed file.
# RUN: yaml2obj input.yaml -o input.o
# RUN: not llvm-ar rc bad.a input.o 2>&1 | FileCheck %s --check-prefix=ERR2

# ERR2: error: bad.a: section header table goes past the end of the file: e_shoff = 0x9999

## Don't emit an error if the symbol table is not required.
# RUN: llvm-ar rcS good.a input.o input.bc
# RUN: llvm-ar t good.a | FileCheck %s --check-prefix=CONTENTS

# CONTENTS: input.o
# CONTENTS-NEXT: input.bc

#--- input.ll
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux"

#--- input.yaml
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: ET_REL
EShOff: 0x9999
11 changes: 11 additions & 0 deletions llvm/test/Object/archive-unknown-filetype.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Show that the archive library does not emit an error or add any symbols to
## the archive symbol table, when it encounters an unknown file type, but still
## adds the file to the archive.

# RUN: echo something > %t
# RUN: rm -f %t.a
# RUN: llvm-ar rc %t.a %t
# RUN: llvm-ar t %t.a | FileCheck %s --check-prefix=CONTENTS -DFILE=%basename_t
# RUN: llvm-nm --print-armap %t.a | FileCheck %s --allow-empty --implicit-check-not={{.}}

# CONTENTS: [[FILE]]

0 comments on commit a20168d

Please sign in to comment.