[lld] [lld] check the format of libary and skip the wrong one (PR #78874)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jan 20 19:47:44 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld

Author: None (W-50243)

<details>
<summary>Changes</summary>

When I use lld linking, I find that when there are two libraries with the same name but one is correctly formatted and the other is incorrectly formatted, the link can be made when the correctly formatted library is in front and the connection fails when the incorrectly formatted library is in front. 
The lld cannot check the format of the library when a path is added, but instead joins the path when the first required library is found and checks the library collective when it is connected. 
So, I added a way for the lld to check the library as it added the library path, and adopted an option `check-format` to control it

---
Full diff: https://github.com/llvm/llvm-project/pull/78874.diff


6 Files Affected:

- (modified) lld/ELF/Config.h (+2-1) 
- (modified) lld/ELF/Driver.cpp (+80) 
- (modified) lld/ELF/DriverUtils.cpp (+6-3) 
- (modified) lld/ELF/Options.td (+4) 
- (added) lld/test/ELF/aarch64-lld-path.s (+49) 
- (added) lld/test/ELF/x86-lld-path.s (+49) 


``````````diff
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 56229334f9a44a..1bb12b955f0535 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -122,6 +122,7 @@ class LinkerDriver {
   void linkerMain(ArrayRef<const char *> args);
   void addFile(StringRef path, bool withLOption);
   void addLibrary(StringRef name);
+  bool checkFile(StringRef name);
 
 private:
   void createFiles(llvm::opt::InputArgList &args);
@@ -424,11 +425,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<llvm::StringRef, llvm::StringRef> 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 07f4263c90e62b..3f0502bf04df12 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)
@@ -1219,6 +1220,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 =
       args.hasFlag(OPT_allow_multiple_definition,
                    OPT_no_allow_multiple_definition, false) ||
@@ -3070,3 +3074,79 @@ void LinkerDriver::link(opt::InputArgList &args) {
   // Write the result to the file.
   invokeELFT(writeResult,);
 }
+
+static bool checkFileFormat(InputFile *file) {
+  if (!file->isElf() && !isa<BitcodeFile>(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;
+
+  Optional<MemoryBufferRef> buffer = readFile(path);
+  if(!buffer)
+    return false;
+
+  MemoryBufferRef mbref = *buffer;
+
+  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<BitcodeFile>(p.first, path, p.second, false)):
+            checkFileFormat(createObjFile(p.first, path));
+          return SingleFile;
+        }
+      }
+      for (const auto &p : members){
+        switch(identify_magic(p.first.getBuffer())){
+          case file_magic::elf_relocatable:
+            if(!checkFileFormat(createObjFile(p.first, path, true)))
+              return false;
+            break;
+          case file_magic::bitcode:
+            if(!checkFileFormat(make<BitcodeFile>(p.first, path, p.second, true)))
+              return false;
+            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();
+      return checkFileFormat(make<SharedFile>(mbref, path));
+    case file_magic::bitcode:
+      return checkFileFormat(make<BitcodeFile>(mbref, "", 0, inLib));
+    case file_magic::elf_relocatable:
+      return checkFileFormat(createObjFile(mbref, "", inLib));
+    default:
+      warn(path + ": unknown file type");
+      return false;
+  }
+}
\ No newline at end of file
diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index 0a27422e3b2df8..da6426549c5117 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -225,7 +225,8 @@ static std::optional<std::string> findFile(StringRef path1,
 std::optional<std::string> elf::findFromSearchPaths(StringRef path) {
   for (StringRef dir : config->searchPaths)
     if (std::optional<std::string> s = findFile(dir, path))
-      return s;
+      if (driver->checkFile(*s))
+        return s;
   return std::nullopt;
 }
 
@@ -235,9 +236,11 @@ std::optional<std::string> elf::searchLibraryBaseName(StringRef name) {
   for (StringRef dir : config->searchPaths) {
     if (!config->isStatic)
       if (std::optional<std::string> s = findFile(dir, "lib" + name + ".so"))
-        return s;
+        if (driver->checkFile(*s))
+          return s;
     if (std::optional<std::string> s = findFile(dir, "lib" + name + ".a"))
-      return s;
+      if (driver->checkFile(*s))
+        return s;
   }
   return std::nullopt;
 }
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index c2c9cabc92a4da..141e6494600077 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<"<symbol>">;
diff --git a/lld/test/ELF/aarch64-lld-path.s b/lld/test/ELF/aarch64-lld-path.s
new file mode 100644
index 00000000000000..b17d7791fa040d
--- /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 at 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 00000000000000..7b80b1328d3fbf
--- /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

``````````

</details>


https://github.com/llvm/llvm-project/pull/78874


More information about the llvm-commits mailing list