diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 691ebfc074320..2b428bf2ba517 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -122,6 +122,7 @@ class LinkerDriver { void linkerMain(ArrayRef args); void addFile(StringRef path, bool withLOption); void addLibrary(StringRef name); + bool checkFile(StringRef name); private: void createFiles(llvm::opt::InputArgList &args); @@ -426,11 +427,11 @@ struct Config { // not supported on Android 11 & 12. bool androidMemtagStack; + bool CheckFormat = true; // When using a unified pre-link LTO pipeline, specify the backend LTO mode. LtoKind ltoKind = LtoKind::Default; unsigned threadCount; - // If an input file equals a key, remap it to the value. llvm::DenseMap remapInputs; // If an input file matches a wildcard pattern, remap it to the value. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 24faa1753f1e3..59f5463d0361f 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -82,6 +82,7 @@ Ctx elf::ctx; static void setConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args); +static bool checkFileFormat(InputFile *file); void elf::errorOrWarn(const Twine &msg) { if (config->noinhibitExec) @@ -1195,6 +1196,9 @@ static void readConfigs(opt::InputArgList &args) { errorHandler().vsDiagnostics = args.hasArg(OPT_visual_studio_diagnostics_format, false); + config->CheckFormat = + args.hasFlag(OPT_check_format, OPT_no_check_format, true); + config->allowMultipleDefinition = hasZOption(args, "muldefs") || args.hasFlag(OPT_allow_multiple_definition, @@ -3056,3 +3060,90 @@ void LinkerDriver::link(opt::InputArgList &args) { // Write the result to the file. invokeELFT(writeResult,); } + +static bool checkFileFormat(InputFile *file) { + if (!file->isElf() && !isa(file)) + return true; + + if (file->ekind == config->ekind && file->emachine == config->emachine) { + if (config->emachine != EM_MIPS) + return true; + if (isMipsN32Abi(file) == config->mipsN32Abi) + return true; + } + + StringRef target = + !config->bfdname.empty() ? config->bfdname : config->emulation; + warn(toString(file) + " is incompatible with " + target); + return false; +} + +bool LinkerDriver::checkFile(StringRef path) { + using namespace sys::fs; + if (config->ekind == ELFNoneKind || !config->CheckFormat) + return true; + + std::optional buffer = readFile(path); + if (!buffer) + return false; + + MemoryBufferRef mbref = *buffer; + + if (config->formatBinary) { + return checkFileFormat(make(mbref)); + } + + switch (identify_magic(mbref.getBuffer())) { + case file_magic::unknown: + return true; + case file_magic::archive: { + auto members = getArchiveMembers(mbref); + if (inWholeArchive) { + for (const auto &p : members) { + bool SingleFile = isBitcode(p.first) + ? checkFileFormat(make( + p.first, path, p.second, false)) + : checkFileFormat(createObjFile(p.first, path)); + return SingleFile; + } + } + for (const auto &p : members) { + auto magic = identify_magic(p.first.getBuffer()); + switch (magic) { + case file_magic::elf_relocatable: + if (!tryAddFatLTOFile(p.first, path, p.second, true)) + return checkFileFormat(createObjFile(p.first, path, true)); + break; + case file_magic::bitcode: + return checkFileFormat( + make(p.first, path, p.second, true)); + break; + default: { + warn(path + ": archive member '" + p.first.getBufferIdentifier() + + "' is neither ET_REL nor LLVM bitcode"); + } + } + } + return true; + } + case file_magic::elf_shared_object: { + if (config->isStatic || config->relocatable) { + warn("attempted static link of dynamic object " + path); + return false; + } + path = mbref.getBufferIdentifier(); + auto *f = make(mbref, false ? path::filename(path) : path); + f->init(); + return checkFileFormat(f); + } + case file_magic::bitcode: + return checkFileFormat(make(mbref, "", 0, inLib)); + case file_magic::elf_relocatable: + if (!tryAddFatLTOFile(mbref, "", 0, inLib)) + return checkFileFormat(createObjFile(mbref, "", inLib)); + break; + default: + warn(path + ": unknown file type"); + return false; + } +} diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp index de8ffab0e2124..6d55f3e9cab1b 100644 --- a/lld/ELF/DriverUtils.cpp +++ b/lld/ELF/DriverUtils.cpp @@ -225,7 +225,8 @@ static std::optional findFile(StringRef path1, std::optional elf::findFromSearchPaths(StringRef path) { for (StringRef dir : config->searchPaths) if (std::optional s = findFile(dir, path)) - return s; + if (ctx.driver.checkFile(*s)) + return s; return std::nullopt; } @@ -235,9 +236,11 @@ std::optional elf::searchLibraryBaseName(StringRef name) { for (StringRef dir : config->searchPaths) { if (!config->isStatic) if (std::optional s = findFile(dir, "lib" + name + ".so")) - return s; + if (ctx.driver.checkFile(*s)) + return s; if (std::optional s = findFile(dir, "lib" + name + ".a")) - return s; + if (ctx.driver.checkFile(*s)) + return s; } return std::nullopt; } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index c10a73e2d9c36..1687e8230b495 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -229,6 +229,10 @@ defm fatal_warnings: B<"fatal-warnings", "Treat warnings as errors", "Do not treat warnings as errors (default)">; +defm check_format: B<"check-format", + "Please check the file format(default)", + "Please not check the file format">; + defm filter: Eq<"filter", "Set DT_FILTER field to the specified name">; defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"">; diff --git a/lld/test/ELF/aarch64-lld-path.s b/lld/test/ELF/aarch64-lld-path.s new file mode 100644 index 0000000000000..b17d7791fa040 --- /dev/null +++ b/lld/test/ELF/aarch64-lld-path.s @@ -0,0 +1,49 @@ +# REQUIRES: aarch64 + +# RUN: rm -rf %t && split-file %s %t && cd %t && mkdir be le +# RUN: llvm-mc -filetype=obj -triple=aarch64 main.s -o main.o +# RUN: llvm-mc -filetype=obj -triple=aarch64 ref.s -o le/ref.o && ld.lld -shared le/ref.o -o le/libref.so +# RUN: llvm-mc -filetype=obj -triple=aarch64_be ref.s -o be/ref.o && ld.lld -shared be/ref.o -o be/libref.so + +# One error formatted library +# RUN: not ld.lld main.o -lref -Lbe -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld main.o -l:libref.so -Lbe -o /dev/null 2>&1 | FileCheck %s + +# Format not specified and the checkFile function is in default. +# RUN: not ld.lld main.o -lref -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld main.o -l:libref.so -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s + +# Format not specified and the checkFile function is closed +# RUN: not ld.lld main.o -no-check-format -lref -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld main.o -no-check-format -l:libref.so -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s + +# Specified format and the checkFile function is closed +# RUN: not ld.lld -m aarch64linux -no-check-format main.o -lref -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR +# RUN: not ld.lld -m aarch64linux -no-check-format main.o -l:libref.so -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR + +# Specified format and the checkFile function is in default with one error libary +# RUN: not ld.lld -m aarch64linux main.o -lref -Lbe -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNTWO +# RUN: not ld.lld -m aarch64linux main.o -l:libref.so -Lbe -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNONE + +# Specified format and the checkFile function is in default +# RUN: ld.lld -m aarch64linux main.o -lref -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN +# RUN: ld.lld -m aarch64linux main.o -l:libref.so -Lbe -Lle -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN + +# CHECK-NOT: error: +# CHECK: error: be/libref.so is incompatible with main.o + +# UNONE: warning: be/libref.so is incompatible with aarch64linux +# UNONE: error: unable to find library -l:libref.so +# UNTWO: warning: be/libref.so is incompatible with aarch64linux +# UNTWO: error: unable to find library -lref +# ERROR: error: be/libref.so is incompatible with aarch64linux +# WARN: warning: be/libref.so is incompatible with aarch64linux + +#--- ref.s +.globl fun +fun: + nop +#--- main.s +.globl _start +_start: + bl fun@PLT \ No newline at end of file diff --git a/lld/test/ELF/x86-lld-path.s b/lld/test/ELF/x86-lld-path.s new file mode 100644 index 0000000000000..7b80b1328d3fb --- /dev/null +++ b/lld/test/ELF/x86-lld-path.s @@ -0,0 +1,49 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t && split-file %s %t && cd %t && mkdir 32bit 64bit +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnu main.s -o main.o +# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux-gnu ref.s -o 32bit/ref.o && ld.lld -shared 32bit/ref.o -o 32bit/libref.so +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnu ref.s -o 64bit/ref.o && ld.lld -shared 64bit/ref.o -o 64bit/libref.so + +# One error formatted library +# RUN: not ld.lld main.o -lref -L32bit -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld main.o -l:libref.so -L32bit -o /dev/null 2>&1 | FileCheck %s + +# Format not specified and the checkFile function is in default. +# RUN: not ld.lld main.o -lref -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld main.o -l:libref.so -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s + +# Format not specified and the checkFile function is closed +# RUN: not ld.lld main.o -no-check-format -lref -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s +# RUN: not ld.lld main.o -no-check-format -l:libref.so -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s + +# Specified format and the checkFile function is closed +# RUN: not ld.lld -m elf_x86_64 -no-check-format main.o -lref -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR +# RUN: not ld.lld -m elf_x86_64 -no-check-format main.o -l:libref.so -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR + +# Specified format and the checkFile function is in default with one error libary +# RUN: not ld.lld -m elf_x86_64 main.o -lref -L32bit -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNTWO +# RUN: not ld.lld -m elf_x86_64 main.o -l:libref.so -L32bit -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNONE + +# Specified format and the checkFile function is in default +# RUN: ld.lld -m elf_x86_64 main.o -lref -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN +# RUN: ld.lld -m elf_x86_64 main.o -l:libref.so -L32bit -L64bit -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN + +# CHECK-NOT: error: +# CHECK: error: 32bit/libref.so is incompatible with main.o + +# UNONE: warning: 32bit/libref.so is incompatible with elf_x86_64 +# UNONE: error: unable to find library -l:libref.so +# UNTWO: warning: 32bit/libref.so is incompatible with elf_x86_64 +# UNTWO: error: unable to find library -lref +# ERROR: error: 32bit/libref.so is incompatible with elf_x86_64 +# WARN: warning: 32bit/libref.so is incompatible with elf_x86_64 + +#--- ref.s +.globl fun +fun: + nop +#--- main.s +.global _start +_start: + call fun