Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

W-50243
Copy link
Contributor

@W-50243 W-50243 commented Jan 21, 2024

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

@llvmbot
Copy link
Collaborator

llvmbot commented Jan 21, 2024

@llvm/pr-subscribers-lld

Author: None (W-50243)

Changes

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 --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@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

@llvmbot
Copy link
Collaborator

llvmbot commented Jan 21, 2024

@llvm/pr-subscribers-lld-elf

Author: None (W-50243)

Changes

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 --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 56229334f9a44ae..1bb12b955f0535b 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 07f4263c90e62b3..3f0502bf04df123 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 0a27422e3b2df80..da6426549c5117a 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 c2c9cabc92a4da4..141e6494600077b 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 000000000000000..b17d7791fa040da
--- /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 000000000000000..7b80b1328d3fbf5
--- /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

Copy link

github-actions bot commented Jan 21, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@MaskRay
Copy link
Member

MaskRay commented Jan 24, 2024

I think GNU ld supports some "wrong e_machine, skip" logic for -lfoo and we have rejected supporting this fishy behavior. We cannot think of a reasonable case this is ever desired. The solution is always to fix the build system to not provide dubious -L paths in the first place. For linker scripts, I think GNU ld tries skipping an incompatible script if an incompatible OUTPUT_FORMAT is seen. However, a question arises about what we should do if we have parsed something before OUTPUT_FORMAT.

@W-50243
Copy link
Contributor Author

W-50243 commented Jan 27, 2024

I think GNU ld supports some "wrong e_machine, skip" logic for -lfoo and we have rejected supporting this fishy behavior. We cannot think of a reasonable case this is ever desired. The solution is always to fix the build system to not provide dubious -L paths in the first place. For linker scripts, I think GNU ld tries skipping an incompatible script if an incompatible OUTPUT_FORMAT is seen. However, a question arises about what we should do if we have parsed something before OUTPUT_FORMAT.

I have some questions about that. What I see in the code is that the library is added and then parsed. Will the link script change the behavior of the lld?

@MaskRay
Copy link
Member

MaskRay commented Jan 27, 2024

I think GNU ld supports some "wrong e_machine, skip" logic for -lfoo and we have rejected supporting this fishy behavior. We cannot think of a reasonable case this is ever desired. The solution is always to fix the build system to not provide dubious -L paths in the first place. For linker scripts, I think GNU ld tries skipping an incompatible script if an incompatible OUTPUT_FORMAT is seen. However, a question arises about what we should do if we have parsed something before OUTPUT_FORMAT.

I have some questions about that. What I see in the code is that the library is added and then parsed. Will the link script change the behavior of the lld?

No behavior change. OUTPUT_FORMAT does cause lld to check whether input files have compatible emachine/ekind with OUTPUT_FORMAT.
I haven't checked GNU ld behavior. I do not know whether it may have accrued some state before an incompatible OUTPUT_FORMAT is seen.

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants