* ldgram.y (ldgram_had_keep, KEEP): New.
(input_section_spec_no_keep): Rename from old input_section_spec.
(input_section_spec): New. Recognize KEEP.
* ldlang.c (wild_section): Handle keep sections.
(lang_gc_wild_section, lang_gc_wild_file, lang_gc_wild): New.
(lang_gc_sections_1, lang_gc_sections): New.
(lang_process): Invoke lang_gc_sections.
(lang_add_wild): Add keep argument. Update all callers.
* ldlang.h (lang_wild_statement_struct): Add keep_sections.
* ldlex.l (KEEP): Match it.
* ldmain.c (main): Error on -r and --gc-sections.
* lexsup.c: Add --gc-sections.
* scripttempl/elf.sc: Merge .text.* etc sections appropriately.
Mark startup sections with KEEP.
* scripttempl/elfppc.sc: Likewise.
* ld.texinfo: Update for --gc-sections and KEEP.
+Wed Jul 1 19:40:34 1998 Richard Henderson <rth@cygnus.com>
+
+ * ld.h (args_type): Add gc_sections.
+ * ldgram.y (ldgram_had_keep, KEEP): New.
+ (input_section_spec_no_keep): Rename from old input_section_spec.
+ (input_section_spec): New. Recognize KEEP.
+ * ldlang.c (wild_section): Handle keep sections.
+ (lang_gc_wild_section, lang_gc_wild_file, lang_gc_wild): New.
+ (lang_gc_sections_1, lang_gc_sections): New.
+ (lang_process): Invoke lang_gc_sections.
+ (lang_add_wild): Add keep argument. Update all callers.
+ * ldlang.h (lang_wild_statement_struct): Add keep_sections.
+ * ldlex.l (KEEP): Match it.
+ * ldmain.c (main): Error on -r and --gc-sections.
+ * lexsup.c: Add --gc-sections.
+
+ * scripttempl/elf.sc: Merge .text.* etc sections appropriately.
+ Mark startup sections with KEEP.
+ * scripttempl/elfppc.sc: Likewise.
+
+ * ld.texinfo: Update for --gc-sections and KEEP.
+
+Wed Jul 1 15:21:20 1998 Ian Lance Taylor <ian@cygnus.com>
+
+ From Peter Jordan <pjordan@chla.usc.edu>:
+ * scripttempl/i386go32.sc: Correct constructor handling for -u.
+
Tue Jun 23 15:17:27 1998 Ian Lance Taylor <ian@cygnus.com>
* Makefile.am (install-data-local): Make ldscripts subdirectory.
/* Name of shared object for whose symbol table this shared object
is an auxiliary filter. From the --auxiliary option. */
char **auxiliary_filters;
+
+ /* Remove unreferenced sections? */
+ boolean gc_sections;
} args_type;
extern args_type command_line;
/* If true, doing a dynamic link. */
boolean dynamic_link;
+ /* If true, -shared is supported. */
+ /* ??? A better way to do this is perhaps to define this in the
+ ld_emulation_xfer_struct since this is really a target dependent
+ parameter. */
+ boolean has_shared;
+
/* If true, build constructors. */
boolean build_constructors;
MIPS ECOFF which supports putting large and small objects into different
sections. This is ignored for other object file formats.
+@kindex --gc-sections
+@cindex garbage collection
+@item --gc-sections
+Enable garbage collection of unused input sections. It is ignored on
+targets that do not support this option. This option is not compatible
+with @samp{-r}, nor should it be used with dynamic linking.
+
@cindex runtime library name
@kindex -h@var{name}
@kindex -soname=@var{name}
instructions in the output object file.
On some platforms these link time global optimizations may make symbolic
-debugging of the resulting executable impossible. This is known to be
+debugging of the resulting executable impossible.
+@ifset GENERIC
+This is known to be
the case for the Matsushita MN10200 and MN10300 family of processors.
+@end ifset
@ifset GENERIC
On platforms where this is not supported, @samp{--relax} is accepted,
* Input Section Basics:: Input section basics
* Input Section Wildcards:: Input section wildcard patterns
* Input Section Common:: Input section for common symbols
+* Input Section Keep:: Input section and garbage collection
* Input Section Example:: Input section example
@end menu
notation is now considered obsolete. It is equivalent to
@samp{*(COMMON)}.
+@node Input Section Keep
+@subsubsection Input section and garbage collection
+@cindex KEEP
+@cindex garbage collection
+When link-time garbage collection is in use (@samp{--gc-sections}),
+it is often useful to mark sections that should not be eliminated.
+This is accomplished by surrounding an input section's wildcard entry
+with @code{KEEP()}, as in @code{KEEP(*(.init))} or
+@code{KEEP(SORT(*)(.ctors))}.
+
@node Input Section Example
@subsubsection Input section example
The following example is a complete linker script. It tells the linker
section command, such as a symbol assignment, then the output section
will always be created, even if there are no matching input sections.
+@cindex /DISCARD/
The special output section name @samp{/DISCARD/} may be used to discard
input sections. Any input sections which are assigned to an output
section named @samp{/DISCARD/} are not included in the output file.
/* A YACC grammer to parse a superset of the AT&T linker scripting languaue.
- Copyright (C) 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
Written by Steve Chamberlain of Cygnus Support (steve@cygnus.com).
This file is part of GNU ld.
lang_memory_region_type *region;
-
-char *current_file;
+struct wildcard_spec current_file;
boolean ldgram_want_filename = true;
boolean had_script = false;
boolean force_make_executable = false;
boolean ldgram_in_script = false;
boolean ldgram_had_equals = false;
-
+boolean ldgram_had_keep = false;
#define ERROR_NAME_MAX 20
static char *error_names[ERROR_NAME_MAX];
%union {
bfd_vma integer;
char *name;
+ const char *cname;
+ struct wildcard_spec wildcard;
int token;
union etree_union *etree;
struct phdr_info
union etree_union *flags;
} phdr;
struct lang_nocrossref *nocrossref;
+ struct lang_output_section_phdr_list *section_phdr;
+ struct bfd_elf_version_deps *deflist;
+ struct bfd_elf_version_expr *versyms;
+ struct bfd_elf_version_tree *versnode;
}
%type <etree> exp opt_exp_with_type mustbe_exp opt_at phdr_type phdr_val
+%type <etree> opt_exp_without_type
%type <integer> fill_opt
%type <name> memspec_opt casesymlist
+%type <cname> wildcard_name
+%type <wildcard> wildcard_spec
%token <integer> INT
%token <name> NAME LNAME
%type <integer> length
%type <phdr> phdr_qualifiers
%type <nocrossref> nocrossref_list
+%type <section_phdr> phdr_opt
+%type <integer> opt_nocrossrefs
%right <token> PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ
%right <token> '?' ':'
%right UNARY
%token END
%left <token> '('
-%token <token> ALIGN_K BLOCK BIND QUAD LONG SHORT BYTE
-%token SECTIONS PHDRS
+%token <token> ALIGN_K BLOCK BIND QUAD SQUAD LONG SHORT BYTE
+%token SECTIONS PHDRS SORT
%token '{' '}'
%token SIZEOF_HEADERS OUTPUT_FORMAT FORCE_COMMON_ALLOCATION OUTPUT_ARCH
%token SIZEOF_HEADERS
%token MEMORY DEFSYMEND
%token NOLOAD DSECT COPY INFO OVERLAY
%token NAME LNAME DEFINED TARGET_K SEARCH_DIR MAP ENTRY
-%token <integer> SIZEOF NEXT ADDR LOADADDR
+%token <integer> NEXT
+%token SIZEOF ADDR LOADADDR MAX MIN
%token STARTUP HLL SYSLIB FLOAT NOFLOAT NOCROSSREFS
%token ORIGIN FILL
%token LENGTH CREATE_OBJECT_SYMBOLS INPUT GROUP OUTPUT CONSTRUCTORS
%token CHIP LIST SECT ABSOLUTE LOAD NEWLINE ENDWORD ORDER NAMEWORD
%token FORMAT PUBLIC DEFSYMEND BASE ALIAS TRUNCATE REL
%token INPUT_SCRIPT INPUT_MRI_SCRIPT INPUT_DEFSYM CASE EXTERN START
+%token <name> VERS_TAG VERS_IDENTIFIER
+%token GLOBAL LOCAL VERSIONK INPUT_VERSION_SCRIPT
+%token KEEP
+%type <versyms> vers_defns
+%type <versnode> vers_tag
+%type <deflist> verdep
%%
file:
INPUT_SCRIPT script_file
| INPUT_MRI_SCRIPT mri_script_file
+ | INPUT_VERSION_SCRIPT version_script_file
| INPUT_DEFSYM defsym_expr
;
mri_script_file:
{
ldlex_mri_script ();
- PUSH_ERROR ("MRI style script");
+ PUSH_ERROR (_("MRI style script"));
}
mri_script_lines
{
CHIP exp
| CHIP exp ',' exp
| NAME {
- einfo("%P%F: unrecognised keyword in MRI style script '%s'\n",$1);
+ einfo(_("%P%F: unrecognised keyword in MRI style script '%s'\n"),$1);
}
| LIST {
config.map_filename = "-";
| low_level_library
| floating_point_support
| statement_anywhere
+ | version
| ';'
| TARGET_K '(' NAME ')'
{ lang_add_target($3); }
/* The '*' and '?' cases are there because the lexer returns them as
separate tokens rather than as NAME. */
-file_NAME_list:
+wildcard_name:
NAME
- { lang_add_wild ($1, current_file); }
+ {
+ $$ = $1;
+ }
| '*'
- { lang_add_wild ("*", current_file); }
+ {
+ $$ = "*";
+ }
| '?'
- { lang_add_wild ("?", current_file); }
- | file_NAME_list opt_comma NAME
- { lang_add_wild ($3, current_file); }
- | file_NAME_list opt_comma '*'
- { lang_add_wild ("*", current_file); }
- | file_NAME_list opt_comma '?'
- { lang_add_wild ("?", current_file); }
+ {
+ $$ = "?";
+ }
;
-input_section_spec:
- NAME
- {
- lang_add_wild((char *)NULL, $1);
- }
- | '['
+wildcard_spec:
+ wildcard_name
{
- current_file = (char *)NULL;
+ $$.name = $1;
+ $$.sorted = false;
}
- file_NAME_list
- ']'
- | NAME
+ | SORT '(' wildcard_name ')'
{
- current_file = $1;
+ $$.name = $3;
+ $$.sorted = true;
}
- '(' file_NAME_list ')'
- | '?'
- /* This case is needed because the lexer returns a
- single question mark as '?' rather than NAME. */
+ ;
+
+file_NAME_list:
+ wildcard_spec
{
- current_file = "?";
+ lang_add_wild ($1.name, $1.sorted,
+ current_file.name,
+ current_file.sorted,
+ ldgram_had_keep);
}
- '(' file_NAME_list ')'
- | '*'
+ | file_NAME_list opt_comma wildcard_spec
+ {
+ lang_add_wild ($3.name, $3.sorted,
+ current_file.name,
+ current_file.sorted,
+ ldgram_had_keep);
+ }
+ ;
+
+input_section_spec_no_keep:
+ NAME
+ {
+ lang_add_wild (NULL, false, $1, false,
+ ldgram_had_keep);
+ }
+ | '['
+ {
+ current_file.name = NULL;
+ current_file.sorted = false;
+ }
+ file_NAME_list ']'
+ | wildcard_spec
{
- current_file = (char *)NULL;
+ current_file = $1;
+ /* '*' matches any file name. */
+ if (strcmp (current_file.name, "*") == 0)
+ current_file.name = NULL;
}
'(' file_NAME_list ')'
;
+input_section_spec:
+ input_section_spec_no_keep
+ | KEEP '('
+ { ldgram_had_keep = true; }
+ input_section_spec_no_keep ')'
+ { ldgram_had_keep = false; }
+ ;
+
statement:
assignment end
| CREATE_OBJECT_SYMBOLS
length:
QUAD
{ $$ = $1; }
+ | SQUAD
+ { $$ = $1; }
| LONG
{ $$ = $1; }
| SHORT
region->origin =
exp_get_vma($3, 0L,"origin", lang_first_phase_enum);
}
- ; length_spec:
+ ;
+
+length_spec:
LENGTH '=' mustbe_exp
{ region->length = exp_get_vma($3,
~((bfd_vma)0),
attributes_opt:
'(' NAME ')'
{
- lang_set_flags(®ion->flags, $2);
+ lang_set_flags(region, $2);
}
|
{ $$ = exp_unop(ALIGN_K,$3); }
| NAME
{ $$ = exp_nameop(NAME,$1); }
+ | MAX '(' exp ',' exp ')'
+ { $$ = exp_binop (MAX, $3, $5 ); }
+ | MIN '(' exp ',' exp ')'
+ { $$ = exp_binop (MIN, $3, $5 ); }
;
'}' { ldlex_popstate (); ldlex_expression (); }
memspec_opt phdr_opt fill_opt
{
- ldlex_popstate();
- lang_leave_output_section_statement($13, $11);
+ ldlex_popstate ();
+ lang_leave_output_section_statement ($13, $11, $12);
}
opt_comma
+ | OVERLAY
+ { ldlex_expression (); }
+ opt_exp_without_type opt_nocrossrefs opt_at
+ { ldlex_popstate (); ldlex_script (); }
+ '{'
+ {
+ lang_enter_overlay ($3, $5, (int) $4);
+ }
+ overlay_section
+ '}'
+ { ldlex_popstate (); ldlex_expression (); }
+ memspec_opt phdr_opt fill_opt
+ {
+ ldlex_popstate ();
+ lang_leave_overlay ($14, $12, $13);
+ }
+ opt_comma
| /* The GROUP case is just enough to support the gcc
svr3.ifile script. It is not intended to be full
support. I'm not even sure what GROUP is supposed
atype:
'(' type ')'
| /* EMPTY */ { sectype = normal_section; }
+ | '(' ')' { sectype = normal_section; }
;
opt_exp_with_type:
{ $$ = $3; }
;
+opt_exp_without_type:
+ exp ':' { $$ = $1; }
+ | ':' { $$ = (etree_type *) NULL; }
+ ;
+
+opt_nocrossrefs:
+ /* empty */
+ { $$ = 0; }
+ | NOCROSSREFS
+ { $$ = 1; }
+ ;
+
memspec_opt:
'>' NAME
{ $$ = $2; }
phdr_opt:
/* empty */
+ {
+ $$ = NULL;
+ }
| phdr_opt ':' NAME
{
- lang_section_in_phdr ($3);
+ struct lang_output_section_phdr_list *n;
+
+ n = ((struct lang_output_section_phdr_list *)
+ xmalloc (sizeof *n));
+ n->name = $3;
+ n->used = false;
+ n->next = $1;
+ $$ = n;
}
;
+overlay_section:
+ /* empty */
+ | overlay_section
+ NAME
+ {
+ ldlex_script ();
+ lang_enter_overlay_section ($2);
+ }
+ '{' statement_list_opt '}'
+ { ldlex_popstate (); ldlex_expression (); }
+ phdr_opt fill_opt
+ {
+ ldlex_popstate ();
+ lang_leave_overlay_section ($9, $8);
+ }
+ opt_comma
+ ;
+
phdrs:
PHDRS '{' phdr_list '}'
;
else if (strcmp ($1, "FLAGS") == 0 && $2 != NULL)
$$.flags = $2;
else
- einfo ("%X%P:%S: PHDRS syntax error at `%s'\n", $1);
+ einfo (_("%X%P:%S: PHDRS syntax error at `%s'\n"), $1);
}
| AT '(' exp ')' phdr_qualifiers
{
}
;
+/* This syntax is used within an external version script file. */
+
+version_script_file:
+ {
+ ldlex_version_file ();
+ PUSH_ERROR (_("VERSION script"));
+ }
+ vers_nodes
+ {
+ ldlex_popstate ();
+ POP_ERROR ();
+ }
+ ;
+
+/* This is used within a normal linker script file. */
+
+version:
+ {
+ ldlex_version_script ();
+ }
+ VERSIONK '{' vers_nodes '}'
+ {
+ ldlex_popstate ();
+ }
+ ;
+
+vers_nodes:
+ vers_node
+ | vers_nodes vers_node
+ ;
+
+vers_node:
+ VERS_TAG '{' vers_tag '}' ';'
+ {
+ lang_register_vers_node ($1, $3, NULL);
+ }
+ | VERS_TAG '{' vers_tag '}' verdep ';'
+ {
+ lang_register_vers_node ($1, $3, $5);
+ }
+ ;
+
+verdep:
+ VERS_TAG
+ {
+ $$ = lang_add_vers_depend (NULL, $1);
+ }
+ | verdep VERS_TAG
+ {
+ $$ = lang_add_vers_depend ($1, $2);
+ }
+ ;
+
+vers_tag:
+ /* empty */
+ {
+ $$ = lang_new_vers_node (NULL, NULL);
+ }
+ | vers_defns ';'
+ {
+ $$ = lang_new_vers_node ($1, NULL);
+ }
+ | GLOBAL ':' vers_defns ';'
+ {
+ $$ = lang_new_vers_node ($3, NULL);
+ }
+ | LOCAL ':' vers_defns ';'
+ {
+ $$ = lang_new_vers_node (NULL, $3);
+ }
+ | GLOBAL ':' vers_defns ';' LOCAL ':' vers_defns ';'
+ {
+ $$ = lang_new_vers_node ($3, $7);
+ }
+ ;
+
+vers_defns:
+ VERS_IDENTIFIER
+ {
+ $$ = lang_new_vers_regex (NULL, $1);
+ }
+ | vers_defns ';' VERS_IDENTIFIER
+ {
+ $$ = lang_new_vers_regex ($1, $3);
+ }
+ ;
+
%%
void
yyerror(arg)
const char *arg;
{
if (ldfile_assumed_script)
- einfo ("%P:%s: file format not recognized; treating as linker script\n",
+ einfo (_("%P:%s: file format not recognized; treating as linker script\n"),
ldfile_input_filename);
if (error_index > 0 && error_index < ERROR_NAME_MAX)
einfo ("%P%F:%S: %s in %s\n", arg, error_names[error_index-1]);
static void lang_set_startof PARAMS ((void));
static void reset_memory_regions PARAMS ((void));
static void lang_record_phdrs PARAMS ((void));
+static void lang_gc_wild_section
+ PARAMS ((lang_wild_statement_type *, const char *,
+ lang_input_statement_type *));
+static void lang_gc_wild_file
+ PARAMS ((lang_wild_statement_type *, const char *,
+ lang_input_statement_type *));
+static void lang_gc_wild
+ PARAMS ((lang_wild_statement_type *, const char *, const char *));
+static void lang_gc_sections_1 PARAMS ((lang_statement_union_type *));
+static void lang_gc_sections PARAMS ((void));
+
/* EXPORTS */
lang_output_section_statement_type *abs_output_section;
{
lang_statement_union_type *before;
+ /* If the wild pattern was marked KEEP, the member sections
+ should be as well. */
+ if (ptr->keep_sections)
+ s->flags |= SEC_KEEP;
+
before = wild_sort (ptr, file, s);
/* Here BEFORE points to the lang_input_section which
else
{
bfd_vma val;
- char *send;
+ CONST char *send;
/* We couldn't find the entry symbol. Try parsing it as a
number. */
}
}
+/* ??? At some point this traversal for GC should share code with the
+ traversal for manipulating the output file. */
+
+/* Expand a wild statement for a particular FILE, marking its sections KEEP
+ as needed. SECTION may be NULL, in which case it is a wild card. */
+
+static void
+lang_gc_wild_section (ptr, section, file)
+ lang_wild_statement_type *ptr;
+ const char *section;
+ lang_input_statement_type *file;
+{
+ if (file->just_syms_flag == false)
+ {
+ register asection *s;
+ boolean wildcard;
+
+ if (section == NULL)
+ wildcard = false;
+ else
+ wildcard = wildcardp (section);
+
+ for (s = file->the_bfd->sections; s != NULL; s = s->next)
+ {
+ boolean match;
+
+ if (section == NULL)
+ match = true;
+ else
+ {
+ const char *name;
+
+ name = bfd_get_section_name (file->the_bfd, s);
+ if (wildcard)
+ match = fnmatch (section, name, 0) == 0 ? true : false;
+ else
+ match = strcmp (section, name) == 0 ? true : false;
+ }
+
+ if (match)
+ {
+ /* If the wild pattern was marked KEEP, the member sections
+ should be as well. */
+ if (ptr->keep_sections)
+ s->flags |= SEC_KEEP;
+ }
+ }
+ }
+}
+
+/* Handle a wild statement for a single file F. */
+
+static void
+lang_gc_wild_file (s, section, f)
+ lang_wild_statement_type *s;
+ const char *section;
+ lang_input_statement_type *f;
+{
+ if (f->the_bfd == NULL
+ || ! bfd_check_format (f->the_bfd, bfd_archive))
+ lang_gc_wild_section (s, section, f);
+ else
+ {
+ bfd *member;
+
+ /* This is an archive file. We must map each member of the
+ archive separately. */
+ member = bfd_openr_next_archived_file (f->the_bfd, (bfd *) NULL);
+ while (member != NULL)
+ {
+ /* When lookup_name is called, it will call the add_symbols
+ entry point for the archive. For each element of the
+ archive which is included, BFD will call ldlang_add_file,
+ which will set the usrdata field of the member to the
+ lang_input_statement. */
+ if (member->usrdata != NULL)
+ {
+ lang_gc_wild_section (s, section,
+ (lang_input_statement_type *) member->usrdata);
+ }
+
+ member = bfd_openr_next_archived_file (f->the_bfd, member);
+ }
+ }
+}
+
+/* Handle a wild statement, marking it against GC. SECTION or FILE or both
+ may be NULL, indicating that it is a wildcard. */
+
+static void
+lang_gc_wild (s, section, file)
+ lang_wild_statement_type *s;
+ const char *section;
+ const char *file;
+{
+ lang_input_statement_type *f;
+
+ if (file == (char *) NULL)
+ {
+ /* Perform the iteration over all files in the list */
+ for (f = (lang_input_statement_type *) file_chain.head;
+ f != (lang_input_statement_type *) NULL;
+ f = (lang_input_statement_type *) f->next)
+ {
+ lang_gc_wild_file (s, section, f);
+ }
+ }
+ else if (wildcardp (file))
+ {
+ for (f = (lang_input_statement_type *) file_chain.head;
+ f != (lang_input_statement_type *) NULL;
+ f = (lang_input_statement_type *) f->next)
+ {
+ if (fnmatch (file, f->filename, FNM_FILE_NAME) == 0)
+ lang_gc_wild_file (s, section, f);
+ }
+ }
+ else
+ {
+ /* Perform the iteration over a single file */
+ f = lookup_name (file);
+ lang_gc_wild_file (s, section, f);
+ }
+}
+
+/* Iterate over sections marking them against GC. */
+
+static void
+lang_gc_sections_1 (s)
+ lang_statement_union_type * s;
+{
+ for (; s != (lang_statement_union_type *) NULL; s = s->next)
+ {
+ switch (s->header.type)
+ {
+ case lang_wild_statement_enum:
+ lang_gc_wild (&s->wild_statement,
+ s->wild_statement.section_name,
+ s->wild_statement.filename);
+ break;
+ case lang_constructors_statement_enum:
+ lang_gc_sections_1 (constructor_list.head);
+ break;
+ case lang_output_section_statement_enum:
+ lang_gc_sections_1 (s->output_section_statement.children.head);
+ break;
+ case lang_group_statement_enum:
+ lang_gc_sections_1 (s->group_statement.children.head);
+ break;
+ }
+ }
+}
+
+static void
+lang_gc_sections ()
+{
+ struct bfd_link_hash_entry *h;
+ ldlang_undef_chain_list_type *ulist, fake_list_start;
+
+ /* Keep all sections so marked in the link script. */
+
+ lang_gc_sections_1 (statement_list.head);
+
+ /* Keep all sections containing symbols undefined on the command-line.
+ Handle the entry symbol at the same time. */
+
+ fake_list_start.next = ldlang_undef_chain_list_head;
+ fake_list_start.name = entry_symbol;
+
+ for (ulist = &fake_list_start; ulist; ulist = ulist->next)
+ {
+ h = bfd_link_hash_lookup (link_info.hash, ulist->name,
+ false, false, false);
+
+ if (h != (struct bfd_link_hash_entry *) NULL
+ && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak)
+ && ! bfd_is_abs_section (h->u.def.section))
+ {
+ h->u.def.section->flags |= SEC_KEEP;
+ }
+ }
+
+ bfd_gc_sections (output_bfd, &link_info);
+}
+
void
lang_process ()
{
files. */
ldctor_build_sets ();
+ /* Remove unreferenced sections if asked to. */
+ if (command_line.gc_sections)
+ lang_gc_sections ();
+
/* Size up the common data */
lang_common ();
/* EXPORTED TO YACC */
void
-lang_add_wild (section_name, sections_sorted, filename, filenames_sorted)
+lang_add_wild (section_name, sections_sorted, filename, filenames_sorted,
+ keep_sections)
const char *const section_name;
boolean sections_sorted;
const char *const filename;
boolean filenames_sorted;
+ boolean keep_sections;
{
lang_wild_statement_type *new = new_stat (lang_wild_statement,
stat_ptr);
new->sections_sorted = sections_sorted;
new->filename = filename;
new->filenames_sorted = filenames_sorted;
+ new->keep_sections = keep_sections;
lang_list_init (&new->children);
}
%{
-/* Copyright (C) 1991, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
This file is part of GLD, the Gnu Linker.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GLD; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+along with GLD; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
/*
This was written by steve chamberlain
yylex and yyparse (indirectly) both check this. */
input_type parser_input;
-/* Radix to use for bfd_scan_vma -- 0 (default to base 10) or 16. */
-int hex_mode;
-
/* Line number in the current input file.
(FIXME Actually, it doesn't appear to get reset for each file?) */
unsigned int lineno = 1;
NOCFILENAMECHAR [_a-zA-Z0-9\/\.\-\_\+\$\:\[\]\\\~]
V_TAG [.$_a-zA-Z][._a-zA-Z0-9]*
-V_IDENTIFIER [*?$_a-zA-Z][*?_a-zA-Z0-9]*
+V_IDENTIFIER [*?.$_a-zA-Z][*?_a-zA-Z0-9]*
%s SCRIPT
%s EXPRESSION
ibase);
return INT;
}
-<SCRIPT,DEFSYMEXP,MRI,BOTH,EXPRESSION>"$"?"0x"?([0-9A-Fa-f])+(M|K|m|k)? {
- yylval.integer = bfd_scan_vma (yytext, 0,
- hex_mode);
- if (yytext[yyleng-1]=='M'
- || yytext[yyleng-1] == 'm') {
- yylval.integer *= 1024*1024;
- }
- if (yytext[yyleng-1]=='K'
- || yytext[yyleng-1]=='k') {
- yylval.integer *= 1024;
- }
+<SCRIPT,DEFSYMEXP,MRI,BOTH,EXPRESSION>((("$"|"0x")([0-9A-Fa-f])+)|(([0-9])+))(M|K|m|k)? {
+ char *s = yytext;
+
+ if (*s == '$')
+ ++s;
+ yylval.integer = bfd_scan_vma (s, 0, 0);
+ if (yytext[yyleng-1] == 'M'
+ || yytext[yyleng-1] == 'm')
+ yylval.integer *= 1024 * 1024;
+ if (yytext[yyleng-1] == 'K'
+ || yytext[yyleng-1]=='k')
+ yylval.integer *= 1024;
return INT;
}
<BOTH,SCRIPT,EXPRESSION,MRI>"]" { RTOKEN(']');}
<BOTH,SCRIPT,EXPRESSION,MRI>";" { RTOKEN(';');}
<BOTH,SCRIPT>"MEMORY" { RTOKEN(MEMORY);}
<BOTH,SCRIPT>"ORIGIN" { RTOKEN(ORIGIN);}
-<BOTH,SCRIPT>"VERSION" { RTOKEN(VERSION);}
+<BOTH,SCRIPT>"VERSION" { RTOKEN(VERSIONK);}
<EXPRESSION,BOTH,SCRIPT>"BLOCK" { RTOKEN(BLOCK);}
<EXPRESSION,BOTH,SCRIPT>"BIND" { RTOKEN(BIND);}
<BOTH,SCRIPT>"LENGTH" { RTOKEN(LENGTH);}
<BOTH,SCRIPT>"NOFLOAT" { RTOKEN(NOFLOAT);}
<EXPRESSION,BOTH,SCRIPT>"NOCROSSREFS" { RTOKEN(NOCROSSREFS);}
<BOTH,SCRIPT>"OVERLAY" { RTOKEN(OVERLAY); }
+<BOTH,SCRIPT>"SORT" { RTOKEN(SORT); }
<EXPRESSION,BOTH,SCRIPT>"NOLOAD" { RTOKEN(NOLOAD);}
<EXPRESSION,BOTH,SCRIPT>"DSECT" { RTOKEN(DSECT);}
<EXPRESSION,BOTH,SCRIPT>"COPY" { RTOKEN(COPY);}
<BOTH,SCRIPT>"PHDRS" { RTOKEN (PHDRS); }
<EXPRESSION,BOTH,SCRIPT>"AT" { RTOKEN(AT);}
<EXPRESSION,BOTH,SCRIPT>"PROVIDE" { RTOKEN(PROVIDE); }
-<MRI>"#".*\n?\r? { ++ lineno; }
+<EXPRESSION,BOTH,SCRIPT>"KEEP" { RTOKEN(KEEP); }
+<MRI>"#".*\n? { ++ lineno; }
<MRI>"\n" { ++ lineno; RTOKEN(NEWLINE); }
-<MRI>"\r" { ++ lineno; RTOKEN(NEWLINE); }
<MRI>"*".* { /* Mri comment line */ }
<MRI>";".* { /* Mri comment line */ }
<MRI>"END" { RTOKEN(ENDWORD); }
yylval.name = buystring (yytext + 2);
return LNAME;
}
-<SCRIPT>{WILDCHAR}* { yylval.name = buystring(yytext); return NAME; }
+<SCRIPT>{WILDCHAR}* {
+ /* Annoyingly, this pattern can match comments, and we have
+ longest match issues to consider. So if the first two
+ characters are a comment opening, put the input back and
+ try again. */
+ if (yytext[0] == '/' && yytext[1] == '*')
+ {
+ yyless(2);
+ comment ();
+ }
+ else
+ {
+ yylval.name = buystring(yytext);
+ return NAME;
+ }
+ }
<EXPRESSION,BOTH,SCRIPT>"\""[^\"]*"\"" {
/* No matter the state, quotes
return NAME;
}
<BOTH,SCRIPT,EXPRESSION>"\n" { lineno++;}
-<BOTH,SCRIPT,EXPRESSION>"\r" { lineno++;}
-<MRI,BOTH,SCRIPT,EXPRESSION>[ \t]
+<MRI,BOTH,SCRIPT,EXPRESSION>[ \t\r]+ { }
<VERS_NODE,VERS_SCRIPT>[:,;] { return *yytext; }
<VERS_SCRIPT>"{" { BEGIN(VERS_NODE); return *yytext; }
<VERS_SCRIPT,VERS_NODE>"}" { BEGIN(VERS_SCRIPT); return *yytext; }
-<VERS_START,VERS_NODE,VERS_SCRIPT>[\n\r] { lineno++; }
+<VERS_START,VERS_NODE,VERS_SCRIPT>[\n] { lineno++; }
<VERS_START,VERS_NODE,VERS_SCRIPT>#.* { /* Eat up comments */ }
-<VERS_START,VERS_NODE,VERS_SCRIPT>[ \t]+ { /* Eat up whitespace */ }
+<VERS_START,VERS_NODE,VERS_SCRIPT>[ \t\r]+ { /* Eat up whitespace */ }
<<EOF>> {
include_stack_ptr--;
c = input();
while (c != '*' && c != EOF)
{
- if (c == '\n' || c == '\r')
+ if (c == '\n')
lineno++;
c = input();
}
break; /* found the end */
}
- if (c == '\n' || c == '\r')
+ if (c == '\n')
lineno++;
if (c == EOF)
#define OPTION_WHOLE_ARCHIVE (OPTION_SPLIT_BY_FILE + 1)
#define OPTION_WRAP (OPTION_WHOLE_ARCHIVE + 1)
#define OPTION_FORCE_EXE_SUFFIX (OPTION_WRAP + 1)
+#define OPTION_GC_SECTIONS (OPTION_FORCE_EXE_SUFFIX + 1)
+#define OPTION_NO_GC_SECTIONS (OPTION_GC_SECTIONS + 1)
/* The long options. This structure is used for both the option
parsing and the help text. */
'F', N_("SHLIB"), N_("Filter for shared object symbol table"), TWO_DASHES },
{ {NULL, no_argument, NULL, '\0'},
'g', NULL, N_("Ignored"), ONE_DASH },
+ { {"gc-sections", no_argument, NULL, OPTION_GC_SECTIONS},
+ '\0', NULL, N_("Remove unused sections on certain targets"),
+ TWO_DASHES },
+ { {"no-gc-sections", no_argument, NULL, OPTION_NO_GC_SECTIONS},
+ '\0', NULL, N_("(Don't) Remove unused sections on certain targets"),
+ TWO_DASHES },
{ {"gpsize", required_argument, NULL, 'G'},
'G', N_("SIZE"), N_("Small data size (if no size, same as --shared)"),
TWO_DASHES },
case 'g':
/* Ignore. */
break;
+ case OPTION_GC_SECTIONS:
+ command_line.gc_sections = true;
+ break;
case OPTION_HELP:
help ();
xexit (0);
config.magic_demand_paged = false;
config.dynamic_link = false;
break;
+ case OPTION_NO_GC_SECTIONS:
+ command_line.gc_sections = false;
+ break;
case OPTION_NO_KEEP_MEMORY:
link_info.keep_memory = false;
break;
link_info.strip = strip_all;
break;
case OPTION_SHARED:
- link_info.shared = true;
+ if (config.has_shared)
+ link_info.shared = true;
+ else
+ einfo (_("%P%F: -shared not supported\n"));
break;
case 'h': /* Used on Solaris. */
case OPTION_SONAME:
.gnu.version_d ${RELOCATING-0} : { *(.gnu.version_d) }
.gnu.version_r ${RELOCATING-0} : { *(.gnu.version_r) }
.rela.text ${RELOCATING-0} :
- { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ {
+ *(.rela.text)
+ ${RELOCATING+*(.rela.text.*)}
+ ${RELOCATING+*(.rela.gnu.linkonce.t*)}
+ }
.rela.data ${RELOCATING-0} :
- { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ {
+ *(.rela.data)
+ ${RELOCATING+*(.rela.data.*)}
+ ${RELOCATING+*(.rela.gnu.linkonce.d*)}
+ }
.rela.rodata ${RELOCATING-0} :
- { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ {
+ *(.rela.rodata)
+ ${RELOCATING+*(.rela.rodata.*)}
+ ${RELOCATING+*(.rela.gnu.linkonce.r*)}
+ }
.rela.got ${RELOCATING-0} : { *(.rela.got) }
.rela.got1 ${RELOCATING-0} : { *(.rela.got1) }
.rela.got2 ${RELOCATING-0} : { *(.rela.got2) }
{
${RELOCATING+${TEXT_START_SYMBOLS}}
*(.text)
+ ${RELOCATING+*(.text.*)}
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
- *(.gnu.linkonce.t*)
+ ${RELOCATING+*(.gnu.linkonce.t*)}
} =${NOP-0}
- .init ${RELOCATING-0} : { *(.init) } =${NOP-0}
- .fini ${RELOCATING-0} : { *(.fini) } =${NOP-0}
- .rodata ${RELOCATING-0} : { *(.rodata) *(.gnu.linkonce.r*) }
+ .init ${RELOCATING-0} : { KEEP (*(.init)) } =${NOP-0}
+ .fini ${RELOCATING-0} : { KEEP (*(.fini)) } =${NOP-0}
+ .rodata ${RELOCATING-0} :
+ {
+ *(.rodata)
+ ${RELOCATING+*(.rodata.*)}
+ ${RELOCATING+*(.gnu.linkonce.r*)}
+ }
.rodata1 ${RELOCATING-0} : { *(.rodata1) }
${RELOCATING+_etext = .;}
${RELOCATING+PROVIDE (etext = .);}
{
${RELOCATING+${DATA_START_SYMBOLS}}
*(.data)
- *(.gnu.linkonce.d*)
+ ${RELOCATING+*(.data.*)}
+ ${RELOCATING+*(.gnu.linkonce.d*)}
${CONSTRUCTING+CONSTRUCTORS}
}
.data1 ${RELOCATING-0} : { *(.data1) }
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
- *crtbegin.o(.ctors)
- *(SORT(.ctors.*))
- *(.ctors) }
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors)) }
${RELOCATING+PROVIDE (__CTOR_END__ = .);}
${RELOCATING+PROVIDE (__DTOR_LIST__ = .);}
.dtors ${RELOCATING-0} : {
- *crtbegin.o(.dtors)
- *(SORT(.dtors.*))
- *(.dtors) }
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors)) }
${RELOCATING+PROVIDE (__DTOR_END__ = .);}
${RELOCATING+PROVIDE (_FIXUP_START_ = .);}