driverdriver.c [plain text]
/* APPLE LOCAL file driver driver */
/* Darwin driver program that handles -arch commands and invokes
appropriate compiler driver.
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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 2, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <mach-o/arch.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <regex.h>
#include "libiberty.h"
#include "filenames.h"
#include "stdbool.h"
/* Hack!.
Pay the price for including darwin.h. */
typedef int tree;
typedef int rtx;
#define GTY(x) /* nothing */
#define USED_FOR_TARGET 1
/* Include darwin.h for SWITCH_TAKES_ARG and
WORD_SWIATCH_TAKES_ARG. */
#include "darwin.h"
/* Include gcc.h for DEFAULT_SWITCH_TAKES_ARG and
DEFAULT_WORD_SWIATCH_TAKES_ARG. */
#include "gcc.h"
/* This program name. */
const char *progname;
/* driver prefix. */
const char *driver_exec_prefix;
/* driver prefix length. */
int prefix_len;
/* current working directory. */
char *curr_dir;
/* Use if -o flag is absent. */
const char *final_output = "a.out";
/* Variabless to track presence and/or absence of important command
line options. */
int compile_only_request = 0;
int asm_output_request = 0;
int dash_capital_m_seen = 0;
int preprocessed_output_request = 0;
int ima_is_used = 0;
int dash_dynamiclib_seen = 0;
int verbose_flag = 0;
int save_temps_seen = 0;
int dash_m32_seen = 0;
int dash_m64_seen = 0;
/* Support at the max 10 arch. at a time. This is historical limit. */
#define MAX_ARCHES 10
/* Name of user supplied architectures. */
const char *arches[MAX_ARCHES];
/* -arch counter. */
static int num_arches;
/* Input filenames. */
struct input_filename
{
const char *name;
int index;
struct input_filename *next;
};
struct input_filename *in_files;
struct input_filename *last_infile;
static int num_infiles;
/* User specified output file name. */
const char *output_filename = NULL;
/* Output file names used for arch specific driver invocation. These
are input file names for 'lipo'. */
const char **out_files;
static int num_outfiles;
/* Architecture names used by config.guess does not match the names
used by NXGet... Use this hand coded mapping to connect them. */
struct arch_config_guess_map
{
const char *arch_name;
const char *config_string;
};
struct arch_config_guess_map arch_config_map [] =
{
{"i386", "i686"},
{"ppc", "powerpc"},
{"ppc64", "powerpc"},
{"x86_64", "i686"},
/* Note: It's required that all supported ARM architectures be listed
* here explicitly */
{"arm", "arm"},
{"armv4t", "arm"},
{"armv5", "arm"},
{"xscale", "arm"},
{"armv6", "arm"},
{"armv7", "arm"},
{NULL, NULL}
};
/* List of interpreted command line flags. Supply this to gcc driver. */
const char **new_argv;
int new_argc;
/* For each of the options in new_argv, specifies an architecture to
which the option applies (or if NULL, the option applies to all). */
const char **arch_conditional;
/* Argument list for 'lipo'. */
const char **lipo_argv;
/* Info about the sub process. Need one subprocess for each arch plus
additional one for 'lipo'. */
struct command
{
const char *prog;
const char **argv;
int pid;
} commands[MAX_ARCHES+1];
/* total number of argc. */
static int total_argc;
static int greatest_status = 0;
static int signal_count = 0;
#ifndef SWITCH_TAKES_ARG
#define SWITCH_TAKES_ARG(CHAR) DEFAULT_SWITCH_TAKES_ARG(CHAR)
#endif
#ifndef WORD_SWITCH_TAKES_ARG
#define WORD_SWITCH_TAKES_ARG(STR) DEFAULT_WORD_SWITCH_TAKES_ARG (STR)
#endif
/* Local function prototypes. */
static const char * get_arch_name (const char *);
static char * get_driver_name (const char *);
static void delete_out_files (void);
static char * strip_path_and_suffix (const char *, const char *);
static void initialize (void);
static void final_cleanup (void);
static int do_wait (int, const char *);
static void do_lipo (int, const char *);
static void do_compile (const char **, int);
static void do_compile_separately (void);
static void do_lipo_separately (void);
static int filter_args_for_arch (const char **, int, const char **,
const char *);
static int add_arch_options (int, const char **, int);
static int remove_arch_options (const char**, int);
static void add_arch (const char *);
static const char *resolve_symlink (const char *, char *, int, int);
static const char *resolve_path_to_executable (const char *filename);
static int get_prog_name_len (const char *prog);
/* Find arch name for the given input string. If input name is NULL then local
arch name is used. */
static const char *
get_arch_name (const char *name)
{
NXArchInfo * a_info;
const NXArchInfo * all_info;
cpu_type_t cputype;
struct arch_config_guess_map *map;
const char *aname;
if (name) {
/* Find config name based on arch name. */
aname = NULL;
map = arch_config_map;
while (map->arch_name) {
if (!strcmp (map->arch_name, name))
return name;
else map++;
}
a_info = (NXArchInfo *) NXGetArchInfoFromName (name);
/* radr://7148788 emit diagnostic for ARM architectures not explicitly
* handled by the driver. */
if (a_info && a_info->cputype == CPU_TYPE_ARM)
a_info = NULL;
} else {
a_info = (NXArchInfo *) NXGetLocalArchInfo();
if (a_info) {
if (dash_m32_seen) {
/* If -m32 is seen then do not change cpu type. */
} else if (dash_m64_seen) {
/* If -m64 is seen then enable CPU_ARCH_ABI64. */
a_info->cputype |= CPU_ARCH_ABI64;
} else if (sizeof (long) == 8)
/* On x86, by default (name is NULL here) enable 64 bit code. */
a_info->cputype |= CPU_ARCH_ABI64;
}
}
if (!a_info)
fatal ("Invalid arch name : %s", name);
all_info = NXGetAllArchInfos();
if (!all_info)
fatal ("Unable to get architecture information");
/* Find first arch. that matches cputype. */
cputype = a_info->cputype;
while (all_info->name)
{
if (all_info->cputype == cputype)
break;
else
all_info++;
}
return all_info->name;
}
/* Find driver name based on input arch name. */
static char *
get_driver_name (const char *arch_name)
{
char *driver_name;
const char *config_name;
int len;
int index;
struct arch_config_guess_map *map;
/* find config name based on arch name. */
config_name = NULL;
map = arch_config_map;
while (map->arch_name)
{
if (!strcmp (map->arch_name, arch_name))
{
config_name = map->config_string;
break;
}
else map++;
}
if (!config_name)
fatal ("Unable to guess config name for arch %s", arch_name);
len = strlen (config_name) + strlen (PDN) + prefix_len + 1;
driver_name = (char *) malloc (sizeof (char) * len);
driver_name[0] = '\0';
if (driver_exec_prefix)
strcpy (driver_name, driver_exec_prefix);
strcat (driver_name, config_name);
strcat (driver_name, PDN);
return driver_name;
}
/* Delete out_files. */
static void
delete_out_files (void)
{
const char *temp;
struct stat st;
int i = 0;
for (i = 0, temp = out_files[i];
temp && i < total_argc * MAX_ARCHES;
temp = out_files[++i])
if (stat (temp, &st) >= 0 && S_ISREG (st.st_mode))
unlink (temp);
}
/* Put fatal error message on stderr and exit. */
void
fatal (const char *msgid, ...)
{
va_list ap;
va_start (ap, msgid);
fprintf (stderr, "%s: ", progname);
vfprintf (stderr, msgid, ap);
va_end (ap);
fprintf (stderr, "\n");
delete_out_files ();
exit (1);
}
/* Print error message and exit. */
static void
pfatal_pexecute (const char *errmsg_fmt, const char *errmsg_arg)
{
if (errmsg_arg)
{
int save_errno = errno;
/* Space for trailing '\0' is in %s. */
char *msg = (char *) malloc (strlen (errmsg_fmt) + strlen (errmsg_arg));
sprintf (msg, errmsg_fmt, errmsg_arg);
errmsg_fmt = msg;
errno = save_errno;
}
fprintf (stderr,"%s: %s: %s\n", progname, errmsg_fmt, xstrerror (errno));
delete_out_files ();
exit (1);
}
#ifdef DEBUG
static void
debug_command_line (const char **debug_argv, int debug_argc)
{
int i;
fprintf (stderr,"%s: debug_command_line\n", progname);
fprintf (stderr,"%s: arg count = %d\n", progname, debug_argc);
for (i = 0; debug_argv[i]; i++)
fprintf (stderr,"%s: arg [%d] %s\n", progname, i, debug_argv[i]);
}
#endif
/* Strip directory name from the input file name and replace file name
suffix with new. */
static char *
strip_path_and_suffix (const char *full_name, const char *new_suffix)
{
char *name;
char *p;
if (!full_name || !new_suffix)
return NULL;
/* Strip path name. */
p = (char *)full_name + strlen (full_name);
while (p != full_name && !IS_DIR_SEPARATOR (p[-1]))
--p;
/* Now 'p' is a file name with suffix. */
name = (char *) malloc (strlen (p) + 1 + strlen (new_suffix));
strcpy (name, p);
p = name + strlen (name);
while (p != name && *p != '.')
--p;
/* If did not reach at the beginning of name then '.' is found.
Replace '.' with NULL. */
if (p != name)
*p = '\0';
strcat (name, new_suffix);
return name;
}
/* Initialization */
static void
initialize (void)
{
int i;
/* Let's count, how many additional arguments driver driver will supply
to compiler driver:
Each "-arch" "<blah>" is replaced by approriate "-mcpu=<blah>".
That leaves one additional arg space available.
Note that only one -m* is supplied to each compiler driver. Which
means, extra "-arch" "<blah>" are removed from the original command
line. But lets not count how many additional slots are available.
Driver driver may need to specify temp. output file name, say
"-o" "foobar". That needs two extra argments.
Sometimes linker wants one additional "-Wl,-arch_multiple".
Sometimes linker wants to see "-final_output" "outputname".
In the end, we may need five extra arguments, plus one extra
space for the NULL terminator. */
new_argv = (const char **) malloc ((total_argc + 6) * sizeof (const char *));
if (!new_argv)
abort ();
arch_conditional = (const char **) malloc ((total_argc + 6) * sizeof (const char *));
if (!arch_conditional)
abort ();
for (i = 0; i < total_argc + 6; i++)
arch_conditional[i] = NULL;
/* First slot, new_argv[0] is reserved for the driver name. */
new_argc = 1;
/* For each -arch, three arguments are needed.
For example, "-arch" "ppc" "file". Additional slots are for
"lipo" "-create" "-o" "outputfilename" and the NULL. */
lipo_argv = (const char **) malloc ((total_argc * 3 + 5) * sizeof (const char *));
if (!lipo_argv)
abort ();
/* Need separate out_files for each arch, max is MAX_ARCHES.
Need separate out_files for each input file. */
out_files = (const char **) malloc ((total_argc * MAX_ARCHES) * sizeof (const char *));
if (!out_files)
abort ();
num_arches = 0;
num_infiles = 0;
in_files = NULL;
last_infile = NULL;
for (i = 0; i < (MAX_ARCHES + 1); i++)
{
commands[i].prog = NULL;
commands[i].argv = NULL;
commands[i].pid = 0;
}
}
/* Cleanup. */
static void
final_cleanup (void)
{
int i;
struct input_filename *next;
delete_out_files ();
free (new_argv);
free (lipo_argv);
free (out_files);
for (i = 0, next = in_files;
i < num_infiles && next;
i++)
{
next = in_files->next;
free (in_files);
in_files = next;
}
}
/* Wait for the process pid and return appropriate code. */
static int
do_wait (int pid, const char *prog)
{
int status = 0;
int ret = 0;
pid = pwait (pid, &status, 0);
if (WIFSIGNALED (status))
{
if (!signal_count &&
WEXITSTATUS (status) > greatest_status)
greatest_status = WEXITSTATUS (status);
ret = -1;
}
else if (WIFEXITED (status)
&& WEXITSTATUS (status) >= 1)
{
if (WEXITSTATUS (status) > greatest_status)
greatest_status = WEXITSTATUS (status);
signal_count++;
ret = -1;
}
return ret;
}
/* Invoke 'lipo' and combine and all output files. */
static void
do_lipo (int start_outfile_index, const char *out_file)
{
int i, j, pid;
char *errmsg_fmt, *errmsg_arg;
/* Populate lipo arguments. */
lipo_argv[0] = "lipo";
lipo_argv[1] = "-create";
lipo_argv[2] = "-o";
lipo_argv[3] = out_file;
/* Already 4 lipo arguments are set. Now add all lipo inputs. */
j = 4;
for (i = 0; i < num_arches; i++)
lipo_argv[j++] = out_files[start_outfile_index + i];
/* Add the NULL at the end. */
lipo_argv[j++] = NULL;
#ifdef DEBUG
debug_command_line (lipo_argv, j);
#endif
if (verbose_flag)
{
for (i = 0; lipo_argv[i]; i++)
fprintf (stderr, "%s ", lipo_argv[i]);
fprintf (stderr, "\n");
}
pid = pexecute (lipo_argv[0], (char *const *)lipo_argv, progname, NULL, &errmsg_fmt,
&errmsg_arg, PEXECUTE_SEARCH | PEXECUTE_ONE);
if (pid == -1)
pfatal_pexecute (errmsg_fmt, errmsg_arg);
do_wait (pid, lipo_argv[0]);
}
/* Invoke compiler for all architectures. */
static void
do_compile (const char **current_argv, int current_argc)
{
char *errmsg_fmt, *errmsg_arg;
int index = 0;
int dash_o_index = current_argc;
int of_index = current_argc + 1;
int argc_count = current_argc + 2;
const char **arch_specific_argv;
int arch_specific_argc;
while (index < num_arches)
{
int additional_arch_options = 0;
current_argv[0] = get_driver_name (get_arch_name (arches[index]));
/* setup output file. */
out_files[num_outfiles] = make_temp_file (".out");
current_argv[dash_o_index] = "-o";
current_argv[of_index] = out_files [num_outfiles];
num_outfiles++;
/* Add arch option as the last option. Do not add any other option
before removing this option. */
additional_arch_options = add_arch_options (index, current_argv, argc_count);
argc_count += additional_arch_options;
current_argv[argc_count] = NULL;
arch_specific_argv =
(const char **) malloc ((argc_count + 1) * sizeof (const char *));
arch_specific_argc = filter_args_for_arch (current_argv,
argc_count,
arch_specific_argv,
get_arch_name (arches[index]));
commands[index].prog = arch_specific_argv[0];
commands[index].argv = arch_specific_argv;
#ifdef DEBUG
debug_command_line (arch_specific_argv, arch_specific_argc);
#endif
commands[index].pid = pexecute (arch_specific_argv[0],
(char *const *)arch_specific_argv,
progname, NULL,
&errmsg_fmt,
&errmsg_arg,
PEXECUTE_SEARCH | PEXECUTE_ONE);
if (commands[index].pid == -1)
pfatal_pexecute (errmsg_fmt, errmsg_arg);
do_wait (commands[index].pid, commands[index].prog);
fflush (stdout);
/* Remove the last arch option added in the current_argv list. */
if (additional_arch_options)
argc_count -= remove_arch_options (current_argv, argc_count);
index++;
free (arch_specific_argv);
}
}
/* Invoke compiler for each input file separately.
Construct command line for each invocation with one input file. */
static void
do_compile_separately (void)
{
const char **new_new_argv;
int i, new_new_argc;
struct input_filename *current_ifn;
if (num_infiles == 1 || ima_is_used)
abort ();
/* Total number of arguments in separate compiler invocation is :
total number of original arguments - total no input files + one input
file + "-o" + output file + arch specific options + NULL . */
new_new_argv = (const char **) malloc ((new_argc - num_infiles + 5) * sizeof (const char *));
if (!new_new_argv)
abort ();
for (current_ifn = in_files; current_ifn && current_ifn->name;
current_ifn = current_ifn->next)
{
struct input_filename *ifn = in_files;
int go_back = 0;
new_new_argc = 1;
bool ifn_found = false;
for (i = 1; i < new_argc; i++)
{
if (ifn && ifn->name && !strcmp (new_argv[i], ifn->name))
{
/* This argument is one of the input file. */
if (!strcmp (new_argv[i], current_ifn->name))
{
if (ifn_found)
fatal ("file %s specified more than once on the command line", current_ifn->name);
/* If it is current input file name then add it in the new
list. */
new_new_argv[new_new_argc] = new_argv[i];
arch_conditional[new_new_argc] = arch_conditional[i];
new_new_argc++;
ifn_found = true;
}
/* This input file can not appear in
again on the command line so next time look for next input
file. */
ifn = ifn->next;
}
else
{
/* This argument is not a input file name. Add it into new
list. */
new_new_argv[new_new_argc] = new_argv[i];
arch_conditional[new_new_argc] = arch_conditional[i];
new_new_argc++;
}
}
/* OK now we have only one input file and all other arguments. */
do_compile (new_new_argv, new_new_argc);
}
}
/* Invoke 'lipo' on set of output files and create multile FAT binaries. */
static void
do_lipo_separately (void)
{
int ifn_index;
struct input_filename *ifn;
for (ifn_index = 0, ifn = in_files;
ifn_index < num_infiles && ifn && ifn->name;
ifn_index++, ifn = ifn->next)
do_lipo (ifn_index * num_arches,
strip_path_and_suffix (ifn->name, ".o"));
}
/* Remove all options which are architecture-specific and are not for the
current architecture (arch). */
static int
filter_args_for_arch (const char **orig_argv, int orig_argc,
const char **new_argv, const char *arch)
{
int new_argc = 0;
int i;
for (i = 0; i < orig_argc; i++)
if (arch_conditional[i] == NULL
|| *arch_conditional[i] == '\0'
|| ! strcmp (arch_conditional[i], arch))
new_argv[new_argc++] = orig_argv[i];
new_argv[new_argc] = NULL;
return new_argc;
}
/* Replace -arch <blah> options with appropriate "-mcpu=<blah>" OR
"-march=<blah>". INDEX is the index in arches[] table. We cannot
return more than 1 as do_compile_separately only allocated one
extra slot for us. */
static int
add_arch_options (int index, const char **current_argv, int arch_index)
{
int count;
/* We are adding 1 argument for selected arches. */
count = 1;
#ifdef DEBUG
fprintf (stderr, "%s: add_arch_options: %s\n", progname, arches[index]);
#endif
if (!strcmp (arches[index], "ppc601"))
current_argv[arch_index] = "-mcpu=601";
else if (!strcmp (arches[index], "ppc603"))
current_argv[arch_index] = "-mcpu=603";
else if (!strcmp (arches[index], "ppc604"))
current_argv[arch_index] = "-mcpu=604";
else if (!strcmp (arches[index], "ppc604e"))
current_argv[arch_index] = "-mcpu=604e";
else if (!strcmp (arches[index], "ppc750"))
current_argv[arch_index] = "-mcpu=750";
else if (!strcmp (arches[index], "ppc7400"))
current_argv[arch_index] = "-mcpu=7400";
else if (!strcmp (arches[index], "ppc7450"))
current_argv[arch_index] = "-mcpu=7450";
else if (!strcmp (arches[index], "ppc970"))
current_argv[arch_index] = "-mcpu=970";
else if (!strcmp (arches[index], "ppc64"))
current_argv[arch_index] = "-m64";
else if (!strcmp (arches[index], "i486"))
current_argv[arch_index] = "-march=i486";
else if (!strcmp (arches[index], "i586"))
current_argv[arch_index] = "-march=i586";
else if (!strcmp (arches[index], "i686"))
current_argv[arch_index] = "-march=i686";
else if (!strcmp (arches[index], "pentium"))
current_argv[arch_index] = "-march=pentium";
else if (!strcmp (arches[index], "pentium2"))
current_argv[arch_index] = "-march=pentium2";
else if (!strcmp (arches[index], "pentpro"))
current_argv[arch_index] = "-march=pentiumpro";
else if (!strcmp (arches[index], "pentIIm3"))
current_argv[arch_index] = "-march=pentium2";
else if (!strcmp (arches[index], "x86_64"))
current_argv[arch_index] = "-m64";
else if (!strcmp (arches[index], "arm"))
current_argv[arch_index] = "-march=armv4t";
else if (!strcmp (arches[index], "armv4t"))
current_argv[arch_index] = "-march=armv4t";
else if (!strcmp (arches[index], "armv5"))
current_argv[arch_index] = "-march=armv5tej";
else if (!strcmp (arches[index], "xscale"))
current_argv[arch_index] = "-march=xscale";
else if (!strcmp (arches[index], "armv6"))
current_argv[arch_index] = "-march=armv6k";
else if (!strcmp (arches[index], "armv7"))
current_argv[arch_index] = "-march=armv7a";
else
count = 0;
return count;
}
/* Remove the last option, which is arch option, added by
add_arch_options. Return how count of arguments removed. */
static int
remove_arch_options (const char **current_argv, int arch_index)
{
#ifdef DEBUG
fprintf (stderr, "%s: Removing argument no %d\n", progname, arch_index);
#endif
current_argv[arch_index] = '\0';
#ifdef DEBUG
debug_command_line (current_argv, arch_index);
#<">
gipoco.com
is neither affiliated with the authors of this page nor responsible
for its contents. This is a safe-cache copy of the original web site.
gipoco.com
is neither affiliated with the authors of this page nor responsible
for its contents. This is a safe-cache copy of the original web site.