From Craig Silverstein: Implement OPTION in linker scripts.

This commit is contained in:
Ian Lance Taylor 2007-10-30 06:27:03 +00:00
parent c1866bd5e3
commit a0451b389c
3 changed files with 189 additions and 144 deletions

View file

@ -395,12 +395,13 @@ options::Command_line_options::options[] =
NULL, TWO_DASHES, &General_options::set_stats),
GENERAL_ARG('\0', "sysroot", N_("Set target system root directory"),
N_("--sysroot DIR"), TWO_DASHES, &General_options::set_sysroot),
SPECIAL('T', "script", N_("Read linker script"),
N_("-T FILE, --script FILE"), TWO_DASHES,
&invoke_script),
GENERAL_ARG('\0', "Ttext", N_("Set the address of the .text section"),
N_("-Ttext ADDRESS"), ONE_DASH,
&General_options::set_text_segment_address),
// This must come after -Ttext since it's a prefix of it.
SPECIAL('T', "script", N_("Read linker script"),
N_("-T FILE, --script FILE"), TWO_DASHES,
&invoke_script),
GENERAL_NOARG('\0', "threads", N_("Run the linker multi-threaded"),
NULL, TWO_DASHES, &General_options::set_threads),
GENERAL_NOARG('\0', "no-threads", N_("Do not run the linker multi-threaded"),
@ -623,149 +624,160 @@ Command_line::Command_line()
{
}
// Process the command line options.
// Process the command line options. For process_one_option,
// i is the index of argv to process next, and the return value
// is the index of the next option to process (i+1 or i+2, or argc
// to indicate processing is done). no_more_options is set to true
// if (and when) "--" is seen as an option.
int
Command_line::process_one_option(int argc, char** argv, int i,
bool* no_more_options)
{
const int options_size = options::Command_line_options::options_size;
const options::One_option* options = options::Command_line_options::options;
gold_assert(i < argc);
if (argv[i][0] != '-' || *no_more_options)
{
this->add_file(argv[i], false);
return i + 1;
}
// Option starting with '-'.
int dashes = 1;
if (argv[i][1] == '-')
{
dashes = 2;
if (argv[i][2] == '\0')
{
*no_more_options = true;
return i + 1;
}
}
// Look for a long option match.
char* opt = argv[i] + dashes;
char first = opt[0];
int skiparg = 0;
char* arg = strchr(opt, '=');
bool argument_with_equals = arg != NULL;
if (arg != NULL)
{
*arg = '\0';
++arg;
}
else if (i + 1 < argc)
{
arg = argv[i + 1];
skiparg = 1;
}
int j;
for (j = 0; j < options_size; ++j)
{
if (options[j].long_option != NULL
&& (dashes == 2
|| (options[j].dash
!= options::One_option::EXACTLY_TWO_DASHES))
&& first == options[j].long_option[0]
&& strcmp(opt, options[j].long_option) == 0)
{
if (options[j].special)
{
// Restore the '=' we clobbered above.
if (arg != NULL && skiparg == 0)
arg[-1] = '=';
i += options[j].special(argc - i, argv + i, opt, true, this);
}
else
{
if (!options[j].takes_argument())
{
if (argument_with_equals)
this->usage(_("unexpected argument"), argv[i]);
arg = NULL;
skiparg = 0;
}
else
{
if (arg == NULL)
this->usage(_("missing argument"), argv[i]);
}
this->apply_option(options[j], arg);
i += skiparg + 1;
}
break;
}
}
if (j < options_size)
return i;
// If we saw two dashes, we needed to have seen a long option.
if (dashes == 2)
this->usage(_("unknown option"), argv[i]);
// Look for a short option match. There may be more than one
// short option in a given argument.
bool done = false;
char* s = argv[i] + 1;
++i;
while (*s != '\0' && !done)
{
char opt = *s;
int j;
for (j = 0; j < options_size; ++j)
{
if (options[j].short_option == opt)
{
if (options[j].special)
{
// Undo the argument skip done above.
--i;
i += options[j].special(argc - i, argv + i, s, false,
this);
done = true;
}
else
{
arg = NULL;
if (options[j].takes_argument())
{
if (s[1] != '\0')
{
arg = s + 1;
done = true;
}
else if (i < argc)
{
arg = argv[i];
++i;
}
else
this->usage(_("missing argument"), opt);
}
this->apply_option(options[j], arg);
}
break;
}
}
if (j >= options_size)
this->usage(_("unknown option"), *s);
++s;
}
return i;
}
void
Command_line::process(int argc, char** argv)
{
const int options_size = options::Command_line_options::options_size;
const options::One_option* options =
options::Command_line_options::options;
bool no_more_options = false;
int i = 0;
while (i < argc)
{
if (argv[i][0] != '-' || no_more_options)
{
this->add_file(argv[i], false);
++i;
continue;
}
// Option starting with '-'.
int dashes = 1;
if (argv[i][1] == '-')
{
dashes = 2;
if (argv[i][2] == '\0')
{
no_more_options = true;
continue;
}
}
// Look for a long option match.
char* opt = argv[i] + dashes;
char first = opt[0];
int skiparg = 0;
char* arg = strchr(opt, '=');
bool argument_with_equals = arg != NULL;
if (arg != NULL)
{
*arg = '\0';
++arg;
}
else if (i + 1 < argc)
{
arg = argv[i + 1];
skiparg = 1;
}
int j;
for (j = 0; j < options_size; ++j)
{
if (options[j].long_option != NULL
&& (dashes == 2
|| (options[j].dash
!= options::One_option::EXACTLY_TWO_DASHES))
&& first == options[j].long_option[0]
&& strcmp(opt, options[j].long_option) == 0)
{
if (options[j].special)
{
// Restore the '=' we clobbered above.
if (arg != NULL && skiparg == 0)
arg[-1] = '=';
i += options[j].special(argc - i, argv + i, opt, true, this);
}
else
{
if (!options[j].takes_argument())
{
if (argument_with_equals)
this->usage(_("unexpected argument"), argv[i]);
arg = NULL;
skiparg = 0;
}
else
{
if (arg == NULL)
this->usage(_("missing argument"), argv[i]);
}
this->apply_option(options[j], arg);
i += skiparg + 1;
}
break;
}
}
if (j < options_size)
continue;
// If we saw two dashes, we need to see a long option.
if (dashes == 2)
this->usage(_("unknown option"), argv[i]);
// Look for a short option match. There may be more than one
// short option in a given argument.
bool done = false;
char* s = argv[i] + 1;
++i;
while (*s != '\0' && !done)
{
char opt = *s;
int j;
for (j = 0; j < options_size; ++j)
{
if (options[j].short_option == opt)
{
if (options[j].special)
{
// Undo the argument skip done above.
--i;
i += options[j].special(argc - i, argv + i, s, false,
this);
done = true;
}
else
{
arg = NULL;
if (options[j].takes_argument())
{
if (s[1] != '\0')
{
arg = s + 1;
done = true;
}
else if (i < argc)
{
arg = argv[i];
++i;
}
else
this->usage(_("missing argument"), opt);
}
this->apply_option(options[j], arg);
}
break;
}
}
if (j >= options_size)
this->usage(_("unknown option"), *s);
++s;
}
}
i = process_one_option(argc, argv, i, &no_more_options);
if (this->inputs_.in_group())
{

View file

@ -696,6 +696,11 @@ class Command_line
void
process(int argc, char** argv);
// Process one command-line option. This takes the index of argv to
// process, and returns the index for the next option.
int
process_one_option(int argc, char** argv, int i, bool* no_more_options);
// Handle a -l option.
int
process_l_option(int, char**, char*, bool);

View file

@ -831,9 +831,11 @@ class Parser_closure
Parser_closure(const char* filename,
const Position_dependent_options& posdep_options,
bool in_group, bool is_in_sysroot,
Command_line* command_line,
const Lex::Token_sequence* tokens)
: filename_(filename), posdep_options_(posdep_options),
in_group_(in_group), is_in_sysroot_(is_in_sysroot), tokens_(tokens),
in_group_(in_group), is_in_sysroot_(is_in_sysroot),
command_line_(command_line), tokens_(tokens),
next_token_index_(0), inputs_(NULL)
{ }
@ -859,6 +861,12 @@ class Parser_closure
is_in_sysroot() const
{ return this->is_in_sysroot_; }
// Returns the Command_line structure passed in at constructor time.
// This value may be NULL. The caller may modify this, which modifies
// the passed-in Command_line object (not a copy).
Command_line* command_line()
{ return this->command_line_; }
// Whether we are at the end of the token list.
bool
at_eof() const
@ -897,6 +905,8 @@ class Parser_closure
bool in_group_;
// Whether the script was found in a sysrooted directory.
bool is_in_sysroot_;
// May be NULL if the user chooses not to pass one in.
Command_line* command_line_;
// The tokens to be returned by the lexer.
const Lex::Token_sequence* tokens_;
@ -927,6 +937,7 @@ read_input_script(Workqueue* workqueue, const General_options& options,
input_argument->file().options(),
input_group != NULL,
input_file->is_in_sysroot(),
NULL,
&lex.tokens());
if (yyparse(&closure) != 0)
@ -973,9 +984,8 @@ read_input_script(Workqueue* workqueue, const General_options& options,
bool
read_commandline_script(const char* filename, Command_line* cmdline)
{
// We don't need to use the real directory search path here:
// FILENAME was specified on the command line, and we don't want to
// search for it.
// TODO: if filename is a relative filename, search for it manually
// using "." + cmdline->options()->search_path() -- not dirsearch.
Dirsearch dirsearch;
Input_file_argument input_argument(filename, false, "",
@ -996,6 +1006,7 @@ read_commandline_script(const char* filename, Command_line* cmdline)
cmdline->position_dependent_options(),
false,
input_file.is_in_sysroot(),
cmdline,
&lex.tokens());
if (yyparse(&closure) != 0)
{
@ -1306,5 +1317,22 @@ extern "C" void
script_parse_option(void* closurev, const char* option)
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
printf("%s: Saw option %s\n", closure->filename(), option); //!!
// We treat the option as a single command-line option, even if
// it has internal whitespace.
if (closure->command_line() == NULL)
{
// There are some options that we could handle here--e.g.,
// -lLIBRARY. Should we bother?
gold_warning(_("%s: Ignoring command OPTION; OPTION is only valid"
" for scripts specified via -T"),
closure->filename());
}
else
{
bool past_a_double_dash_option = false;
char* mutable_option = strdup(option);
closure->command_line()->process_one_option(1, &mutable_option, 0,
&past_a_double_dash_option);
free(mutable_option);
}
}