[binutils-gdb] ld: Use stat to check if linker script appears multiple times

H.J. Lu hjl@sourceware.org
Thu Aug 14 15:02:48 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=d048eee2910878bf46f333f2fdc8ef940bd1638e

commit d048eee2910878bf46f333f2fdc8ef940bd1638e
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Tue Aug 12 07:37:57 2025 -0700

    ld: Use stat to check if linker script appears multiple times
    
    Use stat, instead of strcmp, to check if the same linker script file
    appears multiple times for
    
    $ ld -L... -T ././/script.t -T script.t ...
    
    Although ././/script.t and script.t access the same file, but their
    filenames are different.  strcmp won't work here.
    
    Copy gnulib/import/same-inode.h to include since the gnulib directory
    isn't included in the binutils tarball.
    
    include/
    
            PR ld/24576
            * same-inode.h: New file.  Copied from gnulib/import/same-inode.h.
    
    ld/
    
            PR ld/24576
            * ldfile.c: Include "same-inode.h".
            (ldfile_find_command_file): Change the second argument from bool
            to enum script_open_style.  Check if the same linker script file
            appears multiple times by using stat, instead using strcmp.
            (ldfile_open_command_file_1): Don't check if the same linker
            script file appears multiple times here.
            * testsuite/ld-scripts/pr24576-1.d: Adjusted.
            * testsuite/ld-scripts/pr24576-2.d: New.
            * testsuite/ld-scripts/script.exp: Run pr24576-2.
    
    Signed-off-by: H.J. Lu <hjl.tools@gmail.com>

Diff:
---
 include/same-inode.h                | 47 +++++++++++++++++++++++
 ld/ldfile.c                         | 74 +++++++++++++++++++++----------------
 ld/testsuite/ld-scripts/pr24576-1.d |  2 +-
 ld/testsuite/ld-scripts/pr24576-2.d |  3 ++
 ld/testsuite/ld-scripts/script.exp  |  1 +
 5 files changed, 95 insertions(+), 32 deletions(-)

diff --git a/include/same-inode.h b/include/same-inode.h
new file mode 100644
index 00000000000..f65f3d039ed
--- /dev/null
+++ b/include/same-inode.h
@@ -0,0 +1,47 @@
+/* Determine whether two stat buffers are known to refer to the same file.
+
+   Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef SAME_INODE_H
+# define SAME_INODE_H 1
+
+# include <sys/types.h>
+
+# if defined __VMS && __CRTL_VER < 80200000
+#  define SAME_INODE(a, b)             \
+    ((a).st_ino[0] == (b).st_ino[0]    \
+     && (a).st_ino[1] == (b).st_ino[1] \
+     && (a).st_ino[2] == (b).st_ino[2] \
+     && (a).st_dev == (b).st_dev)
+# elif defined _WIN32 && ! defined __CYGWIN__
+   /* Native Windows.  */
+#  if _GL_WINDOWS_STAT_INODES
+    /* stat() and fstat() set st_dev and st_ino to 0 if information about
+       the inode is not available.  */
+#   define SAME_INODE(a, b) \
+     (!((a).st_ino == 0 && (a).st_dev == 0) \
+      && (a).st_ino == (b).st_ino && (a).st_dev == (b).st_dev)
+#  else
+    /* stat() and fstat() set st_ino to 0 always.  */
+#   define SAME_INODE(a, b) 0
+#  endif
+# else
+#  define SAME_INODE(a, b)    \
+    ((a).st_ino == (b).st_ino \
+     && (a).st_dev == (b).st_dev)
+# endif
+
+#endif
diff --git a/ld/ldfile.c b/ld/ldfile.c
index e642c7f6620..ce81fdc266f 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -35,6 +35,7 @@
 #include "libiberty.h"
 #include "filenames.h"
 #include <fnmatch.h>
+#include "same-inode.h"
 #if BFD_SUPPORTS_PLUGINS
 #include "plugin.h"
 #endif /* BFD_SUPPORTS_PLUGINS */
@@ -828,19 +829,26 @@ find_scripts_dir (void)
 
 static FILE *
 ldfile_find_command_file (const char *name,
-			  bool default_only,
+			  enum script_open_style open_how,
 			  bool *sysrooted)
 {
   search_dirs_type *search;
   FILE *result = NULL;
-  char *path;
+  char *path = NULL;
+  const char *filename = NULL;
+  struct script_name_list *script;
+  size_t len;
+  struct stat sbuf1;
 
-  if (!default_only)
+  if (open_how != script_defaultT)
     {
       /* First try raw name.  */
       result = try_open (name, sysrooted);
       if (result != NULL)
-	return result;
+	{
+	  filename = name;
+	  goto success;
+	}
     }
 
   if (!script_search)
@@ -861,20 +869,47 @@ ldfile_find_command_file (const char *name,
   *search_tail_ptr = script_search;
 
   /* Try now prefixes.  */
-  for (search = default_only ? script_search : search_head;
+  for (search = open_how == script_defaultT ? script_search : search_head;
        search != NULL;
        search = search->next)
     {
       path = concat (search->name, slash, name, (const char *) NULL);
       result = try_open (path, sysrooted);
-      free (path);
       if (result)
-	break;
+	{
+	  filename = path;
+	  break;
+	}
     }
 
   /* Restore the original path list.  */
   *search_tail_ptr = NULL;
 
+ success:
+  /* PR 24576: Catch the case where the user has accidentally included
+     the same linker script twice.  */
+  if (stat (filename, &sbuf1) == 0)
+    {
+      struct stat sbuf2;
+      for (script = processed_scripts;
+	   script != NULL;
+	   script = script->next)
+	if ((open_how != script_nonT || script->open_how != script_nonT)
+	    && stat (script->name, &sbuf2) == 0
+	    && SAME_INODE (sbuf1, sbuf2))
+	  fatal (_("%P: error: linker script file '%s (%s)'"
+		   " appears multiple times\n"), filename, script->name);
+    }
+
+  len = strlen (filename);
+  script = xmalloc (sizeof (*script) + len);
+  script->next = processed_scripts;
+  script->open_how = open_how;
+  memcpy (script->name, filename, len + 1);
+  processed_scripts = script;
+
+  free (path);
+
   return result;
 }
 
@@ -886,31 +921,8 @@ ldfile_open_command_file_1 (const char *name, enum script_open_style open_how)
 {
   FILE *ldlex_input_stack;
   bool sysrooted;
-  struct script_name_list *script;
-  size_t len;
-
-  /* PR 24576: Catch the case where the user has accidentally included
-     the same linker script twice.  */
-  for (script = processed_scripts; script != NULL; script = script->next)
-    {
-      if ((open_how != script_nonT || script->open_how != script_nonT)
-	  && strcmp (name, script->name) == 0)
-	{
-	  fatal (_("%P: error: linker script file '%s'"
-		   " appears multiple times\n"), name);
-	  return;
-	}
-    }
-
-  len = strlen (name);
-  script = xmalloc (sizeof (*script) + len);
-  script->next = processed_scripts;
-  script->open_how = open_how;
-  memcpy (script->name, name, len + 1);
-  processed_scripts = script;
 
-  ldlex_input_stack = ldfile_find_command_file (name,
-						open_how == script_defaultT,
+  ldlex_input_stack = ldfile_find_command_file (name, open_how,
 						&sysrooted);
   if (ldlex_input_stack == NULL)
     {
diff --git a/ld/testsuite/ld-scripts/pr24576-1.d b/ld/testsuite/ld-scripts/pr24576-1.d
index 9f9f4877cd5..6cc7621aadb 100644
--- a/ld/testsuite/ld-scripts/pr24576-1.d
+++ b/ld/testsuite/ld-scripts/pr24576-1.d
@@ -1,3 +1,3 @@
 #source: default-script.s
 #ld: -defsym _START=0x800 -T default-script.t -T default-script.t
-#error: .*default-script.t' appears multiple times
+#error: .*default-script.t\)' appears multiple times
diff --git a/ld/testsuite/ld-scripts/pr24576-2.d b/ld/testsuite/ld-scripts/pr24576-2.d
new file mode 100644
index 00000000000..2d26ab32783
--- /dev/null
+++ b/ld/testsuite/ld-scripts/pr24576-2.d
@@ -0,0 +1,3 @@
+#source: default-script.s
+#ld: -defsym _START=0x800 -T ././/default-script.t -T default-script.t
+#error: .*default-script.t\)' appears multiple times
diff --git a/ld/testsuite/ld-scripts/script.exp b/ld/testsuite/ld-scripts/script.exp
index c223135ae3d..0b37675ebe8 100644
--- a/ld/testsuite/ld-scripts/script.exp
+++ b/ld/testsuite/ld-scripts/script.exp
@@ -234,6 +234,7 @@ run_dump_test "output-section-types"
 run_dump_test "ld-version"
 run_dump_test "ld-version-2"
 run_dump_test "pr24576-1"
+run_dump_test "pr24576-2"
 
 run_dump_test "segment-start" {{name (default)}}
 run_dump_test "segment-start" {{name (overridden)} \


More information about the Binutils-cvs mailing list