aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2024-05-10 16:59:05 +0100
committerNick Clifton <nickc@redhat.com>2024-05-10 16:59:05 +0100
commit6e8376fa569e62379a42b91b0afd1f4086f1d897 (patch)
treed9bddaa0a8b3b242ba063df4dbbee272220ea385
parentad658482c1e4feec4ac99c8e7df17ff78d0c2996 (diff)
downloadbinutils-6e8376fa569e62379a42b91b0afd1f4086f1d897.zip
binutils-6e8376fa569e62379a42b91b0afd1f4086f1d897.tar.gz
binutils-6e8376fa569e62379a42b91b0afd1f4086f1d897.tar.bz2
Add --section-ordering command line option to the bfd linker.
-rw-r--r--ld/NEWS3
-rw-r--r--ld/ld.h4
-rw-r--r--ld/ld.texi72
-rw-r--r--ld/ldfile.c15
-rw-r--r--ld/ldfile.h19
-rw-r--r--ld/ldgram.y36
-rw-r--r--ld/ldlang.c81
-rw-r--r--ld/ldlang.h5
-rw-r--r--ld/ldlex.h2
-rw-r--r--ld/ldlex.l11
-rw-r--r--ld/ldmain.c55
-rw-r--r--ld/lexsup.c9
-rw-r--r--ld/testsuite/ld-scripts/section-order-1a.d22
-rw-r--r--ld/testsuite/ld-scripts/section-order-1a.s29
-rw-r--r--ld/testsuite/ld-scripts/section-order-1a.t14
-rw-r--r--ld/testsuite/ld-scripts/section-order-1b.d18
-rw-r--r--ld/testsuite/ld-scripts/section-order-1b.s34
-rw-r--r--ld/testsuite/ld-scripts/section-order-1b.t7
-rw-r--r--ld/testsuite/ld-scripts/section-order-1c.d14
-rw-r--r--ld/testsuite/ld-scripts/section-order-1d.d18
-rw-r--r--ld/testsuite/ld-scripts/section-order.exp45
-rw-r--r--ld/testsuite/ld-scripts/start.s14
22 files changed, 462 insertions, 65 deletions
diff --git a/ld/NEWS b/ld/NEWS
index f70d215..00eb460 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,8 @@
-*- text -*-
+* Add --section-ordering-file <FILE> option to add extra mapping of input
+ sections to output sections.
+
* Add -plugin-save-temps to store plugin intermediate files permanently.
Changes in 2.42:
diff --git a/ld/ld.h b/ld/ld.h
index fcdd9a2..0dee944 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -196,6 +196,9 @@ typedef struct
/* Default linker script. */
char *default_script;
+
+ /* Linker script fragment provided by the --section-order command line option. */
+ char *section_ordering_file;
} args_type;
extern args_type command_line;
@@ -325,6 +328,7 @@ extern ld_config_type config;
extern FILE * saved_script_handle;
extern bool force_make_executable;
+extern bool in_section_ordering;
extern int yyparse (void);
extern void add_cref (const char *, bfd *, asection *, bfd_vma);
diff --git a/ld/ld.texi b/ld/ld.texi
index ca9574d..b32bb46 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -302,6 +302,7 @@ and the script command language. If @emph{no} binary input files at all
are specified, the linker does not produce any output, and issues the
message @samp{No input files}.
+@anchor{unrecognised-input-files}
If the linker cannot recognize the format of an object file, it will
assume that it is a linker script. A script specified in this way
augments the main linker script used for the link (either the default
@@ -1163,18 +1164,32 @@ a linker bug report.
@itemx --script=@var{scriptfile}
Use @var{scriptfile} as the linker script. This script replaces
@command{ld}'s default linker script (rather than adding to it),
-unless the script contains @code{INSERT}, so
-@var{commandfile} must specify everything necessary to describe the
-output file. @xref{Scripts}. If @var{scriptfile} does not exist in
-the current directory, @code{ld} looks for it in the directories
-specified by any preceding @samp{-L} options. Multiple @samp{-T}
-options accumulate.
+unless the script contains @code{INSERT}, so @var{commandfile} must
+specify everything necessary to describe the output file.
+@xref{Scripts}.
+
+If @var{scriptfile} does not exist in the current directory, @code{ld}
+looks for it in the directories specified by any preceding @samp{-L}
+options.
+
+Command line options that appear before the @option{-T} option can
+affect the script, but command line options that appear after it do
+not.
+
+Multiple @samp{-T} options will accumulate if they are augmenting the
+current script, otherwise the last, non-augmenting, @option{-T} option
+will be used.
+
+There are other ways of specifying linker scripts. See
+@xref{--default-script}, @xref{--section-ordering-file} and
+@xref{unrecognised-input-files}.
@kindex -dT @var{script}
@kindex --default-script=@var{script}
@cindex script files
@item -dT @var{scriptfile}
@itemx --default-script=@var{scriptfile}
+@anchor{--default-script}
Use @var{scriptfile} as the default linker script. @xref{Scripts}.
This option is similar to the @option{--script} option except that
@@ -2521,6 +2536,51 @@ warning and continue with the link.
@end ifset
+@kindex --section-ordering-file
+@item --section-ordering-file=@var{script}
+@anchor{--section-ordering-file}
+This option is used to augment the current linker script with
+additional mapping of input sections to output sections. This file
+must use the same syntax for @code{SECTIONS} as is used in normal
+linker scripts, but it should not do anything other than place input
+sections into output sections. @pxref{SECTIONS}
+
+A second constraint on the section ordering script is that it can only
+reference output sections that are already defined by whichever linker
+script is currently in use. (Ie the default linker script or a script
+specified on the command line). The benefit of the section ordering
+script however is that the input sections are mapped to the start of
+the output sections, so that they can ensure the ordering of sections
+in the output section. For example, imagine that the default linker
+script looks like this:
+
+@smallexample
+SECTIONS @{
+ .text : @{ *(.text.hot) ; *(.text .text.*) @}
+ .data : @{ *(.data.big) ; *(.data .data.*) @}
+ @}
+@end smallexample
+
+Then if a section ordering file like this is used:
+
+@smallexample
+ .text : @{ *(.text.first) ; *(.text.z*) @}
+ .data : @{ foo.o(.data.first) ; *(.data.small) @}
+@end smallexample
+
+This would be equivalent to a linker script like this:
+
+@smallexample
+SECTIONS @{
+ .text : @{ *(.text.first) ; *(.text.z*) ; *(.text.hot) ; *(.text .text.*) @}
+ .data : @{ foo.o(.data.first) ; *(.data.small) ; *(.data.big) ; *(.data .data.*) @}
+ @}
+@end smallexample
+
+The advantage of the section ordering file is that it can be used to
+order those sections that matter to the user without having to worry
+about any other sections, or memory regions, or anything else.
+
@kindex -shared
@kindex -Bshareable
@item -shared
diff --git a/ld/ldfile.c b/ld/ldfile.c
index dc9875d..f1107a1 100644
--- a/ld/ldfile.c
+++ b/ld/ldfile.c
@@ -871,19 +871,7 @@ ldfile_find_command_file (const char *name,
return result;
}
-enum script_open_style {
- script_nonT,
- script_T,
- script_defaultT
-};
-
-struct script_name_list
-{
- struct script_name_list *next;
- enum script_open_style open_how;
- char name[1];
-};
-
+struct script_name_list *processed_scripts = NULL;
/* Open command file NAME. */
static void
@@ -891,7 +879,6 @@ ldfile_open_command_file_1 (const char *name, enum script_open_style open_how)
{
FILE *ldlex_input_stack;
bool sysrooted;
- static struct script_name_list *processed_scripts = NULL;
struct script_name_list *script;
size_t len;
diff --git a/ld/ldfile.h b/ld/ldfile.h
index f17677e..f79abf2 100644
--- a/ld/ldfile.h
+++ b/ld/ldfile.h
@@ -29,7 +29,8 @@ extern const char *ldfile_output_machine_name;
/* Structure used to hold the list of directories to search for
libraries. */
-typedef struct search_dirs {
+typedef struct search_dirs
+{
/* Next directory on list. */
struct search_dirs *next;
/* Name of directory. */
@@ -38,6 +39,22 @@ typedef struct search_dirs {
bool cmdline;
} search_dirs_type;
+enum script_open_style
+{
+ script_nonT,
+ script_T,
+ script_defaultT
+};
+
+struct script_name_list
+{
+ struct script_name_list * next;
+ enum script_open_style open_how;
+ char name[1];
+};
+
+extern struct script_name_list * processed_scripts;
+
extern search_dirs_type *search_head;
extern void ldfile_add_arch
diff --git a/ld/ldgram.y b/ld/ldgram.y
index 0d531fd..07c19ba 100644
--- a/ld/ldgram.y
+++ b/ld/ldgram.y
@@ -157,7 +157,7 @@ static void yyerror (const char *);
%token LOG2CEIL FORMAT PUBLIC DEFSYMEND BASE ALIAS TRUNCATE REL
%token INPUT_SCRIPT INPUT_MRI_SCRIPT INPUT_DEFSYM CASE EXTERN START
%token <name> VERS_TAG VERS_IDENTIFIER
-%token GLOBAL LOCAL VERSIONK INPUT_VERSION_SCRIPT
+%token GLOBAL LOCAL VERSIONK INPUT_VERSION_SCRIPT INPUT_SECTION_ORDERING_SCRIPT
%token KEEP ONLY_IF_RO ONLY_IF_RW SPECIAL INPUT_SECTION_FLAGS ALIGN_WITH_INPUT
%token EXCLUDE_FILE
%token CONSTANT
@@ -172,6 +172,7 @@ file:
INPUT_SCRIPT script_file
| INPUT_MRI_SCRIPT mri_script_file
| INPUT_VERSION_SCRIPT version_script_file
+ | INPUT_SECTION_ORDERING_SCRIPT section_ordering_script_file
| INPUT_DYNAMIC_LIST dynamic_list_file
| INPUT_DEFSYM defsym_expr
;
@@ -1539,6 +1540,39 @@ opt_semicolon:
| ';'
;
+section_ordering_script_file:
+ {
+ ldlex_script ();
+ PUSH_ERROR (_("section-ordering-file script"));
+ }
+ section_ordering_list
+ {
+ ldlex_popstate ();
+ POP_ERROR ();
+ }
+ ;
+
+section_ordering_list:
+ section_ordering_list section_order
+ | section_ordering_list statement_anywhere
+ |
+ ;
+
+section_order: NAME ':'
+ {
+ ldlex_wild ();
+ lang_enter_output_section_statement
+ ($1, NULL, 0, NULL, NULL, NULL, NULL, 0, 0);
+ }
+ '{'
+ statement_list_opt
+ '}'
+ {
+ ldlex_popstate ();
+ lang_leave_output_section_statement (NULL, NULL, NULL, NULL);
+ }
+ opt_comma
+
%%
static void
yyerror (const char *arg)
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 54d1af6..9e8cc22 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -1299,6 +1299,7 @@ output_section_statement_newfunc (struct bfd_hash_entry *entry,
ret->s.output_section_statement.section_alignment = NULL;
ret->s.output_section_statement.block_value = 1;
lang_list_init (&ret->s.output_section_statement.children);
+ lang_list_init (&ret->s.output_section_statement.sort_children);
lang_statement_append (stat_ptr, &ret->s, &ret->s.header.next);
/* For every output section statement added to the list, except the
@@ -7613,13 +7614,22 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
lang_output_section_statement_type *os;
os = lang_output_section_statement_lookup (output_section_statement_name,
- constraint, 2);
+ constraint,
+ in_section_ordering ? 0 : 2);
+ if (os == NULL) /* && in_section_ordering */
+ einfo (_("%F%P:%pS: error: output section '%s' must already exist\n"),
+ NULL, output_section_statement_name);
current_section = os;
+ /* Make next things chain into subchain of this. */
+ push_stat_ptr (in_section_ordering ? &os->sort_children : &os->children);
+
+ if (in_section_ordering)
+ return os;
+
if (os->addr_tree == NULL)
- {
- os->addr_tree = address_exp;
- }
+ os->addr_tree = address_exp;
+
os->sectype = sectype;
if (sectype == type_section || sectype == typed_readonly_section)
os->sectype_value = sectype_value;
@@ -7629,9 +7639,6 @@ lang_enter_output_section_statement (const char *output_section_statement_name,
os->flags = SEC_NO_FLAGS;
os->block_value = 1;
- /* Make next things chain into subchain of this. */
- push_stat_ptr (&os->children);
-
os->align_lma_with_input = align_with_input == ALIGN_WITH_INPUT;
if (os->align_lma_with_input && align != NULL)
einfo (_("%F%P:%pS: error: align with input and explicit align specified\n"),
@@ -7971,21 +7978,6 @@ find_rescan_insertion (lang_input_statement_type *add)
return iter;
}
-/* Insert SRCLIST into DESTLIST after given element by chaining
- on FIELD as the next-pointer. (Counterintuitively does not need
- a pointer to the actual after-node itself, just its chain field.) */
-
-static void
-lang_list_insert_after (lang_statement_list_type *destlist,
- lang_statement_list_type *srclist,
- lang_statement_union_type **field)
-{
- *(srclist->tail) = *field;
- *field = srclist->head;
- if (destlist->tail == field)
- destlist->tail = srclist->tail;
-}
-
/* Detach new nodes added to DESTLIST since the time ORIGLIST
was taken as a copy of it and leave them in ORIGLIST. */
@@ -8033,6 +8025,21 @@ find_next_input_statement (lang_statement_union_type **s)
}
#endif /* BFD_SUPPORTS_PLUGINS */
+/* Insert SRCLIST into DESTLIST after given element by chaining
+ on FIELD as the next-pointer. (Counterintuitively does not need
+ a pointer to the actual after-node itself, just its chain field.) */
+
+static void
+lang_list_insert_after (lang_statement_list_type *destlist,
+ lang_statement_list_type *srclist,
+ lang_statement_union_type **field)
+{
+ *(srclist->tail) = *field;
+ *field = srclist->head;
+ if (destlist->tail == field)
+ destlist->tail = srclist->tail;
+}
+
/* Add NAME to the list of garbage collection entry points. */
void
@@ -8127,9 +8134,34 @@ reset_resolved_wilds (void)
lang_for_each_statement (reset_one_wild);
}
+/* For each output section statement, splice any entries on the
+ sort_children list before the first wild statement on the children
+ list. */
+
+static void
+lang_os_merge_sort_children (void)
+{
+ lang_output_section_statement_type *os;
+ for (os = (void *) lang_os_list.head; os != NULL; os = os->next)
+ {
+ if (os->sort_children.head != NULL)
+ {
+ lang_statement_union_type **where;
+ for (where = &os->children.head;
+ *where != NULL;
+ where = &(*where)->header.next)
+ if ((*where)->header.type == lang_wild_statement_enum)
+ break;
+ lang_list_insert_after (&os->children, &os->sort_children, where);
+ }
+ }
+}
+
void
lang_process (void)
{
+ lang_os_merge_sort_children ();
+
/* Finalize dynamic list. */
if (link_info.dynamic_list)
lang_finalize_version_expr_head (&link_info.dynamic_list->head);
@@ -8817,6 +8849,10 @@ lang_leave_output_section_statement (fill_type *fill, const char *memspec,
lang_output_section_phdr_list *phdrs,
const char *lma_memspec)
{
+ pop_stat_ptr ();
+ if (in_section_ordering)
+ return;
+
lang_get_regions (&current_section->region,
&current_section->lma_region,
memspec, lma_memspec,
@@ -8825,7 +8861,6 @@ lang_leave_output_section_statement (fill_type *fill, const char *memspec,
current_section->fill = fill;
current_section->phdrs = phdrs;
- pop_stat_ptr ();
}
/* Set the output format type. -oformat overrides scripts. */
diff --git a/ld/ldlang.h b/ld/ldlang.h
index ea1c26d..4c1bb00 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -141,7 +141,12 @@ typedef struct lang_output_section_phdr_list
typedef struct lang_output_section_statement_struct
{
lang_statement_header_type header;
+ /* Input sections to be mapped to this output section. */
lang_statement_list_type children;
+ /* Input sections to be mapped to the start of this output section.
+ These sections are provided by the --section-ordering file, if used. */
+ lang_statement_list_type sort_children;
+
struct lang_output_section_statement_struct *next;
struct lang_output_section_statement_struct *prev;
const char *name;
diff --git a/ld/ldlex.h b/ld/ldlex.h
index d575562..7a0c3b4 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -62,6 +62,7 @@ enum option_values
OPTION_SONAME,
OPTION_SORT_COMMON,
OPTION_SORT_SECTION,
+ OPTION_SECTION_ORDERING_FILE,
OPTION_STATS,
OPTION_SYMBOLIC,
OPTION_SYMBOLIC_FUNCTIONS,
@@ -477,6 +478,7 @@ typedef enum input_enum
input_script,
input_mri_script,
input_version_script,
+ input_section_ordering_script,
input_dynamic_list,
input_defsym
} input_type;
diff --git a/ld/ldlex.l b/ld/ldlex.l
index e113c90..aa61310 100644
--- a/ld/ldlex.l
+++ b/ld/ldlex.l
@@ -120,11 +120,12 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
parser_input = input_selected;
switch (t)
{
- case input_script: return INPUT_SCRIPT; break;
- case input_mri_script: return INPUT_MRI_SCRIPT; break;
- case input_version_script: return INPUT_VERSION_SCRIPT; break;
- case input_dynamic_list: return INPUT_DYNAMIC_LIST; break;
- case input_defsym: return INPUT_DEFSYM; break;
+ case input_script: return INPUT_SCRIPT;
+ case input_mri_script: return INPUT_MRI_SCRIPT;
+ case input_version_script: return INPUT_VERSION_SCRIPT;
+ case input_section_ordering_script: return INPUT_SECTION_ORDERING_SCRIPT;
+ case input_dynamic_list: return INPUT_DYNAMIC_LIST;
+ case input_defsym: return INPUT_DEFSYM;
default: abort ();
}
}
diff --git a/ld/ldmain.c b/ld/ldmain.c
index fe38968..037099b 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -90,6 +90,8 @@ bool version_printed;
/* TRUE if we should demangle symbol names. */
bool demangling;
+bool in_section_ordering;
+
args_type command_line;
ld_config_type config;
@@ -246,6 +248,26 @@ ld_bfd_error_handler (const char *fmt, va_list ap)
(*default_bfd_error_handler) (fmt, ap);
}
+static void
+display_external_script (void)
+{
+ if (saved_script_handle == NULL)
+ return;
+
+ static const int ld_bufsz = 8193;
+ size_t n;
+ char *buf = (char *) xmalloc (ld_bufsz);
+
+ rewind (saved_script_handle);
+ while ((n = fread (buf, 1, ld_bufsz - 1, saved_script_handle)) > 0)
+ {
+ buf[n] = 0;
+ info_msg ("%s", buf);
+ }
+ rewind (saved_script_handle);
+ free (buf);
+}
+
int
main (int argc, char **argv)
{
@@ -416,26 +438,13 @@ main (int argc, char **argv)
if (verbose)
{
if (saved_script_handle)
- info_msg (_("using external linker script:"));
+ info_msg (_("using external linker script: %s"), processed_scripts->name);
else
info_msg (_("using internal linker script:"));
info_msg ("\n==================================================\n");
if (saved_script_handle)
- {
- static const int ld_bufsz = 8193;
- size_t n;
- char *buf = (char *) xmalloc (ld_bufsz);
-
- rewind (saved_script_handle);
- while ((n = fread (buf, 1, ld_bufsz - 1, saved_script_handle)) > 0)
- {
- buf[n] = 0;
- info_msg ("%s", buf);
- }
- rewind (saved_script_handle);
- free (buf);
- }
+ display_external_script ();
else
{
int isfile;
@@ -446,6 +455,22 @@ main (int argc, char **argv)
info_msg ("\n==================================================\n");
}
+ if (command_line.section_ordering_file)
+ {
+ FILE *hold_script_handle;
+
+ hold_script_handle = saved_script_handle;
+ ldfile_open_command_file (command_line.section_ordering_file);
+ if (verbose)
+ display_external_script ();
+ saved_script_handle = hold_script_handle;
+ in_section_ordering = true;
+ parser_input = input_section_ordering_script;
+ yyparse ();
+ in_section_ordering = false;
+
+ }
+
if (command_line.force_group_allocation
|| !bfd_link_relocatable (&link_info))
link_info.resolve_section_groups = true;
diff --git a/ld/lexsup.c b/ld/lexsup.c
index dad3b60..4125d84 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -487,6 +487,9 @@ static const struct ld_option ld_options[] =
{ {"sort-section", required_argument, NULL, OPTION_SORT_SECTION},
'\0', N_("name|alignment"),
N_("Sort sections by name or maximum alignment"), TWO_DASHES },
+ { {"section-ordering-file", required_argument, NULL, OPTION_SECTION_ORDERING_FILE},
+ '\0', N_("FILE"),
+ N_("Sort sections by statements in FILE"), TWO_DASHES },
{ {"spare-dynamic-tags", required_argument, NULL, OPTION_SPARE_DYNAMIC_TAGS},
'\0', N_("COUNT"), N_("How many tags to reserve in .dynamic section"),
TWO_DASHES },
@@ -1400,6 +1403,12 @@ parse_args (unsigned argc, char **argv)
einfo (_("%F%P: invalid section sorting option: %s\n"),
optarg);
break;
+ case OPTION_SECTION_ORDERING_FILE:
+ if (command_line.section_ordering_file != NULL
+ && strcmp (optarg, command_line.section_ordering_file) != 0)
+ einfo (_("%P: warning: section ordering file changed. Ignoring earlier definition\n"));
+ command_line.section_ordering_file = optarg;
+ break;
case OPTION_STATS:
config.stats = true;
break;
diff --git a/ld/testsuite/ld-scripts/section-order-1a.d b/ld/testsuite/ld-scripts/section-order-1a.d
new file mode 100644
index 0000000..40730b8
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1a.d
@@ -0,0 +1,22 @@
+#name: Text Section Ordering (section-order-1a)
+#source: section-order-1b.s
+#source: section-order-1a.s
+#source: start.s
+#ld: --section-ordering-file section-order-1a.t
+#nm: -n
+
+#...
+[0-9a-f]+ T yyy
+#...
+[0-9a-f]+ T bar
+#...
+[0-9a-f]+ T [_]+start
+#...
+[0-9a-f]+ T xxx
+#...
+[0-9a-f]+ T foo
+#...
+[0-9a-f]+ T qqq
+#...
+[0-9a-f]+ T zzz
+#pass
diff --git a/ld/testsuite/ld-scripts/section-order-1a.s b/ld/testsuite/ld-scripts/section-order-1a.s
new file mode 100644
index 0000000..2394a7b
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1a.s
@@ -0,0 +1,29 @@
+ .section .text.foo
+ .globl foo
+foo:
+ .dc.a 0
+
+ .section .text.bar
+ .globl bar
+bar:
+ .dc.a 0
+
+ .section .data.small
+ .globl small
+small:
+ .dc.a 0
+
+ .section .bar
+ .global bar
+bart:
+ .dc.a 0
+
+ .section .text.zzz
+ .global zzz
+zzz:
+ .dc.a 0
+
+ .section .data.bbb
+ .global bbb
+bbb:
+ .dc.a 0
diff --git a/ld/testsuite/ld-scripts/section-order-1a.t b/ld/testsuite/ld-scripts/section-order-1a.t
new file mode 100644
index 0000000..2e28bfa
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1a.t
@@ -0,0 +1,14 @@
+.text : {
+ *(.text.yyy)
+ *(.text.b?r)
+ *(.text)
+ *(.text.xxx .text.foo)
+}
+
+.data : {
+ *(.data.small)
+ *(.big*)
+ *(.bar .baz*)
+ *(.data.ccc)
+}
+
diff --git a/ld/testsuite/ld-scripts/section-order-1b.d b/ld/testsuite/ld-scripts/section-order-1b.d
new file mode 100644
index 0000000..caf713d
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1b.d
@@ -0,0 +1,18 @@
+#name: Text Section Ordering (section-order-1b)
+#source: section-order-1a.s
+#source: section-order-1b.s
+#source: start.s
+#ld: --section-ordering-file section-order-1b.t
+#nm: -n
+
+#...
+[0-9a-f]+ T yyy
+#...
+[0-9a-f]+ T bar
+#...
+[0-9a-f]+ T [_]+start
+#...
+[0-9a-f]+ T xxx
+#...
+[0-9a-f]+ T foo
+#pass
diff --git a/ld/testsuite/ld-scripts/section-order-1b.s b/ld/testsuite/ld-scripts/section-order-1b.s
new file mode 100644
index 0000000..49a0b32
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1b.s
@@ -0,0 +1,34 @@
+ .section .text.xxx
+ .globl xxx
+xxx:
+ .dc.a 0
+
+ .section .text.yyy
+ .globl yyy
+yyy:
+ .dc.a 0
+
+ .section .big
+ .global big
+big:
+ .dc.a 0
+
+ .section .baz
+ .global baz
+baz:
+ .dc.a 0
+
+ .section .text.qqq
+ .global qqq
+qqq:
+ .dc.a 0
+
+ .section .data.ccc
+ .global ccc
+ccc:
+ .dc.a 0
+
+ .data
+ .global data_symbol
+data_symbol:
+ .dc.a 0
diff --git a/ld/testsuite/ld-scripts/section-order-1b.t b/ld/testsuite/ld-scripts/section-order-1b.t
new file mode 100644
index 0000000..6a36250
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1b.t
@@ -0,0 +1,7 @@
+.text : {
+ *(.text.yyy)
+ *(.text.b?r)
+ *(*t)
+ *(.text.xxx)
+ *(.text.foo)
+}
diff --git a/ld/testsuite/ld-scripts/section-order-1c.d b/ld/testsuite/ld-scripts/section-order-1c.d
new file mode 100644
index 0000000..ad06d4a
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1c.d
@@ -0,0 +1,14 @@
+#name: Data Section Ordering (section-order-1c)
+#source: section-order-1b.s
+#source: section-order-1a.s
+#source: start.s
+#ld: --section-ordering-file section-order-1a.t
+#nm: -n
+
+#...
+[0-9a-f]+ D small
+#...
+[0-9a-f]+ D big
+#...
+[0-9a-f]+ D ba.*
+#pass
diff --git a/ld/testsuite/ld-scripts/section-order-1d.d b/ld/testsuite/ld-scripts/section-order-1d.d
new file mode 100644
index 0000000..d016505
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order-1d.d
@@ -0,0 +1,18 @@
+#name: Data Section Ordering (section-order-1d)
+#source: section-order-1a.s
+#source: section-order-1b.s
+#source: start.s
+#ld: --section-ordering-file section-order-1a.t
+#nm: -n
+
+#...
+[0-9a-f]+ D small
+#...
+[0-9a-f]+ D big
+#...
+[0-9a-f]+ d bart
+#...
+[0-9a-f]+ D ccc
+#...
+[0-9a-f]+ D bbb
+#pass
diff --git a/ld/testsuite/ld-scripts/section-order.exp b/ld/testsuite/ld-scripts/section-order.exp
new file mode 100644
index 0000000..9b87e74
--- /dev/null
+++ b/ld/testsuite/ld-scripts/section-order.exp
@@ -0,0 +1,45 @@
+# Test for --section-ordering-file FILE.
+# Copyright (C) 2024 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.
+
+# The --section-ordering-file option is only supported by ELF and
+# PE COFF linkers, which allow for arbitrarily named sections, eg:
+# .text.*
+if { !([is_elf_format] || [is_pecoff_format]) } {
+ return
+}
+
+set old_ldflags $LDFLAGS
+if { [istarget spu*-*-*] } then {
+ set LDFLAGS "$LDFLAGS --local-store 0:0 --no-overlays"
+} elseif { [is_pecoff_format] } then {
+ set LDFLAGS "$LDFLAGS --image-base 0"
+} elseif { [is_xcoff_format] } then {
+ set LDFLAGS "$LDFLAGS -bnogc"
+}
+
+set test_list [lsort [glob -nocomplain $srcdir/$subdir/section-order*.d]]
+foreach test_file $test_list {
+ set test_name [file rootname $test_file]
+ set map_file "tmpdir/[file tail $test_name].map"
+ verbose $test_name
+ run_dump_test $test_name
+}
+
+set LDFLAGS $old_ldflags
diff --git a/ld/testsuite/ld-scripts/start.s b/ld/testsuite/ld-scripts/start.s
new file mode 100644
index 0000000..3f64626
--- /dev/null
+++ b/ld/testsuite/ld-scripts/start.s
@@ -0,0 +1,14 @@
+ .text
+ .global start /* Used by SH targets. */
+start:
+ .global _start
+_start:
+ .global __start
+__start:
+ .global _mainCRTStartup /* Used by PE targets. */
+_mainCRTStartup:
+ .global main /* Used by HPPA targets. */
+main:
+ .globl _main /* Used by LynxOS targets. */
+_main:
+ .dc.a 0