1dedc12d18
This patch simplifies (!!!) the logic governing the naming of dump files and auxiliary output files in the driver, in the compiler, and in the LTO wrapper. No changes are made to the naming of primary outputs, there are often ways to restore past behavior, and a number of inconsistencies are fixed. Some internal options are removed (-auxbase and -auxbase-strip), sensible existing uses of -dumpdir and -dumpbase options remain unchanged, additional useful cases are added, making for what is still admittedly quite complex. Extensive documentation and testcases provide numerous examples, from normal to corner cases. The most visible changes are: - aux and dump files now always go in the same directory, that defaults to the directory of the primary output, but that can be overridden with -dumpdir, -save-temps=*, or, preserving past behavior, with a -dumpbase with a directory component. - driver and compiler now have the same notion of naming of auxiliary outputs, e.g. .dwo files will no longer be in one location while the debug info suggests they are elsewhere, and -save-temps and .dwo auxiliary outputs now go in the same location as .su, .ci and coverage data, with consistent naming. - explicitly-specified primary output names guide not only the location of aux and dump outputs: the output base name is also used in their base name, as a prefix when also linking (e.g. foo.c bar.c -o foobar creates foobar-foo.dwo and foobar-bar.dwo with -gsplit-dwarf), or as the base name instead of the input name (foo.c -c -o whatever.o creates whatever.su rather than foo.su with -fstack-usage). The preference for the input file base name, quite useful for our testsuite, can be restored with -dumpbase "". When compiling and linking tests in the testsuite with additional inputs, we now use this flag. Files named in dejagnu board ldflags, libs, and ldscripts are now quoted in the gcc testsuite with -Wl, so that they are not counted as additional inputs by the compiler driver. - naming a -dumpbase when compiling multiple sources used to cause dumps from later compiles to overwrite those of earlier ones; it is now used as a prefix when compiling multiple sources, like an executable name above. - the dumpbase, explicitly specified or computed from output or input names, now also governs the naming of aux outputs; since aux outputs usually replaced the suffix from the input name, while dump outputs append their own additional suffixes, a -dumpbase-ext option is introduced to enable a chosen suffix to be dropped from dumpbase to form aux output names. - LTO dump and aux outputs were quite a mess, sometimes leaking temporary output names into -save-temps output names, sometimes conversely generating desirable aux outputs in temporary locations. They now obey the same logic of compiler aux and dump outputs, landing in the expected location and taking the linker output name or an explicit dumpbase overrider into account. - Naming of -fdump-final-insns outputs now follows the dump file naming logic for the .gkd files, and the .gk dump files generated in the second -fcompare-debug compilation get the .gk inserted before the suffix that -dumpbase-ext drops in aux outputs. gcc/ChangeLog: * common.opt (aux_base_name): Define. (dumpbase, dumpdir): Mark as Driver options. (-dumpbase, -dumpdir): Likewise. (dumpbase-ext, -dumpbase-ext): New. (auxbase, auxbase-strip): Drop. * doc/invoke.texi (-dumpbase, -dumpbase-ext, -dumpdir): Document. (-o): Introduce the notion of primary output, mention it influences auxiliary and dump output names as well, add examples. (-save-temps): Adjust, move examples into -dump*. (-save-temps=cwd, -save-temps=obj): Likewise. (-fdump-final-insns): Adjust. * dwarf2out.c (gen_producer_string): Drop auxbase and auxbase_strip; add dumpbase_ext. * gcc.c (enum save_temps): Add SAVE_TEMPS_DUMP. (save_temps_prefix, save_temps_length): Drop. (save_temps_overrides_dumpdir): New. (dumpdir, dumpbase, dumpbase_ext): New. (dumpdir_length, dumpdir_trailing_dash_added): New. (outbase, outbase_length): New. (The Specs Language): Introduce %". Adjust %b and %B. (ASM_FINAL_SPEC): Use %b.dwo for an aux output name always. Precede object file with %w when it's the primary output. (cpp_debug_options): Do not pass on incoming -dumpdir, -dumpbase and -dumpbase-ext options; recompute them with %:dumps. (cc1_options): Drop auxbase with and without compare-debug; use cpp_debug_options instead of dumpbase. Mark asm output with %w when it's the primary output. (static_spec_functions): Drop %:compare-debug-auxbase-opt and %:replace-exception. Add %:dumps. (driver_handle_option): Implement -save-temps=*/-dumpdir mutual overriding logic. Save dumpdir, dumpbase and dumpbase-ext options. Do not save output_file in save_temps_prefix. (adds_single_suffix_p): New. (single_input_file_index): New. (process_command): Combine output dir, output base name, and dumpbase into dumpdir and outbase. (set_collect_gcc_options): Pass a possibly-adjusted -dumpdir. (do_spec_1): Optionally dumpdir instead of save_temps_prefix, and outbase instead of input_basename in %b, %B and in -save-temps aux files. Handle empty argument %". (driver::maybe_run_linker): Adjust dumpdir and auxbase. (compare_debug_dump_opt_spec_function): Adjust gkd dump file naming. Spec-quote the computed -fdump-final-insns file name. (debug_auxbase_opt): Drop. (compare_debug_self_opt_spec_function): Drop auxbase-strip computation. (compare_debug_auxbase_opt_spec_function): Drop. (not_actual_file_p): New. (replace_extension_spec_func): Drop. (dumps_spec_func): New. (convert_white_space): Split-out parts into... (quote_string, whitespace_to_convert_p): ... these. New. (quote_spec_char_p, quote_spec, quote_spec_arg): New. (driver::finalize): Release and reset new variables; drop removed ones. * lto-wrapper.c (HAVE_TARGET_EXECUTABLE_SUFFIX): Define if... (TARGET_EXECUTABLE_SUFFIX): ... is defined; define this to the empty string otherwise. (DUMPBASE_SUFFIX): Drop leading period. (debug_objcopy): Use concat. (run_gcc): Recognize -save-temps=* as -save-temps too. Obey -dumpdir. Pass on empty dumpdir and dumpbase with a directory component. Simplify temp file names. * opts.c (finish_options): Drop aux base name handling. (common_handle_option): Drop auxbase-strip handling. * toplev.c (print_switch_values): Drop auxbase, add dumpbase-ext. (process_options): Derive aux_base_name from dump_base_name and dump_base_ext. (lang_dependent_init): Compute dump_base_ext along with dump_base_name. Disable stack usage and callgraph-info during lto generation and compare-debug recompilation. gcc/fortran/ChangeLog: * options.c (gfc_get_option_string): Drop auxbase, add dumpbase_ext. gcc/ada/ChangeLog: * gcc-interface/lang-specs.h: Drop auxbase and auxbase-strip. Use %:dumps instead of -dumpbase. Add %w for implicit .s primary output. * switch.adb (Is_Internal_GCC_Switch): Recognize dumpdir and dumpbase-ext. Drop auxbase and auxbase-strip. lto-plugin/ChangeLog: * lto-plugin.c (skip_in_suffix): New. (exec_lto_wrapper): Use skip_in_suffix and concat to build non-temporary output names. (onload): Look for -dumpdir in COLLECT_GCC_OPTIONS, and override link_output_name with it. contrib/ChangeLog: * compare-debug: Adjust for .gkd files named as dump files, with the source suffix rather than the object suffix. gcc/testsuite/ChangeLog: * gcc.misc-tests/outputs.exp: New. * gcc.misc-tests/outputs-0.c: New. * gcc.misc-tests/outputs-1.c: New. * gcc.misc-tests/outputs-2.c: New. * lib/gcc-defs.exp (gcc_adjusted_linker_flags): New. (gcc_adjust_linker_flags): New. (dg-additional-files-options): Call it. Pass -dumpbase "" when there are additional sources. * lib/profopt.exp (profopt-execute): Pass the executable suffix with -dumpbase-ext. * lib/scandump.exp (dump-base): Mention -dumpbase "" use. * lib/scanltranstree.exp: Adjust dump suffix expectation. * lib/scanwpaipa.exp: Likewise.
1523 lines
39 KiB
C
1523 lines
39 KiB
C
/* 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
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
/* The plugin has only one external function: onload. Gold passes it an array of
|
|
function that the plugin uses to communicate back to gold.
|
|
|
|
With the functions provided by gold, the plugin can be notified when
|
|
gold first analyzes a file and pass a symbol table back to gold. The plugin
|
|
is also notified when all symbols have been read and it is time to generate
|
|
machine code for the necessary symbols.
|
|
|
|
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 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.
|
|
-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 <inttypes.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
#include <sys/wait.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"
|
|
|
|
/* 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
|
|
|
|
/* 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.
|
|
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;
|
|
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. */
|
|
|
|
struct plugin_file_info
|
|
{
|
|
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_register_all_symbols_read register_all_symbols_read;
|
|
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 char **lto_wrapper_argv;
|
|
static int lto_wrapper_num_args;
|
|
|
|
static char **pass_through_items = NULL;
|
|
static unsigned int num_pass_through_items;
|
|
|
|
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 bool linker_output_set;
|
|
static bool linker_output_known;
|
|
static bool linker_output_auto_nolto_rel;
|
|
static const char *link_output_name = NULL;
|
|
|
|
/* This indicates link_output_name already contains the dot of the
|
|
suffix, so we can skip it in extensions. */
|
|
static int skip_in_suffix = 0;
|
|
|
|
/* 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_1 (int gate, enum ld_plugin_level level, const char *text)
|
|
{
|
|
if (gate)
|
|
return;
|
|
|
|
if (message)
|
|
message (level, text);
|
|
else
|
|
{
|
|
/* If there is no nicer way to inform the user, fallback to stderr. */
|
|
fprintf (stderr, "%s\n", text);
|
|
if (level == LDPL_FATAL)
|
|
abort ();
|
|
}
|
|
}
|
|
|
|
/* 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,
|
|
struct sym_aux *aux)
|
|
{
|
|
unsigned char t;
|
|
enum ld_plugin_symbol_kind translate_kind[] =
|
|
{
|
|
LDPK_DEF,
|
|
LDPK_WEAKDEF,
|
|
LDPK_UNDEF,
|
|
LDPK_WEAKUNDEF,
|
|
LDPK_COMMON
|
|
};
|
|
|
|
enum ld_plugin_symbol_visibility translate_visibility[] =
|
|
{
|
|
LDPV_DEFAULT,
|
|
LDPV_PROTECTED,
|
|
LDPV_INTERNAL,
|
|
LDPV_HIDDEN
|
|
};
|
|
|
|
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++;
|
|
|
|
entry->version = NULL;
|
|
|
|
entry->comdat_key = p;
|
|
while (*p)
|
|
p++;
|
|
p++;
|
|
|
|
if (strlen (entry->comdat_key) == 0)
|
|
entry->comdat_key = NULL;
|
|
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];
|
|
p++;
|
|
|
|
t = *p;
|
|
check (t <= 3, LDPL_FATAL, "invalid symbol visibility found");
|
|
entry->visibility = translate_visibility[t];
|
|
p++;
|
|
|
|
memcpy (&entry->size, p, sizeof (uint64_t));
|
|
p += 8;
|
|
|
|
memcpy (&aux->slot, p, sizeof (uint32_t));
|
|
p += 4;
|
|
|
|
entry->resolution = LDPR_UNKNOWN;
|
|
|
|
aux->next_conflict = -1;
|
|
|
|
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. */
|
|
|
|
static void
|
|
translate (char *data, char *end, struct plugin_symtab *out)
|
|
{
|
|
struct sym_aux *aux;
|
|
struct ld_plugin_symbol *syms = NULL;
|
|
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->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 (struct plugin_file_info *files, unsigned num_files)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < num_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);
|
|
free (s->comdat_key);
|
|
}
|
|
free (symtab->syms);
|
|
symtab->syms = NULL;
|
|
}
|
|
}
|
|
|
|
/* Free all remaining memory. */
|
|
|
|
static void
|
|
free_2 (void)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < num_claimed_files; i++)
|
|
{
|
|
struct plugin_file_info *info = &claimed_files[i];
|
|
struct plugin_symtab *symtab = &info->symtab;
|
|
free (symtab->aux);
|
|
free (info->name);
|
|
}
|
|
|
|
for (i = 0; i < num_output_files; i++)
|
|
free (output_files[i]);
|
|
free (output_files);
|
|
|
|
free (claimed_files);
|
|
claimed_files = NULL;
|
|
num_claimed_files = 0;
|
|
|
|
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
|
|
write_resolution (void)
|
|
{
|
|
unsigned int i;
|
|
FILE *f;
|
|
|
|
check (resolution_file, LDPL_FATAL, "resolution file not specified");
|
|
f = fopen (resolution_file, "w");
|
|
check (f, LDPL_FATAL, "could not open file");
|
|
|
|
fprintf (f, "%d\n", num_claimed_files);
|
|
|
|
for (i = 0; i < num_claimed_files; i++)
|
|
{
|
|
struct plugin_file_info *info = &claimed_files[i];
|
|
struct plugin_symtab *symtab = &info->symtab;
|
|
struct ld_plugin_symbol *syms = symtab->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);
|
|
|
|
finish_conflict_resolution (symtab, &info->conflicts);
|
|
|
|
fprintf (f, "%s %d\n", info->name, symtab->nsyms + info->conflicts.nsyms);
|
|
dump_symtab (f, symtab);
|
|
if (info->conflicts.nsyms)
|
|
{
|
|
dump_symtab (f, &info->conflicts);
|
|
free_symtab (&info->conflicts);
|
|
}
|
|
}
|
|
fclose (f);
|
|
}
|
|
|
|
/* Pass files generated by the lto-wrapper to the linker. FD is lto-wrapper's
|
|
stdout. */
|
|
|
|
static void
|
|
add_output_files (FILE *f)
|
|
{
|
|
for (;;)
|
|
{
|
|
const unsigned piece = 32;
|
|
char *buf, *s = xmalloc (piece);
|
|
size_t len;
|
|
|
|
buf = s;
|
|
cont:
|
|
if (!fgets (buf, piece, f))
|
|
{
|
|
free (s);
|
|
break;
|
|
}
|
|
len = strlen (s);
|
|
if (s[len - 1] != '\n')
|
|
{
|
|
s = xrealloc (s, len + piece);
|
|
buf = s + len;
|
|
goto cont;
|
|
}
|
|
s[len - 1] = '\0';
|
|
|
|
num_output_files++;
|
|
output_files
|
|
= xrealloc (output_files, num_output_files * sizeof (char *));
|
|
output_files[num_output_files - 1] = s;
|
|
add_input_file (output_files[num_output_files - 1]);
|
|
}
|
|
}
|
|
|
|
/* Execute the lto-wrapper. ARGV[0] is the binary. The rest of ARGV is the
|
|
argument list. */
|
|
|
|
static void
|
|
exec_lto_wrapper (char *argv[])
|
|
{
|
|
int t, i;
|
|
int status;
|
|
char *at_args;
|
|
FILE *args;
|
|
FILE *wrapper_output;
|
|
char *new_argv[3];
|
|
struct pex_obj *pex;
|
|
const char *errmsg;
|
|
|
|
/* 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 = concat (link_output_name,
|
|
".lto_wrapper_args"
|
|
+ skip_in_suffix, NULL);
|
|
else
|
|
arguments_file_name = make_temp_file (".lto_wrapper_args");
|
|
check (arguments_file_name, LDPL_FATAL,
|
|
"Failed to generate a temorary file name");
|
|
|
|
args = fopen (arguments_file_name, "w");
|
|
check (args, LDPL_FATAL, "could not open arguments file");
|
|
|
|
t = writeargv (&argv[1], args);
|
|
check (t == 0, LDPL_FATAL, "could not write arguments");
|
|
t = fclose (args);
|
|
check (t == 0, LDPL_FATAL, "could not close arguments file");
|
|
|
|
at_args = concat ("@", arguments_file_name, NULL);
|
|
check (at_args, LDPL_FATAL, "could not allocate");
|
|
|
|
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')
|
|
{
|
|
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;
|
|
|
|
if (debug)
|
|
{
|
|
for (i = 0; new_argv[i]; i++)
|
|
fprintf (stderr, "%s ", new_argv[i]);
|
|
fprintf (stderr, "\n");
|
|
}
|
|
|
|
pex = pex_init (PEX_USE_PIPES, "lto-wrapper", NULL);
|
|
check (pex != NULL, LDPL_FATAL, "could not pex_init lto-wrapper");
|
|
|
|
errmsg = pex_run (pex, 0, new_argv[0], new_argv, NULL, NULL, &t);
|
|
check (errmsg == NULL, LDPL_FATAL, "could not run lto-wrapper");
|
|
check (t == 0, LDPL_FATAL, "could not run lto-wrapper");
|
|
|
|
wrapper_output = pex_read_output (pex, 0);
|
|
check (wrapper_output, LDPL_FATAL, "could not read lto-wrapper output");
|
|
|
|
add_output_files (wrapper_output);
|
|
|
|
t = pex_get_status (pex, 1, &status);
|
|
check (t == 1, LDPL_FATAL, "could not get lto-wrapper exit status");
|
|
check (WIFEXITED (status) && WEXITSTATUS (status) == 0, LDPL_FATAL,
|
|
"lto-wrapper failed");
|
|
|
|
pex_free (pex);
|
|
|
|
free (at_args);
|
|
}
|
|
|
|
/* Pass the original files back to the linker. */
|
|
|
|
static void
|
|
use_original_files (void)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < num_claimed_files; i++)
|
|
{
|
|
struct plugin_file_info *info = &claimed_files[i];
|
|
add_input_file (info->name);
|
|
}
|
|
}
|
|
|
|
|
|
/* Called by the linker once all symbols have been read. */
|
|
|
|
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;
|
|
char **lto_argv;
|
|
const char *linker_output_str = NULL;
|
|
const char **lto_arg_ptr;
|
|
if (num_claimed_files + num_offload_files == 0)
|
|
return LDPS_OK;
|
|
|
|
if (nop)
|
|
{
|
|
use_original_files ();
|
|
return LDPS_OK;
|
|
}
|
|
|
|
lto_argv = (char **) xcalloc (sizeof (char *), num_lto_args);
|
|
lto_arg_ptr = (const char **) lto_argv;
|
|
assert (lto_wrapper_argv);
|
|
|
|
write_resolution ();
|
|
|
|
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];
|
|
|
|
*lto_arg_ptr++ = info->name;
|
|
}
|
|
|
|
*lto_arg_ptr++ = NULL;
|
|
exec_lto_wrapper (lto_argv);
|
|
|
|
free (lto_argv);
|
|
|
|
/* --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++)
|
|
{
|
|
if (strncmp (pass_through_items[i], "-l", 2) == 0)
|
|
add_input_library (pass_through_items[i] + 2);
|
|
else
|
|
add_input_file (pass_through_items[i]);
|
|
free (pass_through_items[i]);
|
|
pass_through_items[i] = NULL;
|
|
}
|
|
free (pass_through_items);
|
|
pass_through_items = NULL;
|
|
}
|
|
|
|
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;
|
|
|
|
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++)
|
|
{
|
|
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++;
|
|
}
|
|
|
|
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
|
|
{
|
|
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;
|
|
|
|
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
|
|
the result in CLAIMED. */
|
|
|
|
static enum ld_plugin_status
|
|
claim_file_handler (const struct ld_plugin_input_file *file, int *claimed)
|
|
{
|
|
enum ld_plugin_status status;
|
|
struct plugin_objfile obj;
|
|
struct plugin_file_info lto_file;
|
|
int err;
|
|
const char *errmsg;
|
|
|
|
memset (<o_file, 0, sizeof (struct plugin_file_info));
|
|
|
|
if (file->offset != 0)
|
|
{
|
|
/* 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);
|
|
}
|
|
lto_file.handle = file->handle;
|
|
|
|
*claimed = 0;
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
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 (obj.objfile)
|
|
simple_object_release_read (obj.objfile);
|
|
|
|
return LDPS_OK;
|
|
}
|
|
|
|
/* Parse the plugin options. */
|
|
|
|
static void
|
|
process_option (const char *option)
|
|
{
|
|
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=")))
|
|
{
|
|
num_pass_through_items++;
|
|
pass_through_items = xrealloc (pass_through_items,
|
|
num_pass_through_items * sizeof (char *));
|
|
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;
|
|
char *opt = xstrdup (option);
|
|
lto_wrapper_num_args += 1;
|
|
size = lto_wrapper_num_args * sizeof (char *);
|
|
lto_wrapper_argv = (char **) xrealloc (lto_wrapper_argv, size);
|
|
lto_wrapper_argv[lto_wrapper_num_args - 1] = opt;
|
|
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. */
|
|
|
|
enum ld_plugin_status
|
|
onload (struct ld_plugin_tv *tv)
|
|
{
|
|
struct ld_plugin_tv *p;
|
|
enum ld_plugin_status status;
|
|
|
|
p = tv;
|
|
while (p->tv_tag)
|
|
{
|
|
switch (p->tv_tag)
|
|
{
|
|
case LDPT_MESSAGE:
|
|
message = p->tv_u.tv_message;
|
|
break;
|
|
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_REGISTER_CLEANUP_HOOK:
|
|
register_cleanup = p->tv_u.tv_register_cleanup;
|
|
break;
|
|
case LDPT_ADD_INPUT_FILE:
|
|
add_input_file = p->tv_u.tv_add_input_file;
|
|
break;
|
|
case LDPT_ADD_INPUT_LIBRARY:
|
|
add_input_library = p->tv_u.tv_add_input_library;
|
|
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;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
check (register_claim_file, LDPL_FATAL, "register_claim_file not found");
|
|
check (add_symbols, LDPL_FATAL, "add_symbols not found");
|
|
status = register_claim_file (claim_file_handler);
|
|
check (status == LDPS_OK, LDPL_FATAL,
|
|
"could not register the claim_file callback");
|
|
|
|
if (register_cleanup)
|
|
{
|
|
status = register_cleanup (cleanup_handler);
|
|
check (status == LDPS_OK, LDPL_FATAL,
|
|
"could not register the cleanup callback");
|
|
}
|
|
|
|
if (register_all_symbols_read)
|
|
{
|
|
check (get_symbols, LDPL_FATAL, "get_symbols not found");
|
|
status = register_all_symbols_read (all_symbols_read_handler);
|
|
check (status == LDPS_OK, LDPL_FATAL,
|
|
"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;
|
|
|
|
const char *p;
|
|
if ((p = strstr (collect_gcc_options, "'-dumpdir'")))
|
|
{
|
|
p += sizeof ("'-dumpdir'");
|
|
while (*p == ' ')
|
|
p++;
|
|
const char *start = p;
|
|
int ticks = 0, escapes = 0;
|
|
/* Count ticks (') and escaped (\.) characters. Stop at the
|
|
end of the options or at a blank after an even number of
|
|
ticks (not counting escaped ones. */
|
|
for (p = start; *p; p++)
|
|
{
|
|
if (*p == '\'')
|
|
{
|
|
ticks++;
|
|
continue;
|
|
}
|
|
else if ((ticks % 2) != 0)
|
|
{
|
|
if (*p == ' ')
|
|
break;
|
|
if (*p == '\\')
|
|
{
|
|
if (*++p)
|
|
escapes++;
|
|
else
|
|
p--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now allocate a new link_output_name and decode dumpdir
|
|
into it. The loop uses the same logic, except it counts
|
|
ticks and escapes backwards (so ticks is adjusted if we
|
|
find an odd number of them), and it copies characters
|
|
that are escaped or not otherwise skipped. */
|
|
int len = p - start - ticks - escapes + 1;
|
|
char *q = xmalloc (len);
|
|
link_output_name = q;
|
|
int oddticks = (ticks % 2);
|
|
ticks += oddticks;
|
|
for (p = start; *p; p++)
|
|
{
|
|
if (*p == '\'')
|
|
{
|
|
ticks--;
|
|
continue;
|
|
}
|
|
else if ((ticks % 2) != 0)
|
|
{
|
|
if (*p == ' ')
|
|
break;
|
|
if (*p == '\\')
|
|
{
|
|
if (*++p)
|
|
escapes--;
|
|
else
|
|
p--;
|
|
}
|
|
}
|
|
*q++ = *p;
|
|
}
|
|
*q = '\0';
|
|
assert (escapes == 0);
|
|
assert (ticks == oddticks);
|
|
assert (q - link_output_name == len - 1);
|
|
skip_in_suffix = 1;
|
|
}
|
|
}
|
|
|
|
return LDPS_OK;
|
|
}
|