Add --remap-inputs option to the BFD linker.

PR 30374
  * ldfile.c (struct input_remap): New structure. (ldfile_add_remap): New function. (ldfile_remap_input_free): New function. (ldfile_add_remap_file): New function. (ldfile_possibly_remap_input): New function. (ldfile_print_input_remaps): New function. * ldfile.h: Add prototypes for new functions.
  * ldlang.c (new_afile): Call ldfile_possibly_remap_input. (lang_finish): Call ldfile_remap_input_free. (lang_map): Call ldfile_print_input_remaps.
  * ldlex.h (OPTION_REMAP_INPUTS, OPTION_REMAP_INPUTS_FILE): Define.
  * lexsup.c (ld_options): Add --remap-inputs-file and --remap-inputs. (parse_args): Handle new options.
  * NEWS: Mention the new feature.
  * ld.texi: Document the new options.
  * testsuite/ld-misc/input-remap.exp: New test driver.
  * testsuite/ld-misc/remaps.r: New file: Expected linker output.
  * testsuite/ld-misc/remaps.txt: New file.  Input remaps file.
This commit is contained in:
Nick Clifton 2023-06-14 13:39:03 +01:00
parent 6f860418d5
commit fb221fba1a
11 changed files with 429 additions and 0 deletions

View File

@ -1,3 +1,25 @@
2023-06-14 Nick Clifton <nickc@redhat.com>
PR 30374
* ldfile.c (struct input_remap): New structure.
(ldfile_add_remap): New function.
(ldfile_remap_input_free): New function.
(ldfile_add_remap_file): New function.
(ldfile_possibly_remap_input): New function.
(ldfile_print_input_remaps): New function.
* ldfile.h: Add prototypes for new functions.
* ldlang.c (new_afile): Call ldfile_possibly_remap_input.
(lang_finish): Call ldfile_remap_input_free.
(lang_map): Call ldfile_print_input_remaps.
* ldlex.h (OPTION_REMAP_INPUTS, OPTION_REMAP_INPUTS_FILE): Define.
* lexsup.c (ld_options): Add --remap-inputs-file and --remap-inputs.
(parse_args): Handle new options.
* NEWS: Mention the new feature.
* ld.texi: Document the new options.
* testsuite/ld-misc/input-remap.exp: New test driver.
* testsuite/ld-misc/remaps.r: New file: Expected linker output.
* testsuite/ld-misc/remaps.txt: New file. Input remaps file.
2023-06-07 Nick Clifton <nickc@redhat.com>
PR 30499

View File

@ -1,5 +1,10 @@
-*- text -*-
* The linker now accepts a command line option of --remap-inputs
<PATTERN>=<FILE> to relace any input file that matches <PATTERN> with
<FILE>. In addition the option --remap-inputs-file=<FILE> can be used to
specify a file containing any number of these remapping directives.
* The linker command line option --print-map-locals can be used to include
local symbols in a linker map. (ELF targets only).

View File

@ -793,6 +793,66 @@ If the @samp{-m} option is not used, the emulation is taken from the
Otherwise, the default emulation depends upon how the linker was
configured.
@cindex remapping inputs
@kindex --remap-inputs=@file{pattern}=@file{filename}
@kindex --remap-inputs-file=@file{file}
@item --remap-inputs=@file{pattern}=@file{filename}
@itemx --remap-inputs-file=@file{file}
These options allow the names of input files to be changed before the
linker attempts to open them. The option
@option{--remap-inputs=foo.o=bar.o} will cause any attempt to load a
file called @file{foo.o} to instead try to load a file called
@file{bar.o}. Wildcard patterns are permitted in the first filename,
so @option{--remap-inputs=foo*.o=bar.o} will rename any input file that
matches @file{foo*.o} to @file{bar.o}.
An alternative form of the option
@option{--remap-inputs-file=filename} allows the remappings to be read
from a file. Each line in the file can contain a single remapping.
Blank lines are ignored. Anything from a hash character (@samp{#}) to
the end of a line is considered to be a comment and is also ignored.
The mapping pattern can be separated from the filename by whitespace
or an equals (@samp{=}) character.
The options can be specified multiple times. Their contents
accumulate. The remappings will be processed in the order in which
they occur on the command line, and if they come from a file, in the
order in which they occur in the file. If a match is made, no further
checking for that filename will be performed.
If the replacement filename is @file{/dev/null} or just @file{NUL}
then the remapping will actually cause the input file to be ignored.
This can be a convenient way to experiment with removing input files
from a complicated build environment.
Note that this option is position dependent and only affects filenames
that come after it on the command line. Thus:
@smallexample
ld foo.o --remap-inputs=foo.o=bar.o
@end smallexample
Will have no effect, whereas:
@smallexample
ld --remap-inputs=foo.o=bar.o foo.o
@end smallexample
Will rename the input file @file{foo.o} to @file{bar.o}.
Note - these options also affect files referenced by @emph{INPUT}
statements in linker scripts. But since linker scripts are processed
after the entire command line is read, the position of the remap
options on the command line is not significant.
If the @option{verbose} option is enabled then any mappings that match
will be reported, although again the @option{verbose} option needs to
be enabled on the command line @emph{before} the remaped filenames
appear.
If the @option{-Map} or @option{--print-map} options are enabled then
the remapping list will be included in the map output.
@cindex link map
@kindex -M
@kindex --print-map

View File

@ -34,6 +34,7 @@
#include "ldemul.h"
#include "libiberty.h"
#include "filenames.h"
#include <fnmatch.h>
#if BFD_SUPPORTS_PLUGINS
#include "plugin-api.h"
#include "plugin.h"
@ -65,6 +66,215 @@ static search_dirs_type **search_tail_ptr = &search_head;
static search_arch_type *search_arch_head;
static search_arch_type **search_arch_tail_ptr = &search_arch_head;
typedef struct input_remap
{
const char * pattern; /* Pattern to match input files. */
const char * renamed; /* Filename to use if the pattern matches. */
struct input_remap * next; /* Link in a chain of these structures. */
} input_remap;
static struct input_remap * input_remaps = NULL;
void
ldfile_add_remap (const char * pattern, const char * renamed)
{
struct input_remap * new_entry;
new_entry = xmalloc (sizeof * new_entry);
new_entry->pattern = xstrdup (pattern);
new_entry->next = NULL;
/* Look for special filenames that mean that the input file should be ignored. */
if (strcmp (renamed, "/dev/null") == 0
|| strcmp (renamed, "NUL") == 0)
new_entry->renamed = NULL;
else
/* FIXME: Should we add sanity checking of the 'renamed' string ? */
new_entry->renamed = xstrdup (renamed);
/* It would be easier to add this new node at the start of the chain,
but users expect that remapping will occur in the order in which
they occur on the command line, and in the remapping files. */
if (input_remaps == NULL)
{
input_remaps = new_entry;
}
else
{
struct input_remap * i;
for (i = input_remaps; i->next != NULL; i = i->next)
;
i->next = new_entry;
}
}
void
ldfile_remap_input_free (void)
{
while (input_remaps != NULL)
{
struct input_remap * i = input_remaps;
input_remaps = i->next;
free ((void *) i->pattern);
free ((void *) i->renamed);
free (i);
}
}
bool
ldfile_add_remap_file (const char * file)
{
FILE * f;
f = fopen (file, FOPEN_RT);
if (f == NULL)
return false;
size_t linelen = 256;
char * line = xmalloc (linelen);
do
{
char * p = line;
char * q;
/* Normally this would use getline(3), but we need to be portable. */
while ((q = fgets (p, linelen - (p - line), f)) != NULL
&& strlen (q) == linelen - (p - line) - 1
&& line[linelen - 2] != '\n')
{
line = xrealloc (line, 2 * linelen);
p = line + linelen - 1;
linelen += linelen;
}
if (q == NULL && p == line)
break;
p = strchr (line, '\n');
if (p)
*p = '\0';
/* Because the file format does not know any form of quoting we
can search forward for the next '#' character and if found
make it terminating the line. */
p = strchr (line, '#');
if (p)
*p = '\0';
/* Remove leading whitespace. NUL is no whitespace character. */
p = line;
while (*p == ' ' || *p == '\f' || *p == '\r' || *p == '\t' || *p == '\v')
++p;
/* If the line is blank it is ignored. */
if (*p == '\0')
continue;
char * pattern = p;
/* Advance past the pattern. We accept whitespace or '=' as an
end-of-pattern marker. */
while (*p && *p != '=' && *p != ' ' && *p != '\t' && *p != '\f'
&& *p != '\r' && *p != '\v')
++p;
if (*p == '\0')
{
einfo ("%F%P: malformed remap file entry: %s\n", line);
continue;
}
* p++ = '\0';
/* Skip whitespace again. */
while (*p == ' ' || *p == '\f' || *p == '\r' || *p == '\t' || *p == '\v')
++p;
if (*p == '\0')
{
einfo ("%F%P: malformed remap file entry: %s\n", line);
continue;
}
char * rename = p;
/* Advance past the rename entry. */
while (*p && *p != '=' && *p != ' ' && *p != '\t' && *p != '\f'
&& *p != '\r' && *p != '\v')
++p;
/* And terminate it. */
*p = '\0';
ldfile_add_remap (pattern, rename);
}
while (! feof (f));
free (line);
fclose (f);
return true;
}
const char *
ldfile_possibly_remap_input (const char * filename)
{
struct input_remap * i;
if (filename == NULL)
return NULL;
for (i = input_remaps; i != NULL; i = i->next)
{
if (fnmatch (i->pattern, filename, 0) == 0)
{
if (verbose)
{
if (strpbrk ((i->pattern), "?*[") != NULL)
{
if (i->renamed)
info_msg (_("remap input file '%s' to '%s' based upon pattern '%s'\n"),
filename, i->renamed, i->pattern);
else
info_msg (_("remove input file '%s' based upon pattern '%s'\n"),
filename, i->pattern);
}
else
{
if (i->renamed)
info_msg (_("remap input file '%s' to '%s'\n"),
filename, i->renamed);
else
info_msg (_("remove input file '%s'\n"),
filename);
}
}
return i->renamed;
}
}
return filename;
}
void
ldfile_print_input_remaps (void)
{
if (input_remaps == NULL)
return;
minfo (_("\nInput File Remapping\n\n"));
struct input_remap * i;
for (i = input_remaps; i != NULL; i = i->next)
minfo (_(" Pattern: %s\tMaps To: %s\n"), i->pattern,
i->renamed ? i->renamed : _("<discard>"));
}
/* Test whether a pathname, after canonicalization, is the same or a
sub-directory of the sysroot directory. */

View File

@ -60,4 +60,15 @@ extern bool ldfile_open_file_search
(const char *arch, struct lang_input_statement_struct *,
const char *lib, const char *suffix);
extern void ldfile_add_remap
(const char *, const char *);
extern bool ldfile_add_remap_file
(const char *);
extern void ldfile_remap_input_free
(void);
extern const char * ldfile_possibly_remap_input
(const char *);
extern void ldfile_print_input_remaps
(void);
#endif

View File

@ -1125,6 +1125,10 @@ new_afile (const char *name,
lang_has_input_file = true;
name = ldfile_possibly_remap_input (name);
if (name == NULL)
return NULL;
p = new_stat (lang_input_statement, stat_ptr);
memset (&p->the_bfd, 0,
sizeof (*p) - offsetof (lang_input_statement_type, the_bfd));
@ -1328,6 +1332,7 @@ void
lang_finish (void)
{
output_section_statement_table_free ();
ldfile_remap_input_free ();
}
/*----------------------------------------------------------------------
@ -2280,6 +2285,8 @@ lang_map (void)
lang_memory_region_type *m;
bool dis_header_printed = false;
ldfile_print_input_remaps ();
LANG_FOR_EACH_INPUT_STATEMENT (file)
{
asection *s;

View File

@ -174,6 +174,8 @@ enum option_values
OPTION_NO_WARN_RWX_SEGMENTS,
OPTION_ENABLE_LINKER_VERSION,
OPTION_DISABLE_LINKER_VERSION,
OPTION_REMAP_INPUTS,
OPTION_REMAP_INPUTS_FILE,
};
/* The initial parser states. */

View File

@ -219,6 +219,12 @@ static const struct ld_option ld_options[] =
{ {"just-symbols", required_argument, NULL, 'R'},
'R', N_("FILE"), N_("Just link symbols (if directory, same as --rpath)"),
TWO_DASHES },
{ {"remap-inputs-file", required_argument, NULL, OPTION_REMAP_INPUTS_FILE},
'\0', N_("FILE"), "Provide a FILE containing input remapings", TWO_DASHES },
{ {"remap-inputs", required_argument, NULL, OPTION_REMAP_INPUTS},
'\0', N_("PATTERN=FILE"), "Remap input files matching PATTERN to FILE", TWO_DASHES },
{ {"strip-all", no_argument, NULL, 's'},
's', NULL, N_("Strip all symbols"), TWO_DASHES },
{ {"strip-debug", no_argument, NULL, 'S'},
@ -1682,6 +1688,27 @@ parse_args (unsigned argc, char **argv)
link_info.fini_function = optarg;
break;
case OPTION_REMAP_INPUTS_FILE:
if (! ldfile_add_remap_file (optarg))
einfo (_("%F%P: failed to add remap file %s\n"), optarg);
break;
case OPTION_REMAP_INPUTS:
{
char *optarg2 = strchr (optarg, '=');
if (optarg2 == NULL)
/* FIXME: Should we allow --remap-inputs=@myfile as a synonym
for --remap-inputs-file=myfile ? */
einfo (_("%F%P: invalid argument to option --remap-inputs\n"));
size_t len = optarg2 - optarg;
char * pattern = xmalloc (len + 1);
memcpy (pattern, optarg, len);
pattern[len] = 0;
ldfile_add_remap (pattern, optarg2 + 1);
free (pattern);
}
break;
case OPTION_REDUCE_MEMORY_OVERHEADS:
link_info.reduce_memory_overheads = true;
if (config.hash_table_size == 0)

View File

@ -0,0 +1,75 @@
# Test handling of --input-remap
# Copyright (C) 2023-2023 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.
#
# First make sure that linking without remapping does not work:
file delete tmpdir/foo.o
setup_xfail *-*-*
run_ld_link_tests {
{ "--remap-inputs (expected fail)"
""
"-e 0"
"-o tmpdir/barzzz.o"
{foo.s}
{}
"foo" }
}
global srcdir
global subdir
# Now use the remap options to allow the linking to succeed.
# Note: we have to use the [list... format when we need to
# have $-substitutions.
run_ld_link_tests [list \
{ "--remap-inputs (simple)"
"--remap-inputs=tmpdir/foo.o=tmpdir/barzzz.o"
"-e 0"
"-o tmpdir/barzzz.o"
{foo.s}
{}
"foo"
} \
{ "--remap-inputs (wildcard)"
"--remap-inputs=*/foo.o=tmpdir/barzzz.o"
"-e 0"
"-o tmpdir/barzzz.o"
{foo.s}
{}
"foo"
} \
[ list "--remap-inputs-file" \
"--remap-inputs-file $srcdir/$subdir/remaps.txt" \
"-e 0 bazzz.o" \
"-o tmpdir/barzzz.o" \
{foo.s} \
{} \
"foo" \
] \
[ list "--remap-inputs-file (with map output)" \
"--remap-inputs-file $srcdir/$subdir/remaps.txt" \
"-e 0 --print-map" \
"-o tmpdir/barzzz.o" \
{foo.s} \
{ { ld remaps.r } } \
"foo" \
] \
]

View File

@ -0,0 +1,6 @@
#...
Input File Remapping
Pattern: \*/foo\.o Maps To: tmpdir/barzzz\.o
Pattern: bazzz\.o Maps To: \<discard\>
#pass

View File

@ -0,0 +1,4 @@
# A comment
*/foo.o tmpdir/barzzz.o # A remapping
bazzz.o=/dev/null # A deletion