diff options
Diffstat (limited to 'ld')
30 files changed, 802 insertions, 54 deletions
@@ -1,5 +1,14 @@ -*- text -*- +* The linker's --stats option can take an optional argument which if used is + interpreted as a filename into which resource usage information should be + stored. As an alternative mechanism the LD_STATS environment variable can + also be used to achieve the same results. Resource usage information for + various phases of the linking operation is now included in the report. + If a map file is being produced then the information is also included there. + The --no-stats option can be used to disable stat reporting, should it have + been enabled. + * Remove the linker -taso option for Alpha target, as Linux/Alpha kernel support for 32-bit pointers has been removed. diff --git a/ld/config.in b/ld/config.in index 2d7b640..e10c9e7 100644 --- a/ld/config.in +++ b/ld/config.in @@ -122,6 +122,9 @@ /* Define to 1 if you have the `getpagesize' function. */ #undef HAVE_GETPAGESIZE +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + /* Define if the GNU gettext() function is already present or preinstalled. */ #undef HAVE_GETTEXT diff --git a/ld/configure b/ld/configure index b7af25d..3f745ac 100755 --- a/ld/configure +++ b/ld/configure @@ -18753,7 +18753,7 @@ fi done -for ac_func in close glob lseek mkstemp open realpath waitpid +for ac_func in close getrusage glob lseek mkstemp open realpath waitpid do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/ld/configure.ac b/ld/configure.ac index 228f2ee..1ee0c0c 100644 --- a/ld/configure.ac +++ b/ld/configure.ac @@ -414,7 +414,7 @@ AC_SUBST(NATIVE_LIB_DIRS) AC_CHECK_HEADERS(fcntl.h elf-hints.h limits.h inttypes.h stdint.h \ sys/file.h sys/mman.h sys/param.h sys/stat.h sys/time.h \ sys/types.h unistd.h) -AC_CHECK_FUNCS(close glob lseek mkstemp open realpath waitpid) +AC_CHECK_FUNCS(close getrusage glob lseek mkstemp open realpath waitpid) BFD_BINARY_FOPEN diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em index 9a2b576..50bb082 100644 --- a/ld/emultempl/pe.em +++ b/ld/emultempl/pe.em @@ -7,11 +7,11 @@ else fi case ${target} in - *-*-cygwin*) - cygwin_behavior=1 + *-*-mingw*) + mingw_behavior=1 ;; *) - cygwin_behavior=0; + mingw_behavior=0 ;; esac @@ -126,9 +126,10 @@ fragment <<EOF #define DEFAULT_PSEUDO_RELOC_VERSION 1 #endif -#define DEFAULT_DLL_CHARACTERISTICS (${cygwin_behavior} ? 0 : \ - IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE \ - | IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) +#define DEFAULT_DLL_CHARACTERISTICS (${mingw_behavior} \ + ? IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE \ + | IMAGE_DLL_CHARACTERISTICS_NX_COMPAT \ + : 0) #if defined(TARGET_IS_i386pe) || ! defined(DLL_SUPPORT) #define PE_DEF_SUBSYSTEM IMAGE_SUBSYSTEM_WINDOWS_CUI diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em index 440c0bf..60a8339 100644 --- a/ld/emultempl/pep.em +++ b/ld/emultempl/pep.em @@ -9,11 +9,15 @@ fi case ${target} in *-*-cygwin*) move_default_addr_high=1 - cygwin_behavior=1 + mingw_behavior=0 + ;; + *-*-mingw*) + move_default_addr_high=0 + mingw_behavior=1 ;; *) - move_default_addr_high=0; - cygwin_behavior=0; + move_default_addr_high=0 + mingw_behavior=0 ;; esac @@ -126,10 +130,11 @@ fragment <<EOF #define DLL_SUPPORT #endif -#define DEFAULT_DLL_CHARACTERISTICS (${cygwin_behavior} ? 0 : \ - IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE \ - | IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA \ - | IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) +#define DEFAULT_DLL_CHARACTERISTICS (${mingw_behavior} \ + ? IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE \ + | IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA \ + | IMAGE_DLL_CHARACTERISTICS_NX_COMPAT \ + : 0) #if defined(TARGET_IS_i386pep) || defined(COFF_WITH_peAArch64) || ! defined(DLL_SUPPORT) #define PE_DEF_SUBSYSTEM IMAGE_SUBSYSTEM_WINDOWS_CUI diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em index f7a8f1e..857cf54 100644 --- a/ld/emultempl/ppc64elf.em +++ b/ld/emultempl/ppc64elf.em @@ -606,14 +606,15 @@ gld${EMULATION_NAME}_finish (void) einfo (_("%X%P: can not build stubs: %E\n")); fflush (stdout); + FILE * out = config.stats_file ? config.stats_file : stderr; for (line = msg; line != NULL; line = endline) { endline = strchr (line, '\n'); if (endline != NULL) *endline++ = '\0'; - fprintf (stderr, "%s: %s\n", program_name, line); + fprintf (out, "%s: %s\n", program_name, line); } - fflush (stderr); + fflush (out); free (msg); ldelf_finish (); @@ -295,6 +295,10 @@ typedef struct char *map_filename; FILE *map_file; + char *stats_filename; + /* If non-NULL then resource use information should be written to this file. */ + FILE *stats_file; + char *dependency_file; unsigned int split_by_reloc; @@ -330,6 +334,39 @@ typedef struct enum compressed_debug_section_type compress_debug; } ld_config_type; +/* An enumeration of the linker phases for which resource usage information + is recorded. PHASE_ALL is special as it covers the entire link process. + + Instructions for adding a new phase: + 1. Add an entry to this enumeration. + 2. Add an entry for the phase to the phase_data[] structure in ldmain.c. + 3. Add calls to ld_start_phase(PHASE_xxx) and ld_stop_phase(PHASE_xxx) + at the appropriate place(s) in the code. It does not matter if the + new phase overlaps with or is contained by any other phase. + + Instructions for adding a new resource: + 1. If necessary add a new field to the phase_data structure defined in + ldmain.c. + 2. Add code to initialise the field in ld_main.c:ld_start_phase(). + 3. Add code to finalise the field in ld_main.c:ld_stop_phase(). + 4. Add code to report the field in ld_main.c:report_phases(). */ +typedef enum +{ + PHASE_ALL = 0, + PHASE_CTF, + PHASE_MERGE, + PHASE_PARSE, + PHASE_PLUGINS, + PHASE_PROCESS, + PHASE_WRITE, + + NUM_PHASES /* This must be the last entry. */ +} +ld_phase; + +extern void ld_start_phase (ld_phase); +extern void ld_stop_phase (ld_phase); + extern ld_config_type config; extern FILE * saved_script_handle; @@ -2184,6 +2184,9 @@ Memory region Used Size Region Size %age Used RAM: 32 B 2 GB 0.00% @end smallexample +Note: if you want to find out about the memory usage of the linker +itself, then the @option{--stats} option will do this. + @cindex help @cindex usage @kindex --help @@ -2706,10 +2709,76 @@ more than @var{count} relocations one output section will contain that many relocations. @var{count} defaults to a value of 32768. @kindex --stats -@item --stats +@item --stats[=@var{filename}] Compute and display statistics about the operation of the linker, such as execution time and memory usage. +If the optional @var{filename} argument is not supplied then only +basic information is reported, and it is sent to the standard error +output stream. If the @var{filename} argument is supplied then +extended information is written to the named file. If @var{filename} +is set to just the @var{-} symbol, then the extended information is +sent to the standard output stream. If the @var{filename} starts with +@var{+} then the file is opened in append mode rather than overwrite +mode. + +If the @option{-Map} option has been enabled then the information is +also recorded in the map file as well. Note: if both the +@option{--stats} option and the @option{-Map} options have been given +@var{filename} arguments and they match, then the information will +only be written out once not twice. + +If the @code{LD_STATS} environment variable is defined then this +behaves likes the @option{--stats} option. If the variable's value is +a string then this will used as the name of a file into which the +information should be recorded. Otherwise the information +will be sent to the standard output stream. Using the environment +variable allows stats to be recorded without having to alter the +linker's command line. Note: if both the environment variable and the +@option{--stats} option are used then the @option{--stats} option +takes precedence. + +The extended information reported includes the cpu time used and, if +the @var{getrusage()} system library call is available then memory use +is recorded as well. This information is reported for individual +parts of the linking process which are referred to as @emph{phases}. +In addition the information is also reported for a special phase +called @emph{ALL} which covers the entire linking process. Note that +individual phases can contain or overlap with each other so it should +not be assumed that the overall resources used by the linker is the +sum of the resources used by the individual phases. + +In addition when extended information is being reported the linker +version, command line arguments and linker start time are also +included. This makes it easier to handle the situation where multiple +links are being invoked by a build system and to indentify exactly +which arguments were responsible for producing the statistics that are +reported. + +The extended output looks something like this: + +@smallexample +Stats: linker version: (GNU Binutils) 2.44.50.20250401 +Stats: linker started: Wed Apr 2 09:36:41 2025 +Stats: args: ld -z norelro -z nomemory-seal -z no-separate-code -o a.out [...] + +Stats: phase cpu time memory user time system time +Stats: name (microsec) (KiB) (seconds) (seconds) +Stats: ALL 390082 217740 0 0 +Stats: ctf processing 12 0 0 0 +Stats: string merge 1324 0 0 0 +Stats: parsing 349 288 0 0 +Stats: plugins 1 0 0 0 +Stats: processing files 259616 214524 0 0 +Stats: write 116493 0 0 0 +@end smallexample + +@kindex --no-stats +@item --no-stats +Disables the reporting of usage statistics, should it have been +enabled via the @option{--stats} command line option or the +@var{LD_STATS} environment variable. + @kindex --sysroot=@var{directory} @item --sysroot=@var{directory} Use @var{directory} as the location of the sysroot, overriding the @@ -3712,7 +3781,8 @@ of the PE file header: @item --high-entropy-va @itemx --disable-high-entropy-va Image is compatible with 64-bit address space layout randomization -(ASLR). This option is enabled by default for 64-bit PE images. +(ASLR). This option is enabled by default for 64-bit PE images in +MinGW targets. This option also implies @option{--dynamicbase} and @option{--enable-reloc-section}. @@ -3722,9 +3792,9 @@ This option also implies @option{--dynamicbase} and @itemx --disable-dynamicbase The image base address may be relocated using address space layout randomization (ASLR). This feature was introduced with MS Windows -Vista for i386 PE targets. This option is enabled by default but -can be disabled via the @option{--disable-dynamicbase} option. -This option also implies @option{--enable-reloc-section}. +Vista for i386 PE targets. This option is enabled by default for MinGW +targets but can be disabled via the @option{--disable-dynamicbase} +option. This option also implies @option{--enable-reloc-section}. @kindex --forceinteg @item --forceinteg @@ -3737,7 +3807,7 @@ default. @item --disable-nxcompat The image is compatible with the Data Execution Prevention. This feature was introduced with MS Windows XP SP2 for i386 PE -targets. The option is enabled by default. +targets. The option is enabled by default for MinGW targets. @kindex --no-isolation @item --no-isolation @@ -4078,6 +4148,15 @@ If the PE/COFF specific @option{--insert-timestamp} is active and the timestamp value in this variable will be inserted into the COFF header instead of the current time. +@kindex LD_STATS +@cindex LD_STATS +If the @code{LD_STATS} environment variable is defined then linker +resource use information will be recorded, just as if the +@option{--stats} option had been used. If the @code{LD_STATS} +variable has a string value then this will used as the name of a file +into which the information should be stored. Otherwise the information +will be sent to the standard output stream. + @c man end @end ifset diff --git a/ld/ldlang.c b/ld/ldlang.c index 0048dfa..0bb9e17 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -3807,6 +3807,8 @@ ldlang_open_ctf (void) int any_ctf = 0; int err; + ld_start_phase (PHASE_CTF); + LANG_FOR_EACH_INPUT_STATEMENT (file) { asection *sect; @@ -3844,17 +3846,23 @@ ldlang_open_ctf (void) if (!any_ctf) { ctf_output = NULL; + ld_stop_phase (PHASE_CTF); return; } if ((ctf_output = ctf_create (&err)) != NULL) - return; + { + ld_stop_phase (PHASE_CTF); + return; + } einfo (_("%P: warning: CTF output not created: `%s'\n"), ctf_errmsg (err)); LANG_FOR_EACH_INPUT_STATEMENT (errfile) ctf_close (errfile->the_ctf); + + ld_stop_phase (PHASE_CTF); } /* Merge together CTF sections. After this, only the symtab-dependent @@ -3869,6 +3877,8 @@ lang_merge_ctf (void) if (!ctf_output) return; + ld_start_phase (PHASE_CTF); + output_sect = bfd_get_section_by_name (link_info.output_bfd, ".ctf"); /* If the section was discarded, don't waste time merging. */ @@ -3882,6 +3892,8 @@ lang_merge_ctf (void) ctf_close (file->the_ctf); file->the_ctf = NULL; } + + ld_stop_phase (PHASE_CTF); return; } @@ -3924,6 +3936,8 @@ lang_merge_ctf (void) } /* Output any lingering errors that didn't come from ctf_link. */ lang_ctf_errs_warnings (ctf_output); + + ld_stop_phase (PHASE_CTF); } /* Let the emulation acquire strings from the dynamic strtab to help it optimize @@ -3932,7 +3946,9 @@ lang_merge_ctf (void) void ldlang_ctf_acquire_strings (struct elf_strtab_hash *dynstrtab) { + ld_start_phase (PHASE_CTF); ldemul_acquire_strings_for_ctf (ctf_output, dynstrtab); + ld_stop_phase (PHASE_CTF); } /* Inform the emulation about the addition of a new dynamic symbol, in BFD @@ -3954,16 +3970,24 @@ lang_write_ctf (int late) if (!ctf_output) return; + ld_start_phase (PHASE_CTF); + if (late) { /* Emit CTF late if this emulation says it can do so. */ if (ldemul_emit_ctf_early ()) - return; + { + ld_stop_phase (PHASE_CTF); + return; + } } else { if (!ldemul_emit_ctf_early ()) - return; + { + ld_stop_phase (PHASE_CTF); + return; + } } /* Inform the emulation that all the symbols that will be received have @@ -3998,6 +4022,8 @@ lang_write_ctf (int late) LANG_FOR_EACH_INPUT_STATEMENT (file) file->the_ctf = NULL; + + ld_stop_phase (PHASE_CTF); } /* Write out the CTF section late, if the emulation needs that. */ @@ -8547,6 +8573,8 @@ lang_process (void) { asection *found; + ld_start_phase (PHASE_MERGE); + /* Merge SEC_MERGE sections. This has to be done after GC of sections, so that GCed sections are not merged, but before assigning dynamic symbols, since removing whole input sections @@ -8554,6 +8582,8 @@ lang_process (void) if (!bfd_merge_sections (link_info.output_bfd, &link_info)) fatal (_("%P: bfd_merge_sections failed: %E\n")); + ld_stop_phase (PHASE_MERGE); + /* Look for a text section and set the readonly attribute in it. */ found = bfd_get_section_by_name (link_info.output_bfd, ".text"); @@ -46,6 +46,7 @@ enum option_values OPTION_MAP, OPTION_NO_DEMANGLE, OPTION_NO_KEEP_MEMORY, + OPTION_NO_STATS, OPTION_NO_WARN_MISMATCH, OPTION_NO_WARN_SEARCH_MISMATCH, OPTION_NOINHIBIT_EXEC, diff --git a/ld/ldmain.c b/ld/ldmain.c index 54a834e..67c60c3 100644 --- a/ld/ldmain.c +++ b/ld/ldmain.c @@ -21,6 +21,7 @@ #include "sysdep.h" #include "bfd.h" +#include "bfdver.h" #include "safe-ctype.h" #include "libiberty.h" #include "bfdlink.h" @@ -51,6 +52,10 @@ #include <string.h> +#if defined (HAVE_GETRUSAGE) +#include <sys/resource.h> +#endif + #ifndef TARGET_SYSTEM_ROOT #define TARGET_SYSTEM_ROOT "" #endif @@ -224,6 +229,10 @@ ld_cleanup (void) bfd_close_all_done (ibfd); } #if BFD_SUPPORTS_PLUGINS + /* Note - we do not call ld_plugin_start (PHASE_PLUGINS) here as this + function is only called when the linker is exiting - ie after any + stats may have been reported, and potentially in the middle of a + phase where we have already started recording plugin stats. */ plugin_call_cleanup (); #endif if (output_filename && delete_output_file_on_failure) @@ -270,11 +279,305 @@ display_external_script (void) free (buf); } +struct ld_phase_data +{ + const char * name; + + unsigned long start; + unsigned long duration; + + bool started; + bool broken; + +#if defined (HAVE_GETRUSAGE) + struct rusage begin; + struct rusage use; +#endif +}; + +static struct ld_phase_data phase_data [NUM_PHASES] = +{ + [PHASE_ALL] = { .name = "ALL" }, + [PHASE_CTF] = { .name = "ctf processing" }, + [PHASE_MERGE] = { .name = "string merge" }, + [PHASE_PARSE] = { .name = "parsing" }, + [PHASE_PLUGINS] = { .name = "plugins" }, + [PHASE_PROCESS] = { .name = "processing files" }, + [PHASE_WRITE] = { .name = "write" }, +}; + +void +ld_start_phase (ld_phase phase) +{ + struct ld_phase_data * pd = phase_data + phase; + + /* We record data even if config.stats_file is NULL. This allows + us to record data about phases that start before the command line + arguments have been parsed. ie PHASE_ALL and PHASE_PARSE. */ + + /* Do not overwrite the fields if we have already started recording. */ + if (pd->started) + { + /* Since we do not queue phase starts and stops, if a phase is started + multiple times there is a likelyhood that it will be stopped multiple + times as well. This is problematic as we will only record the data + for the first time the phase stops and ignore all of the other stops. + + So let the user know. Ideally real users will never actually see + this message, and instead only developers who are adding new phase + tracking code will ever encounter it. */ + einfo ("%P: --stats: phase %s started twice - data may be unreliable\n", + pd->name); + return; + } + + /* It is OK if other phases are also active at this point. + It just means that the phases overlap or that one phase is a sub-task + of another. Since we record resources on a per-phase basis, this + should not matter. */ + + pd->started = true; + pd->start = get_run_time (); + +#if defined (HAVE_GETRUSAGE) + /* Record the resource usage at the start of the phase. */ + struct rusage usage; + + if (getrusage (RUSAGE_SELF, & usage) != 0) + /* FIXME: Complain ? */ + return; + + memcpy (& pd->begin, & usage, sizeof usage); +#endif +} + +void +ld_stop_phase (ld_phase phase) +{ + struct ld_phase_data * pd = phase_data + phase; + + if (!pd->started) + { + /* We set the broken flag to indicate that the data + recorded for this phase is inconsistent. */ + pd->broken = true; + return; + } + + pd->duration += get_run_time () - pd->start; + pd->started = false; + +#if defined (HAVE_GETRUSAGE) + struct rusage usage; + + if (getrusage (RUSAGE_SELF, & usage) != 0) + /* FIXME: Complain ? */ + return; + + if (phase == PHASE_ALL) + memcpy (& pd->use, & usage, sizeof usage); + else + { + struct timeval t; + + /* For sub-phases we record the increase in specific fields. */ + /* FIXME: Most rusage{} fields appear to be irrelevent to when considering + linker resource usage. Currently we record maxrss and user and system + cpu times. Are there any other fields that might be useful ? */ + +#ifndef timeradd /* Macros copied from <sys/time.h>. */ +#define timeradd(a, b, result) \ + do \ + { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) \ + { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } \ + while (0) +#endif + +#ifndef timersub +#define timersub(a, b, result) \ + do \ + { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) \ + { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } \ + while (0) +#endif + + timersub (& usage.ru_utime, & pd->begin.ru_utime, & t); + timeradd (& pd->use.ru_utime, &t, & pd->use.ru_utime); + + timersub (& usage.ru_stime, & pd->begin.ru_stime, & t); + timeradd (& pd->use.ru_stime, &t, & pd->use.ru_stime); + + if (pd->begin.ru_maxrss < usage.ru_maxrss) + pd->use.ru_maxrss += usage.ru_maxrss - pd->begin.ru_maxrss; + } +#endif +} + +static void +report_phases (FILE * file, time_t * start, char ** argv) +{ + unsigned long i; + + if (file == NULL) + return; + + /* We might be writing to stdout, so make sure + that we do not have any pending error output. */ + fflush (stderr); + + /* We do not translate "Stats" as we provide this as a key + word that can be searched for by grep and the like. */ +#define STATS_PREFIX "Stats: " + + fprintf (file, STATS_PREFIX "linker version: %s\n", BFD_VERSION_STRING); + + /* No \n at the end of the string as ctime() provides its own. */ + fprintf (file, STATS_PREFIX "linker started: %s", ctime (start)); + + /* We include the linker command line arguments since + they can be hard to track down by other means. */ + if (argv != NULL) + { + fprintf (file, STATS_PREFIX "args: "); + for (i = 0; argv[i] != NULL; i++) + fprintf (file, "%s ", argv[i]); + fprintf (file, "\n\n"); /* Blank line to separate the args from the stats. */ + } + + /* All of this song and dance with the column_info struct and printf + formatting is so that we can have a nicely formated table with regular + column spacing, whilst allowing for the column headers to be translated, + and coping nicely with extra long strings or numbers. */ + struct column_info + { + const char * header; + const char * sub_header; + int width; + int pad; + } columns[] = +#define COLUMNS_FIELD(HEADER,SUBHEADER) \ + { .header = N_( HEADER ), .sub_header = N_( SUBHEADER ) }, + { + COLUMNS_FIELD ("phase", "name") + COLUMNS_FIELD ("cpu time", "(microsec)") +#if defined (HAVE_GETRUSAGE) + /* Note: keep these columns in sync with the + information recorded in ld_stop_phase(). */ + COLUMNS_FIELD ("memory", "(KiB)") + COLUMNS_FIELD ("user time", "(seconds)") + COLUMNS_FIELD ("system time", "(seconds)") +#endif + }; + +#ifndef max +#define max(A,B) ((A) < (B) ? (B) : (A)) +#endif + + size_t maxwidth = 1; + for (i = 0; i < NUM_PHASES; i++) + maxwidth = max (maxwidth, strlen (phase_data[i].name)); + + fprintf (file, "%s", STATS_PREFIX); + + for (i = 0; i < ARRAY_SIZE (columns); i++) + { + int padding; + + if (i == 0) + columns[i].width = fprintf (file, "%-*s", (int) maxwidth, columns[i].header); + else + columns[i].width = fprintf (file, "%s", columns[i].header); + padding = columns[i].width % 8; + if (padding < 4) + padding = 4; + columns[i].pad = fprintf (file, "%*c", padding, ' '); + } + + fprintf (file, "\n"); + + int bias = 0; +#define COLUMN_ENTRY(VAL, FORMAT, N) \ + do \ + { \ + int l; \ + \ + if (N == 0) \ + l = fprintf (file, "%-*" FORMAT, columns[N].width, VAL); \ + else \ + l = fprintf (file, "%*" FORMAT, columns[N].width - bias, VAL); \ + bias = 0; \ + if (l < columns[N].width) \ + l = columns[N].pad; \ + else if (l < columns[N].width + columns[N].pad) \ + l = columns[N].pad - (l - columns[N].width); \ + else \ + { \ + bias = l - (columns[N].width + columns[N].pad); \ + l = 0; \ + } \ + if (l) \ + fprintf (file, "%*c", l, ' '); \ + } \ + while (0) + + fprintf (file, "%s", STATS_PREFIX); + + for (i = 0; i < ARRAY_SIZE (columns); i++) + COLUMN_ENTRY (columns[i].sub_header, "s", i); + + fprintf (file, "\n"); + + for (i = 0; i < NUM_PHASES; i++) + { + struct ld_phase_data * pd = phase_data + i; + /* This should not be needed... */ + const char * name = pd->name ? pd->name : "<unnamed>"; + + if (pd->broken) + { + fprintf (file, "%s %s: %s", + STATS_PREFIX, name, _("WARNING: Data is unreliable!\n")); + continue; + } + + fprintf (file, "%s", STATS_PREFIX); + + /* Care must be taken to keep the lines below in sync with + entries in the columns_info array. + FIXME: There ought to be a better way to do this... */ + COLUMN_ENTRY (name, "s", 0); + COLUMN_ENTRY (pd->duration, "ld", 1); +#if defined (HAVE_GETRUSAGE) + COLUMN_ENTRY (pd->use.ru_maxrss, "ld", 2); + COLUMN_ENTRY ((int64_t) pd->use.ru_utime.tv_sec, PRId64, 3); + COLUMN_ENTRY ((int64_t) pd->use.ru_stime.tv_sec, PRId64, 4); +#endif + fprintf (file, "\n"); + } + + fflush (file); +} + int main (int argc, char **argv) { char *emulation; long start_time = get_run_time (); + time_t start_seconds = time (NULL); #ifdef HAVE_LC_MESSAGES setlocale (LC_MESSAGES, ""); @@ -286,7 +589,23 @@ main (int argc, char **argv) program_name = argv[0]; xmalloc_set_program_name (program_name); + /* Check the LD_STATS environment variable before parsing the command line + so that the --stats option, if used, can override the environment variable. */ + char * stats_filename; + if ((stats_filename = getenv ("LD_STATS")) != NULL) + { + if (ISPRINT (stats_filename[0])) + config.stats_filename = stats_filename; + else + config.stats_filename = "-"; + config.stats = true; + } + + ld_start_phase (PHASE_ALL); + ld_start_phase (PHASE_PARSE); + expandargv (&argc, &argv); + char ** saved_argv = dupargv (argv); if (bfd_init () != BFD_INIT_MAGIC) fatal (_("%P: fatal error: libbfd ABI mismatch\n")); @@ -404,11 +723,17 @@ main (int argc, char **argv) if (config.hash_table_size != 0) bfd_hash_set_default_size (config.hash_table_size); + ld_stop_phase (PHASE_PARSE); + #if BFD_SUPPORTS_PLUGINS + ld_start_phase (PHASE_PLUGINS); /* Now all the plugin arguments have been gathered, we can load them. */ plugin_load_plugins (); + ld_stop_phase (PHASE_PLUGINS); #endif /* BFD_SUPPORTS_PLUGINS */ + ld_start_phase (PHASE_PARSE); + ldemul_set_symbols (); /* If we have not already opened and parsed a linker script, @@ -531,7 +856,31 @@ main (int argc, char **argv) link_info.has_map_file = true; } + if (config.stats_filename != NULL) + { + if (config.map_filename != NULL + && strcmp (config.stats_filename, config.map_filename) == 0) + config.stats_file = NULL; + else if (strcmp (config.stats_filename, "-") == 0) + config.stats_file = stdout; + else + { + if (config.stats_filename[0] == '+') + config.stats_file = fopen (config.stats_filename + 1, "a"); + else + config.stats_file = fopen (config.stats_filename, "w"); + + if (config.stats_file == NULL) + einfo ("%P: Warning: failed to open resource record file: %s\n", + config.stats_filename); + } + } + + ld_stop_phase (PHASE_PARSE); + + ld_start_phase (PHASE_PROCESS); lang_process (); + ld_stop_phase (PHASE_PROCESS); /* Print error messages for any missing symbols, for any warning symbols, and possibly multiple definitions. */ @@ -558,7 +907,11 @@ main (int argc, char **argv) link_info.output_bfd->flags |= flags & bfd_applicable_file_flags (link_info.output_bfd); + + ld_start_phase (PHASE_WRITE); ldwrite (); + ld_stop_phase (PHASE_WRITE); + if (config.map_file != NULL) lang_map (); @@ -653,19 +1006,38 @@ main (int argc, char **argv) if (config.emit_gnu_object_only) cmdline_emit_object_only_section (); + ld_stop_phase (PHASE_ALL); + if (config.stats) { - long run_time = get_run_time () - start_time; + report_phases (config.map_file, & start_seconds, saved_argv); + + if (config.stats_filename) + { + report_phases (config.stats_file, & start_seconds, saved_argv); + + if (config.stats_file != stdout && config.stats_file != stderr) + { + fclose (config.stats_file); + config.stats_file = NULL; + } + } + else /* This is for backwards compatibility. */ + { + long run_time = get_run_time () - start_time; - fflush (stdout); - fprintf (stderr, _("%s: total time in link: %ld.%06ld\n"), - program_name, run_time / 1000000, run_time % 1000000); - fflush (stderr); + fflush (stdout); + fprintf (stderr, _("%s: total time in link: %ld.%06ld\n"), + program_name, run_time / 1000000, run_time % 1000000); + fflush (stderr); + } } /* Prevent ld_cleanup from deleting the output file. */ output_filename = NULL; + freeargv (saved_argv); + xexit (0); return 0; } @@ -942,8 +1314,11 @@ add_archive_element (struct bfd_link_info *info, && (!no_more_claiming || bfd_get_lto_type (abfd) != lto_fat_ir_object)) { + ld_start_phase (PHASE_PLUGINS); /* We must offer this archive member to the plugins to claim. */ plugin_maybe_claim (input); + ld_stop_phase (PHASE_PLUGINS); + if (input->flags.claimed) { if (no_more_claiming) diff --git a/ld/lexsup.c b/ld/lexsup.c index 7de6e25..bde2046 100644 --- a/ld/lexsup.c +++ b/ld/lexsup.c @@ -499,8 +499,10 @@ static const struct ld_option ld_options[] = { {"split-by-reloc", optional_argument, NULL, OPTION_SPLIT_BY_RELOC}, '\0', N_("[=COUNT]"), N_("Split output sections every COUNT relocs"), TWO_DASHES }, - { {"stats", no_argument, NULL, OPTION_STATS}, - '\0', NULL, N_("Print memory usage statistics"), TWO_DASHES }, + { {"stats", optional_argument, NULL, OPTION_STATS}, + '\0', NULL, N_("Print resource usage statistics"), TWO_DASHES }, + { {"no-stats", optional_argument, NULL, OPTION_NO_STATS}, + '\0', NULL, N_("Do not print resource usage statistics"), TWO_DASHES }, { {"target-help", no_argument, NULL, OPTION_TARGET_HELP}, '\0', NULL, N_("Display target specific options"), TWO_DASHES }, { {"task-link", required_argument, NULL, OPTION_TASK_LINK}, @@ -1412,6 +1414,17 @@ parse_args (unsigned argc, char **argv) break; case OPTION_STATS: config.stats = true; + if (optarg) + config.stats_filename = optarg; + else + { + config.stats_filename = NULL; + config.stats_file = stderr; + } + break; + case OPTION_NO_STATS: + config.stats = false; + config.stats_filename = NULL; break; case OPTION_NO_SYMBOLIC: opt_symbolic = symbolic_unset; diff --git a/ld/pe-dll.c b/ld/pe-dll.c index de1cfaf..4a2ea03 100644 --- a/ld/pe-dll.c +++ b/ld/pe-dll.c @@ -2639,9 +2639,9 @@ make_import_fixup_mark (arelent *rel, char *name) memcpy (fixup_name, buf, prefix_len); bh = NULL; - bfd_coff_link_add_one_symbol (&link_info, abfd, fixup_name, BSF_GLOBAL, - current_sec, /* sym->section, */ - rel->address, NULL, true, false, &bh); + _bfd_generic_link_add_one_symbol (&link_info, abfd, fixup_name, BSF_GLOBAL, + current_sec, /* sym->section, */ + rel->address, NULL, true, false, &bh); return bh->root.string; } diff --git a/ld/testsuite/ld-aarch64/protections/bti-and-memory-seal-plt-1-a.d b/ld/testsuite/ld-aarch64/protections/bti-and-memory-seal-plt-1-a.d new file mode 100644 index 0000000..f8b1c21 --- /dev/null +++ b/ld/testsuite/ld-aarch64/protections/bti-and-memory-seal-plt-1-a.d @@ -0,0 +1,45 @@ +#name: No '-z force-bti' with '-z memory-seal' with feature properties (BTI) forces the generation of BTI PLT (shared) +#source: bti-plt-1.s +#source: bti-plt-2.s +#target: [check_shared_lib_support] +#as: -mabi=lp64 -defsym __property_bti__=1 +#ld: -shared -z memory-seal -T bti-plt.ld -L./tmpdir -lbti-plt-so +#objdump: -dr -j .plt + +[^:]*: *file format elf64-.*aarch64 + +Disassembly of section \.plt: + +[0-9]+ <\.plt>: +.*: d503245f bti c +.*: a9bf7bf0 stp x16, x30, \[sp, #-16\]! +.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_> +.*: f9400e11 ldr x17, \[x16, #24\] +.*: 91006210 add x16, x16, #0x18 +.*: d61f0220 br x17 +.*: d503201f nop +.*: d503201f nop + +[0-9]+ <.*>: +.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_> +.*: f9401211 ldr x17, \[x16, #32\] +.*: 91008210 add x16, x16, #0x20 +.*: d61f0220 br x17 + +[0-9]+ <.*>: +.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_> +.*: f9401611 ldr x17, \[x16, #40\] +.*: 9100a210 add x16, x16, #0x28 +.*: d61f0220 br x17 + +[0-9]+ <.*>: +.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_> +.*: f9401a11 ldr x17, \[x16, #48\] +.*: 9100c210 add x16, x16, #0x30 +.*: d61f0220 br x17 + +[0-9]+ <.*>: +.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_> +.*: f9401e11 ldr x17, \[x16, #56\] +.*: 9100e210 add x16, x16, #0x38 +.*: d61f0220 br x17 diff --git a/ld/testsuite/ld-aarch64/protections/bti-and-memory-seal-plt-1-b.d b/ld/testsuite/ld-aarch64/protections/bti-and-memory-seal-plt-1-b.d new file mode 100644 index 0000000..0dadcc9 --- /dev/null +++ b/ld/testsuite/ld-aarch64/protections/bti-and-memory-seal-plt-1-b.d @@ -0,0 +1,14 @@ +#name: No '-z force-bti' with '-z memory-seal' all input objects have BTI emits BTI feature (shared) +#source: bti-plt-1.s +#source: bti-plt-2.s +#target: [check_shared_lib_support] +#as: -mabi=lp64 -defsym __property_bti__=1 +#ld: -z memory-seal -shared -T bti-plt.ld +#readelf: -n + +Displaying notes found in: .note.gnu.property +[ ]+Owner[ ]+Data size[ ]+Description + GNU 0x00000018 NT_GNU_PROPERTY_TYPE_0 + Properties: memory seal\s +\s+AArch64 feature: BTI +#pass diff --git a/ld/testsuite/ld-aarch64/protections/bti-plt-1-b.d b/ld/testsuite/ld-aarch64/protections/bti-plt-1-b.d index 1bf956c..4b0e424 100644 --- a/ld/testsuite/ld-aarch64/protections/bti-plt-1-b.d +++ b/ld/testsuite/ld-aarch64/protections/bti-plt-1-b.d @@ -2,7 +2,7 @@ #source: bti-plt-1.s #target: [check_shared_lib_support] #as: -mabi=lp64 -defsym __property_bti__=1 -#ld: -shared -z force-bti -T bti-plt.ld -L./tmpdir -lbti-plt-so +#ld: -shared -T bti-plt.ld -L./tmpdir -lbti-plt-so #objdump: -dr -j .plt [^:]*: *file format elf64-.*aarch64 diff --git a/ld/testsuite/ld-elf/sec64k.exp b/ld/testsuite/ld-elf/sec64k.exp index 8dcb021..deb46d3 100644 --- a/ld/testsuite/ld-elf/sec64k.exp +++ b/ld/testsuite/ld-elf/sec64k.exp @@ -168,9 +168,9 @@ if [catch { set ofd [open "tmpdir/$test2.d" w] } x] { return } -# too big for avr, d10v and msp -# lack of fancy orphan section handling causes overlap on fr30 and iq2000 -# bfin and lm32 complain about relocations in read-only sections +# Too big for avr, d10v and msp. +# Lack of fancy orphan section handling causes overlap on fr30 and iq2000. +# bfin and lm32 complain about relocations in read-only sections. if { ![istarget "d10v-*-*"] && ![istarget "avr-*-*"] && ![istarget "msp*-*-*"] @@ -179,7 +179,13 @@ if { ![istarget "d10v-*-*"] && ![istarget "bfin-*-linux*"] && ![istarget "lm32-*-linux*"] && ![istarget "pru-*-*"] } { + + # Create a 64ksec.d test control file... + + # List the input files. foreach sfile $sfiles { puts $ofd "#source: $sfile" } + + # Add any needed linker command line options. if { [istarget spu*-*-*] } { puts $ofd "#ld: --local-store 0:0" } elseif { [istarget "i?86-*-linux*"] || [istarget "x86_64-*-linux*"] } { @@ -187,10 +193,20 @@ if { ![istarget "d10v-*-*"] } else { puts $ofd "#ld:" } - #force z80 target to compile for eZ80 in ADL mode + + # Enable the accumulation of internal linker statistics in a separate file. + # Enabled this way as you cannot have multiple #ld: options in a .d file. + # The + character causes the file to opened in append mode, so that multiple + # runs of this test will accumulate data over time. Thus allowing regular + # testers to see changes in the performance of the linker. + puts $ofd "#ld_after_inputfiles: --stats=+tmpdir/$test2.stats" + + # Force z80 target to compile for eZ80 in ADL mode. if { [istarget "z80-*-*"] } then { puts $ofd "#as: -ez80-adl" } + + # Add a test of the linked binary. puts $ofd "#readelf: -W -wN -Ss" puts $ofd "There are 660.. section headers.*:" puts $ofd "#..." @@ -199,6 +215,7 @@ if { ![istarget "d10v-*-*"] puts $ofd " \\\[65279\\\] \\.foo\\.\[0-9\]+ .*" puts $ofd " \\\[65280\\\] \\.foo\\.\[0-9\]+ .*" puts $ofd "#..." + if { [is_elf_unused_section_symbols ] } { puts $ofd " 660..: \[0-9a-f\]+\[ \]+0\[ \]+SECTION\[ \]+LOCAL\[ \]+DEFAULT\[ \]+660...*" puts $ofd "#..." @@ -209,6 +226,7 @@ if { ![istarget "d10v-*-*"] puts $ofd " 66...: \[0-9a-f\]+\[ \]+0\[ \]+NOTYPE\[ \]+LOCAL\[ \]+DEFAULT\[ \]+660.. bar_66000$" } puts $ofd "#..." + # Global symbols are not in "alphanumeric" order, so we just check # that the first and the last are present in any order (assuming no # duplicates). @@ -217,9 +235,14 @@ if { ![istarget "d10v-*-*"] puts $ofd ".* (\[0-9\] foo_1|66... foo_66000)$" puts $ofd "#pass" close $ofd + + # Now run the constructed test file. run_dump_test "tmpdir/$test2" + + # Leave the test file around in case the user wants to examine it. } +# Tidy up. for { set i 1 } { $i < $max_sec / $secs_per_file } { incr i } { catch "exec rm -f tmpdir/dump$i.o" status } diff --git a/ld/testsuite/ld-pe/secidx.d b/ld/testsuite/ld-pe/secidx.d index 184cc9f..d0e52c9 100644 --- a/ld/testsuite/ld-pe/secidx.d +++ b/ld/testsuite/ld-pe/secidx.d @@ -1,11 +1,11 @@ tmpdir/secidx\.x: +file format pei-.* -Contents of section .text: +Contents of section \.text: .*1000 3e3e3e3e 3c3c3c3c 3e3e3e3e 3e3c3c3c >>>><<<<>>>>><<< .*1010 3e3e3e3e 3e3e3c3c 3e3e3e3e 3e3e3e3c >>>>>><<>>>>>>>< .*1020 3c3c3c3c 3e3e3e3e 3e909090 <<<<>>>>>... -Contents of section .data: +Contents of section \.data: .*2000 3e3e3e3e 3c3c3c3c 3e3e3e3e 3e3c3c3c >>>><<<<>>>>><<< .*2010 3e3e3e3e 3e3e3c3c 3e3e3e3e 3e3e3e3c >>>>>><<>>>>>>>< .*2020 3e3e3e3e 01001101 00110100 11010011 >>>>............ @@ -15,13 +15,13 @@ Contents of section .data: .*2060 3c3c3c3c 3c3c3c3c 3e3e3e3e 01001102 <<<<<<<<>>>>.... .*2070 00110300 113c3c3c 3c3c3c3c 3c000000 .....<<<<<<<<... .*2080 3c3c3c3e 3e3e3e3e 3e000000 <<<>>>>>>... -Contents of section .rdata: +Contents of section \.rdata: .*3000 3e3e3e3e 3c3c3c3c 3e3e3e3e 3e3c3c3c >>>><<<<>>>>><<< .*3010 3e3e3e3e 3e3e3c3c 3e3e3e3e 3e3e3e3c >>>>>><<>>>>>>>< .*3020 3e3e3e3e 00000000 00000000 00000000 >>>>............ .*3030 3c3c3c3e 3e3e3e3e 3e000000 ffffffff <<<>>>>>>....... .*3040 00000000 ffffffff 00000000 ............ -Contents of section .idata: +Contents of section \.idata: .*4000 00000000 00000000 00000000 00000000 ................ .*4010 00000000 .... #... diff --git a/ld/testsuite/ld-plugin/lto-20.ver b/ld/testsuite/ld-plugin/lto-20.ver new file mode 100644 index 0000000..ac906ac --- /dev/null +++ b/ld/testsuite/ld-plugin/lto-20.ver @@ -0,0 +1 @@ +FOO { global: foo; }; diff --git a/ld/testsuite/ld-plugin/lto-20a.c b/ld/testsuite/ld-plugin/lto-20a.c new file mode 100644 index 0000000..3d6dac9 --- /dev/null +++ b/ld/testsuite/ld-plugin/lto-20a.c @@ -0,0 +1,2 @@ +extern int foo (); +int main () { return foo (); } diff --git a/ld/testsuite/ld-plugin/lto-20b.c b/ld/testsuite/ld-plugin/lto-20b.c new file mode 100644 index 0000000..ba123cb --- /dev/null +++ b/ld/testsuite/ld-plugin/lto-20b.c @@ -0,0 +1,11 @@ +extern int printf (const char *, ...); +int foo () +{ +#ifdef SHARED + printf ("PASS\n"); + return 0; +#else + printf ("FAIL\n"); + return 1; +#endif +} diff --git a/ld/testsuite/ld-plugin/lto.exp b/ld/testsuite/ld-plugin/lto.exp index 556bbe9..3a56fb5 100644 --- a/ld/testsuite/ld-plugin/lto.exp +++ b/ld/testsuite/ld-plugin/lto.exp @@ -477,6 +477,12 @@ set lto_link_elf_tests [list \ [list {liblto-19.so} \ {-shared tmpdir/lto-19b.o tmpdir/liblto-19.a} {-O2 -fPIC} \ {dummy.c} {} {liblto-19.so}] \ + [list {liblto-20_static.a} \ + {} {-fPIC} \ + {lto-20b.c} {} {liblto-20_static.a}] \ + [list {liblto-20.so} \ + {-shared -Wl,--version-script=lto-20.ver} {-DSHARED -fPIC} \ + {lto-20b.c} {} {liblto-20.so}] \ [list {pr26806.so} \ {-shared} {-fpic -O2 -flto} \ {pr26806.c} {{nm {-D} pr26806.d}} {pr26806.so}] \ @@ -880,6 +886,10 @@ set lto_run_elf_shared_tests [list \ {-Wl,--as-needed,-R,tmpdir} {} \ {lto-19c.c} {lto-19.exe} {pass.out} {-flto -O2} {c} {} \ {tmpdir/liblto-19.so tmpdir/liblto-19.a}] \ + [list {lto-20} \ + {-Wl,--as-needed,-R,tmpdir} {} \ + {lto-20a.c} {lto-20.exe} {pass.out} {-flto} {c} {} \ + {tmpdir/liblto-20.so tmpdir/liblto-20_static.a -Wl,--no-as-needed}] \ [list {pr31482a} \ {-Wl,--no-as-needed,-R,tmpdir} {} \ {pr31482a.c} {pr31482a.exe} {pass.out} {-flto} {c} {} \ @@ -1202,6 +1212,38 @@ if { [is_elf_format] } { if { [is_elf_format] && [check_lto_shared_available] } { run_ld_link_exec_tests $lto_run_elf_shared_tests + if { [check_lto_fat_available] } { + run_cc_link_tests [list \ + [list \ + "Build libpr32846a.a" \ + "$plug_opt" "-fPIC -O2 -flto $lto_no_fat" \ + {pr32846a.c pr32846b.c} {} "libpr32846a.a" \ + ] \ + [list \ + "Build libpr32846b.a" \ + "$plug_opt" "-fPIC -O2 -flto $lto_no_fat" \ + {pr32846a.c pr32846b.c pr32846c.c} {} "libpr32846b.a" \ + ] \ + [list \ + "Build pr32846d.o" \ + "$plug_opt" "-fPIC -O2 -flto $lto_no_fat" \ + {pr32846d.c} {} \ + ] \ + [list \ + "Build pr32846e.o" \ + "$plug_opt" "-fPIC -O2 -flto $lto_no_fat" \ + {pr32846e.c} {} \ + ] \ + [list \ + "Build pr32846" \ + "-shared -fPIC -O2 -flto $lto_no_fat -Wl,--no-undefined \ + tmpdir/pr32846d.o tmpdir/libpr32846a.a \ + tmpdir/libpr32846b.a tmpdir/pr32846e.o" \ + "-O2 -fPIC -flto $lto_no_fat" \ + {dummy.c} {} "pr32846" \ + ] \ + ] \ + } } proc pr20103 {cflags libs} { diff --git a/ld/testsuite/ld-plugin/pr32846a.c b/ld/testsuite/ld-plugin/pr32846a.c new file mode 100644 index 0000000..8c16171 --- /dev/null +++ b/ld/testsuite/ld-plugin/pr32846a.c @@ -0,0 +1,6 @@ +extern void mkdir_p (void); +void +mkdir_parents (void) +{ + mkdir_p (); +} diff --git a/ld/testsuite/ld-plugin/pr32846b.c b/ld/testsuite/ld-plugin/pr32846b.c new file mode 100644 index 0000000..9776a37 --- /dev/null +++ b/ld/testsuite/ld-plugin/pr32846b.c @@ -0,0 +1,4 @@ +void +hash_new (void) +{ +} diff --git a/ld/testsuite/ld-plugin/pr32846c.c b/ld/testsuite/ld-plugin/pr32846c.c new file mode 100644 index 0000000..f87cffb --- /dev/null +++ b/ld/testsuite/ld-plugin/pr32846c.c @@ -0,0 +1,6 @@ +extern void hash_new (void); +void +kmod_new (void) +{ + hash_new(); +} diff --git a/ld/testsuite/ld-plugin/pr32846d.c b/ld/testsuite/ld-plugin/pr32846d.c new file mode 100644 index 0000000..c6f4102 --- /dev/null +++ b/ld/testsuite/ld-plugin/pr32846d.c @@ -0,0 +1,12 @@ +extern void kmod_new (void); +extern void mkdir_parents (void); +void +do_lsmod (void) +{ + kmod_new (); +} +void +do_static_nodes (void) +{ + mkdir_parents(); +} diff --git a/ld/testsuite/ld-plugin/pr32846e.c b/ld/testsuite/ld-plugin/pr32846e.c new file mode 100644 index 0000000..c4e5e56 --- /dev/null +++ b/ld/testsuite/ld-plugin/pr32846e.c @@ -0,0 +1,4 @@ +void +mkdir_p (void) +{ +} diff --git a/ld/testsuite/ld-scripts/map-address.exp b/ld/testsuite/ld-scripts/map-address.exp index 2291302..776fed4 100644 --- a/ld/testsuite/ld-scripts/map-address.exp +++ b/ld/testsuite/ld-scripts/map-address.exp @@ -130,19 +130,38 @@ if { [is_elf_format] } { $IMAGE_BASE tmpdir/map-address.o \ -Map=tmpdir/map-locals.map --print-map-locals"]} { fail $testname - return - } - if [is_remote host] then { - remote_upload host "tmpdir/map-locals.map" - } + } else { - # Some ELF targets do not preserve their local symbols. - setup_xfail "d30v-*-*" "dlx-*-*" "pj-*-*" "s12z-*-*" "xgate-*-*" + if [is_remote host] then { + remote_upload host "tmpdir/map-locals.map" + } + + # Some ELF targets do not preserve their local symbols. + setup_xfail "d30v-*-*" "dlx-*-*" "pj-*-*" "s12z-*-*" "xgate-*-*" + if {[regexp_diff \ + "tmpdir/map-locals.map" \ + "$srcdir/$subdir/map-locals.d"]} { + fail $testname + } else { + pass $testname + } + } +} + +set testname "map with resource usage" + +if {![ld_link $ld tmpdir/map-address \ + "$LDFLAGS -T $srcdir/$subdir/map-address.t \ + $IMAGE_BASE tmpdir/map-address.o \ + -Map=tmpdir/map-locals.map \ + --stats=tmpdir/map-stats.map"]} { + fail $testname +} else { if {[regexp_diff \ - "tmpdir/map-locals.map" \ - "$srcdir/$subdir/map-locals.d"]} { + "tmpdir/map-stats.map" \ + "$srcdir/$subdir/map-stats.d"]} { fail $testname } else { pass $testname diff --git a/ld/testsuite/ld-scripts/map-stats.d b/ld/testsuite/ld-scripts/map-stats.d new file mode 100644 index 0000000..ba9adf8 --- /dev/null +++ b/ld/testsuite/ld-scripts/map-stats.d @@ -0,0 +1,5 @@ +#... +Stats: phase.* +Stats: name.* +Stats: ALL.* +#pass |