From 161719466ac9ea5f186514312f6bce842181804f Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 22 Apr 2020 16:20:02 +0100 Subject: [PATCH] For relative paths in INPUT() and GROUP(), search the directory of the current linker script before searching other paths. PR ld/25806 * ldlang.h (struct lang_input_statement_struct): Add extra_search_path. * ldlang.c (current_input_file): New. (ldirname): New. (new_afile): Add from_filename parameter. Set extra_search_path. (lang_add_input_file): Pass current_input_file to new_afile. (load_symbols): Set current_input_file. --- ld/ChangeLog | 14 ++++++++++++++ ld/NEWS | 4 ++++ ld/ld.texi | 17 ++++++++++------- ld/ldfile.c | 19 ++++++++++++++++++- ld/ldlang.c | 32 ++++++++++++++++++++++++++++---- ld/ldlang.h | 3 +++ 6 files changed, 77 insertions(+), 12 deletions(-) diff --git a/ld/ChangeLog b/ld/ChangeLog index 79f8e63adcc..341ad1d90dc 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,17 @@ +2020-04-22 Fangrui Song + + PR ld/25806 + * ldlang.h (struct lang_input_statement_struct): Add extra_search_path. + * ldlang.c (current_input_file): New. + (ldirname): New. + (new_afile): Add from_filename parameter. Set extra_search_path. + (lang_add_input_file): Pass current_input_file to new_afile. + (load_symbols): Set current_input_file. + * ldfile.c (ldfile_open_file): If extra_search_path has been set + then scan it for the file that needs to be opened. + * ld.texi: Document the new behaviour. + * NEWS: Mention the new feature. + 2020-04-22 Alan Modra * testsuite/ld-scripts/default-script1.d: Correct mingw skip. diff --git a/ld/NEWS b/ld/NEWS index 9795b58a5f1..9f5bbe51cf6 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -7,6 +7,10 @@ IMAGIC (0411) for separate instruction and data spaces, and change the default format option for pdp11-aout to be --omagic. +* Relative pathnames in INPUT() and GROUP() directives in linker scripts are + searched relative to the directory of the linker script before other search + paths. + Changes in 2.34: * The ld check for "PHDR segment not covered by LOAD segment" is more diff --git a/ld/ld.texi b/ld/ld.texi index 8286af65eef..4dc78e65fa2 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -3657,13 +3657,16 @@ script, and then invoke the linker with nothing but a @samp{-T} option. In case a @dfn{sysroot prefix} is configured, and the filename starts with the @samp{/} character, and the script being processed was located inside the @dfn{sysroot prefix}, the filename will be looked -for in the @dfn{sysroot prefix}. Otherwise, the linker will try to -open the file in the current directory. If it is not found, the -linker will search through the archive library search path. -The @dfn{sysroot prefix} can also be forced by specifying @code{=} -as the first character in the filename path, or prefixing the filename -path with @code{$SYSROOT}. See also the description of @samp{-L} in -@ref{Options,,Command-line Options}. +for in the @dfn{sysroot prefix}. The @dfn{sysroot prefix} can also be forced by specifying +@code{=} as the first character in the filename path, or prefixing the +filename path with @code{$SYSROOT}. See also the description of +@samp{-L} in @ref{Options,,Command-line Options}. + +If a @dfn{sysroot prefix} is not used then the linker will try to open +the file in the directory containing the linker script. If it is not +found the linker will then search the current directory. If it is still +not found the linker will search through the archive library search +path. If you use @samp{INPUT (-l@var{file})}, @command{ld} will transform the name to @code{lib@var{file}.a}, as with the command-line argument diff --git a/ld/ldfile.c b/ld/ldfile.c index d98429d7b88..53112c86947 100644 --- a/ld/ldfile.c +++ b/ld/ldfile.c @@ -416,7 +416,24 @@ ldfile_open_file (lang_input_statement_type *entry) search_arch_type *arch; bfd_boolean found = FALSE; - /* Try to open or lib.a */ + /* If extra_search_path is set, entry->filename is a relative path. + Search the directory of the current linker script before searching + other paths. */ + if (entry->extra_search_path) + { + char *path = concat (entry->extra_search_path, slash, entry->filename, + (const char *)0); + if (ldfile_try_open_bfd (path, entry)) + { + entry->filename = path; + entry->flags.search_dirs = FALSE; + return; + } + + free (path); + } + + /* Try to open or lib.a. */ for (arch = search_arch_head; arch != NULL; arch = arch->next) { found = ldfile_open_file_search (arch->name, entry, "lib", ".a"); diff --git a/ld/ldlang.c b/ld/ldlang.c index 0bb5f3c044c..2ef234f90b2 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -117,6 +117,7 @@ lang_statement_list_type file_chain = { NULL, NULL }; lang_input_statement_type statement (reached via input_statement field in a lang_statement_union). */ lang_statement_list_type input_file_chain; +static const char *current_input_file; struct bfd_sym_chain entry_symbol = { NULL, NULL }; const char *entry_section = ".text"; struct lang_input_statement_flags input_flags; @@ -176,6 +177,21 @@ name_match (const char *pattern, const char *name) return strcmp (pattern, name); } +static char * +ldirname (const char *name) +{ + const char *base = lbasename (name); + char *dirname; + + while (base > name && IS_DIR_SEPARATOR (base[-1])) + --base; + if (base == name) + return strdup ("."); + dirname = strdup (name); + dirname[base - name] = '\0'; + return dirname; +} + /* If PATTERN is of the form archive:file, return a pointer to the separator. If not, return NULL. */ @@ -1093,7 +1109,8 @@ new_statement (enum statement_enum type, static lang_input_statement_type * new_afile (const char *name, lang_input_file_enum_type file_type, - const char *target) + const char *target, + const char *from_filename) { lang_input_statement_type *p; @@ -1102,6 +1119,7 @@ new_afile (const char *name, p = new_stat (lang_input_statement, stat_ptr); memset (&p->the_bfd, 0, sizeof (*p) - offsetof (lang_input_statement_type, the_bfd)); + p->extra_search_path = NULL; p->target = target; p->flags.dynamic = input_flags.dynamic; p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic; @@ -1142,6 +1160,10 @@ new_afile (const char *name, case lang_input_file_is_search_file_enum: p->filename = name; p->local_sym_name = name; + /* If name is a relative path, search the directory of the current linker + script first. */ + if (from_filename && !IS_ABSOLUTE_PATH (name)) + p->extra_search_path = ldirname (from_filename); p->flags.real = TRUE; p->flags.search_dirs = TRUE; break; @@ -1181,12 +1203,12 @@ lang_add_input_file (const char *name, within the sysroot subdirectory.) */ unsigned int outer_sysrooted = input_flags.sysrooted; input_flags.sysrooted = 0; - ret = new_afile (sysrooted_name, file_type, target); + ret = new_afile (sysrooted_name, file_type, target, NULL); input_flags.sysrooted = outer_sysrooted; return ret; } - return new_afile (name, file_type, target); + return new_afile (name, file_type, target, current_input_file); } struct out_section_hash_entry @@ -2909,7 +2931,7 @@ lookup_name (const char *name) lang_statement_union_type *rest = *after; stat_ptr->tail = after; search = new_afile (name, lang_input_file_is_search_file_enum, - default_target); + default_target, NULL); *stat_ptr->tail = rest; if (*tail == NULL) stat_ptr->tail = tail; @@ -3051,7 +3073,9 @@ load_symbols (lang_input_statement_type *entry, ldfile_assumed_script = TRUE; parser_input = input_script; + current_input_file = entry->filename; yyparse (); + current_input_file = NULL; ldfile_assumed_script = FALSE; /* missing_file is sticky. sysrooted will already have been diff --git a/ld/ldlang.h b/ld/ldlang.h index 8dd4bfda190..2aa3930f95a 100644 --- a/ld/ldlang.h +++ b/ld/ldlang.h @@ -293,6 +293,9 @@ typedef struct lang_input_statement_struct Usually the same as filename, but for a file spec'd with -l this is the -l switch itself rather than the filename. */ const char *local_sym_name; + /* Extra search path. Used to find a file relative to the + directory of the current linker script. */ + const char *extra_search_path; bfd *the_bfd; -- 2.30.2