/* LTO plugin for gold and/or GNU ld.
- Copyright (C) 2009-2018 Free Software Foundation, Inc.
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
Contributed by Rafael Avila de Espindola (espindola@google.com).
This program is free software; you can redistribute it and/or modify
More information at http://gcc.gnu.org/wiki/whopr/driver.
This plugin should be passed the lto-wrapper options and will forward them.
- It also has 2 options of its own:
+ It also has options at his own:
-debug: Print the command line used to run lto-wrapper.
-nop: Instead of running lto-wrapper, pass the original to the plugin. This
- only works if the input files are hybrid. */
+ only works if the input files are hybrid.
+ -linker-output-known: Do not determine linker output
+ -linker-output-auto-notlo-rel: Switch from rel to nolto-rel mode without
+ warning. This is used on systems like VxWorks (kernel) where the link is
+ always partial and repeated incremental linking is generally not used.
+ -sym-style={none,win32,underscore|uscore}
+ -pass-through */
#ifdef HAVE_CONFIG_H
#include "config.h"
#if HAVE_STDINT_H
#include <stdint.h>
#endif
+#include <stdbool.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
/* LTO magic section name. */
-#define LTO_SECTION_PREFIX ".gnu.lto_.symtab"
-#define LTO_SECTION_PREFIX_LEN (sizeof (LTO_SECTION_PREFIX) - 1)
-#define OFFLOAD_SECTION ".gnu.offload_lto_.opts"
-#define OFFLOAD_SECTION_LEN (sizeof (OFFLOAD_SECTION) - 1)
+#define LTO_SYMTAB_PREFIX ".gnu.lto_.symtab"
+#define LTO_SYMTAB_PREFIX_LEN (sizeof (LTO_SYMTAB_PREFIX) - 1)
+#define LTO_SYMTAB_EXT_PREFIX ".gnu.lto_.ext_symtab"
+#define LTO_SYMTAB_EXT_PREFIX_LEN (sizeof (LTO_SYMTAB_EXT_PREFIX) - 1)
+#define LTO_LTO_PREFIX ".gnu.lto_.lto"
+#define LTO_LTO_PREFIX_LEN (sizeof (LTO_LTO_PREFIX) - 1)
+#define OFFLOAD_SECTION ".gnu.offload_lto_.opts"
+#define OFFLOAD_SECTION_LEN (sizeof (OFFLOAD_SECTION) - 1)
/* The part of the symbol table the plugin has to keep track of. Note that we
must keep SYMS until all_symbols_read is called to give the linker time to
static ld_plugin_add_input_file add_input_file;
static ld_plugin_add_input_library add_input_library;
static ld_plugin_message message;
-static ld_plugin_add_symbols add_symbols;
+static ld_plugin_add_symbols add_symbols, add_symbols_v2;
static struct plugin_file_info *claimed_files = NULL;
static unsigned int num_claimed_files = 0;
+static unsigned int non_claimed_files = 0;
/* List of files with offloading. */
static struct plugin_offload_file *offload_files;
static char **pass_through_items = NULL;
static unsigned int num_pass_through_items;
-static char debug;
+static bool debug;
+static bool save_temps;
+static bool verbose;
static char nop;
static char *resolution_file = NULL;
static enum ld_plugin_output_file_type linker_output;
-static int linker_output_set;
+static bool linker_output_set;
+static bool linker_output_known;
+static bool linker_output_auto_nolto_rel;
+static const char *link_output_name = NULL;
/* The version of gold being used, or -1 if not gold. The number is
MAJOR * 100 + MINOR. */
else
entry->comdat_key = xstrdup (entry->comdat_key);
+ entry->unused = entry->section_kind = entry->symbol_type = 0;
+
t = *p;
check (t <= 4, LDPL_FATAL, "invalid symbol kind found");
entry->def = translate_kind[t];
return p;
}
+/* Parse an entry of the IL symbol table. The data to be parsed is pointed
+ by P and the result is written in ENTRY. The slot number is stored in SLOT.
+ Returns the address of the next entry. */
+
+static char *
+parse_table_entry_extension (char *p, struct ld_plugin_symbol *entry)
+{
+ unsigned char t;
+ enum ld_plugin_symbol_type symbol_types[] =
+ {
+ LDST_UNKNOWN,
+ LDST_FUNCTION,
+ LDST_VARIABLE,
+ };
+
+ t = *p;
+ check (t <= 2, LDPL_FATAL, "invalid symbol type found");
+ entry->symbol_type = symbol_types[t];
+ p++;
+ entry->section_kind = *p;
+ p++;
+
+ return p;
+}
+
+
/* Translate the IL symbol table located between DATA and END. Append the
slots and symbols to OUT. */
out->aux = aux;
}
+static void
+parse_symtab_extension (char *data, char *end, struct plugin_symtab *out)
+{
+ unsigned i;
+
+ unsigned char version = *data;
+ data++;
+
+ /* Version 1 contains the following data per entry:
+ - symbol_type
+ - section_kind
+ . */
+
+ if (version == 1)
+ for (i = 0; i < out->nsyms; i++)
+ data = parse_table_entry_extension (data, &out->syms[i]);
+}
+
/* Free all memory that is no longer needed after writing the symbol
resolution. */
for (i = 0; i < symtab->nsyms; i++)
{
- int resolution = LDPR_UNKNOWN;
+ char resolution = LDPR_UNKNOWN;
if (symtab->aux[i].next_conflict == -1)
continue;
struct pex_obj *pex;
const char *errmsg;
- /* Write argv to a file to avoid a command line that is too long. */
- arguments_file_name = make_temp_file ("");
+ /* Write argv to a file to avoid a command line that is too long
+ Save the file locally on save-temps. */
+ if (save_temps && link_output_name)
+ {
+ arguments_file_name = (char *) xmalloc (strlen (link_output_name)
+ + sizeof (".lto_wrapper_args") + 1);
+ strcpy (arguments_file_name, link_output_name);
+ strcat (arguments_file_name, ".lto_wrapper_args");
+ }
+ else
+ arguments_file_name = make_temp_file (".lto_wrapper_args");
check (arguments_file_name, LDPL_FATAL,
"Failed to generate a temorary file name");
for (i = 1; argv[i]; i++)
{
char *a = argv[i];
+ /* Check the input argument list for a verbose marker too. */
if (a[0] == '-' && a[1] == 'v' && a[2] == '\0')
{
- for (i = 0; argv[i]; i++)
- fprintf (stderr, "%s ", argv[i]);
- fprintf (stderr, "\n");
+ verbose = true;
break;
}
}
+ if (verbose)
+ {
+ for (i = 0; argv[i]; i++)
+ fprintf (stderr, "%s ", argv[i]);
+ fprintf (stderr, "\n");
+ }
+
new_argv[0] = argv[0];
new_argv[1] = at_args;
new_argv[2] = NULL;
fprintf (stderr, "\n");
}
-
pex = pex_init (PEX_USE_PIPES, "lto-wrapper", NULL);
check (pex != NULL, LDPL_FATAL, "could not pex_init lto-wrapper");
static enum ld_plugin_status
all_symbols_read_handler (void)
{
+ const unsigned num_lto_args
+ = num_claimed_files + lto_wrapper_num_args + 2
+ + !linker_output_known + !linker_output_auto_nolto_rel;
unsigned i;
- unsigned num_lto_args = num_claimed_files + lto_wrapper_num_args + 3;
char **lto_argv;
const char *linker_output_str = NULL;
const char **lto_arg_ptr;
for (i = 0; i < lto_wrapper_num_args; i++)
*lto_arg_ptr++ = lto_wrapper_argv[i];
- assert (linker_output_set);
- switch (linker_output)
+ if (!linker_output_known)
{
- case LDPO_REL:
- linker_output_str = "-flinker-output=rel";
- break;
- case LDPO_DYN:
- linker_output_str = "-flinker-output=dyn";
- break;
- case LDPO_PIE:
- linker_output_str = "-flinker-output=pie";
- break;
- case LDPO_EXEC:
- linker_output_str = "-flinker-output=exec";
- break;
- default:
- message (LDPL_FATAL, "unsupported linker output %i", linker_output);
- break;
+ assert (linker_output_set);
+ switch (linker_output)
+ {
+ case LDPO_REL:
+ if (non_claimed_files)
+ {
+ if (!linker_output_auto_nolto_rel)
+ message (LDPL_WARNING, "incremental linking of LTO and non-LTO"
+ " objects; using -flinker-output=nolto-rel which will"
+ " bypass whole program optimization");
+ linker_output_str = "-flinker-output=nolto-rel";
+ }
+ else
+ linker_output_str = "-flinker-output=rel";
+ break;
+ case LDPO_DYN:
+ linker_output_str = "-flinker-output=dyn";
+ break;
+ case LDPO_PIE:
+ linker_output_str = "-flinker-output=pie";
+ break;
+ case LDPO_EXEC:
+ linker_output_str = "-flinker-output=exec";
+ break;
+ default:
+ message (LDPL_FATAL, "unsupported linker output %i", linker_output);
+ break;
+ }
+ *lto_arg_ptr++ = xstrdup (linker_output_str);
}
- *lto_arg_ptr++ = xstrdup (linker_output_str);
if (num_offload_files > 0)
{
return LDPS_OK;
}
+/* Helper, as used in collect2. */
+static int
+file_exists (const char *name)
+{
+ return access (name, R_OK) == 0;
+}
+
+/* Unlink FILE unless we have save-temps set.
+ Note that we're saving files if verbose output is set. */
+
+static void
+maybe_unlink (const char *file)
+{
+ if (save_temps && file_exists (file))
+ {
+ if (verbose)
+ fprintf (stderr, "[Leaving %s]\n", file);
+ return;
+ }
+
+ unlink_if_ordinary (file);
+}
+
/* Remove temporary files at the end of the link. */
static enum ld_plugin_status
cleanup_handler (void)
{
unsigned int i;
- int t;
if (debug)
return LDPS_OK;
if (arguments_file_name)
- {
- t = unlink (arguments_file_name);
- check (t == 0, LDPL_FATAL, "could not unlink arguments file");
- }
+ maybe_unlink (arguments_file_name);
for (i = 0; i < num_output_files; i++)
- {
- t = unlink (output_files[i]);
- check (t == 0, LDPL_FATAL, "could not unlink output file");
- }
+ maybe_unlink (output_files[i]);
free_2 ();
return LDPS_OK;
char *s;
char *secdatastart, *secdata;
- if (strncmp (name, LTO_SECTION_PREFIX, LTO_SECTION_PREFIX_LEN) != 0)
+ if (strncmp (name, LTO_SYMTAB_PREFIX, LTO_SYMTAB_PREFIX_LEN) != 0)
return 1;
s = strrchr (name, '.');
return 0;
}
+/* Process one section of an object file. */
+
+static int
+process_symtab_extension (void *data, const char *name, off_t offset,
+ off_t length)
+{
+ struct plugin_objfile *obj = (struct plugin_objfile *)data;
+ char *s;
+ char *secdatastart, *secdata;
+
+ if (strncmp (name, LTO_SYMTAB_EXT_PREFIX, LTO_SYMTAB_EXT_PREFIX_LEN) != 0)
+ return 1;
+
+ s = strrchr (name, '.');
+ if (s)
+ sscanf (s, ".%" PRI_LL "x", &obj->out->id);
+ secdata = secdatastart = xmalloc (length);
+ offset += obj->file->offset;
+ if (offset != lseek (obj->file->fd, offset, SEEK_SET))
+ goto err;
+
+ do
+ {
+ ssize_t got = read (obj->file->fd, secdata, length);
+ if (got == 0)
+ break;
+ else if (got > 0)
+ {
+ secdata += got;
+ length -= got;
+ }
+ else if (errno != EINTR)
+ goto err;
+ }
+ while (length > 0);
+ if (length > 0)
+ goto err;
+
+ parse_symtab_extension (secdatastart, secdata, obj->out);
+ obj->found++;
+ free (secdatastart);
+ return 1;
+
+err:
+ if (message)
+ message (LDPL_FATAL, "%s: corrupt object file", obj->file->name);
+ /* Force claim_file_handler to abandon this file. */
+ obj->found = 0;
+ free (secdatastart);
+ return 0;
+}
+
+
/* Find an offload section of an object file. */
static int
if (!obj.objfile && !err)
goto err;
- if (obj.objfile)
- errmsg = simple_object_find_sections (obj.objfile, process_symtab, &obj, &err);
+ if (obj.objfile)
+ {
+ errmsg = simple_object_find_sections (obj.objfile, process_symtab, &obj,
+ &err);
+ /* Parsing symtab extension should be done only for add_symbols_v2 and
+ later versions. */
+ if (!errmsg && add_symbols_v2 != NULL)
+ errmsg = simple_object_find_sections (obj.objfile,
+ process_symtab_extension,
+ &obj, &err);
+ }
if (!obj.objfile || errmsg)
{
if (obj.found > 0)
{
- status = add_symbols (file->handle, lto_file.symtab.nsyms,
- lto_file.symtab.syms);
+ if (add_symbols_v2)
+ status = add_symbols_v2 (file->handle, lto_file.symtab.nsyms,
+ lto_file.symtab.syms);
+ else
+ status = add_symbols (file->handle, lto_file.symtab.nsyms,
+ lto_file.symtab.syms);
check (status == LDPS_OK, LDPL_FATAL, "could not add symbols");
num_claimed_files++;
goto cleanup;
err:
+ non_claimed_files++;
free (lto_file.name);
cleanup:
static void
process_option (const char *option)
{
- if (strcmp (option, "-debug") == 0)
- debug = 1;
+ if (strcmp (option, "-linker-output-known") == 0)
+ linker_output_known = true;
+ else if (strcmp (option, "-linker-output-auto-notlo-rel") == 0)
+ linker_output_auto_nolto_rel = true;
+ else if (strcmp (option, "-debug") == 0)
+ debug = true;
+ else if ((strcmp (option, "-v") == 0)
+ || (strcmp (option, "--verbose") == 0))
+ verbose = true;
+ else if (strcmp (option, "-save-temps") == 0)
+ save_temps = true;
else if (strcmp (option, "-nop") == 0)
nop = 1;
else if (!strncmp (option, "-pass-through=", strlen("-pass-through=")))
if (strncmp (option, "-fresolution=", sizeof ("-fresolution=") - 1) == 0)
resolution_file = opt + sizeof ("-fresolution=") - 1;
}
+ save_temps = save_temps || debug;
+ verbose = verbose || debug;
}
/* Called by gold after loading the plugin. TV is the transfer vector. */
case LDPT_REGISTER_CLAIM_FILE_HOOK:
register_claim_file = p->tv_u.tv_register_claim_file;
break;
+ case LDPT_ADD_SYMBOLS_V2:
+ add_symbols_v2 = p->tv_u.tv_add_symbols;
+ break;
case LDPT_ADD_SYMBOLS:
add_symbols = p->tv_u.tv_add_symbols;
break;
break;
case LDPT_LINKER_OUTPUT:
linker_output = (enum ld_plugin_output_file_type) p->tv_u.tv_val;
- linker_output_set = 1;
+ linker_output_set = true;
+ break;
+ case LDPT_OUTPUT_NAME:
+ /* We only use this to make user-friendly temp file names. */
+ link_output_name = p->tv_u.tv_string;
break;
default:
break;
"could not register the all_symbols_read callback");
}
- /* Support -fno-use-linker-plugin by failing to load the plugin
- for the case where it is auto-loaded by BFD. */
char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS");
- if (collect_gcc_options
- && strstr (collect_gcc_options, "'-fno-use-linker-plugin'"))
- return LDPS_ERR;
+ if (collect_gcc_options)
+ {
+ /* Support -fno-use-linker-plugin by failing to load the plugin
+ for the case where it is auto-loaded by BFD. */
+ if (strstr (collect_gcc_options, "'-fno-use-linker-plugin'"))
+ return LDPS_ERR;
+
+ if ( strstr (collect_gcc_options, "'-save-temps'"))
+ save_temps = true;
+
+ if (strstr (collect_gcc_options, "'-v'")
+ || strstr (collect_gcc_options, "'--verbose'"))
+ verbose = true;
+ }
return LDPS_OK;
}