[PATCH 4/4] ld: Allow EXCLUDE_FILE to be used outside of the section list

Andrew Burgess andrew.burgess@embecosm.com
Sun Oct 30 21:13:00 GMT 2016


Currently the EXCLUDE_FILE linker script construct can only be used
within the input section list, and applied only to the section pattern
immediately following the EXCLUDE_FILE.  For example:

    *.o (EXCLUDE_FILE (a.o) .text .rodata)

In this case all sections matching '.text' are included from all files
matching '*.o' but not from the file 'a.o'.  All sections matching
'.rodata' are also included from all files matching '*.o' (incluing from
'a.o').

If the user wants to restrict the inclusion of section '.rodata' so that
this too is not taken from the file 'a.o' then the above example must be
extended like this:

    *.o (EXCLUDE_FILE (a.o) .text EXCLUDE_FILE (a.o) .rodata)

However, due to the internal grammar of the linker script language the
snippet 'EXCLUDE_FILE (a.o) .text' is parsed by a pattern called
'wildcard_spec'.  The same 'wildcard_spec' pattern is also used to parse
the input file name snippet '*.o' in the above examples.  As a result of
this pattern reuse within the linker script grammar then the following
is also a valid linker script construct:

    EXCLUDE_FILE (a.o) *.o (.text .rodata)

However, though the linker accepts this without complaint the
EXCLUDE_FILE part is silently ignored and has no effect.

This commit takes this last example and makes it a useful, valid,
construct.  The last example now means to include sections '.text' and
'.rodata' from all files matching '*.o' except for the file 'a.o'.

If the list of input sections is long, and the user knows that the file
exclusion applies across the list then the second form might be a
clearer alternative to replicating the EXCLUDE_FILE construct.

I've added a set of tests for EXCLUDE_FILE to the linker, including
tests for the new functionality.

ld/ChangeLog:

	* ldlang.h (struct lang_wild_statement_struct): Add
	exclude_name_list field.
	* ldlang.c (walk_wild_file_in_exclude_list): New function.
	(walk_wild_consider_section): Use new
	walk_wild_file_in_exclude_list function.
	(walk_wild_file): Add call to walk_wild_file_in_exclude_list.
	(print_wild_statement): Print new exclude_name_list field.
	(lang_add_wild): Initialise new exclude_name_list field.
	* testsuite/ld-scripts/exclude-file-1.d: New file.
	* testsuite/ld-scripts/exclude-file-1.map: New file.
	* testsuite/ld-scripts/exclude-file-1.t: New file.
	* testsuite/ld-scripts/exclude-file-2.d: New file.
	* testsuite/ld-scripts/exclude-file-2.map: New file.
	* testsuite/ld-scripts/exclude-file-2.t: New file.
	* testsuite/ld-scripts/exclude-file-3.d: New file.
	* testsuite/ld-scripts/exclude-file-3.map: New file.
	* testsuite/ld-scripts/exclude-file-3.t: New file.
	* testsuite/ld-scripts/exclude-file-4.d: New file.
	* testsuite/ld-scripts/exclude-file-4.map: New file.
	* testsuite/ld-scripts/exclude-file-4.t: New file.
	* testsuite/ld-scripts/exclude-file-a.s: New file.
	* testsuite/ld-scripts/exclude-file-b.s: New file.
	* testsuite/ld-scripts/exclude-file.exp: New file.
	* ld.texinfo (Input Section Basics): Update description of
	EXCLUDE_FILE to cover the new features.
	* NEWS: Mention new EXCLUDE_FILE usage.
---
 ld/ChangeLog                               | 29 +++++++++++++++
 ld/NEWS                                    |  4 ++
 ld/ld.texinfo                              | 28 +++++++++++---
 ld/ldlang.c                                | 59 ++++++++++++++++++++++--------
 ld/ldlang.h                                |  1 +
 ld/testsuite/ld-scripts/exclude-file-1.d   |  5 +++
 ld/testsuite/ld-scripts/exclude-file-1.map |  8 ++++
 ld/testsuite/ld-scripts/exclude-file-1.t   | 10 +++++
 ld/testsuite/ld-scripts/exclude-file-2.d   |  5 +++
 ld/testsuite/ld-scripts/exclude-file-2.map |  7 ++++
 ld/testsuite/ld-scripts/exclude-file-2.t   | 10 +++++
 ld/testsuite/ld-scripts/exclude-file-3.d   |  5 +++
 ld/testsuite/ld-scripts/exclude-file-3.map |  7 ++++
 ld/testsuite/ld-scripts/exclude-file-3.t   | 10 +++++
 ld/testsuite/ld-scripts/exclude-file-4.d   |  5 +++
 ld/testsuite/ld-scripts/exclude-file-4.map |  7 ++++
 ld/testsuite/ld-scripts/exclude-file-4.t   | 10 +++++
 ld/testsuite/ld-scripts/exclude-file-a.s   |  5 +++
 ld/testsuite/ld-scripts/exclude-file-b.s   |  5 +++
 ld/testsuite/ld-scripts/exclude-file.exp   | 32 ++++++++++++++++
 20 files changed, 231 insertions(+), 21 deletions(-)
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-1.d
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-1.map
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-1.t
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-2.d
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-2.map
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-2.t
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-3.d
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-3.map
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-3.t
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-4.d
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-4.map
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-4.t
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-a.s
 create mode 100644 ld/testsuite/ld-scripts/exclude-file-b.s
 create mode 100644 ld/testsuite/ld-scripts/exclude-file.exp

diff --git a/ld/NEWS b/ld/NEWS
index 2bb429f..2e6fe5f 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,9 @@
 -*- text -*-
 
+* The EXCLUDE_FILE linker script construct can now be applied outside of the
+  section list in order for the exclusions to apply over all input sections in
+  the list.
+
 Changes in 2.28:
 
 * The command line option --no-eh-frame-hdr can now be used in ELF based
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index 6528b6a..f29bb73 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -4192,14 +4192,24 @@
 @end smallexample
 @noindent
 Here the @samp{*} is a wildcard which matches any file name.  To exclude a list
+@cindex EXCLUDE_FILE
 of files from matching the file name wildcard, EXCLUDE_FILE may be used to
 match all files except the ones specified in the EXCLUDE_FILE list.  For
 example:
 @smallexample
+EXCLUDE_FILE (*crtend.o *otherfile.o) *(.ctors)
+@end smallexample
+@noindent
+will cause all .ctors sections from all files except @file{crtend.o}
+and @file{otherfile.o} to be included.  The EXCLUDE_FILE can also be
+placed inside the section list, for example:
+@smallexample
 *(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors)
 @end smallexample
-will cause all .ctors sections from all files except @file{crtend.o} and
-@file{otherfile.o} to be included.
+@noindent
+will behave identically to the previous example.  The difference
+between the two syntaxes for EXCLUDE_FILE is revealed when the section
+list contained more than one section.  This is described below.
 
 There are two ways to include more than one section:
 @smallexample
@@ -4214,8 +4224,9 @@
 @samp{.text} input sections will appear first, followed by all
 @samp{.rdata} input sections.
 
-When using EXCLUDE_FILE with more than one section, the exclusion only
-applies to the section immediately following, for example:
+When using EXCLUDE_FILE with more than one section, if the exclusion
+is within the section list then the exclusion only applies to the
+immediately following section, for example:
 @smallexample
 *(EXCLUDE_FILE (*somefile.o) .text .rdata)
 @end smallexample
@@ -4224,10 +4235,17 @@
 @file{somefile.o} to be included, while all @samp{.rdata} sections
 from all files, including @file{somefile.o}, will be included.  To
 exclude the @samp{.rdata} sections from @file{somefile.o} the example
-should be modified to:
+could be modified to:
 @smallexample
 *(EXCLUDE_FILE (*somefile.o) .text EXCLUDE_FILE (*somefile.o) .rdata)
 @end smallexample
+@noindent
+Alternatively, placing the EXCLUDE_FILE outside of the section list,
+before the input file selection, will cause the exclusion to apply for
+all sections.  Thus the previous example can be rewritten as:
+@smallexample
+EXCLUDE_FILE (*somefile.o) *(.text .rdata)
+@end smallexample
 
 You can specify a file name to include sections from a particular file.
 You would do this if one or more of your files contain special data that
diff --git a/ld/ldlang.c b/ld/ldlang.c
index af2ca99..7d495c0 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -223,23 +223,16 @@ unique_section_p (const asection *sec,
 
 /* Generic traversal routines for finding matching sections.  */
 
-/* Try processing a section against a wildcard.  This just calls
-   the callback unless the filename exclusion list is present
-   and excludes the file.  It's hardly ever present so this
-   function is very fast.  */
+/* Return true if FILE matches a pattern in EXCLUDE_LIST, otherwise return
+   false.  */
 
-static void
-walk_wild_consider_section (lang_wild_statement_type *ptr,
-			    lang_input_statement_type *file,
-			    asection *s,
-			    struct wildcard_list *sec,
-			    callback_t callback,
-			    void *data)
+static bfd_boolean
+walk_wild_file_in_exclude_list (struct name_list *exclude_list,
+                                lang_input_statement_type *file)
 {
   struct name_list *list_tmp;
 
-  /* Don't process sections from files which were excluded.  */
-  for (list_tmp = sec->spec.exclude_name_list;
+  for (list_tmp = exclude_list;
        list_tmp;
        list_tmp = list_tmp->next)
     {
@@ -248,11 +241,11 @@ walk_wild_consider_section (lang_wild_statement_type *ptr,
       if (p != NULL)
 	{
 	  if (input_statement_is_archive_path (list_tmp->name, p, file))
-	    return;
+	    return TRUE;
 	}
 
       else if (name_match (list_tmp->name, file->filename) == 0)
-	return;
+	return TRUE;
 
       /* FIXME: Perhaps remove the following at some stage?  Matching
 	 unadorned archives like this was never documented and has
@@ -261,9 +254,29 @@ walk_wild_consider_section (lang_wild_statement_type *ptr,
 	       && file->the_bfd->my_archive != NULL
 	       && name_match (list_tmp->name,
 			      file->the_bfd->my_archive->filename) == 0)
-	return;
+	return TRUE;
     }
 
+  return FALSE;
+}
+
+/* Try processing a section against a wildcard.  This just calls
+   the callback unless the filename exclusion list is present
+   and excludes the file.  It's hardly ever present so this
+   function is very fast.  */
+
+static void
+walk_wild_consider_section (lang_wild_statement_type *ptr,
+			    lang_input_statement_type *file,
+			    asection *s,
+			    struct wildcard_list *sec,
+			    callback_t callback,
+			    void *data)
+{
+  /* Don't process sections from files which were excluded.  */
+  if (walk_wild_file_in_exclude_list (sec->spec.exclude_name_list, file))
+    return;
+
   (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
 }
 
@@ -860,6 +873,9 @@ walk_wild_file (lang_wild_statement_type *s,
 		callback_t callback,
 		void *data)
 {
+  if (walk_wild_file_in_exclude_list (s->exclude_name_list, f))
+    return;
+
   if (f->the_bfd == NULL
       || !bfd_check_format (f->the_bfd, bfd_archive))
     walk_wild_section (s, f, callback, data);
@@ -4419,6 +4435,15 @@ print_wild_statement (lang_wild_statement_type *w,
 
   print_space ();
 
+  if (w->exclude_name_list)
+    {
+      name_list *tmp;
+      minfo ("EXCLUDE_FILE(%s", w->exclude_name_list->name);
+      for (tmp = w->exclude_name_list->next; tmp; tmp = tmp->next)
+        minfo (" %s", tmp->name);
+      minfo (") ");
+    }
+
   if (w->filenames_sorted)
     minfo ("SORT(");
   if (w->filename != NULL)
@@ -7073,11 +7098,13 @@ lang_add_wild (struct wildcard_spec *filespec,
   new_stmt->filename = NULL;
   new_stmt->filenames_sorted = FALSE;
   new_stmt->section_flag_list = NULL;
+  new_stmt->exclude_name_list = NULL;
   if (filespec != NULL)
     {
       new_stmt->filename = filespec->name;
       new_stmt->filenames_sorted = filespec->sorted == by_name;
       new_stmt->section_flag_list = filespec->section_flag_list;
+      new_stmt->exclude_name_list = filespec->exclude_name_list;
     }
   new_stmt->section_list = section_list;
   new_stmt->keep_sections = keep_sections;
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 0cb147c..b70eb99 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -372,6 +372,7 @@ struct lang_wild_statement_struct
   struct wildcard_list *section_list;
   bfd_boolean keep_sections;
   lang_statement_list_type children;
+  struct name_list *exclude_name_list;
 
   walk_wild_section_handler_t walk_wild_section_handler;
   struct wildcard_list *handler_data[4];
diff --git a/ld/testsuite/ld-scripts/exclude-file-1.d b/ld/testsuite/ld-scripts/exclude-file-1.d
new file mode 100644
index 0000000..068ecac
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-1.d
@@ -0,0 +1,5 @@
+#source: exclude-file-a.s
+#source: exclude-file-b.s
+#ld: -T exclude-file-1.t
+#map: exclude-file-1.map
+
diff --git a/ld/testsuite/ld-scripts/exclude-file-1.map b/ld/testsuite/ld-scripts/exclude-file-1.map
new file mode 100644
index 0000000..0fbf601
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-1.map
@@ -0,0 +1,8 @@
+#...
+\.data +0x[0-9a-f]+ +0x[0-9a-f]+
+ \*\(EXCLUDE_FILE\(\*-b\.o\) \.data \.data\.\*\)
+ \.data +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+ \.data\.1 +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+ \.data\.1 +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-b\.o
+
+#...
\ No newline at end of file
diff --git a/ld/testsuite/ld-scripts/exclude-file-1.t b/ld/testsuite/ld-scripts/exclude-file-1.t
new file mode 100644
index 0000000..f75e6c3
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-1.t
@@ -0,0 +1,10 @@
+SECTIONS
+{
+	.data : {
+		* (EXCLUDE_FILE (*-b.o) .data .data.*)
+	}
+
+	/DISCARD/ : {
+		* (*)
+	}
+}
diff --git a/ld/testsuite/ld-scripts/exclude-file-2.d b/ld/testsuite/ld-scripts/exclude-file-2.d
new file mode 100644
index 0000000..7c62c2f
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-2.d
@@ -0,0 +1,5 @@
+#source: exclude-file-a.s
+#source: exclude-file-b.s
+#ld: -T exclude-file-2.t
+#map: exclude-file-2.map
+
diff --git a/ld/testsuite/ld-scripts/exclude-file-2.map b/ld/testsuite/ld-scripts/exclude-file-2.map
new file mode 100644
index 0000000..67acfe7
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-2.map
@@ -0,0 +1,7 @@
+#...
+\.data +0x[0-9a-f]+ +0x[0-9a-f]+
+ \*\(EXCLUDE_FILE\(\*-b\.o\) \.data EXCLUDE_FILE\(\*-b\.o\) \.data\.\*\)
+ \.data +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+ \.data\.1 +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+
+#...
\ No newline at end of file
diff --git a/ld/testsuite/ld-scripts/exclude-file-2.t b/ld/testsuite/ld-scripts/exclude-file-2.t
new file mode 100644
index 0000000..efdb5c0
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-2.t
@@ -0,0 +1,10 @@
+SECTIONS
+{
+	.data : {
+		* (EXCLUDE_FILE (*-b.o) .data EXCLUDE_FILE (*-b.o) .data.*)
+	}
+
+	/DISCARD/ : {
+		* (*)
+	}
+}
diff --git a/ld/testsuite/ld-scripts/exclude-file-3.d b/ld/testsuite/ld-scripts/exclude-file-3.d
new file mode 100644
index 0000000..19bfc4f
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-3.d
@@ -0,0 +1,5 @@
+#source: exclude-file-a.s
+#source: exclude-file-b.s
+#ld: -T exclude-file-3.t
+#map: exclude-file-3.map
+
diff --git a/ld/testsuite/ld-scripts/exclude-file-3.map b/ld/testsuite/ld-scripts/exclude-file-3.map
new file mode 100644
index 0000000..389d170
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-3.map
@@ -0,0 +1,7 @@
+#...
+\.data +0x[0-9a-f]+ +0x[0-9a-f]+
+ EXCLUDE_FILE\(\*-b\.o\) \*\(\.data \.data\.\*\)
+ \.data +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+ \.data\.1 +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+
+#...
\ No newline at end of file
diff --git a/ld/testsuite/ld-scripts/exclude-file-3.t b/ld/testsuite/ld-scripts/exclude-file-3.t
new file mode 100644
index 0000000..4d4be58
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-3.t
@@ -0,0 +1,10 @@
+SECTIONS
+{
+	.data : {
+		EXCLUDE_FILE (*-b.o) * (.data .data.*)
+	}
+
+	/DISCARD/ : {
+		* (*)
+	}
+}
diff --git a/ld/testsuite/ld-scripts/exclude-file-4.d b/ld/testsuite/ld-scripts/exclude-file-4.d
new file mode 100644
index 0000000..b087fee
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-4.d
@@ -0,0 +1,5 @@
+#source: exclude-file-a.s
+#source: exclude-file-b.s
+#ld: -T exclude-file-4.t
+#map: exclude-file-4.map
+
diff --git a/ld/testsuite/ld-scripts/exclude-file-4.map b/ld/testsuite/ld-scripts/exclude-file-4.map
new file mode 100644
index 0000000..8549283
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-4.map
@@ -0,0 +1,7 @@
+#...
+\.data +0x[0-9a-f]+ +0x[0-9a-f]+
+ \*\(EXCLUDE_FILE\(\*-b\.o\) \.data EXCLUDE_FILE\(\*-a\.o\) \.data\.\*\)
+ \.data +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-a\.o
+ \.data\.1 +0x[0-9a-f]+ +0x[0-9a-f]+ tmpdir/exclude-file-b\.o
+
+#...
\ No newline at end of file
diff --git a/ld/testsuite/ld-scripts/exclude-file-4.t b/ld/testsuite/ld-scripts/exclude-file-4.t
new file mode 100644
index 0000000..a9b03c4
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-4.t
@@ -0,0 +1,10 @@
+SECTIONS
+{
+	.data : {
+		* (EXCLUDE_FILE (*-b.o) .data EXCLUDE_FILE (*-a.o) .data.*)
+	}
+
+	/DISCARD/ : {
+		* (*)
+	}
+}
diff --git a/ld/testsuite/ld-scripts/exclude-file-a.s b/ld/testsuite/ld-scripts/exclude-file-a.s
new file mode 100644
index 0000000..19f5def
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-a.s
@@ -0,0 +1,5 @@
+        .section ".data", "aw"
+        .word 0x0
+
+        .section ".data.1", "aw"
+        .word 0x0
diff --git a/ld/testsuite/ld-scripts/exclude-file-b.s b/ld/testsuite/ld-scripts/exclude-file-b.s
new file mode 100644
index 0000000..19f5def
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file-b.s
@@ -0,0 +1,5 @@
+        .section ".data", "aw"
+        .word 0x0
+
+        .section ".data.1", "aw"
+        .word 0x0
diff --git a/ld/testsuite/ld-scripts/exclude-file.exp b/ld/testsuite/ld-scripts/exclude-file.exp
new file mode 100644
index 0000000..79fba17
--- /dev/null
+++ b/ld/testsuite/ld-scripts/exclude-file.exp
@@ -0,0 +1,32 @@
+# Test EXCLUDE_FILE in a linker script.
+# By Nathan Sidwell, CodeSourcery LLC
+#   Copyright (C) 2004-2016 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+if { ![is_elf_format] } then {
+    unsupported exclude-file.exp
+    return
+}
+
+set test_list [lsort [glob -nocomplain $srcdir/$subdir/exclude-file-*.d]]
+foreach t $test_list {
+    # We need to strip the ".d", but can leave the dirname.
+    verbose [file rootname $t]
+    run_dump_test [file rootname $t]
+}
-- 
2.5.1



More information about the Binutils mailing list