mirror of
https://github.com/openharmony/third_party_elfutils.git
synced 2026-07-15 07:28:18 -04:00
1526 lines
44 KiB
C
1526 lines
44 KiB
C
/* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc.
|
|
This file is part of Red Hat elfutils.
|
|
Written by Ulrich Drepper <drepper@redhat.com>, 2001.
|
|
|
|
Red Hat elfutils 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; version 2 of the License.
|
|
|
|
Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
|
|
|
|
Red Hat elfutils is an included package of the Open Invention Network.
|
|
An included package of the Open Invention Network is a package for which
|
|
Open Invention Network licensees cross-license their patents. No patent
|
|
license is granted, either expressly or impliedly, by designation as an
|
|
included package. Should you wish to participate in the Open Invention
|
|
Network licensing program, please visit www.openinventionnetwork.com
|
|
<http://www.openinventionnetwork.com>. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <argp.h>
|
|
#include <assert.h>
|
|
#include <error.h>
|
|
#include <fcntl.h>
|
|
#include <libelf.h>
|
|
#include <libintl.h>
|
|
#include <locale.h>
|
|
#include <mcheck.h>
|
|
#include <stdio.h>
|
|
#include <stdio_ext.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <system.h>
|
|
#include "ld.h"
|
|
#include "list.h"
|
|
|
|
|
|
/* Name and version of program. */
|
|
static void print_version (FILE *stream, struct argp_state *state);
|
|
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
|
|
|
|
/* Bug report address. */
|
|
const char *argp_program_bug_address = PACKAGE_BUGREPORT;
|
|
|
|
|
|
/* Values for the various options. */
|
|
enum
|
|
{
|
|
ARGP_whole_archive = 300,
|
|
ARGP_no_whole_archive,
|
|
ARGP_static,
|
|
ARGP_dynamic,
|
|
ARGP_pagesize,
|
|
ARGP_rpath_link,
|
|
ARGP_runpath,
|
|
ARGP_runpath_link,
|
|
ARGP_version_script,
|
|
ARGP_gc_sections,
|
|
ARGP_no_gc_sections,
|
|
ARGP_no_undefined,
|
|
ARGP_conserve,
|
|
ARGP_as_needed,
|
|
ARGP_no_as_needed,
|
|
ARGP_eh_frame_hdr,
|
|
#if YYDEBUG
|
|
ARGP_yydebug,
|
|
#endif
|
|
};
|
|
|
|
|
|
/* Definitions of arguments for argp functions. */
|
|
static const struct argp_option options[] =
|
|
{
|
|
{ NULL, 0, NULL, 0, N_("Input File Control:"), 0 },
|
|
{ "whole-archive", ARGP_whole_archive, NULL, 0,
|
|
N_("Include whole archives in the output from now on."), 0 },
|
|
{ "no-whole-archive", ARGP_no_whole_archive, NULL, 0,
|
|
N_("Stop including the whole arhives in the output."), 0 },
|
|
{ NULL, 'l', N_("FILE"), OPTION_HIDDEN, NULL, 0 },
|
|
{ NULL, '(', NULL, 0, N_("Start a group."), 0 },
|
|
{ NULL, ')', NULL, 0, N_("End a group."), 0 },
|
|
{ NULL, 'L', N_("PATH"), 0,
|
|
N_("Add PATH to list of directories files are searched in."), 0 },
|
|
{ "as-needed", ARGP_as_needed, NULL, 0,
|
|
N_("Only set DT_NEEDED for following dynamic libs if actually used"), 0 },
|
|
{ "no-as-needed", ARGP_no_as_needed, NULL, 0,
|
|
N_("Always set DT_NEEDED for following dynamic libs"), 0 },
|
|
{ "rpath-link", ARGP_rpath_link, "PATH", OPTION_HIDDEN, NULL, 0 },
|
|
{ NULL, 'i', NULL, 0, N_("Ignore LD_LIBRARY_PATH environment variable."),
|
|
0 },
|
|
|
|
{ NULL, 0, NULL, 0, N_("Output File Control:"), 0 },
|
|
{ "output", 'o', N_("FILE"), 0, N_("Place output in FILE."), 0 },
|
|
{ NULL, 'z', "KEYWORD", OPTION_HIDDEN, NULL, 0 },
|
|
{ "-z nodefaultlib", '\0', NULL, OPTION_DOC,
|
|
N_("Object is marked to not use default search path at runtime."), 0 },
|
|
{ "-z allextract", '\0', NULL, OPTION_DOC,
|
|
N_("Same as --whole-archive."), 0 },
|
|
{ "-z defaultextract", '\0', NULL, OPTION_DOC, N_("\
|
|
Default rules of extracting from archive; weak references are not enough."),
|
|
0 },
|
|
{ "-z weakextract", '\0', NULL, OPTION_DOC,
|
|
N_("Weak references cause extraction from archive."), 0 },
|
|
{ "-z muldefs", '\0', NULL, OPTION_DOC,
|
|
N_("Allow multiple definitions; first is used."), 0 },
|
|
{ "-z defs | nodefs", '\0', NULL, OPTION_DOC,
|
|
N_("Disallow/allow undefined symbols in DSOs."), 0 },
|
|
{ "no-undefined", ARGP_no_undefined, NULL, OPTION_HIDDEN, NULL, 0 },
|
|
{ "-z origin", '\0', NULL, OPTION_DOC,
|
|
N_("Object requires immediate handling of $ORIGIN."), 0 },
|
|
{ "-z now", '\0', NULL, OPTION_DOC,
|
|
N_("Relocation will not be processed lazily."), 0 },
|
|
{ "-z nodelete", '\0', NULL, OPTION_DOC,
|
|
N_("Object cannot be unloaded at runtime."), 0 },
|
|
{ "-z initfirst", '\0', NULL, OPTION_DOC,
|
|
N_("Mark object to be initialized first."), 0 },
|
|
{ "-z lazyload | nolazyload", '\0', NULL, OPTION_DOC,
|
|
N_("Enable/disable lazy-loading flag for following dependencies."), 0 },
|
|
{ "-z nodlopen", '\0', NULL, OPTION_DOC,
|
|
N_("Mark object as not loadable with 'dlopen'."), 0 },
|
|
{ "-z ignore | record", '\0', NULL, OPTION_DOC,
|
|
N_("Ignore/record dependencies on unused DSOs."), 0 },
|
|
{ "-z systemlibrary", '\0', NULL, OPTION_DOC,
|
|
N_("Generated DSO will be a system library."), 0 },
|
|
{ "entry", 'e', N_("ADDRESS"), 0, N_("Set entry point address."), 0 },
|
|
{ "static", ARGP_static, NULL, OPTION_HIDDEN, NULL, 0 },
|
|
{ "-B static", ARGP_static, NULL, OPTION_DOC,
|
|
N_("Do not link against shared libraries."), 0 },
|
|
{ "dynamic", ARGP_dynamic, NULL, OPTION_HIDDEN, NULL, 0 },
|
|
{ "-B dynamic", ARGP_dynamic, NULL, OPTION_DOC,
|
|
N_("Prefer linking against shared libraries."), 0 },
|
|
{ "export-dynamic", 'E', NULL, 0, N_("Export all dynamic symbols."), 0 },
|
|
{ "strip-all", 's', NULL, 0, N_("Strip all symbols."), 0 },
|
|
{ "strip-debug", 'S', NULL, 0, N_("Strip debugging symbols."), 0 },
|
|
{ "pagesize", ARGP_pagesize, "SIZE", 0,
|
|
N_("Assume pagesize for the target system to be SIZE."), 0 },
|
|
{ "rpath", 'R', "PATH", OPTION_HIDDEN, NULL, 0 },
|
|
{ "runpath", ARGP_runpath, "PATH", 0, N_("Set runtime DSO search path."),
|
|
0 },
|
|
{ "runpath-link", ARGP_runpath_link, "PATH", 0,
|
|
N_("Set link time DSO search path."), 0 },
|
|
{ "shared", 'G', NULL, 0, N_("Generate dynamic shared object."), 0 },
|
|
{ NULL, 'r', NULL, 0L, N_("Generate relocatable object."), 0 },
|
|
{ NULL, 'B', "KEYWORD", OPTION_HIDDEN, "", 0 },
|
|
{ "-B local", 'B', NULL, OPTION_DOC,
|
|
N_("Causes symbol not assigned to a version be reduced to local."), 0 },
|
|
{ "gc-sections", ARGP_gc_sections, NULL, 0, N_("Remove unused sections."),
|
|
0 },
|
|
{ "no-gc-sections", ARGP_no_gc_sections, NULL, 0,
|
|
N_("Don't remove unused sections."), 0 },
|
|
{ "soname", 'h', "NAME", 0, N_("Set soname of shared object."), 0 },
|
|
{ "dynamic-linker", 'I', "NAME", 0, N_("Set the dynamic linker name."), 0 },
|
|
{ NULL, 'Q', "YN", OPTION_HIDDEN, NULL, 0 },
|
|
{ "-Q y | n", 'Q', NULL, OPTION_DOC,
|
|
N_("Add/suppress addition indentifying link-editor to .comment section"),
|
|
0 },
|
|
{ "eh-frame-hdr", ARGP_eh_frame_hdr, NULL, 0,
|
|
N_("Create .eh_frame_hdr section"), 0 },
|
|
|
|
{ NULL, 0, NULL, 0, N_("Linker Operation Control:"), 0 },
|
|
{ "verbose", 'v', NULL, 0, N_("Verbose messages."), 0 },
|
|
{ "trace", 't', NULL, 0, N_("Trace file opens."), 0 },
|
|
{ "conserve-memory", ARGP_conserve, NULL, 0,
|
|
N_("Trade speed for less memory usage"), 0 },
|
|
{ NULL, 'O', N_("LEVEL"), OPTION_ARG_OPTIONAL,
|
|
N_("Set optimization level to LEVEL."), 0 },
|
|
{ NULL, 'c', N_("FILE"), 0, N_("Use linker script in FILE."), 0 },
|
|
#if YYDEBUG
|
|
{ "yydebug", ARGP_yydebug, NULL, 0,
|
|
N_("Select to get parser debug information"), 0 },
|
|
#endif
|
|
{ "version-script", ARGP_version_script, "FILE", 0,
|
|
N_("Read version information from FILE."), 0 },
|
|
{ "emulation", 'm', "NAME", 0, N_("Set emulation to NAME."), 0 },
|
|
|
|
{ NULL, 0, NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
/* Short description of program. */
|
|
static const char doc[] = N_("Combine object and archive files.");
|
|
|
|
/* Strings for arguments in help texts. */
|
|
static const char args_doc[] = N_("[FILE]...");
|
|
|
|
/* Prototype for option handler. */
|
|
static error_t parse_opt_1st (int key, char *arg, struct argp_state *state);
|
|
static error_t parse_opt_2nd (int key, char *arg, struct argp_state *state);
|
|
|
|
/* Data structure to communicate with argp functions. */
|
|
static struct argp argp_1st =
|
|
{
|
|
options, parse_opt_1st, args_doc, doc, NULL, NULL, NULL
|
|
};
|
|
static struct argp argp_2nd =
|
|
{
|
|
options, parse_opt_2nd, args_doc, doc, NULL, NULL, NULL
|
|
};
|
|
|
|
|
|
/* Linker state. This contains all global information. */
|
|
struct ld_state ld_state;
|
|
|
|
/* List of the input files. */
|
|
static struct file_list
|
|
{
|
|
const char *name;
|
|
struct file_list *next;
|
|
} *input_file_list;
|
|
|
|
/* If nonzero be verbose. */
|
|
int verbose;
|
|
|
|
/* If nonzero, trade speed for less memory/address space usage. */
|
|
int conserve_memory;
|
|
|
|
/* The emulation name to use. */
|
|
static const char *emulation;
|
|
|
|
/* Keep track of the nesting level. Even though we don't handle nested
|
|
groups we still keep track to improve the error messages. */
|
|
static int group_level;
|
|
|
|
/* The last file we processed. */
|
|
static struct usedfiles *last_file;
|
|
|
|
/* The default linker script. */
|
|
/* XXX We'll do this a bit different in the real solution. */
|
|
static const char *linker_script = SRCDIR "/elf32-i386.script";
|
|
|
|
/* Nonzero if an error occurred while loading the input files. */
|
|
static int error_loading;
|
|
|
|
|
|
/* Intermediate storage for the LD_LIBRARY_PATH information from the
|
|
environment. */
|
|
static char *ld_library_path1;
|
|
|
|
/* Flag used to communicate with the scanner. */
|
|
int ld_scan_version_script;
|
|
|
|
/* Name of the input file. */
|
|
const char *ldin_fname;
|
|
|
|
/* Define by parser if required. */
|
|
extern int lddebug;
|
|
|
|
|
|
/* Prototypes for local functions. */
|
|
static void parse_z_option (const char *arg);
|
|
static void parse_z_option_2 (const char *arg);
|
|
static void parse_B_option (const char *arg);
|
|
static void parse_B_option_2 (const char *arg);
|
|
static void determine_output_format (void);
|
|
static void load_needed (void);
|
|
static void collect_sections (void);
|
|
static void add_rxxpath (struct pathelement **pathp, const char *str);
|
|
static void gen_rxxpath_data (void);
|
|
static void read_version_script (const char *fname);
|
|
static void create_lscript_symbols (void);
|
|
static void create_special_section_symbol (struct symbol **symp,
|
|
const char *name);
|
|
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int remaining;
|
|
int err;
|
|
|
|
#ifndef NDEBUG
|
|
/* Enable memory debugging. */
|
|
mtrace ();
|
|
#endif
|
|
|
|
/* Sanity check. We always want to use the LFS functionality. */
|
|
if (sizeof (off_t) != sizeof (off64_t))
|
|
abort ();
|
|
|
|
/* We use no threads here which can interfere with handling a stream. */
|
|
__fsetlocking (stdin, FSETLOCKING_BYCALLER);
|
|
__fsetlocking (stdout, FSETLOCKING_BYCALLER);
|
|
__fsetlocking (stderr, FSETLOCKING_BYCALLER);
|
|
|
|
/* Set locale. */
|
|
setlocale (LC_ALL, "");
|
|
|
|
/* Make sure the message catalog can be found. */
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
/* Initialize the message catalog. */
|
|
textdomain (PACKAGE);
|
|
|
|
/* Before we start tell the ELF library which version we are using. */
|
|
elf_version (EV_CURRENT);
|
|
|
|
/* The user can use the LD_LIBRARY_PATH environment variable to add
|
|
additional lookup directories. */
|
|
ld_library_path1 = getenv ("LD_LIBRARY_PATH");
|
|
|
|
/* Initialize the memory handling. */
|
|
#define obstack_chunk_alloc xmalloc
|
|
#define obstack_chunk_free free
|
|
obstack_init (&ld_state.smem);
|
|
|
|
/* One quick pass over the parameters which allows us to scan for options
|
|
with global effect which influence the rest of the processing. */
|
|
argp_parse (&argp_1st, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
|
|
|
|
/* We need at least one input file. */
|
|
if (input_file_list == NULL)
|
|
{
|
|
error (0, 0, gettext ("At least one input file needed"));
|
|
argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* Determine which ELF backend to use. */
|
|
determine_output_format ();
|
|
|
|
/* Prepare state. */
|
|
err = ld_prepare_state (emulation);
|
|
if (err != 0)
|
|
error (EXIT_FAILURE, 0, gettext ("error while preparing linking"));
|
|
|
|
/* XXX Read the linker script now. Since we later will have the linker
|
|
script built in we don't go into trouble to make sure we handle GROUP
|
|
statements in the script. This simply must not happen. */
|
|
ldin = fopen (linker_script, "r");
|
|
if (ldin == NULL)
|
|
error (EXIT_FAILURE, errno, gettext ("cannot open linker script '%s'"),
|
|
linker_script);
|
|
/* No need for locking. */
|
|
__fsetlocking (ldin, FSETLOCKING_BYCALLER);
|
|
|
|
ld_state.srcfiles = NULL;
|
|
ldlineno = 1;
|
|
ld_scan_version_script = 0;
|
|
ldin_fname = linker_script;
|
|
if (ldparse () != 0)
|
|
/* Something went wrong during parsing. */
|
|
exit (EXIT_FAILURE);
|
|
fclose (ldin);
|
|
|
|
/* We now might have a list of directories to look for libraries in
|
|
named by the linker script. Put them in a different list so that
|
|
they are searched after all paths given by the user on the
|
|
command line. */
|
|
ld_state.default_paths = ld_state.paths;
|
|
ld_state.paths = ld_state.tailpaths = NULL;
|
|
|
|
/* Get runpath/rpath information in usable form. */
|
|
gen_rxxpath_data ();
|
|
|
|
/* Parse and process arguments for real. */
|
|
argp_parse (&argp_2nd, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
|
|
/* All options should have been processed by the argp parser. */
|
|
assert (remaining == argc);
|
|
|
|
/* Process the last file. */
|
|
while (last_file != NULL)
|
|
/* Try to open the file. */
|
|
error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file);
|
|
|
|
/* Stop if there has been a problem while reading the input files. */
|
|
if (error_loading)
|
|
exit (error_loading);
|
|
|
|
/* See whether all opened -( were closed. */
|
|
if (group_level > 0)
|
|
{
|
|
error (0, 0, gettext ("-( without matching -)"));
|
|
argp_help (&argp_1st, stderr, ARGP_HELP_SEE, "ld");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* When we create a relocatable file we don't have to look for the
|
|
DT_NEEDED DSOs and we also don't test for undefined symbols. */
|
|
if (ld_state.file_type != relocatable_file_type)
|
|
{
|
|
/* At this point we have loaded all the direct dependencies. What
|
|
remains to be done is find the indirect dependencies. These are
|
|
DSOs which are referenced by the DT_NEEDED entries in the DSOs
|
|
which are direct dependencies. We have to transitively find and
|
|
load all these dependencies. */
|
|
load_needed ();
|
|
|
|
/* At this point all object files and DSOs are read. If there
|
|
are still undefined symbols left they might have to be
|
|
synthesized from the linker script. */
|
|
create_lscript_symbols ();
|
|
|
|
/* Now that we have loaded all the object files we can determine
|
|
whether we have any non-weak unresolved references left. If
|
|
there are any we stop. If the user used the '-z nodefs' option
|
|
and we are creating a DSO don't perform the tests. */
|
|
if (FLAG_UNRESOLVED (&ld_state) != 0)
|
|
exit (1);
|
|
}
|
|
|
|
/* Collect information about the relocations which will be carried
|
|
forward into the output. We have to do this here and now since
|
|
we need to know which sections have to be created. */
|
|
if (ld_state.file_type != relocatable_file_type)
|
|
{
|
|
void *p ;
|
|
struct scnhead *h;
|
|
|
|
p = NULL;
|
|
while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL)
|
|
if (h->type == SHT_REL || h->type == SHT_RELA)
|
|
{
|
|
struct scninfo *runp = h->last;
|
|
do
|
|
{
|
|
/* If we are processing the relocations determine how
|
|
many will be in the output file. Also determine
|
|
how many GOT entries are needed. */
|
|
COUNT_RELOCATIONS (&ld_state, runp);
|
|
|
|
ld_state.relsize_total += runp->relsize;
|
|
}
|
|
while ((runp = runp->next) != h->last);
|
|
}
|
|
}
|
|
|
|
/* Not part of the gABI, but part of every psABI: the symbols for the
|
|
GOT section. Add the symbol if necessary. */
|
|
if (ld_state.need_got)
|
|
create_special_section_symbol (&ld_state.got_symbol,
|
|
"_GLOBAL_OFFSET_TABLE_");
|
|
/* Similarly for the _DYNAMIC symbol which points to the dynamic
|
|
section. */
|
|
if (dynamically_linked_p ())
|
|
create_special_section_symbol (&ld_state.dyn_symbol, "_DYNAMIC");
|
|
|
|
/* We are ready to start working on the output file. Not all
|
|
information has been gather or created yet. This will be done as
|
|
we go. Open the file now. */
|
|
if (OPEN_OUTFILE (&ld_state, EM_NONE, ELFCLASSNONE, ELFDATANONE) != 0)
|
|
exit (1);
|
|
|
|
/* Create the sections which are generated by the linker and are not
|
|
present in the input file. The output file must already have
|
|
been opened since we need the ELF descriptor to deduce type
|
|
sizes. */
|
|
GENERATE_SECTIONS (&ld_state);
|
|
|
|
/* At this point we have read all the files and know all the
|
|
sections which have to be linked into the application. We do now
|
|
create an array listing all the sections. We will than pass this
|
|
array to a system specific function which can reorder it at will.
|
|
The functions can also merge sections if this is what is
|
|
wanted. */
|
|
collect_sections ();
|
|
|
|
/* Create the output sections now. This may requires sorting them
|
|
first. */
|
|
CREATE_SECTIONS (&ld_state);
|
|
|
|
/* Create the output file data. Appropriate code for the selected
|
|
output file type is called. */
|
|
if (CREATE_OUTFILE (&ld_state) != 0)
|
|
exit (1);
|
|
|
|
/* Finalize the output file, write the data out. */
|
|
err |= FINALIZE (&ld_state);
|
|
|
|
/* Return with an non-zero exit status also if any error message has
|
|
been printed. */
|
|
return err | (error_message_count != 0);
|
|
}
|
|
|
|
|
|
/* Quick scan of the parameter list for options with global effect. */
|
|
static error_t
|
|
parse_opt_1st (int key, char *arg,
|
|
struct argp_state *state __attribute__ ((unused)))
|
|
{
|
|
switch (key)
|
|
{
|
|
case 'B':
|
|
parse_B_option (arg);
|
|
break;
|
|
|
|
case 'c':
|
|
linker_script = arg;
|
|
break;
|
|
|
|
case 'E':
|
|
ld_state.export_all_dynamic = true;
|
|
break;
|
|
|
|
case 'G':
|
|
if (ld_state.file_type != no_file_type)
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("only one option of -G and -r is allowed"));
|
|
ld_state.file_type = dso_file_type;
|
|
|
|
/* If we generate a DSO we have to export all symbols. */
|
|
ld_state.export_all_dynamic = true;
|
|
break;
|
|
|
|
case 'h':
|
|
ld_state.soname = arg;
|
|
break;
|
|
|
|
case 'i':
|
|
/* Discard the LD_LIBRARY_PATH value we found. */
|
|
ld_library_path1 = NULL;
|
|
break;
|
|
|
|
case 'I':
|
|
ld_state.interp = arg;
|
|
break;
|
|
|
|
case 'm':
|
|
if (emulation != NULL)
|
|
error (EXIT_FAILURE, 0, gettext ("more than one '-m' parameter"));
|
|
emulation = arg;
|
|
break;
|
|
|
|
case 'Q':
|
|
if (arg[1] == '\0' && (arg[0] == 'y' || arg[0] == 'Y'))
|
|
ld_state.add_ld_comment = true;
|
|
else if (arg[1] == '\0' && (arg[0] == 'n' || arg[0] == 'N'))
|
|
ld_state.add_ld_comment = true;
|
|
else
|
|
error (EXIT_FAILURE, 0, gettext ("unknown option `-%c %s'"), 'Q', arg);
|
|
break;
|
|
|
|
case 'r':
|
|
if (ld_state.file_type != no_file_type)
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("only one option of -G and -r is allowed"));
|
|
ld_state.file_type = relocatable_file_type;
|
|
break;
|
|
|
|
case 'S':
|
|
ld_state.strip = strip_debug;
|
|
break;
|
|
|
|
case 't':
|
|
ld_state.trace_files = true;
|
|
break;
|
|
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
|
|
case 'z':
|
|
/* The SysV linker used 'z' to pass various flags to the linker.
|
|
We follow this. See 'parse_z_option' for the options we
|
|
recognize. */
|
|
parse_z_option (arg);
|
|
break;
|
|
|
|
case ARGP_pagesize:
|
|
{
|
|
char *endp;
|
|
ld_state.pagesize = strtoul (arg, &endp, 0);
|
|
if (*endp != '\0')
|
|
{
|
|
if (endp[1] == '\0' && tolower (*endp) == 'k')
|
|
ld_state.pagesize *= 1024;
|
|
else if (endp[1] == '\0' && tolower (*endp) == 'm')
|
|
ld_state.pagesize *= 1024 * 1024;
|
|
else
|
|
{
|
|
error (0, 0,
|
|
gettext ("invalid page size value '%s': ignored"),
|
|
arg);
|
|
ld_state.pagesize = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'R':
|
|
add_rxxpath (&ld_state.rpath, arg);
|
|
break;
|
|
|
|
case ARGP_rpath_link:
|
|
add_rxxpath (&ld_state.rpath_link, arg);
|
|
break;
|
|
|
|
case ARGP_runpath:
|
|
add_rxxpath (&ld_state.runpath, arg);
|
|
break;
|
|
|
|
case ARGP_runpath_link:
|
|
add_rxxpath (&ld_state.runpath_link, arg);
|
|
break;
|
|
|
|
case ARGP_gc_sections:
|
|
case ARGP_no_gc_sections:
|
|
ld_state.gc_sections = key == ARGP_gc_sections;
|
|
break;
|
|
|
|
case ARGP_eh_frame_hdr:
|
|
ld_state.eh_frame_hdr = true;
|
|
break;
|
|
|
|
case 's':
|
|
if (arg == NULL)
|
|
{
|
|
if (ld_state.strip == strip_all)
|
|
ld_state.strip = strip_everything;
|
|
else
|
|
ld_state.strip = strip_all;
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
|
|
case 'e':
|
|
case 'o':
|
|
case 'O':
|
|
case ARGP_whole_archive:
|
|
case ARGP_no_whole_archive:
|
|
case ARGP_as_needed:
|
|
case ARGP_no_as_needed:
|
|
case 'L':
|
|
case '(':
|
|
case ')':
|
|
case 'l':
|
|
case ARGP_static:
|
|
case ARGP_dynamic:
|
|
case ARGP_version_script:
|
|
/* We'll handle these in the second pass. */
|
|
break;
|
|
|
|
case ARGP_KEY_ARG:
|
|
{
|
|
struct file_list *newp;
|
|
|
|
newp = (struct file_list *) xmalloc (sizeof (struct file_list));
|
|
newp->name = arg;
|
|
#ifndef NDEBUG
|
|
newp->next = NULL;
|
|
#endif
|
|
CSNGL_LIST_ADD_REAR (input_file_list, newp);
|
|
}
|
|
break;
|
|
|
|
#if YYDEBUG
|
|
case ARGP_yydebug:
|
|
lddebug = 1;
|
|
break;
|
|
#endif
|
|
|
|
case ARGP_no_undefined:
|
|
ld_state.nodefs = false;
|
|
break;
|
|
|
|
case ARGP_conserve:
|
|
conserve_memory = 1;
|
|
break;
|
|
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Handle program arguments for real. */
|
|
static error_t
|
|
parse_opt_2nd (int key, char *arg,
|
|
struct argp_state *state __attribute__ ((unused)))
|
|
{
|
|
static bool group_start_requested;
|
|
static bool group_end_requested;
|
|
|
|
switch (key)
|
|
{
|
|
case 'B':
|
|
parse_B_option_2 (arg);
|
|
break;
|
|
|
|
case 'e':
|
|
ld_state.entry = arg;
|
|
break;
|
|
|
|
case 'o':
|
|
if (ld_state.outfname != NULL)
|
|
{
|
|
error (0, 0, gettext ("More than one output file name given."));
|
|
see_help:
|
|
argp_help (&argp_2nd, stderr, ARGP_HELP_SEE, "ld");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
ld_state.outfname = arg;
|
|
break;
|
|
|
|
case 'O':
|
|
if (arg == NULL)
|
|
ld_state.optlevel = 1;
|
|
else
|
|
{
|
|
char *endp;
|
|
unsigned long int level = strtoul (arg, &endp, 10);
|
|
if (*endp != '\0')
|
|
{
|
|
error (0, 0, gettext ("Invalid optimization level `%s'"), arg);
|
|
goto see_help;
|
|
}
|
|
ld_state.optlevel = level;
|
|
}
|
|
break;
|
|
|
|
case ARGP_whole_archive:
|
|
ld_state.extract_rule = allextract;
|
|
break;
|
|
case ARGP_no_whole_archive:
|
|
ld_state.extract_rule = defaultextract;
|
|
break;
|
|
|
|
case ARGP_as_needed:
|
|
ld_state.as_needed = true;
|
|
break;
|
|
case ARGP_no_as_needed:
|
|
ld_state.as_needed = false;
|
|
break;
|
|
|
|
case ARGP_static:
|
|
case ARGP_dynamic:
|
|
/* Enable/disable use for DSOs. */
|
|
ld_state.statically = key == ARGP_static;
|
|
break;
|
|
|
|
case 'z':
|
|
/* The SysV linker used 'z' to pass various flags to the linker.
|
|
We follow this. See 'parse_z_option' for the options we
|
|
recognize. */
|
|
parse_z_option_2 (arg);
|
|
break;
|
|
|
|
case ARGP_version_script:
|
|
read_version_script (arg);
|
|
break;
|
|
|
|
case 'L':
|
|
/* Add a new search directory. */
|
|
ld_new_searchdir (arg);
|
|
break;
|
|
|
|
case '(':
|
|
/* Start a link group. We have to be able to determine the object
|
|
file which is named next. Do this by remembering a pointer to
|
|
the pointer which will point to the next object. */
|
|
if (verbose && (group_start_requested || !group_end_requested))
|
|
error (0, 0, gettext ("nested -( -) groups are not allowed"));
|
|
|
|
/* Increment the nesting level. */
|
|
++group_level;
|
|
|
|
/* Record group start. */
|
|
group_start_requested = true;
|
|
group_end_requested = false;
|
|
break;
|
|
|
|
case ')':
|
|
/* End a link group. If there is no group open this is clearly
|
|
a bug. If there is a group open insert a back reference
|
|
pointer in the record for the last object of the group. If
|
|
there is no new object or just one don't do anything. */
|
|
if (!group_end_requested)
|
|
{
|
|
if (group_level == 0)
|
|
{
|
|
error (0, 0, gettext ("-) without matching -("));
|
|
goto see_help;
|
|
}
|
|
}
|
|
else
|
|
last_file->group_end = true;
|
|
|
|
if (group_level > 0)
|
|
--group_level;
|
|
break;
|
|
|
|
case 'l':
|
|
case ARGP_KEY_ARG:
|
|
{
|
|
while (last_file != NULL)
|
|
/* Try to open the file. */
|
|
error_loading |= FILE_PROCESS (-1, last_file, &ld_state, &last_file);
|
|
|
|
last_file = ld_new_inputfile (arg,
|
|
key == 'l'
|
|
? archive_file_type
|
|
: relocatable_file_type);
|
|
if (group_start_requested)
|
|
{
|
|
last_file->group_start = true;
|
|
|
|
group_start_requested = false;
|
|
group_end_requested = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* We can catch all other options here. They either have
|
|
already been handled or, if the parameter was not correct,
|
|
the error has been reported. */
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Load all the DSOs named as dependencies in other DSOs we already
|
|
loaded. */
|
|
static void
|
|
load_needed (void)
|
|
{
|
|
struct usedfiles *first;
|
|
struct usedfiles *runp;
|
|
|
|
/* XXX There is one problem here: do we allow references from
|
|
regular object files to be satisfied by these implicit
|
|
dependencies? The old linker allows this and several libraries
|
|
depend on this. Solaris' linker does not allow this; it provides
|
|
the user with a comprehensive error message explaining the
|
|
situation.
|
|
|
|
XXX IMO the old ld behavior is correct since this is also how the
|
|
dynamic linker will work. It will look for unresolved references
|
|
in all loaded DSOs.
|
|
|
|
XXX Should we add an option to get Solaris compatibility? */
|
|
if (ld_state.needed == NULL)
|
|
return;
|
|
|
|
runp = first = ld_state.needed->next;
|
|
do
|
|
{
|
|
struct usedfiles *ignore;
|
|
struct usedfiles *next = runp->next;
|
|
int err;
|
|
|
|
err = FILE_PROCESS (-1, runp, &ld_state, &ignore);
|
|
if (err != 0)
|
|
/* Something went wrong. */
|
|
exit (err);
|
|
|
|
runp = next;
|
|
}
|
|
while (runp != first);
|
|
}
|
|
|
|
|
|
/* Print the version information. */
|
|
static void
|
|
print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
|
|
{
|
|
fprintf (stream, "ld (%s) %s\n", PACKAGE_NAME, VERSION);
|
|
fprintf (stream, gettext ("\
|
|
Copyright (C) %s Red Hat, Inc.\n\
|
|
This is free software; see the source for copying conditions. There is NO\n\
|
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
|
|
"), "2006");
|
|
fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
|
|
}
|
|
|
|
|
|
/* There are a lot of -z options, parse them here. Some of them have
|
|
to be parsed in the first pass, others must be handled in the
|
|
second pass. */
|
|
static void
|
|
parse_z_option (const char *arg)
|
|
{
|
|
if (strcmp (arg, "nodefaultlib") == 0
|
|
/* This is only meaningful if we create a DSO. */
|
|
&& ld_state.file_type == dso_file_type)
|
|
ld_state.dt_flags_1 |= DF_1_NODEFLIB;
|
|
else if (strcmp (arg, "muldefs") == 0)
|
|
ld_state.muldefs = true;
|
|
else if (strcmp (arg, "nodefs") == 0)
|
|
ld_state.nodefs = true;
|
|
else if (strcmp (arg, "defs") == 0)
|
|
ld_state.nodefs = false;
|
|
else if (strcmp (arg, "now") == 0)
|
|
/* We could also set the DF_1_NOW flag in DT_FLAGS_1 but this isn't
|
|
necessary. */
|
|
ld_state.dt_flags |= DF_BIND_NOW;
|
|
else if (strcmp (arg, "origin") == 0)
|
|
/* We could also set the DF_1_ORIGIN flag in DT_FLAGS_1 but this isn't
|
|
necessary. */
|
|
ld_state.dt_flags |= DF_ORIGIN;
|
|
else if (strcmp (arg, "nodelete") == 0
|
|
/* This is only meaningful if we create a DSO. */
|
|
&& ld_state.file_type == dso_file_type)
|
|
ld_state.dt_flags_1 |= DF_1_NODELETE;
|
|
else if (strcmp (arg, "initfirst") == 0)
|
|
ld_state.dt_flags_1 |= DF_1_INITFIRST;
|
|
else if (strcmp (arg, "nodlopen") == 0
|
|
/* This is only meaningful if we create a DSO. */
|
|
&& ld_state.file_type == dso_file_type)
|
|
ld_state.dt_flags_1 |= DF_1_NOOPEN;
|
|
else if (strcmp (arg, "systemlibrary") == 0)
|
|
ld_state.is_system_library = true;
|
|
else if (strcmp (arg, "execstack") == 0)
|
|
ld_state.execstack = execstack_true;
|
|
else if (strcmp (arg, "noexecstack") == 0)
|
|
ld_state.execstack = execstack_false_force;
|
|
else if (strcmp (arg, "allextract") != 0
|
|
&& strcmp (arg, "defaultextract") != 0
|
|
&& strcmp (arg, "weakextract") != 0
|
|
&& strcmp (arg, "lazyload") != 0
|
|
&& strcmp (arg, "nolazyload") != 0
|
|
&& strcmp (arg, "ignore") != 0
|
|
&& strcmp (arg, "record") != 0)
|
|
error (0, 0, gettext ("unknown option `-%c %s'"), 'z', arg);
|
|
}
|
|
|
|
|
|
static void
|
|
parse_z_option_2 (const char *arg)
|
|
{
|
|
if (strcmp (arg, "allextract") == 0)
|
|
ld_state.extract_rule = allextract;
|
|
else if (strcmp (arg, "defaultextract") == 0)
|
|
ld_state.extract_rule = defaultextract;
|
|
else if (strcmp (arg, "weakextract") == 0)
|
|
ld_state.extract_rule = weakextract;
|
|
else if (strcmp (arg, "lazyload") == 0)
|
|
ld_state.lazyload = true;
|
|
else if (strcmp (arg, "nolazyload") == 0)
|
|
ld_state.lazyload = false;
|
|
else if (strcmp (arg, "ignore") == 0)
|
|
ld_state.as_needed = true;
|
|
else if (strcmp (arg, "record") == 0)
|
|
ld_state.as_needed = false;
|
|
}
|
|
|
|
|
|
/* There are a lot of -B options, parse them here. */
|
|
static void
|
|
parse_B_option (const char *arg)
|
|
{
|
|
if (strcmp (arg, "local") == 0)
|
|
ld_state.default_bind_local = true;
|
|
else if (strcmp (arg, "symbolic") != 0
|
|
&& strcmp (arg, "static") != 0
|
|
&& strcmp (arg, "dynamic") != 0)
|
|
error (0, 0, gettext ("unknown option '-%c %s'"), 'B', arg);
|
|
}
|
|
|
|
|
|
/* The same functionality, but called in the second pass over the
|
|
parameters. */
|
|
static void
|
|
parse_B_option_2 (const char *arg)
|
|
{
|
|
if (strcmp (arg, "static") == 0)
|
|
ld_state.statically = true;
|
|
else if (strcmp (arg, "dynamic") == 0)
|
|
ld_state.statically = false;
|
|
else if (strcmp (arg, "symbolic") == 0
|
|
/* This is only meaningful if we create a DSO. */
|
|
&& ld_state.file_type == dso_file_type)
|
|
ld_state.dt_flags |= DF_SYMBOLIC;
|
|
}
|
|
|
|
|
|
static void
|
|
determine_output_format (void)
|
|
{
|
|
/* First change the 'input_file_list' variable in a simple
|
|
single-linked list. */
|
|
struct file_list *last = input_file_list;
|
|
input_file_list = input_file_list->next;
|
|
last->next = NULL;
|
|
|
|
/* Determine the target configuration which we are supposed to use.
|
|
The user can use the '-m' option to select one. If this is
|
|
missing we are trying to load one file and determine the
|
|
architecture from that. */
|
|
if (emulation != NULL)
|
|
{
|
|
ld_state.ebl = ebl_openbackend_emulation (emulation);
|
|
|
|
assert (ld_state.ebl != NULL);
|
|
}
|
|
else
|
|
{
|
|
/* Find an ELF input file and let it determine the ELf backend. */
|
|
struct file_list *runp = input_file_list;
|
|
|
|
while (runp != NULL)
|
|
{
|
|
int fd = open (runp->name, O_RDONLY);
|
|
if (fd != -1)
|
|
{
|
|
int try (Elf *elf)
|
|
{
|
|
int result = 0;
|
|
|
|
if (elf == NULL)
|
|
return 0;
|
|
|
|
if (elf_kind (elf) == ELF_K_ELF)
|
|
{
|
|
/* We have an ELF file. We now can find out
|
|
what the output format should be. */
|
|
XElf_Ehdr_vardef(ehdr);
|
|
|
|
/* Get the ELF header of the object. */
|
|
xelf_getehdr (elf, ehdr);
|
|
if (ehdr != NULL)
|
|
ld_state.ebl =
|
|
ebl_openbackend_machine (ehdr->e_machine);
|
|
|
|
result = 1;
|
|
}
|
|
else if (elf_kind (elf) == ELF_K_AR)
|
|
{
|
|
/* Try the archive members. This could
|
|
potentially lead to wrong results if the
|
|
archive contains files for more than one
|
|
architecture. But this is the user's
|
|
problem. */
|
|
Elf *subelf;
|
|
Elf_Cmd cmd = ELF_C_READ_MMAP;
|
|
|
|
while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
|
|
{
|
|
cmd = elf_next (subelf);
|
|
|
|
if (try (subelf) != 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
elf_end (elf);
|
|
|
|
return result;
|
|
}
|
|
|
|
if (try (elf_begin (fd, ELF_C_READ_MMAP, NULL)) != 0)
|
|
/* Found a file. */
|
|
break;
|
|
}
|
|
|
|
runp = runp->next;
|
|
}
|
|
|
|
if (ld_state.ebl == NULL)
|
|
{
|
|
error (0, 0, gettext ("\
|
|
could not find input file to determine output file format"));
|
|
error (EXIT_FAILURE, 0, gettext ("\
|
|
try again with an appropriate '-m' parameter"));
|
|
}
|
|
}
|
|
|
|
/* We don't need the list of input files anymore. The second run over
|
|
the parameters will handle them. */
|
|
while (input_file_list != NULL)
|
|
{
|
|
struct file_list *oldp = input_file_list;
|
|
input_file_list = input_file_list->next;
|
|
free (oldp);
|
|
}
|
|
|
|
/* We also know now what kind of file we are supposed to create. If
|
|
the user hasn't selected anythign we create and executable. */
|
|
if (ld_state.file_type == no_file_type)
|
|
ld_state.file_type = executable_file_type;
|
|
}
|
|
|
|
/* Add DIR to the list of directories searched for object files and
|
|
libraries. */
|
|
void
|
|
ld_new_searchdir (const char *dir)
|
|
{
|
|
struct pathelement *newpath;
|
|
|
|
newpath = (struct pathelement *)
|
|
obstack_calloc (&ld_state.smem, sizeof (struct pathelement));
|
|
|
|
newpath->pname = dir;
|
|
|
|
/* Enqueue the file. */
|
|
if (ld_state.tailpaths == NULL)
|
|
ld_state.paths = ld_state.tailpaths = newpath;
|
|
else
|
|
{
|
|
ld_state.tailpaths->next = newpath;
|
|
ld_state.tailpaths = newpath;
|
|
}
|
|
}
|
|
|
|
|
|
struct usedfiles *
|
|
ld_new_inputfile (const char *fname, enum file_type type)
|
|
{
|
|
struct usedfiles *newfile = (struct usedfiles *)
|
|
obstack_calloc (&ld_state.smem, sizeof (struct usedfiles));
|
|
|
|
newfile->soname = newfile->fname = newfile->rfname = fname;
|
|
newfile->file_type = type;
|
|
newfile->extract_rule = ld_state.extract_rule;
|
|
newfile->as_needed = ld_state.as_needed;
|
|
newfile->lazyload = ld_state.lazyload;
|
|
newfile->status = not_opened;
|
|
|
|
return newfile;
|
|
}
|
|
|
|
|
|
/* Create an array listing all the sections. We will than pass this
|
|
array to a system specific function which can reorder it at will.
|
|
The functions can also merge sections if this is what is
|
|
wanted. */
|
|
static void
|
|
collect_sections (void)
|
|
{
|
|
void *p ;
|
|
struct scnhead *h;
|
|
size_t cnt;
|
|
|
|
/* We have that many sections. At least for now. */
|
|
ld_state.nallsections = ld_state.section_tab.filled;
|
|
|
|
/* Allocate the array. We allocate one more entry than computed so
|
|
far since we might need a new section for the copy relocations. */
|
|
ld_state.allsections =
|
|
(struct scnhead **) obstack_alloc (&ld_state.smem,
|
|
(ld_state.nallsections + 1)
|
|
* sizeof (struct scnhead *));
|
|
|
|
/* Fill the array. We rely here on the hash table iterator to
|
|
return the entries in the order they were added. */
|
|
cnt = 0;
|
|
p = NULL;
|
|
while ((h = ld_section_tab_iterate (&ld_state.section_tab, &p)) != NULL)
|
|
{
|
|
struct scninfo *runp;
|
|
bool used = false;
|
|
|
|
if (h->kind == scn_normal)
|
|
{
|
|
runp = h->last;
|
|
do
|
|
{
|
|
if (h->type == SHT_REL || h->type == SHT_RELA)
|
|
{
|
|
if (runp->used)
|
|
/* This is a relocation section. If the section
|
|
it is relocating is used in the result so must
|
|
the relocation section. */
|
|
runp->used
|
|
= runp->fileinfo->scninfo[SCNINFO_SHDR (runp->shdr).sh_info].used;
|
|
}
|
|
|
|
/* Accumulate the result. */
|
|
used |= runp->used;
|
|
|
|
/* Next input section. */
|
|
runp = runp->next;
|
|
}
|
|
while (runp != h->last);
|
|
|
|
h->used = used;
|
|
}
|
|
|
|
ld_state.allsections[cnt++] = h;
|
|
}
|
|
ld_state.nusedsections = cnt;
|
|
|
|
assert (cnt == ld_state.nallsections);
|
|
}
|
|
|
|
|
|
/* Add given path to the end of list. */
|
|
static void
|
|
add_rxxpath (struct pathelement **pathp, const char *str)
|
|
{
|
|
struct pathelement *newp;
|
|
|
|
/* The path elements can in theory be freed after we read all the
|
|
files. But the amount of memory we are talking about is small
|
|
and the cost of free() calls is not neglectable. */
|
|
newp = (struct pathelement *) obstack_alloc (&ld_state.smem, sizeof (*newp));
|
|
newp->pname = str;
|
|
newp->exist = 0;
|
|
#ifndef NDEBUG
|
|
newp->next = NULL;
|
|
#endif
|
|
|
|
CSNGL_LIST_ADD_REAR (*pathp, newp);
|
|
}
|
|
|
|
|
|
/* Convert lists of possibly colon-separated directory lists into lists
|
|
where each entry is for a single directory. */
|
|
static void
|
|
normalize_dirlist (struct pathelement **pathp)
|
|
{
|
|
struct pathelement *firstp = *pathp;
|
|
|
|
do
|
|
{
|
|
const char *pname = (*pathp)->pname;
|
|
const char *colonp = strchrnul (pname, ':');
|
|
|
|
if (colonp != NULL)
|
|
{
|
|
struct pathelement *lastp = *pathp;
|
|
struct pathelement *newp;
|
|
|
|
while (1)
|
|
{
|
|
if (colonp == pname)
|
|
lastp->pname = ".";
|
|
else
|
|
lastp->pname = obstack_strndup (&ld_state.smem, pname,
|
|
colonp - pname);
|
|
|
|
if (*colonp == '\0')
|
|
break;
|
|
pname = colonp + 1;
|
|
|
|
newp = (struct pathelement *) obstack_alloc (&ld_state.smem,
|
|
sizeof (*newp));
|
|
newp->next = lastp->next;
|
|
newp->exist = 0;
|
|
lastp = lastp->next = newp;
|
|
|
|
colonp = strchrnul (pname, ':');
|
|
}
|
|
|
|
pathp = &lastp->next;
|
|
}
|
|
else
|
|
pathp = &(*pathp)->next;
|
|
}
|
|
while (*pathp != firstp);
|
|
}
|
|
|
|
|
|
/* Called after all parameters are parsed to bring the runpath/rpath
|
|
information into a usable form. */
|
|
static void
|
|
gen_rxxpath_data (void)
|
|
{
|
|
char *ld_library_path2;
|
|
|
|
/* Convert the information in true single-linked lists for easy use.
|
|
At this point we also discard the rpath information if runpath
|
|
information is provided. rpath is deprecated and should not be
|
|
used (or ever be invented for that matter). */
|
|
if (ld_state.rpath != NULL)
|
|
{
|
|
struct pathelement *endp = ld_state.rpath;
|
|
ld_state.rpath = ld_state.rpath->next;
|
|
endp->next = NULL;
|
|
}
|
|
if (ld_state.rpath_link != NULL)
|
|
{
|
|
struct pathelement *endp = ld_state.rpath_link;
|
|
ld_state.rpath_link = ld_state.rpath_link->next;
|
|
endp->next = NULL;
|
|
}
|
|
|
|
if (ld_state.runpath != NULL)
|
|
{
|
|
struct pathelement *endp = ld_state.runpath;
|
|
ld_state.runpath = ld_state.runpath->next;
|
|
endp->next = NULL;
|
|
|
|
/* If rpath information is also available discard it.
|
|
XXX Should there be a possibility to avoid this? */
|
|
while (ld_state.rpath != NULL)
|
|
{
|
|
struct pathelement *old = ld_state.rpath;
|
|
ld_state.rpath = ld_state.rpath->next;
|
|
free (old);
|
|
}
|
|
}
|
|
if (ld_state.runpath_link != NULL)
|
|
{
|
|
struct pathelement *endp = ld_state.runpath_link;
|
|
ld_state.runpath_link = ld_state.runpath_link->next;
|
|
endp->next = NULL;
|
|
|
|
/* If rpath information is also available discard it.
|
|
XXX Should there be a possibility to avoid this? */
|
|
while (ld_state.rpath_link != NULL)
|
|
{
|
|
struct pathelement *old = ld_state.rpath_link;
|
|
ld_state.rpath_link = ld_state.rpath_link->next;
|
|
free (old);
|
|
}
|
|
|
|
/* The information in the strings in the list can actually be
|
|
directory lists themselves, with entries separated by colons.
|
|
Convert the list now to a list with one list entry for each
|
|
directory. */
|
|
normalize_dirlist (&ld_state.runpath_link);
|
|
}
|
|
else if (ld_state.rpath_link != NULL)
|
|
/* Same as for the runpath_link above. */
|
|
normalize_dirlist (&ld_state.rpath_link);
|
|
|
|
|
|
/* As a related task, handle the LD_LIBRARY_PATH value here. First
|
|
we have to possibly split the value found (if it contains a
|
|
semicolon). Then we have to split the value in list of
|
|
directories, i.e., split at the colons. */
|
|
if (ld_library_path1 != NULL)
|
|
{
|
|
ld_library_path2 = strchr (ld_library_path1, ';');
|
|
if (ld_library_path2 == NULL)
|
|
{
|
|
/* If no semicolon is present the directories are looked at
|
|
after the -L parameters (-> ld_library_path2). */
|
|
ld_library_path2 = ld_library_path1;
|
|
ld_library_path1 = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* NUL terminate the first part. */
|
|
*ld_library_path2++ = '\0';
|
|
|
|
/* Convert the string value in a list. */
|
|
add_rxxpath (&ld_state.ld_library_path1, ld_library_path1);
|
|
normalize_dirlist (&ld_state.ld_library_path1);
|
|
}
|
|
|
|
add_rxxpath (&ld_state.ld_library_path2, ld_library_path2);
|
|
normalize_dirlist (&ld_state.ld_library_path2);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
read_version_script (const char *fname)
|
|
{
|
|
/* Open the file. The name is supposed to be the complete (relative
|
|
or absolute) path. No search along a path will be performed. */
|
|
ldin = fopen (fname, "r");
|
|
if (ldin == NULL)
|
|
error (EXIT_FAILURE, errno, gettext ("cannot read version script '%s'"),
|
|
fname);
|
|
/* No need for locking. */
|
|
__fsetlocking (ldin, FSETLOCKING_BYCALLER);
|
|
|
|
/* Tell the parser that this is a version script. */
|
|
ld_scan_version_script = 1;
|
|
|
|
ldlineno = 1;
|
|
ldin_fname = fname;
|
|
if (ldparse () != 0)
|
|
/* Something went wrong during parsing. */
|
|
exit (EXIT_FAILURE);
|
|
|
|
fclose (ldin);
|
|
}
|
|
|
|
|
|
static void
|
|
create_lscript_symbols (void)
|
|
{
|
|
/* Walk through the data from the linker script and generate all the
|
|
symbols which are required to be present and and those marked
|
|
with PROVIDE if there is a undefined reference. */
|
|
if (ld_state.output_segments == NULL)
|
|
return;
|
|
|
|
struct output_segment *segment = ld_state.output_segments->next;
|
|
do
|
|
{
|
|
struct output_rule *orule;
|
|
|
|
for (orule = segment->output_rules; orule != NULL; orule = orule->next)
|
|
if (orule->tag == output_assignment
|
|
/* The assignments to "." (i.e., the PC) have to be
|
|
ignored here. */
|
|
&& strcmp (orule->val.assignment->variable, ".") != 0)
|
|
{
|
|
struct symbol *s = ld_state.unresolved;
|
|
|
|
/* Check whether the symbol is needed. */
|
|
if (likely (s != NULL))
|
|
{
|
|
struct symbol *first = s;
|
|
const char *providename = orule->val.assignment->variable;
|
|
|
|
/* Determine whether the provided symbol is still
|
|
undefined. */
|
|
// XXX TODO Loop inside a loop. Gag! Must rewrite. */
|
|
do
|
|
if (strcmp (s->name, providename) == 0)
|
|
{
|
|
/* Not defined but referenced. */
|
|
if (unlikely (!s->defined))
|
|
{
|
|
/* Put on the list of symbols. First remove it from
|
|
whatever list it currently is on. */
|
|
CDBL_LIST_DEL (ld_state.unresolved, s);
|
|
--ld_state.nunresolved;
|
|
goto use_it;
|
|
}
|
|
|
|
if (unlikely (!orule->val.assignment->provide_flag))
|
|
{
|
|
/* The symbol is already defined and now again
|
|
in the linker script. This is an error. */
|
|
error (0, 0, gettext ("\
|
|
duplicate definition of '%s' in linker script"),
|
|
providename);
|
|
goto next_rule;
|
|
}
|
|
}
|
|
while ((s = s->next) != first);
|
|
}
|
|
|
|
/* If the symbol only has to be provided if it is needed,
|
|
ignore it here since it is not undefined. */
|
|
if (orule->val.assignment->provide_flag)
|
|
continue;
|
|
|
|
/* Allocate memory for this new symbol. */
|
|
s = (struct symbol *)
|
|
obstack_calloc (&ld_state.smem, sizeof (struct symbol));
|
|
|
|
/* Initialize it. */
|
|
s->name = orule->val.assignment->variable;
|
|
|
|
/* Insert it into the symbol hash table. */
|
|
unsigned long int hval = elf_hash (s->name);
|
|
if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab,
|
|
hval, s) != 0))
|
|
{
|
|
/* This means the symbol is defined somewhere else.
|
|
Maybe it comes from a DSO or so. Get the
|
|
definition. */
|
|
free (s);
|
|
struct symbol *old = ld_symbol_tab_find (&ld_state.symbol_tab,
|
|
hval, s);
|
|
assert (old != NULL);
|
|
free (s);
|
|
|
|
/* If this is a definition from the application itself
|
|
this means a duplicate definition. */
|
|
if (! old->in_dso)
|
|
{
|
|
error (0, 0, gettext ("\
|
|
duplicate definition of '%s' in linker script"),
|
|
s->name);
|
|
goto next_rule;
|
|
}
|
|
|
|
/* We use the definition from the linker script. */
|
|
s = old;
|
|
}
|
|
|
|
use_it:
|
|
/* The symbol is (now) defined. */
|
|
s->defined = 1;
|
|
s->type = STT_NOTYPE;
|
|
|
|
/* Add a reference to the symbol record. We will come
|
|
across it when creating the output file. */
|
|
orule->val.assignment->sym = s;
|
|
|
|
SNGL_LIST_PUSH (ld_state.lscript_syms, s);
|
|
++ld_state.nlscript_syms;
|
|
|
|
next_rule:
|
|
;
|
|
}
|
|
|
|
segment = segment->next;
|
|
}
|
|
while (segment != ld_state.output_segments->next);
|
|
}
|
|
|
|
|
|
/* Create creation of spection section symbols representing sections in the
|
|
output file. This is done for symbols like _GLOBAL_OFFSET_TABLE_ and
|
|
_DYNAMIC. */
|
|
static void
|
|
create_special_section_symbol (struct symbol **symp, const char *name)
|
|
{
|
|
if (*symp == NULL)
|
|
{
|
|
/* No symbol defined found yet. Create one. */
|
|
struct symbol *newsym = (struct symbol *)
|
|
obstack_calloc (&ld_state.smem, sizeof (*newsym));
|
|
|
|
newsym->name = name;
|
|
// XXX Should we mark the symbol hidden? They are hardly useful
|
|
// used outside the current object.
|
|
|
|
/* Add to the symbol table. */
|
|
if (unlikely (ld_symbol_tab_insert (&ld_state.symbol_tab,
|
|
elf_hash (name), newsym) != 0))
|
|
abort ();
|
|
|
|
*symp = newsym;
|
|
}
|
|
else if ((*symp)->defined)
|
|
/* Cannot happen. We do use this symbol from any input file. */
|
|
abort ();
|
|
|
|
(*symp)->defined = 1;
|
|
(*symp)->type = STT_OBJECT;
|
|
|
|
++ld_state.nsymtab;
|
|
}
|