mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
1231 lines
32 KiB
C
1231 lines
32 KiB
C
/* Simulator option handling.
|
|
Copyright (C) 1996-2024 Free Software Foundation, Inc.
|
|
Contributed by Cygnus Support.
|
|
|
|
This file is part of GDB, the GNU debugger.
|
|
|
|
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 of the License, 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. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
/* This must come before any other includes. */
|
|
#include "defs.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bfd.h"
|
|
#include "environ.h"
|
|
#include "hashtab.h"
|
|
#include "libiberty.h"
|
|
|
|
#include "sim-main.h"
|
|
#include "sim-options.h"
|
|
#include "sim-io.h"
|
|
#include "sim-assert.h"
|
|
#include "version.h"
|
|
|
|
/* Add a set of options to the simulator.
|
|
TABLE is an array of OPTIONS terminated by a NULL `opt.name' entry.
|
|
This is intended to be called by modules in their `install' handler. */
|
|
|
|
SIM_RC
|
|
sim_add_option_table (SIM_DESC sd, sim_cpu *cpu, const OPTION *table)
|
|
{
|
|
struct option_list *ol = ((struct option_list *)
|
|
xmalloc (sizeof (struct option_list)));
|
|
|
|
/* Note: The list is constructed in the reverse order we're called so
|
|
later calls will override earlier ones (in case that ever happens).
|
|
This is the intended behaviour. */
|
|
|
|
if (cpu)
|
|
{
|
|
ol->next = CPU_OPTIONS (cpu);
|
|
ol->options = table;
|
|
CPU_OPTIONS (cpu) = ol;
|
|
}
|
|
else
|
|
{
|
|
ol->next = STATE_OPTIONS (sd);
|
|
ol->options = table;
|
|
STATE_OPTIONS (sd) = ol;
|
|
}
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
/* Standard option table.
|
|
Modules may specify additional ones.
|
|
The caller of sim_parse_args may also specify additional options
|
|
by calling sim_add_option_table first. */
|
|
|
|
static DECLARE_OPTION_HANDLER (standard_option_handler);
|
|
|
|
/* FIXME: We shouldn't print in --help output options that aren't usable.
|
|
Some fine tuning will be necessary. One can either move less general
|
|
options to another table or use a HAVE_FOO macro to ifdef out unavailable
|
|
options. */
|
|
|
|
/* ??? One might want to conditionally compile out the entries that
|
|
aren't enabled. There's a distinction, however, between options a
|
|
simulator can't support and options that haven't been configured in.
|
|
Certainly options a simulator can't support shouldn't appear in the
|
|
output of --help. Whether the same thing applies to options that haven't
|
|
been configured in or not isn't something I can get worked up over.
|
|
[Note that conditionally compiling them out might simply involve moving
|
|
the option to another table.]
|
|
If you decide to conditionally compile them out as well, delete this
|
|
comment and add a comment saying that that is the rule. */
|
|
|
|
typedef enum {
|
|
OPTION_DEBUG_INSN = OPTION_START,
|
|
OPTION_DEBUG_FILE,
|
|
OPTION_DO_COMMAND,
|
|
OPTION_ARCHITECTURE,
|
|
OPTION_TARGET,
|
|
OPTION_TARGET_INFO,
|
|
OPTION_ARCHITECTURE_INFO,
|
|
OPTION_ENVIRONMENT,
|
|
OPTION_ALIGNMENT,
|
|
OPTION_VERBOSE,
|
|
OPTION_ENDIAN,
|
|
OPTION_DEBUG,
|
|
OPTION_HELP,
|
|
OPTION_VERSION,
|
|
OPTION_LOAD_LMA,
|
|
OPTION_LOAD_VMA,
|
|
OPTION_SYSROOT,
|
|
OPTION_ARGV0,
|
|
OPTION_ENV_SET,
|
|
OPTION_ENV_UNSET,
|
|
OPTION_ENV_CLEAR,
|
|
} STANDARD_OPTIONS;
|
|
|
|
static const OPTION standard_options[] =
|
|
{
|
|
{ {"verbose", no_argument, NULL, OPTION_VERBOSE},
|
|
'v', NULL, "Verbose output",
|
|
standard_option_handler, NULL },
|
|
|
|
{ {"endian", required_argument, NULL, OPTION_ENDIAN},
|
|
'E', "B|big|L|little", "Set endianness",
|
|
standard_option_handler, NULL },
|
|
|
|
/* This option isn't supported unless all choices are supported in keeping
|
|
with the goal of not printing in --help output things the simulator can't
|
|
do [as opposed to things that just haven't been configured in]. */
|
|
{ {"environment", required_argument, NULL, OPTION_ENVIRONMENT},
|
|
'\0', "user|virtual|operating", "Set running environment",
|
|
standard_option_handler },
|
|
|
|
{ {"alignment", required_argument, NULL, OPTION_ALIGNMENT},
|
|
'\0', "strict|nonstrict|forced", "Set memory access alignment",
|
|
standard_option_handler },
|
|
|
|
{ {"debug", no_argument, NULL, OPTION_DEBUG},
|
|
'D', NULL, "Print debugging messages",
|
|
standard_option_handler },
|
|
{ {"debug-insn", no_argument, NULL, OPTION_DEBUG_INSN},
|
|
'\0', NULL, "Print instruction debugging messages",
|
|
standard_option_handler },
|
|
{ {"debug-file", required_argument, NULL, OPTION_DEBUG_FILE},
|
|
'\0', "FILE NAME", "Specify debugging output file",
|
|
standard_option_handler },
|
|
|
|
{ {"do-command", required_argument, NULL, OPTION_DO_COMMAND},
|
|
'\0', "COMMAND", ""/*undocumented*/,
|
|
standard_option_handler },
|
|
|
|
{ {"help", no_argument, NULL, OPTION_HELP},
|
|
'h', NULL, "Print help information",
|
|
standard_option_handler },
|
|
{ {"version", no_argument, NULL, OPTION_VERSION},
|
|
'\0', NULL, "Print version information",
|
|
standard_option_handler },
|
|
|
|
{ {"architecture", required_argument, NULL, OPTION_ARCHITECTURE},
|
|
'\0', "MACHINE", "Specify the architecture to use",
|
|
standard_option_handler },
|
|
{ {"architecture-info", no_argument, NULL, OPTION_ARCHITECTURE_INFO},
|
|
'\0', NULL, "List supported architectures",
|
|
standard_option_handler },
|
|
{ {"info-architecture", no_argument, NULL, OPTION_ARCHITECTURE_INFO},
|
|
'\0', NULL, NULL,
|
|
standard_option_handler },
|
|
|
|
{ {"target", required_argument, NULL, OPTION_TARGET},
|
|
'\0', "BFDNAME", "Specify the object-code format for the object files",
|
|
standard_option_handler },
|
|
{ {"target-info", no_argument, NULL, OPTION_TARGET_INFO},
|
|
'\0', NULL, "List supported targets", standard_option_handler },
|
|
{ {"info-target", no_argument, NULL, OPTION_TARGET_INFO},
|
|
'\0', NULL, NULL, standard_option_handler },
|
|
|
|
{ {"load-lma", no_argument, NULL, OPTION_LOAD_LMA},
|
|
'\0', NULL,
|
|
"Use VMA or LMA addresses when loading image (default LMA)",
|
|
standard_option_handler, "load-{lma,vma}" },
|
|
{ {"load-vma", no_argument, NULL, OPTION_LOAD_VMA},
|
|
'\0', NULL, "", standard_option_handler, "" },
|
|
|
|
{ {"sysroot", required_argument, NULL, OPTION_SYSROOT},
|
|
'\0', "SYSROOT",
|
|
"Root for system calls with absolute file-names and cwd at start",
|
|
standard_option_handler, NULL },
|
|
|
|
{ {"argv0", required_argument, NULL, OPTION_ARGV0},
|
|
'\0', "ARGV0", "Set argv[0] to the specified string",
|
|
standard_option_handler, NULL },
|
|
|
|
{ {"env-set", required_argument, NULL, OPTION_ENV_SET},
|
|
'\0', "VAR=VAL", "Set the variable in the program's environment",
|
|
standard_option_handler, NULL },
|
|
{ {"env-unset", required_argument, NULL, OPTION_ENV_UNSET},
|
|
'\0', "VAR", "Unset the variable in the program's environment",
|
|
standard_option_handler, NULL },
|
|
{ {"env-clear", no_argument, NULL, OPTION_ENV_CLEAR},
|
|
'\0', NULL, "Clear the program's environment",
|
|
standard_option_handler, NULL },
|
|
|
|
{ {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
static SIM_RC
|
|
env_set (SIM_DESC sd, const char *arg)
|
|
{
|
|
int i, varlen;
|
|
char *eq;
|
|
char **envp;
|
|
|
|
if (STATE_PROG_ENVP (sd) == NULL)
|
|
STATE_PROG_ENVP (sd) = dupargv (environ);
|
|
|
|
eq = strchr (arg, '=');
|
|
if (eq == NULL)
|
|
{
|
|
sim_io_eprintf (sd, "invalid syntax when setting env var `%s'"
|
|
": missing value", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
/* Include the = in the comparison below. */
|
|
varlen = eq - arg + 1;
|
|
|
|
/* If we can find an existing variable, replace it. */
|
|
envp = STATE_PROG_ENVP (sd);
|
|
for (i = 0; envp[i]; ++i)
|
|
{
|
|
if (strncmp (envp[i], arg, varlen) == 0)
|
|
{
|
|
free (envp[i]);
|
|
envp[i] = xstrdup (arg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we didn't find the var, add it. */
|
|
if (envp[i] == NULL)
|
|
{
|
|
envp = xrealloc (envp, (i + 2) * sizeof (char *));
|
|
envp[i] = xstrdup (arg);
|
|
envp[i + 1] = NULL;
|
|
STATE_PROG_ENVP (sd) = envp;
|
|
}
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
static SIM_RC
|
|
standard_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt,
|
|
char *arg, int is_command)
|
|
{
|
|
int i,n;
|
|
|
|
switch ((STANDARD_OPTIONS) opt)
|
|
{
|
|
case OPTION_VERBOSE:
|
|
STATE_VERBOSE_P (sd) = 1;
|
|
break;
|
|
|
|
case OPTION_ENDIAN:
|
|
if (strcmp (arg, "big") == 0 || strcmp (arg, "B") == 0)
|
|
{
|
|
if (WITH_TARGET_BYTE_ORDER == BFD_ENDIAN_LITTLE)
|
|
{
|
|
sim_io_eprintf (sd, "Simulator compiled for little endian only.\n");
|
|
return SIM_RC_FAIL;
|
|
}
|
|
/* FIXME:wip: Need to set something in STATE_CONFIG. */
|
|
current_target_byte_order = BFD_ENDIAN_BIG;
|
|
}
|
|
else if (strcmp (arg, "little") == 0 || strcmp (arg, "L") == 0)
|
|
{
|
|
if (WITH_TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
|
|
{
|
|
sim_io_eprintf (sd, "Simulator compiled for big endian only.\n");
|
|
return SIM_RC_FAIL;
|
|
}
|
|
/* FIXME:wip: Need to set something in STATE_CONFIG. */
|
|
current_target_byte_order = BFD_ENDIAN_LITTLE;
|
|
}
|
|
else
|
|
{
|
|
sim_io_eprintf (sd, "Invalid endian specification `%s'\n", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
break;
|
|
|
|
case OPTION_ENVIRONMENT:
|
|
if (strcmp (arg, "user") == 0)
|
|
STATE_ENVIRONMENT (sd) = USER_ENVIRONMENT;
|
|
else if (strcmp (arg, "virtual") == 0)
|
|
STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT;
|
|
else if (strcmp (arg, "operating") == 0)
|
|
STATE_ENVIRONMENT (sd) = OPERATING_ENVIRONMENT;
|
|
else
|
|
{
|
|
sim_io_eprintf (sd, "Invalid environment specification `%s'\n", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
if (WITH_ENVIRONMENT != ALL_ENVIRONMENT
|
|
&& WITH_ENVIRONMENT != STATE_ENVIRONMENT (sd))
|
|
{
|
|
const char *type;
|
|
switch (WITH_ENVIRONMENT)
|
|
{
|
|
case USER_ENVIRONMENT: type = "user"; break;
|
|
case VIRTUAL_ENVIRONMENT: type = "virtual"; break;
|
|
case OPERATING_ENVIRONMENT: type = "operating"; break;
|
|
default: abort ();
|
|
}
|
|
sim_io_eprintf (sd, "Simulator compiled for the %s environment only.\n",
|
|
type);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
break;
|
|
|
|
case OPTION_ALIGNMENT:
|
|
if (strcmp (arg, "strict") == 0)
|
|
{
|
|
if (WITH_ALIGNMENT == 0 || WITH_ALIGNMENT == STRICT_ALIGNMENT)
|
|
{
|
|
current_alignment = STRICT_ALIGNMENT;
|
|
break;
|
|
}
|
|
}
|
|
else if (strcmp (arg, "nonstrict") == 0)
|
|
{
|
|
if (WITH_ALIGNMENT == 0 || WITH_ALIGNMENT == NONSTRICT_ALIGNMENT)
|
|
{
|
|
current_alignment = NONSTRICT_ALIGNMENT;
|
|
break;
|
|
}
|
|
}
|
|
else if (strcmp (arg, "forced") == 0)
|
|
{
|
|
if (WITH_ALIGNMENT == 0 || WITH_ALIGNMENT == FORCED_ALIGNMENT)
|
|
{
|
|
current_alignment = FORCED_ALIGNMENT;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sim_io_eprintf (sd, "Invalid alignment specification `%s'\n", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
switch (WITH_ALIGNMENT)
|
|
{
|
|
case STRICT_ALIGNMENT:
|
|
sim_io_eprintf (sd, "Simulator compiled for strict alignment only.\n");
|
|
break;
|
|
case NONSTRICT_ALIGNMENT:
|
|
sim_io_eprintf (sd, "Simulator compiled for nonstrict alignment only.\n");
|
|
break;
|
|
case FORCED_ALIGNMENT:
|
|
sim_io_eprintf (sd, "Simulator compiled for forced alignment only.\n");
|
|
break;
|
|
default: abort ();
|
|
}
|
|
return SIM_RC_FAIL;
|
|
|
|
case OPTION_DEBUG:
|
|
if (! WITH_DEBUG)
|
|
sim_io_eprintf (sd, "Debugging not compiled in, `-D' ignored\n");
|
|
else
|
|
{
|
|
for (n = 0; n < MAX_NR_PROCESSORS; ++n)
|
|
for (i = 0; i < MAX_DEBUG_VALUES; ++i)
|
|
CPU_DEBUG_FLAGS (STATE_CPU (sd, n))[i] = 1;
|
|
}
|
|
break;
|
|
|
|
case OPTION_DEBUG_INSN :
|
|
if (! WITH_DEBUG)
|
|
sim_io_eprintf (sd, "Debugging not compiled in, `--debug-insn' ignored\n");
|
|
else
|
|
{
|
|
for (n = 0; n < MAX_NR_PROCESSORS; ++n)
|
|
CPU_DEBUG_FLAGS (STATE_CPU (sd, n))[DEBUG_INSN_IDX] = 1;
|
|
}
|
|
break;
|
|
|
|
case OPTION_DEBUG_FILE :
|
|
if (! WITH_DEBUG)
|
|
sim_io_eprintf (sd, "Debugging not compiled in, `--debug-file' ignored\n");
|
|
else
|
|
{
|
|
FILE *f = fopen (arg, "w");
|
|
|
|
if (f == NULL)
|
|
{
|
|
sim_io_eprintf (sd, "Unable to open debug output file `%s'\n", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
for (n = 0; n < MAX_NR_PROCESSORS; ++n)
|
|
CPU_DEBUG_FILE (STATE_CPU (sd, n)) = f;
|
|
}
|
|
break;
|
|
|
|
case OPTION_DO_COMMAND:
|
|
sim_do_command (sd, arg);
|
|
break;
|
|
|
|
case OPTION_ARCHITECTURE:
|
|
{
|
|
const struct bfd_arch_info *ap = bfd_scan_arch (arg);
|
|
if (ap == NULL)
|
|
{
|
|
sim_io_eprintf (sd, "Architecture `%s' unknown\n", arg);
|
|
return SIM_RC_FAIL;
|
|
}
|
|
STATE_ARCHITECTURE (sd) = ap;
|
|
break;
|
|
}
|
|
|
|
case OPTION_ARCHITECTURE_INFO:
|
|
{
|
|
const char **list = bfd_arch_list ();
|
|
const char **lp;
|
|
if (list == NULL)
|
|
abort ();
|
|
sim_io_printf (sd, "Possible architectures:");
|
|
for (lp = list; *lp != NULL; lp++)
|
|
sim_io_printf (sd, " %s", *lp);
|
|
sim_io_printf (sd, "\n");
|
|
free (list);
|
|
break;
|
|
}
|
|
|
|
case OPTION_TARGET:
|
|
{
|
|
STATE_TARGET (sd) = xstrdup (arg);
|
|
break;
|
|
}
|
|
|
|
case OPTION_TARGET_INFO:
|
|
{
|
|
const char **list = bfd_target_list ();
|
|
const char **lp;
|
|
if (list == NULL)
|
|
abort ();
|
|
sim_io_printf (sd, "Possible targets:");
|
|
for (lp = list; *lp != NULL; lp++)
|
|
sim_io_printf (sd, " %s", *lp);
|
|
sim_io_printf (sd, "\n");
|
|
free (list);
|
|
break;
|
|
}
|
|
|
|
case OPTION_LOAD_LMA:
|
|
{
|
|
STATE_LOAD_AT_LMA_P (sd) = 1;
|
|
break;
|
|
}
|
|
|
|
case OPTION_LOAD_VMA:
|
|
{
|
|
STATE_LOAD_AT_LMA_P (sd) = 0;
|
|
break;
|
|
}
|
|
|
|
case OPTION_HELP:
|
|
sim_print_help (sd, is_command);
|
|
if (STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
|
exit (0);
|
|
/* FIXME: 'twould be nice to do something similar if gdb. */
|
|
break;
|
|
|
|
case OPTION_VERSION:
|
|
sim_print_version (sd, is_command);
|
|
if (STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
|
exit (0);
|
|
break;
|
|
|
|
case OPTION_SYSROOT:
|
|
/* Don't leak memory in the odd event that there's lots of
|
|
--sysroot=... options. We treat "" specially since this
|
|
is the statically initialized value and cannot free it. */
|
|
if (simulator_sysroot[0] != '\0')
|
|
free (simulator_sysroot);
|
|
if (arg[0] != '\0')
|
|
simulator_sysroot = xstrdup (arg);
|
|
else
|
|
simulator_sysroot = "";
|
|
break;
|
|
|
|
case OPTION_ARGV0:
|
|
free (STATE_PROG_ARGV0 (sd));
|
|
STATE_PROG_ARGV0 (sd) = xstrdup (arg);
|
|
break;
|
|
|
|
case OPTION_ENV_SET:
|
|
return env_set (sd, arg);
|
|
|
|
case OPTION_ENV_UNSET:
|
|
{
|
|
int varlen;
|
|
char **envp;
|
|
|
|
if (STATE_PROG_ENVP (sd) == NULL)
|
|
STATE_PROG_ENVP (sd) = dupargv (environ);
|
|
|
|
varlen = strlen (arg);
|
|
|
|
/* If we can find an existing variable, replace it. */
|
|
envp = STATE_PROG_ENVP (sd);
|
|
for (i = 0; envp[i]; ++i)
|
|
{
|
|
char *env = envp[i];
|
|
|
|
if (strncmp (env, arg, varlen) == 0
|
|
&& (env[varlen] == '\0' || env[varlen] == '='))
|
|
{
|
|
free (envp[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we clear the var, shift the array down. */
|
|
for (; envp[i]; ++i)
|
|
envp[i] = envp[i + 1];
|
|
|
|
break;
|
|
}
|
|
|
|
case OPTION_ENV_CLEAR:
|
|
freeargv (STATE_PROG_ENVP (sd));
|
|
STATE_PROG_ENVP (sd) = xmalloc (sizeof (char *));
|
|
STATE_PROG_ENVP (sd)[0] = NULL;
|
|
break;
|
|
}
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
/* Add the standard option list to the simulator. */
|
|
|
|
SIM_RC
|
|
standard_install (SIM_DESC sd)
|
|
{
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
if (sim_add_option_table (sd, NULL, standard_options) != SIM_RC_OK)
|
|
return SIM_RC_FAIL;
|
|
STATE_LOAD_AT_LMA_P (sd) = 1;
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
/* Return non-zero if arg is a duplicate argument.
|
|
If ARG is NULL, initialize. */
|
|
|
|
static int
|
|
dup_arg_p (const char *arg)
|
|
{
|
|
static htab_t arg_table = NULL;
|
|
void **slot;
|
|
|
|
if (arg == NULL)
|
|
{
|
|
if (arg_table == NULL)
|
|
arg_table = htab_create_alloc (10, htab_hash_string,
|
|
htab_eq_string, NULL,
|
|
xcalloc, free);
|
|
htab_empty (arg_table);
|
|
return 0;
|
|
}
|
|
|
|
slot = htab_find_slot (arg_table, arg, INSERT);
|
|
if (*slot != NULL)
|
|
return 1;
|
|
*slot = (void *) arg;
|
|
return 0;
|
|
}
|
|
|
|
/* Called by sim_open to parse the arguments. */
|
|
|
|
SIM_RC
|
|
sim_parse_args (SIM_DESC sd, char * const *argv)
|
|
{
|
|
int c, i, argc, num_opts, save_opterr;
|
|
char *p, *short_options;
|
|
/* The `val' option struct entry is dynamically assigned for options that
|
|
only come in the long form. ORIG_VAL is used to get the original value
|
|
back. */
|
|
int *orig_val;
|
|
struct option *lp, *long_options;
|
|
const struct option_list *ol;
|
|
const OPTION *opt;
|
|
OPTION_HANDLER **handlers;
|
|
sim_cpu **opt_cpu;
|
|
SIM_RC result = SIM_RC_OK;
|
|
|
|
/* Count the number of arguments. */
|
|
argc = countargv (argv);
|
|
|
|
/* Count the number of options. */
|
|
num_opts = 0;
|
|
for (ol = STATE_OPTIONS (sd); ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
++num_opts;
|
|
for (i = 0; i < MAX_NR_PROCESSORS; ++i)
|
|
for (ol = CPU_OPTIONS (STATE_CPU (sd, i)); ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
++num_opts;
|
|
|
|
/* Initialize duplicate argument checker. */
|
|
(void) dup_arg_p (NULL);
|
|
|
|
/* Build the option table for getopt. */
|
|
|
|
long_options = NZALLOC (struct option, num_opts + 1);
|
|
lp = long_options;
|
|
short_options = NZALLOC (char, num_opts * 3 + 1);
|
|
p = short_options;
|
|
handlers = NZALLOC (OPTION_HANDLER *, OPTION_START + num_opts);
|
|
orig_val = NZALLOC (int, OPTION_START + num_opts);
|
|
opt_cpu = NZALLOC (sim_cpu *, OPTION_START + num_opts);
|
|
|
|
/* Set '+' as first char so argument permutation isn't done. This
|
|
is done to stop getopt_long returning options that appear after
|
|
the target program. Such options should be passed unchanged into
|
|
the program image. */
|
|
*p++ = '+';
|
|
|
|
for (i = OPTION_START, ol = STATE_OPTIONS (sd); ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
{
|
|
if (dup_arg_p (opt->opt.name))
|
|
continue;
|
|
if (opt->shortopt != 0)
|
|
{
|
|
*p++ = opt->shortopt;
|
|
if (opt->opt.has_arg == required_argument)
|
|
*p++ = ':';
|
|
else if (opt->opt.has_arg == optional_argument)
|
|
{ *p++ = ':'; *p++ = ':'; }
|
|
handlers[(unsigned char) opt->shortopt] = opt->handler;
|
|
if (opt->opt.val != 0)
|
|
orig_val[(unsigned char) opt->shortopt] = opt->opt.val;
|
|
else
|
|
orig_val[(unsigned char) opt->shortopt] = opt->shortopt;
|
|
}
|
|
if (opt->opt.name != NULL)
|
|
{
|
|
*lp = opt->opt;
|
|
/* Dynamically assign `val' numbers for long options. */
|
|
lp->val = i++;
|
|
handlers[lp->val] = opt->handler;
|
|
orig_val[lp->val] = opt->opt.val;
|
|
opt_cpu[lp->val] = NULL;
|
|
++lp;
|
|
}
|
|
}
|
|
|
|
for (c = 0; c < MAX_NR_PROCESSORS; ++c)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, c);
|
|
for (ol = CPU_OPTIONS (cpu); ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
{
|
|
#if 0 /* Each option is prepended with --<cpuname>- so this greatly cuts down
|
|
on the need for dup_arg_p checking. Maybe in the future it'll be
|
|
needed so this is just commented out, and not deleted. */
|
|
if (dup_arg_p (opt->opt.name))
|
|
continue;
|
|
#endif
|
|
/* Don't allow short versions of cpu specific options for now. */
|
|
if (opt->shortopt != 0)
|
|
{
|
|
sim_io_eprintf (sd, "internal error, short cpu specific option");
|
|
result = SIM_RC_FAIL;
|
|
break;
|
|
}
|
|
if (opt->opt.name != NULL)
|
|
{
|
|
char *name;
|
|
*lp = opt->opt;
|
|
/* Prepend --<cpuname>- to the option. */
|
|
if (asprintf (&name, "%s-%s", CPU_NAME (cpu), lp->name) < 0)
|
|
{
|
|
sim_io_eprintf (sd, "internal error, out of memory");
|
|
result = SIM_RC_FAIL;
|
|
break;
|
|
}
|
|
lp->name = name;
|
|
/* Dynamically assign `val' numbers for long options. */
|
|
lp->val = i++;
|
|
handlers[lp->val] = opt->handler;
|
|
orig_val[lp->val] = opt->opt.val;
|
|
opt_cpu[lp->val] = cpu;
|
|
++lp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Terminate the short and long option lists. */
|
|
*p = 0;
|
|
lp->name = NULL;
|
|
|
|
/* Ensure getopt is initialized. */
|
|
optind = 0;
|
|
|
|
/* Do not lot getopt throw errors for us. But don't mess with the state for
|
|
any callers higher up by saving/restoring it. */
|
|
save_opterr = opterr;
|
|
opterr = 0;
|
|
|
|
while (1)
|
|
{
|
|
int longind, optc;
|
|
|
|
optc = getopt_long (argc, argv, short_options, long_options, &longind);
|
|
if (optc == -1)
|
|
{
|
|
if (STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
|
{
|
|
char **new_argv;
|
|
|
|
free (STATE_PROG_FILE (sd));
|
|
STATE_PROG_FILE (sd) = NULL;
|
|
|
|
/* Handle any inline variables if -- wasn't used. */
|
|
if (argv[optind] != NULL && optind > 0
|
|
&& strcmp (argv[optind - 1], "--") != 0)
|
|
{
|
|
while (1)
|
|
{
|
|
const char *arg = argv[optind];
|
|
|
|
if (strchr (arg, '=') == NULL)
|
|
break;
|
|
|
|
env_set (sd, arg);
|
|
++optind;
|
|
}
|
|
}
|
|
|
|
new_argv = dupargv (argv + optind);
|
|
freeargv (STATE_PROG_ARGV (sd));
|
|
STATE_PROG_ARGV (sd) = new_argv;
|
|
|
|
/* Skip steps when argc == 0. */
|
|
if (argv[optind] != NULL)
|
|
{
|
|
STATE_PROG_FILE (sd) = xstrdup (argv[optind]);
|
|
|
|
if (STATE_PROG_ARGV0 (sd) != NULL)
|
|
{
|
|
free (new_argv[0]);
|
|
new_argv[0] = xstrdup (STATE_PROG_ARGV0 (sd));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (optc == '?')
|
|
{
|
|
/* If getopt rejects a short option, optopt is set to the bad char.
|
|
If it rejects a long option, we have to look at optind. In the
|
|
short option case, argv could be multiple short options. */
|
|
const char *badopt;
|
|
char optbuf[3];
|
|
|
|
if (optopt)
|
|
{
|
|
sprintf (optbuf, "-%c", optopt);
|
|
badopt = optbuf;
|
|
}
|
|
else
|
|
badopt = argv[optind - 1];
|
|
|
|
sim_io_eprintf (sd,
|
|
"%s: unrecognized option '%s'\n"
|
|
"Use --help for a complete list of options.\n",
|
|
STATE_MY_NAME (sd), badopt);
|
|
|
|
result = SIM_RC_FAIL;
|
|
break;
|
|
}
|
|
|
|
if ((*handlers[optc]) (sd, opt_cpu[optc], orig_val[optc], optarg, 0/*!is_command*/) == SIM_RC_FAIL)
|
|
{
|
|
result = SIM_RC_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
opterr = save_opterr;
|
|
|
|
free (long_options);
|
|
free (short_options);
|
|
free (handlers);
|
|
free (opt_cpu);
|
|
free (orig_val);
|
|
return result;
|
|
}
|
|
|
|
/* Utility of sim_print_help to print a list of option tables. */
|
|
|
|
static void
|
|
print_help (SIM_DESC sd, sim_cpu *cpu, const struct option_list *ol, int is_command)
|
|
{
|
|
const OPTION *opt;
|
|
|
|
for ( ; ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
{
|
|
const int indent = 30;
|
|
int comma, len;
|
|
const OPTION *o;
|
|
|
|
if (dup_arg_p (opt->opt.name))
|
|
continue;
|
|
|
|
if (opt->doc == NULL)
|
|
continue;
|
|
|
|
if (opt->doc_name != NULL && opt->doc_name [0] == '\0')
|
|
continue;
|
|
|
|
sim_io_printf (sd, " ");
|
|
|
|
comma = 0;
|
|
len = 2;
|
|
|
|
/* list any short options (aliases) for the current OPT */
|
|
if (!is_command)
|
|
{
|
|
o = opt;
|
|
do
|
|
{
|
|
if (o->shortopt != '\0')
|
|
{
|
|
sim_io_printf (sd, "%s-%c", comma ? ", " : "", o->shortopt);
|
|
len += (comma ? 2 : 0) + 2;
|
|
if (o->arg != NULL)
|
|
{
|
|
if (o->opt.has_arg == optional_argument)
|
|
{
|
|
sim_io_printf (sd, "[%s]", o->arg);
|
|
len += 1 + strlen (o->arg) + 1;
|
|
}
|
|
else
|
|
{
|
|
sim_io_printf (sd, " %s", o->arg);
|
|
len += 1 + strlen (o->arg);
|
|
}
|
|
}
|
|
comma = 1;
|
|
}
|
|
++o;
|
|
}
|
|
while (OPTION_VALID_P (o) && o->doc == NULL);
|
|
}
|
|
|
|
/* list any long options (aliases) for the current OPT */
|
|
o = opt;
|
|
do
|
|
{
|
|
const char *name;
|
|
const char *cpu_prefix = cpu ? CPU_NAME (cpu) : NULL;
|
|
if (o->doc_name != NULL)
|
|
name = o->doc_name;
|
|
else
|
|
name = o->opt.name;
|
|
if (name != NULL)
|
|
{
|
|
sim_io_printf (sd, "%s%s%s%s%s",
|
|
comma ? ", " : "",
|
|
is_command ? "" : "--",
|
|
cpu ? cpu_prefix : "",
|
|
cpu ? "-" : "",
|
|
name);
|
|
len += ((comma ? 2 : 0)
|
|
+ (is_command ? 0 : 2)
|
|
+ strlen (name));
|
|
if (o->arg != NULL)
|
|
{
|
|
if (o->opt.has_arg == optional_argument)
|
|
{
|
|
sim_io_printf (sd, "[=%s]", o->arg);
|
|
len += 2 + strlen (o->arg) + 1;
|
|
}
|
|
else
|
|
{
|
|
sim_io_printf (sd, " %s", o->arg);
|
|
len += 1 + strlen (o->arg);
|
|
}
|
|
}
|
|
comma = 1;
|
|
}
|
|
++o;
|
|
}
|
|
while (OPTION_VALID_P (o) && o->doc == NULL);
|
|
|
|
if (len >= indent)
|
|
{
|
|
sim_io_printf (sd, "\n%*s", indent, "");
|
|
}
|
|
else
|
|
sim_io_printf (sd, "%*s", indent - len, "");
|
|
|
|
/* print the description, word wrap long lines */
|
|
{
|
|
const char *chp = opt->doc;
|
|
unsigned doc_width = 80 - indent;
|
|
while (strlen (chp) >= doc_width) /* some slack */
|
|
{
|
|
const char *end = chp + doc_width - 1;
|
|
while (end > chp && !isspace (*end))
|
|
end --;
|
|
if (end == chp)
|
|
end = chp + doc_width - 1;
|
|
/* The cast should be ok - its distances between to
|
|
points in a string. */
|
|
sim_io_printf (sd, "%.*s\n%*s", (int) (end - chp), chp, indent,
|
|
"");
|
|
chp = end;
|
|
while (isspace (*chp) && *chp != '\0')
|
|
chp++;
|
|
}
|
|
sim_io_printf (sd, "%s\n", chp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Print help messages for the options. */
|
|
|
|
void
|
|
sim_print_help (SIM_DESC sd, int is_command)
|
|
{
|
|
if (STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
|
sim_io_printf (sd,
|
|
"Usage: %s [options] [VAR=VAL|--] program [program args]\n",
|
|
STATE_MY_NAME (sd));
|
|
|
|
/* Initialize duplicate argument checker. */
|
|
(void) dup_arg_p (NULL);
|
|
|
|
if (STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
|
sim_io_printf (sd, "Options:\n");
|
|
else
|
|
sim_io_printf (sd, "Commands:\n");
|
|
|
|
print_help (sd, NULL, STATE_OPTIONS (sd), is_command);
|
|
sim_io_printf (sd, "\n");
|
|
|
|
/* Print cpu-specific options. */
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_NR_PROCESSORS; ++i)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, i);
|
|
if (CPU_OPTIONS (cpu) == NULL)
|
|
continue;
|
|
sim_io_printf (sd, "CPU %s specific options:\n", CPU_NAME (cpu));
|
|
print_help (sd, cpu, CPU_OPTIONS (cpu), is_command);
|
|
sim_io_printf (sd, "\n");
|
|
}
|
|
}
|
|
|
|
sim_io_printf (sd, "Note: Depending on the simulator configuration some %ss\n",
|
|
STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE ? "option" : "command");
|
|
sim_io_printf (sd, " may not be applicable\n");
|
|
|
|
if (STATE_OPEN_KIND (sd) == SIM_OPEN_STANDALONE)
|
|
{
|
|
sim_io_printf (sd, "\n");
|
|
sim_io_printf (sd,
|
|
"VAR=VAL Environment variables to set. "
|
|
"Ignored if -- is used.\n");
|
|
sim_io_printf (sd, "program args Arguments to pass to simulated program.\n");
|
|
sim_io_printf (sd, " Note: Very few simulators support this.\n");
|
|
}
|
|
}
|
|
|
|
/* Print version information. */
|
|
|
|
void
|
|
sim_print_version (SIM_DESC sd, int is_command)
|
|
{
|
|
sim_io_printf (sd, "GNU simulator %s%s\n", PKGVERSION, version);
|
|
|
|
sim_io_printf (sd, "Copyright (C) 2024 Free Software Foundation, Inc.\n");
|
|
|
|
/* Following the copyright is a brief statement that the program is
|
|
free software, that users are free to copy and change it on
|
|
certain conditions, that it is covered by the GNU GPL, and that
|
|
there is no warranty. */
|
|
|
|
sim_io_printf (sd, "\
|
|
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>\
|
|
\nThis is free software: you are free to change and redistribute it.\n\
|
|
There is NO WARRANTY, to the extent permitted by law.\n");
|
|
|
|
if (!is_command)
|
|
return;
|
|
|
|
sim_io_printf (sd, "This SIM was configured as:\n");
|
|
sim_config_print (sd);
|
|
|
|
if (REPORT_BUGS_TO[0])
|
|
{
|
|
sim_io_printf (sd, "For bug reporting instructions, please see:\n\
|
|
%s.\n",
|
|
REPORT_BUGS_TO);
|
|
}
|
|
sim_io_printf (sd, "Find the SIM homepage & other documentation resources \
|
|
online at:\n <https://sourceware.org/gdb/wiki/Sim/>.\n");
|
|
}
|
|
|
|
/* Utility of sim_args_command to find the closest match for a command.
|
|
Commands that have "-" in them can be specified as separate words.
|
|
e.g. sim memory-region 0x800000,0x4000
|
|
or sim memory region 0x800000,0x4000
|
|
If CPU is non-null, use its option table list, otherwise use the main one.
|
|
*PARGI is where to start looking in ARGV. It is updated to point past
|
|
the found option. */
|
|
|
|
static const OPTION *
|
|
find_match (SIM_DESC sd, sim_cpu *cpu, char *argv[], int *pargi)
|
|
{
|
|
const struct option_list *ol;
|
|
const OPTION *opt;
|
|
/* most recent option match */
|
|
const OPTION *matching_opt = NULL;
|
|
int matching_argi = -1;
|
|
|
|
if (cpu)
|
|
ol = CPU_OPTIONS (cpu);
|
|
else
|
|
ol = STATE_OPTIONS (sd);
|
|
|
|
/* Skip passed elements specified by *PARGI. */
|
|
argv += *pargi;
|
|
|
|
for ( ; ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
{
|
|
int argi = 0;
|
|
const char *name = opt->opt.name;
|
|
if (name == NULL)
|
|
continue;
|
|
while (argv [argi] != NULL
|
|
&& strncmp (name, argv [argi], strlen (argv [argi])) == 0)
|
|
{
|
|
name = &name [strlen (argv[argi])];
|
|
if (name [0] == '-')
|
|
{
|
|
/* leading match ...<a-b-c>-d-e-f - continue search */
|
|
name ++; /* skip `-' */
|
|
argi ++;
|
|
continue;
|
|
}
|
|
else if (name [0] == '\0')
|
|
{
|
|
/* exact match ...<a-b-c-d-e-f> - better than before? */
|
|
if (argi > matching_argi)
|
|
{
|
|
matching_argi = argi;
|
|
matching_opt = opt;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pargi = matching_argi;
|
|
return matching_opt;
|
|
}
|
|
|
|
static char **
|
|
complete_option_list (char **ret, size_t *cnt, const struct option_list *ol,
|
|
const char *text, const char *word)
|
|
{
|
|
const OPTION *opt = NULL;
|
|
size_t len = strlen (word);
|
|
|
|
for ( ; ol != NULL; ol = ol->next)
|
|
for (opt = ol->options; OPTION_VALID_P (opt); ++opt)
|
|
{
|
|
const char *name = opt->opt.name;
|
|
|
|
/* A long option to match against? */
|
|
if (!name)
|
|
continue;
|
|
|
|
/* Does this option actually match? */
|
|
if (strncmp (name, word, len))
|
|
continue;
|
|
|
|
ret = xrealloc (ret, ++*cnt * sizeof (ret[0]));
|
|
ret[*cnt - 2] = xstrdup (name);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* All leading text is stored in @text, while the current word being
|
|
completed is stored in @word. Trailing text of @word is not. */
|
|
|
|
char **
|
|
sim_complete_command (SIM_DESC sd, const char *text, const char *word)
|
|
{
|
|
char **ret = NULL;
|
|
size_t cnt = 1;
|
|
sim_cpu *cpu;
|
|
|
|
/* Only complete first word for now. */
|
|
if (text != word)
|
|
return ret;
|
|
|
|
cpu = STATE_CPU (sd, 0);
|
|
if (cpu)
|
|
ret = complete_option_list (ret, &cnt, CPU_OPTIONS (cpu), text, word);
|
|
ret = complete_option_list (ret, &cnt, STATE_OPTIONS (sd), text, word);
|
|
|
|
if (ret)
|
|
ret[cnt - 1] = NULL;
|
|
return ret;
|
|
}
|
|
|
|
SIM_RC
|
|
sim_args_command (SIM_DESC sd, const char *cmd)
|
|
{
|
|
/* something to do? */
|
|
if (cmd == NULL)
|
|
return SIM_RC_OK; /* FIXME - perhaps help would be better */
|
|
|
|
if (cmd [0] == '-')
|
|
{
|
|
/* user specified -<opt> ... form? */
|
|
char **argv = buildargv (cmd);
|
|
SIM_RC rc = sim_parse_args (sd, argv);
|
|
freeargv (argv);
|
|
return rc;
|
|
}
|
|
else
|
|
{
|
|
char **argv = buildargv (cmd);
|
|
const OPTION *matching_opt = NULL;
|
|
int matching_argi;
|
|
sim_cpu *cpu;
|
|
|
|
if (argv [0] == NULL)
|
|
{
|
|
freeargv (argv);
|
|
return SIM_RC_OK; /* FIXME - perhaps help would be better */
|
|
}
|
|
|
|
/* First check for a cpu selector. */
|
|
{
|
|
char *cpu_name = xstrdup (argv[0]);
|
|
char *hyphen = strchr (cpu_name, '-');
|
|
if (hyphen)
|
|
*hyphen = 0;
|
|
cpu = sim_cpu_lookup (sd, cpu_name);
|
|
if (cpu)
|
|
{
|
|
/* If <cpuname>-<command>, point argv[0] at <command>. */
|
|
if (hyphen)
|
|
{
|
|
matching_argi = 0;
|
|
argv[0] += hyphen - cpu_name + 1;
|
|
}
|
|
else
|
|
matching_argi = 1;
|
|
matching_opt = find_match (sd, cpu, argv, &matching_argi);
|
|
/* If hyphen found restore argv[0]. */
|
|
if (hyphen)
|
|
argv[0] -= hyphen - cpu_name + 1;
|
|
}
|
|
free (cpu_name);
|
|
}
|
|
|
|
/* If that failed, try the main table. */
|
|
if (matching_opt == NULL)
|
|
{
|
|
matching_argi = 0;
|
|
matching_opt = find_match (sd, NULL, argv, &matching_argi);
|
|
}
|
|
|
|
if (matching_opt != NULL)
|
|
{
|
|
switch (matching_opt->opt.has_arg)
|
|
{
|
|
case no_argument:
|
|
if (argv [matching_argi + 1] == NULL)
|
|
matching_opt->handler (sd, cpu, matching_opt->opt.val,
|
|
NULL, 1/*is_command*/);
|
|
else
|
|
sim_io_eprintf (sd, "Command `%s' takes no arguments\n",
|
|
matching_opt->opt.name);
|
|
break;
|
|
case optional_argument:
|
|
if (argv [matching_argi + 1] == NULL)
|
|
matching_opt->handler (sd, cpu, matching_opt->opt.val,
|
|
NULL, 1/*is_command*/);
|
|
else if (argv [matching_argi + 2] == NULL)
|
|
matching_opt->handler (sd, cpu, matching_opt->opt.val,
|
|
argv [matching_argi + 1], 1/*is_command*/);
|
|
else
|
|
sim_io_eprintf (sd, "Command `%s' requires no more than one argument\n",
|
|
matching_opt->opt.name);
|
|
break;
|
|
case required_argument:
|
|
if (argv [matching_argi + 1] == NULL)
|
|
sim_io_eprintf (sd, "Command `%s' requires an argument\n",
|
|
matching_opt->opt.name);
|
|
else if (argv [matching_argi + 2] == NULL)
|
|
matching_opt->handler (sd, cpu, matching_opt->opt.val,
|
|
argv [matching_argi + 1], 1/*is_command*/);
|
|
else
|
|
sim_io_eprintf (sd, "Command `%s' requires only one argument\n",
|
|
matching_opt->opt.name);
|
|
}
|
|
freeargv (argv);
|
|
return SIM_RC_OK;
|
|
}
|
|
|
|
freeargv (argv);
|
|
}
|
|
|
|
/* didn't find anything that remotly matched */
|
|
return SIM_RC_FAIL;
|
|
}
|