-/* LTO plugin for gold.
- Copyright (C) 2009 Free Software Foundation, Inc.
+/* LTO plugin for gold and/or GNU ld.
+ 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"
+#endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdbool.h>
#include <assert.h>
+#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
+#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
-#include <stdbool.h>
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(S) (((S) & 0xff) == 0)
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(S) (((S) & 0xff00) >> 8)
+#endif
#include <libiberty.h>
+#include <hashtab.h>
+#include "../gcc/lto/common.h"
+#include "simple-object.h"
+#include "plugin-api.h"
-/* The presence of gelf.h is checked by the toplevel configure script. */
-#include <gelf.h>
+/* We need to use I64 instead of ll width-specifier on native Windows.
+ The reason for this is that older MS-runtimes don't support the ll. */
+#ifdef __MINGW32__
+#define PRI_LL "I64"
+#else
+#define PRI_LL "ll"
+#endif
-#include "plugin-api.h"
-#include "../gcc/lto/common.h"
+/* Handle opening elf files on hosts, such as Windows, that may use
+ text file handling that will break binary access. */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+/* Segment name for LTO sections. This is only used for Mach-O.
+ FIXME: This needs to be kept in sync with darwin.c. */
+
+#define LTO_SEGMENT_NAME "__GNU_LTO"
+
+/* LTO magic section name. */
+
+#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
- copy the symbol information. */
+ copy the symbol information.
+ The id must be 64bit to minimze collisions. */
+
+struct sym_aux
+{
+ uint32_t slot;
+ unsigned long long id;
+ unsigned next_conflict;
+};
struct plugin_symtab
{
int nsyms;
- uint32_t *slots;
+ struct sym_aux *aux;
struct ld_plugin_symbol *syms;
+ unsigned long long id;
+};
+
+/* Encapsulates object file data during symbol scan. */
+struct plugin_objfile
+{
+ int found;
+ int offload;
+ simple_object_read *objfile;
+ struct plugin_symtab *out;
+ const struct ld_plugin_input_file *file;
};
/* All that we have to remember about a file. */
char *name;
void *handle;
struct plugin_symtab symtab;
+ struct plugin_symtab conflicts;
+};
+
+/* List item with name of the file with offloading. */
+
+struct plugin_offload_file
+{
+ char *name;
+ struct plugin_offload_file *next;
};
+/* Until ASM_OUTPUT_LABELREF can be hookized and decoupled from
+ stdio file streams, we do simple label translation here. */
+
+enum symbol_style
+{
+ ss_none, /* No underscore prefix. */
+ ss_win32, /* Underscore prefix any symbol not beginning with '@'. */
+ ss_uscore, /* Underscore prefix all symbols. */
+};
static char *arguments_file_name;
static ld_plugin_register_claim_file register_claim_file;
-static ld_plugin_add_symbols add_symbols;
static ld_plugin_register_all_symbols_read register_all_symbols_read;
-static ld_plugin_get_symbols get_symbols;
+static ld_plugin_get_symbols get_symbols, get_symbols_v2;
static ld_plugin_register_cleanup register_cleanup;
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, 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;
+/* Last file in the list. */
+static struct plugin_offload_file *offload_files_last;
+/* Last non-archive file in the list. */
+static struct plugin_offload_file *offload_files_last_obj;
+/* Last LTO file in the list. */
+static struct plugin_offload_file *offload_files_last_lto;
+/* Total number of files with offloading. */
+static unsigned num_offload_files;
static char **output_files = NULL;
static unsigned int num_output_files = 0;
static unsigned int num_pass_through_items;
static bool debug;
-static bool nop;
+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 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. */
+static int gold_version = -1;
+
+/* Not used by default, but can be overridden at runtime
+ by using -plugin-opt=-sym-style={none,win32,underscore|uscore}
+ (in fact, only first letter of style arg is checked.) */
+static enum symbol_style sym_style = ss_none;
static void
-check (bool gate, enum ld_plugin_level level, const char *text)
+check_1 (int gate, enum ld_plugin_level level, const char *text)
{
if (gate)
return;
}
}
+/* This little wrapper allows check to be called with a non-integer
+ first argument, such as a pointer that must be non-NULL. We can't
+ use c99 bool type to coerce it into range, so we explicitly test. */
+#define check(GATE, LEVEL, TEXT) check_1 (((GATE) != 0), (LEVEL), (TEXT))
+
/* 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 (char *p, struct ld_plugin_symbol *entry, uint32_t *slot)
+parse_table_entry (char *p, struct ld_plugin_symbol *entry,
+ struct sym_aux *aux)
{
unsigned char t;
enum ld_plugin_symbol_kind translate_kind[] =
LDPV_HIDDEN
};
- entry->name = xstrdup (p);
+ switch (sym_style)
+ {
+ case ss_win32:
+ if (p[0] == '@')
+ {
+ /* cf. Duff's device. */
+ case ss_none:
+ entry->name = xstrdup (p);
+ break;
+ }
+ /* FALL-THROUGH. */
+ case ss_uscore:
+ entry->name = concat ("_", p, NULL);
+ break;
+ default:
+ check (0, LDPL_FATAL, "invalid symbol style requested");
+ break;
+ }
while (*p)
p++;
p++;
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];
entry->visibility = translate_visibility[t];
p++;
- entry->size = *(uint64_t *) p;
+ memcpy (&entry->size, p, sizeof (uint64_t));
p += 8;
- *slot = *(uint32_t *) p;
+ memcpy (&aux->slot, p, sizeof (uint32_t));
p += 4;
entry->resolution = LDPR_UNKNOWN;
+ aux->next_conflict = -1;
+
return p;
}
-/* Return the section in ELF that is named NAME. */
+/* 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 Elf_Scn *
-get_section (Elf *elf, const char *name)
+static char *
+parse_table_entry_extension (char *p, struct ld_plugin_symbol *entry)
{
- Elf_Scn *section = 0;
- GElf_Ehdr header;
- GElf_Ehdr *t = gelf_getehdr (elf, &header);
- if (t == NULL)
- return NULL;
- assert (t == &header);
-
- while ((section = elf_nextscn(elf, section)) != 0)
+ unsigned char t;
+ enum ld_plugin_symbol_type symbol_types[] =
{
- GElf_Shdr shdr;
- GElf_Shdr *tshdr = gelf_getshdr (section, &shdr);
- const char *t;
- assert (tshdr == &shdr);
- t = elf_strptr (elf, header.e_shstrndx, shdr.sh_name);
- assert (t != NULL);
- if (strcmp (t, name) == 0)
- return section;
- }
- return NULL;
-}
-
-/* Returns the IL symbol table of file ELF. */
+ LDST_UNKNOWN,
+ LDST_FUNCTION,
+ LDST_VARIABLE,
+ };
-static Elf_Data *
-get_symtab (Elf *elf)
-{
- Elf_Data *data = 0;
- Elf_Scn *section = get_section (elf, ".gnu.lto_.symtab");
- if (!section)
- return NULL;
+ t = *p;
+ check (t <= 2, LDPL_FATAL, "invalid symbol type found");
+ entry->symbol_type = symbol_types[t];
+ p++;
+ entry->section_kind = *p;
+ p++;
- data = elf_getdata (section, data);
- assert (data);
- return data;
+ return p;
}
-/* Translate the IL symbol table SYMTAB. Write the slots and symbols in OUT. */
+
+/* Translate the IL symbol table located between DATA and END. Append the
+ slots and symbols to OUT. */
static void
-translate (Elf_Data *symtab, struct plugin_symtab *out)
+translate (char *data, char *end, struct plugin_symtab *out)
{
- uint32_t *slots = NULL;
- char *data = symtab->d_buf;
- char *end = data + symtab->d_size;
+ struct sym_aux *aux;
struct ld_plugin_symbol *syms = NULL;
- int n = 0;
-
- while (data < end)
- {
- n++;
- syms = xrealloc (syms, n * sizeof (struct ld_plugin_symbol));
- check (syms, LDPL_FATAL, "could not allocate memory");
- slots = xrealloc (slots, n * sizeof (uint32_t));
- check (slots, LDPL_FATAL, "could not allocate memory");
- data = parse_table_entry (data, &syms[n - 1], &slots[n - 1]);
+ int n, len;
+
+ /* This overestimates the output buffer sizes, but at least
+ the algorithm is O(1) now. */
+
+ len = (end - data)/8 + out->nsyms + 1;
+ syms = xrealloc (out->syms, len * sizeof (struct ld_plugin_symbol));
+ aux = xrealloc (out->aux, len * sizeof (struct sym_aux));
+
+ for (n = out->nsyms; data < end; n++)
+ {
+ aux[n].id = out->id;
+ data = parse_table_entry (data, &syms[n], &aux[n]);
}
+ assert(n < len);
+
out->nsyms = n;
out->syms = syms;
- out->slots = slots;
+ 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. */
static void
-free_1 (void)
+free_1 (struct plugin_file_info *files, unsigned num_files)
{
unsigned int i;
- for (i = 0; i < num_claimed_files; i++)
+ for (i = 0; i < num_files; i++)
{
- struct plugin_file_info *info = &claimed_files[i];
+ struct plugin_file_info *info = &files[i];
struct plugin_symtab *symtab = &info->symtab;
unsigned int j;
for (j = 0; j < symtab->nsyms; j++)
{
struct ld_plugin_symbol *s = &symtab->syms[j];
free (s->name);
- if (s->comdat_key)
- free (s->comdat_key);
+ free (s->comdat_key);
}
free (symtab->syms);
symtab->syms = NULL;
{
struct plugin_file_info *info = &claimed_files[i];
struct plugin_symtab *symtab = &info->symtab;
- free (symtab->slots);
+ free (symtab->aux);
free (info->name);
}
claimed_files = NULL;
num_claimed_files = 0;
- if (arguments_file_name)
- free (arguments_file_name);
+ while (offload_files)
+ {
+ struct plugin_offload_file *ofld = offload_files;
+ offload_files = offload_files->next;
+ free (ofld);
+ }
+ num_offload_files = 0;
+
+ free (arguments_file_name);
arguments_file_name = NULL;
}
+/* Dump SYMTAB to resolution file F. */
+
+static void
+dump_symtab (FILE *f, struct plugin_symtab *symtab)
+{
+ unsigned j;
+
+ for (j = 0; j < symtab->nsyms; j++)
+ {
+ uint32_t slot = symtab->aux[j].slot;
+ unsigned int resolution = symtab->syms[j].resolution;
+
+ assert (resolution != LDPR_UNKNOWN);
+
+ fprintf (f, "%u %" PRI_LL "x %s %s\n",
+ (unsigned int) slot, symtab->aux[j].id,
+ lto_resolution_str[resolution],
+ symtab->syms[j].name);
+ }
+}
+
+/* Finish the conflicts' resolution information after the linker resolved
+ the original symbols */
+
+static void
+finish_conflict_resolution (struct plugin_symtab *symtab,
+ struct plugin_symtab *conflicts)
+{
+ int i, j;
+
+ if (conflicts->nsyms == 0)
+ return;
+
+ for (i = 0; i < symtab->nsyms; i++)
+ {
+ char resolution = LDPR_UNKNOWN;
+
+ if (symtab->aux[i].next_conflict == -1)
+ continue;
+
+ switch (symtab->syms[i].def)
+ {
+ case LDPK_DEF:
+ case LDPK_COMMON: /* ??? */
+ resolution = LDPR_RESOLVED_IR;
+ break;
+ case LDPK_WEAKDEF:
+ resolution = LDPR_PREEMPTED_IR;
+ break;
+ case LDPK_UNDEF:
+ case LDPK_WEAKUNDEF:
+ resolution = symtab->syms[i].resolution;
+ break;
+ default:
+ assert (0);
+ }
+
+ assert (resolution != LDPR_UNKNOWN);
+
+ for (j = symtab->aux[i].next_conflict;
+ j != -1;
+ j = conflicts->aux[j].next_conflict)
+ conflicts->syms[j].resolution = resolution;
+ }
+}
+
+/* Free symbol table SYMTAB. */
+
+static void
+free_symtab (struct plugin_symtab *symtab)
+{
+ free (symtab->syms);
+ symtab->syms = NULL;
+ free (symtab->aux);
+ symtab->aux = NULL;
+}
+
/* Writes the relocations to disk. */
static void
struct plugin_file_info *info = &claimed_files[i];
struct plugin_symtab *symtab = &info->symtab;
struct ld_plugin_symbol *syms = symtab->syms;
- unsigned j;
- assert (syms);
- get_symbols (info->handle, symtab->nsyms, syms);
+ /* Version 2 of API supports IRONLY_EXP resolution that is
+ accepted by GCC-4.7 and newer. */
+ if (get_symbols_v2)
+ get_symbols_v2 (info->handle, symtab->nsyms, syms);
+ else
+ get_symbols (info->handle, symtab->nsyms, syms);
- fprintf (f, "%s %d\n", info->name, info->symtab.nsyms);
+ finish_conflict_resolution (symtab, &info->conflicts);
- for (j = 0; j < info->symtab.nsyms; j++)
+ fprintf (f, "%s %d\n", info->name, symtab->nsyms + info->conflicts.nsyms);
+ dump_symtab (f, symtab);
+ if (info->conflicts.nsyms)
{
- uint32_t slot = symtab->slots[j];
- unsigned int resolution = syms[j].resolution;
- fprintf (f, "%d %s %s\n", slot, lto_resolution_str[resolution], syms[j].name);
+ dump_symtab (f, &info->conflicts);
+ free_symtab (&info->conflicts);
}
}
fclose (f);
buf = s;
cont:
if (!fgets (buf, piece, f))
- break;
+ {
+ free (s);
+ break;
+ }
len = strlen (s);
if (s[len - 1] != '\n')
{
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 + 1;
char **lto_argv;
+ const char *linker_output_str = NULL;
const char **lto_arg_ptr;
- if (num_claimed_files == 0)
+ if (num_claimed_files + num_offload_files == 0)
return LDPS_OK;
if (nop)
write_resolution ();
- free_1 ();
+ free_1 (claimed_files, num_claimed_files);
for (i = 0; i < lto_wrapper_num_args; i++)
*lto_arg_ptr++ = lto_wrapper_argv[i];
+ if (!linker_output_known)
+ {
+ 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);
+ }
+
+ if (num_offload_files > 0)
+ {
+ FILE *f;
+ char *arg;
+ char *offload_objects_file_name;
+ struct plugin_offload_file *ofld;
+
+ offload_objects_file_name = make_temp_file (".ofldlist");
+ check (offload_objects_file_name, LDPL_FATAL,
+ "Failed to generate a temporary file name");
+ f = fopen (offload_objects_file_name, "w");
+ check (f, LDPL_FATAL, "could not open file with offload objects");
+ fprintf (f, "%u\n", num_offload_files);
+
+ /* Skip the dummy item at the start of the list. */
+ ofld = offload_files->next;
+ while (ofld)
+ {
+ fprintf (f, "%s\n", ofld->name);
+ ofld = ofld->next;
+ }
+ fclose (f);
+
+ arg = concat ("-foffload-objects=", offload_objects_file_name, NULL);
+ check (arg, LDPL_FATAL, "could not allocate");
+ *lto_arg_ptr++ = arg;
+ }
+
for (i = 0; i < num_claimed_files; i++)
{
struct plugin_file_info *info = &claimed_files[i];
free (lto_argv);
- if (pass_through_items)
+ /* --pass-through is not needed when using gold 1.11 or later. */
+ if (pass_through_items && gold_version < 111)
{
unsigned int i;
for (i = 0; i < num_pass_through_items; i++)
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)
+ maybe_unlink (arguments_file_name);
+
+ for (i = 0; i < num_output_files; i++)
+ maybe_unlink (output_files[i]);
+
+ free_2 ();
+ return LDPS_OK;
+}
+
+#define SWAP(type, a, b) \
+ do { type tmp_; tmp_ = (a); (a) = (b); (b) = tmp_; } while(0)
+
+/* Compare two hash table entries */
+
+static int eq_sym (const void *a, const void *b)
+{
+ const struct ld_plugin_symbol *as = (const struct ld_plugin_symbol *)a;
+ const struct ld_plugin_symbol *bs = (const struct ld_plugin_symbol *)b;
+
+ return !strcmp (as->name, bs->name);
+}
+
+/* Hash a symbol */
+
+static hashval_t hash_sym (const void *a)
+{
+ const struct ld_plugin_symbol *as = (const struct ld_plugin_symbol *)a;
+
+ return htab_hash_string (as->name);
+}
+
+/* Determine how strong a symbol is */
+
+static int symbol_strength (struct ld_plugin_symbol *s)
+{
+ switch (s->def)
+ {
+ case LDPK_UNDEF:
+ case LDPK_WEAKUNDEF:
+ return 0;
+ case LDPK_WEAKDEF:
+ return 1;
+ default:
+ return 2;
+ }
+}
+
+/* In the ld -r case we can get dups in the LTO symbol tables, where
+ the same symbol can have different resolutions (e.g. undefined and defined).
+
+ We have to keep that in the LTO symbol tables, but the dups confuse
+ gold and then finally gcc by supplying incorrect resolutions.
+
+ Problem is that the main gold symbol table doesn't know about subids
+ and does not distingush the same symbols in different states.
+
+ So we drop duplicates from the linker visible symbol table
+ and keep them in a private table. Then later do own symbol
+ resolution for the duplicated based on the results for the
+ originals.
+
+ Then when writing out the resolution file readd the dropped symbols.
+
+ XXX how to handle common? */
+
+static void
+resolve_conflicts (struct plugin_symtab *t, struct plugin_symtab *conflicts)
+{
+ htab_t symtab = htab_create (t->nsyms, hash_sym, eq_sym, NULL);
+ int i;
+ int out;
+ int outlen;
+
+ outlen = t->nsyms;
+ conflicts->syms = xmalloc (sizeof (struct ld_plugin_symbol) * outlen);
+ conflicts->aux = xmalloc (sizeof (struct sym_aux) * outlen);
+
+ /* Move all duplicate symbols into the auxiliary conflicts table. */
+ out = 0;
+ for (i = 0; i < t->nsyms; i++)
{
- t = unlink (arguments_file_name);
- check (t == 0, LDPL_FATAL, "could not unlink arguments file");
+ struct ld_plugin_symbol *s = &t->syms[i];
+ struct sym_aux *aux = &t->aux[i];
+ void **slot;
+
+ slot = htab_find_slot (symtab, s, INSERT);
+ if (*slot != NULL)
+ {
+ int cnf;
+ struct ld_plugin_symbol *orig = (struct ld_plugin_symbol *)*slot;
+ struct sym_aux *orig_aux = &t->aux[orig - t->syms];
+
+ /* Always let the linker resolve the strongest symbol */
+ if (symbol_strength (orig) < symbol_strength (s))
+ {
+ SWAP (struct ld_plugin_symbol, *orig, *s);
+ SWAP (uint32_t, orig_aux->slot, aux->slot);
+ SWAP (unsigned long long, orig_aux->id, aux->id);
+ /* Don't swap conflict chain pointer */
+ }
+
+ /* Move current symbol into the conflicts table */
+ cnf = conflicts->nsyms++;
+ conflicts->syms[cnf] = *s;
+ conflicts->aux[cnf] = *aux;
+ aux = &conflicts->aux[cnf];
+
+ /* Update conflicts chain of the original symbol */
+ aux->next_conflict = orig_aux->next_conflict;
+ orig_aux->next_conflict = cnf;
+
+ continue;
+ }
+
+ /* Remove previous duplicates in the main table */
+ if (out < i)
+ {
+ t->syms[out] = *s;
+ t->aux[out] = *aux;
+ }
+
+ /* Put original into the hash table */
+ *slot = &t->syms[out];
+ out++;
}
- for (i = 0; i < num_output_files; i++)
+ assert (conflicts->nsyms <= outlen);
+ assert (conflicts->nsyms + out == t->nsyms);
+
+ t->nsyms = out;
+ htab_delete (symtab);
+}
+
+/* Process one section of an object file. */
+
+static int
+process_symtab (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_PREFIX, LTO_SYMTAB_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
{
- t = unlink (output_files[i]);
- check (t == 0, LDPL_FATAL, "could not unlink output file");
+ 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;
- free_2 ();
- return LDPS_OK;
+ translate (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;
+}
+
+/* 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
+process_offload_section (void *data, const char *name, off_t offset, off_t len)
+{
+ if (!strncmp (name, OFFLOAD_SECTION, OFFLOAD_SECTION_LEN))
+ {
+ struct plugin_objfile *obj = (struct plugin_objfile *) data;
+ obj->offload = 1;
+ return 0;
+ }
+
+ return 1;
}
/* Callback used by gold to check if the plugin will claim FILE. Writes
claim_file_handler (const struct ld_plugin_input_file *file, int *claimed)
{
enum ld_plugin_status status;
- Elf *elf;
+ struct plugin_objfile obj;
struct plugin_file_info lto_file;
- Elf_Data *symtab;
+ int err;
+ const char *errmsg;
+
+ memset (<o_file, 0, sizeof (struct plugin_file_info));
if (file->offset != 0)
{
- char *objname;
- Elf *archive;
- off_t offset;
- /* We pass the offset of the actual file, not the archive header. */
- int t = asprintf (&objname, "%s@0x%" PRIx64, file->name,
- (int64_t) file->offset);
- check (t >= 0, LDPL_FATAL, "asprintf failed");
- lto_file.name = objname;
-
- archive = elf_begin (file->fd, ELF_C_READ, NULL);
- check (elf_kind (archive) == ELF_K_AR, LDPL_FATAL,
- "Not an archive and offset not 0");
-
- /* elf_rand expects the offset to point to the ar header, not the
- object itself. Subtract the size of the ar header (60 bytes).
- We don't uses sizeof (struct ar_hd) to avoid including ar.h */
-
- offset = file->offset - 60;
- check (offset == elf_rand (archive, offset), LDPL_FATAL,
- "could not seek in archive");
- elf = elf_begin (file->fd, ELF_C_READ, archive);
- check (elf != NULL, LDPL_FATAL, "could not find archive member");
- elf_end (archive);
+ /* We pass the offset of the actual file, not the archive header.
+ Can't use PRIx64, because that's C99, so we have to print the
+ 64-bit hex int as two 32-bit ones. Use xasprintf instead of
+ asprintf because asprintf doesn't work as expected on some older
+ mingw32 hosts. */
+ int lo, hi;
+ lo = file->offset & 0xffffffff;
+ hi = ((int64_t)file->offset >> 32) & 0xffffffff;
+ lto_file.name = hi ? xasprintf ("%s@0x%x%08x", file->name, hi, lo)
+ : xasprintf ("%s@0x%x", file->name, lo);
}
else
{
lto_file.name = xstrdup (file->name);
- elf = elf_begin (file->fd, ELF_C_READ, NULL);
}
lto_file.handle = file->handle;
*claimed = 0;
-
- if (!elf)
+ obj.file = file;
+ obj.found = 0;
+ obj.offload = 0;
+ obj.out = <o_file.symtab;
+ errmsg = NULL;
+ obj.objfile = simple_object_start_read (file->fd, file->offset, LTO_SEGMENT_NAME,
+ &errmsg, &err);
+ /* No file, but also no error code means unrecognized format; just skip it. */
+ if (!obj.objfile && !err)
goto err;
- symtab = get_symtab (elf);
- if (!symtab)
+ 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 (err && message)
+ message (LDPL_FATAL, "%s: %s: %s", file->name, errmsg,
+ xstrerror (err));
+ else if (message)
+ message (LDPL_FATAL, "%s: %s", file->name, errmsg);
+ goto err;
+ }
+
+ if (obj.objfile)
+ simple_object_find_sections (obj.objfile, process_offload_section,
+ &obj, &err);
+
+ if (obj.found == 0 && obj.offload == 0)
goto err;
- translate (symtab, <o_file.symtab);
+ if (obj.found > 1)
+ resolve_conflicts (<o_file.symtab, <o_file.conflicts);
+
+ if (obj.found > 0)
+ {
+ 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++;
+ claimed_files =
+ xrealloc (claimed_files,
+ num_claimed_files * sizeof (struct plugin_file_info));
+ claimed_files[num_claimed_files - 1] = lto_file;
+
+ *claimed = 1;
+ }
+
+ if (offload_files == NULL)
+ {
+ /* Add dummy item to the start of the list. */
+ offload_files = xmalloc (sizeof (struct plugin_offload_file));
+ offload_files->name = NULL;
+ offload_files->next = NULL;
+ offload_files_last = offload_files;
+ }
- status = add_symbols (file->handle, lto_file.symtab.nsyms,
- lto_file.symtab.syms);
- check (status == LDPS_OK, LDPL_FATAL, "could not add symbols");
+ /* If this is an LTO file without offload, and it is the first LTO file, save
+ the pointer to the last offload file in the list. Further offload LTO
+ files will be inserted after it, if any. */
+ if (*claimed && obj.offload == 0 && offload_files_last_lto == NULL)
+ offload_files_last_lto = offload_files_last;
- *claimed = 1;
- num_claimed_files++;
- claimed_files =
- xrealloc (claimed_files,
- num_claimed_files * sizeof (struct plugin_file_info));
- claimed_files[num_claimed_files - 1] = lto_file;
+ if (obj.offload == 1)
+ {
+ /* Add file to the list. The order must be exactly the same as the final
+ order after recompilation and linking, otherwise host and target tables
+ with addresses wouldn't match. If a static library contains both LTO
+ and non-LTO objects, ld and gold link them in a different order. */
+ struct plugin_offload_file *ofld
+ = xmalloc (sizeof (struct plugin_offload_file));
+ ofld->name = lto_file.name;
+ ofld->next = NULL;
+
+ if (*claimed && offload_files_last_lto == NULL && file->offset != 0
+ && gold_version == -1)
+ {
+ /* ld only: insert first LTO file from the archive after the last real
+ object file immediately preceding the archive, or at the begin of
+ the list if there was no real objects before archives. */
+ if (offload_files_last_obj != NULL)
+ {
+ ofld->next = offload_files_last_obj->next;
+ offload_files_last_obj->next = ofld;
+ }
+ else
+ {
+ ofld->next = offload_files->next;
+ offload_files->next = ofld;
+ }
+ }
+ else if (*claimed && offload_files_last_lto != NULL)
+ {
+ /* Insert LTO file after the last LTO file in the list. */
+ ofld->next = offload_files_last_lto->next;
+ offload_files_last_lto->next = ofld;
+ }
+ else
+ /* Add non-LTO file or first non-archive LTO file to the end of the
+ list. */
+ offload_files_last->next = ofld;
+
+ if (ofld->next == NULL)
+ offload_files_last = ofld;
+ if (file->offset == 0)
+ offload_files_last_obj = ofld;
+ if (*claimed)
+ offload_files_last_lto = ofld;
+ num_offload_files++;
+ }
goto cleanup;
err:
+ non_claimed_files++;
free (lto_file.name);
cleanup:
- if (elf)
- elf_end (elf);
+ if (obj.objfile)
+ simple_object_release_read (obj.objfile);
return LDPS_OK;
}
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=")))
pass_through_items[num_pass_through_items - 1] =
xstrdup (option + strlen ("-pass-through="));
}
+ else if (!strncmp (option, "-sym-style=", sizeof ("-sym-style=") - 1))
+ {
+ switch (option[sizeof ("-sym-style=") - 1])
+ {
+ case 'w':
+ sym_style = ss_win32;
+ break;
+ case 'u':
+ sym_style = ss_uscore;
+ break;
+ default:
+ sym_style = ss_none;
+ break;
+ }
+ }
else
{
int size;
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. */
struct ld_plugin_tv *p;
enum ld_plugin_status status;
- unsigned version = elf_version (EV_CURRENT);
- check (version != EV_NONE, LDPL_FATAL, "invalid ELF version");
-
p = tv;
while (p->tv_tag)
{
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;
case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
register_all_symbols_read = p->tv_u.tv_register_all_symbols_read;
break;
+ case LDPT_GET_SYMBOLS_V2:
+ get_symbols_v2 = p->tv_u.tv_get_symbols;
+ break;
case LDPT_GET_SYMBOLS:
get_symbols = p->tv_u.tv_get_symbols;
break;
case LDPT_OPTION:
process_option (p->tv_u.tv_string);
break;
+ case LDPT_GOLD_VERSION:
+ gold_version = p->tv_u.tv_val;
+ break;
+ case LDPT_LINKER_OUTPUT:
+ linker_output = (enum ld_plugin_output_file_type) p->tv_u.tv_val;
+ 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");
}
+ char *collect_gcc_options = getenv ("COLLECT_GCC_OPTIONS");
+ 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;
}