--- /dev/null
+/* Offload image generation tool for Intel MIC devices.
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ Contributed by Ilya Verbin <ilya.verbin@intel.com>.
+
+ 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 3, 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 COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include <libgen.h>
+#include "system.h"
+#include "coretypes.h"
+#include "obstack.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "collect-utils.h"
+#include <libgomp_target.h>
+
+const char tool_name[] = "intelmic mkoffload";
+
+const char image_section_name[] = ".gnu.offload_images";
+const char *symbols[3] = { "__offload_image_intelmic_start",
+ "__offload_image_intelmic_end",
+ "__offload_image_intelmic_size" };
+const char *out_obj_filename = NULL;
+
+int num_temps = 0;
+const int MAX_NUM_TEMPS = 10;
+const char *temp_files[MAX_NUM_TEMPS];
+
+/* Shows if we should compile binaries for i386 instead of x86-64. */
+bool target_ilp32 = false;
+
+/* Delete tempfiles and exit function. */
+void
+tool_cleanup (bool from_signal ATTRIBUTE_UNUSED)
+{
+ for (int i = 0; i < num_temps; i++)
+ maybe_unlink (temp_files[i]);
+}
+
+static void
+mkoffload_atexit (void)
+{
+ tool_cleanup (false);
+}
+
+/* Unlink FILE unless we are debugging. */
+void
+maybe_unlink (const char *file)
+{
+ if (debug)
+ notice ("[Leaving %s]\n", file);
+ else
+ unlink_if_ordinary (file);
+}
+
+/* Add or change the value of an environment variable, outputting the
+ change to standard error if in verbose mode. */
+static void
+xputenv (const char *string)
+{
+ if (verbose)
+ fprintf (stderr, "%s\n", string);
+ putenv (CONST_CAST (char *, string));
+}
+
+/* Parse STR, saving found tokens into PVALUES and return their number.
+ Tokens are assumed to be delimited by ':'. */
+static unsigned
+parse_env_var (const char *str, char ***pvalues)
+{
+ const char *curval, *nextval;
+ char **values;
+ unsigned num = 1, i;
+
+ curval = strchr (str, ':');
+ while (curval)
+ {
+ num++;
+ curval = strchr (curval + 1, ':');
+ }
+
+ values = (char **) xmalloc (num * sizeof (char *));
+ curval = str;
+ nextval = strchr (curval, ':');
+ if (nextval == NULL)
+ nextval = strchr (curval, '\0');
+
+ for (i = 0; i < num; i++)
+ {
+ int l = nextval - curval;
+ values[i] = (char *) xmalloc (l + 1);
+ memcpy (values[i], curval, l);
+ values[i][l] = 0;
+ curval = nextval + 1;
+ nextval = strchr (curval, ':');
+ if (nextval == NULL)
+ nextval = strchr (curval, '\0');
+ }
+ *pvalues = values;
+ return num;
+}
+
+/* Auxiliary function that frees elements of PTR and PTR itself.
+ N is number of elements to be freed. If PTR is NULL, nothing is freed.
+ If an element is NULL, subsequent elements are not freed. */
+static void
+free_array_of_ptrs (void **ptr, unsigned n)
+{
+ unsigned i;
+ if (!ptr)
+ return;
+ for (i = 0; i < n; i++)
+ {
+ if (!ptr[i])
+ break;
+ free (ptr[i]);
+ }
+ free (ptr);
+ return;
+}
+
+/* Check whether NAME can be accessed in MODE. This is like access,
+ except that it never considers directories to be executable. */
+static int
+access_check (const char *name, int mode)
+{
+ if (mode == X_OK)
+ {
+ struct stat st;
+
+ if (stat (name, &st) < 0 || S_ISDIR (st.st_mode))
+ return -1;
+ }
+
+ return access (name, mode);
+}
+
+/* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH. */
+static char *
+find_target_compiler (const char *name)
+{
+ bool found = false;
+ char **paths = NULL;
+ unsigned n_paths, i;
+ const char *collect_path = dirname (ASTRDUP (getenv ("COLLECT_GCC")));
+ size_t len = strlen (collect_path) + 1 + strlen (name) + 1;
+ char *target_compiler = XNEWVEC (char, len);
+ sprintf (target_compiler, "%s/%s", collect_path, name);
+ if (access_check (target_compiler, X_OK) == 0)
+ {
+ found = true;
+ goto out;
+ }
+
+ n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths);
+ for (i = 0; i < n_paths; i++)
+ {
+ len = strlen (paths[i]) + 1 + strlen (name) + 1;
+ target_compiler = XRESIZEVEC (char, target_compiler, len);
+ sprintf (target_compiler, "%s/%s", paths[i], name);
+ if (access_check (target_compiler, X_OK) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+out:
+ free_array_of_ptrs ((void **) paths, n_paths);
+ return found ? target_compiler : NULL;
+}
+
+static void
+compile_for_target (struct obstack *argv_obstack)
+{
+ if (target_ilp32)
+ obstack_ptr_grow (argv_obstack, "-m32");
+ obstack_ptr_grow (argv_obstack, NULL);
+ char **argv = XOBFINISH (argv_obstack, char **);
+
+ /* Save environment variables. */
+ const char *epath = getenv ("GCC_EXEC_PREFIX");
+ const char *cpath = getenv ("COMPILER_PATH");
+ const char *lpath = getenv ("LIBRARY_PATH");
+ const char *rpath = getenv ("LD_RUN_PATH");
+ unsetenv ("GCC_EXEC_PREFIX");
+ unsetenv ("COMPILER_PATH");
+ unsetenv ("LIBRARY_PATH");
+ unsetenv ("LD_RUN_PATH");
+
+ fork_execute (argv[0], argv, false);
+ obstack_free (argv_obstack, NULL);
+
+ /* Restore environment variables. */
+ xputenv (concat ("GCC_EXEC_PREFIX=", epath, NULL));
+ xputenv (concat ("COMPILER_PATH=", cpath, NULL));
+ xputenv (concat ("LIBRARY_PATH=", lpath, NULL));
+ xputenv (concat ("LD_RUN_PATH=", rpath, NULL));
+}
+
+/* Generates object file with the descriptor for the target library. */
+static const char *
+generate_target_descr_file (const char *target_compiler)
+{
+ const char *src_filename = make_temp_file ("_target_descr.c");
+ const char *obj_filename = make_temp_file ("_target_descr.o");
+ temp_files[num_temps++] = src_filename;
+ temp_files[num_temps++] = obj_filename;
+ FILE *src_file = fopen (src_filename, "w");
+
+ if (!src_file)
+ fatal_error ("cannot open '%s'", src_filename);
+
+ fprintf (src_file,
+ "extern void *__offload_funcs_end[];\n"
+ "extern void *__offload_vars_end[];\n\n"
+
+ "void *__offload_func_table[0]\n"
+ "__attribute__ ((__used__, visibility (\"hidden\"),\n"
+ "section (\".gnu.offload_funcs\"))) = { };\n\n"
+
+ "void *__offload_var_table[0]\n"
+ "__attribute__ ((__used__, visibility (\"hidden\"),\n"
+ "section (\".gnu.offload_vars\"))) = { };\n\n"
+
+ "void *__OFFLOAD_TARGET_TABLE__[]\n"
+ "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n"
+ " &__offload_func_table, &__offload_funcs_end,\n"
+ " &__offload_var_table, &__offload_vars_end\n"
+ "};\n\n");
+
+ fprintf (src_file,
+ "#ifdef __cplusplus\n"
+ "extern \"C\"\n"
+ "#endif\n"
+ "void target_register_lib (const void *);\n\n"
+
+ "__attribute__((constructor))\n"
+ "static void\n"
+ "init (void)\n"
+ "{\n"
+ " target_register_lib (__OFFLOAD_TARGET_TABLE__);\n"
+ "}\n");
+ fclose (src_file);
+
+ struct obstack argv_obstack;
+ obstack_init (&argv_obstack);
+ obstack_ptr_grow (&argv_obstack, target_compiler);
+ obstack_ptr_grow (&argv_obstack, "-c");
+ obstack_ptr_grow (&argv_obstack, "-shared");
+ obstack_ptr_grow (&argv_obstack, "-fPIC");
+ obstack_ptr_grow (&argv_obstack, src_filename);
+ obstack_ptr_grow (&argv_obstack, "-o");
+ obstack_ptr_grow (&argv_obstack, obj_filename);
+ compile_for_target (&argv_obstack);
+
+ return obj_filename;
+}
+
+/* Generates object file with __offload_*_end symbols for the target
+ library. */
+static const char *
+generate_target_offloadend_file (const char *target_compiler)
+{
+ const char *src_filename = make_temp_file ("_target_offloadend.c");
+ const char *obj_filename = make_temp_file ("_target_offloadend.o");
+ temp_files[num_temps++] = src_filename;
+ temp_files[num_temps++] = obj_filename;
+ FILE *src_file = fopen (src_filename, "w");
+
+ if (!src_file)
+ fatal_error ("cannot open '%s'", src_filename);
+
+ fprintf (src_file,
+ "void *__offload_funcs_end[0]\n"
+ "__attribute__ ((__used__, visibility (\"hidden\"),\n"
+ "section (\".gnu.offload_funcs\"))) = { };\n\n"
+
+ "void *__offload_vars_end[0]\n"
+ "__attribute__ ((__used__, visibility (\"hidden\"),\n"
+ "section (\".gnu.offload_vars\"))) = { };\n");
+ fclose (src_file);
+
+ struct obstack argv_obstack;
+ obstack_init (&argv_obstack);
+ obstack_ptr_grow (&argv_obstack, target_compiler);
+ obstack_ptr_grow (&argv_obstack, "-c");
+ obstack_ptr_grow (&argv_obstack, "-shared");
+ obstack_ptr_grow (&argv_obstack, "-fPIC");
+ obstack_ptr_grow (&argv_obstack, src_filename);
+ obstack_ptr_grow (&argv_obstack, "-o");
+ obstack_ptr_grow (&argv_obstack, obj_filename);
+ compile_for_target (&argv_obstack);
+
+ return obj_filename;
+}
+
+/* Generates object file with the host side descriptor. */
+static const char *
+generate_host_descr_file (const char *host_compiler)
+{
+ const char *src_filename = make_temp_file ("_host_descr.c");
+ const char *obj_filename = make_temp_file ("_host_descr.o");
+ temp_files[num_temps++] = src_filename;
+ temp_files[num_temps++] = obj_filename;
+ FILE *src_file = fopen (src_filename, "w");
+
+ if (!src_file)
+ fatal_error ("cannot open '%s'", src_filename);
+
+ fprintf (src_file,
+ "extern void *__OFFLOAD_TABLE__;\n"
+ "extern void *__offload_image_intelmic_start;\n"
+ "extern void *__offload_image_intelmic_end;\n\n"
+
+ "static const void *__offload_target_data[] = {\n"
+ " &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
+ "};\n\n");
+
+ fprintf (src_file,
+ "#ifdef __cplusplus\n"
+ "extern \"C\"\n"
+ "#endif\n"
+ "void GOMP_offload_register (void *, int, void *);\n\n"
+
+ "__attribute__((constructor))\n"
+ "static void\n"
+ "init (void)\n"
+ "{\n"
+ " GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
+ "}\n", OFFLOAD_TARGET_TYPE_INTEL_MIC);
+ fclose (src_file);
+
+ unsigned new_argc = 0;
+ const char *new_argv[9];
+ new_argv[new_argc++] = host_compiler;
+ new_argv[new_argc++] = "-c";
+ new_argv[new_argc++] = "-fPIC";
+ new_argv[new_argc++] = "-shared";
+ if (target_ilp32)
+ new_argv[new_argc++] = "-m32";
+ new_argv[new_argc++] = src_filename;
+ new_argv[new_argc++] = "-o";
+ new_argv[new_argc++] = obj_filename;
+ new_argv[new_argc++] = NULL;
+
+ fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
+
+ return obj_filename;
+}
+
+static const char *
+prepare_target_image (const char *target_compiler, int argc, char **argv)
+{
+ const char *target_descr_filename
+ = generate_target_descr_file (target_compiler);
+ const char *target_offloadend_filename
+ = generate_target_offloadend_file (target_compiler);
+
+ char *opt1
+ = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename));
+ char *opt2
+ = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename));
+ sprintf (opt1, "-Wl,%s", target_descr_filename);
+ sprintf (opt2, "-Wl,%s", target_offloadend_filename);
+
+ const char *target_so_filename = make_temp_file ("_offload_intelmic.so");
+ temp_files[num_temps++] = target_so_filename;
+ struct obstack argv_obstack;
+ obstack_init (&argv_obstack);
+ obstack_ptr_grow (&argv_obstack, target_compiler);
+ obstack_ptr_grow (&argv_obstack, "-xlto");
+ obstack_ptr_grow (&argv_obstack, "-fopenmp");
+ obstack_ptr_grow (&argv_obstack, "-shared");
+ obstack_ptr_grow (&argv_obstack, "-fPIC");
+ obstack_ptr_grow (&argv_obstack, opt1);
+ for (int i = 1; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "-o") && i + 1 != argc)
+ out_obj_filename = argv[++i];
+ else
+ obstack_ptr_grow (&argv_obstack, argv[i]);
+ }
+ if (!out_obj_filename)
+ fatal_error ("output file not specified");
+ obstack_ptr_grow (&argv_obstack, opt2);
+ obstack_ptr_grow (&argv_obstack, "-o");
+ obstack_ptr_grow (&argv_obstack, target_so_filename);
+ compile_for_target (&argv_obstack);
+
+ /* Run objcopy. */
+ char *rename_section_opt
+ = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name));
+ sprintf (rename_section_opt, ".data=%s", image_section_name);
+ const char *objcopy_argv[11];
+ objcopy_argv[0] = "objcopy";
+ objcopy_argv[1] = "-B";
+ objcopy_argv[2] = "i386";
+ objcopy_argv[3] = "-I";
+ objcopy_argv[4] = "binary";
+ objcopy_argv[5] = "-O";
+ if (target_ilp32)
+ objcopy_argv[6] = "elf32-i386";
+ else
+ objcopy_argv[6] = "elf64-x86-64";
+ objcopy_argv[7] = target_so_filename;
+ objcopy_argv[8] = "--rename-section";
+ objcopy_argv[9] = rename_section_opt;
+ objcopy_argv[10] = NULL;
+ fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false);
+
+ /* Objcopy has created symbols, containing the input file name with
+ special characters replaced with '_'. We are going to rename these
+ new symbols. */
+ size_t symbol_name_len = strlen (target_so_filename);
+ char *symbol_name = XALLOCAVEC (char, symbol_name_len + 1);
+ for (size_t i = 0; i <= symbol_name_len; i++)
+ {
+ char c = target_so_filename[i];
+ if ((c == '/') || (c == '.'))
+ c = '_';
+ symbol_name[i] = c;
+ }
+
+ char *opt_for_objcopy[3];
+ opt_for_objcopy[0] = XALLOCAVEC (char, sizeof ("_binary__start=")
+ + symbol_name_len
+ + strlen (symbols[0]));
+ opt_for_objcopy[1] = XALLOCAVEC (char, sizeof ("_binary__end=")
+ + symbol_name_len
+ + strlen (symbols[1]));
+ opt_for_objcopy[2] = XALLOCAVEC (char, sizeof ("_binary__size=")
+ + symbol_name_len
+ + strlen (symbols[2]));
+ sprintf (opt_for_objcopy[0], "_binary_%s_start=%s", symbol_name, symbols[0]);
+ sprintf (opt_for_objcopy[1], "_binary_%s_end=%s", symbol_name, symbols[1]);
+ sprintf (opt_for_objcopy[2], "_binary_%s_size=%s", symbol_name, symbols[2]);
+
+ objcopy_argv[0] = "objcopy";
+ objcopy_argv[1] = target_so_filename;
+ objcopy_argv[2] = "--redefine-sym";
+ objcopy_argv[3] = opt_for_objcopy[0];
+ objcopy_argv[4] = "--redefine-sym";
+ objcopy_argv[5] = opt_for_objcopy[1];
+ objcopy_argv[6] = "--redefine-sym";
+ objcopy_argv[7] = opt_for_objcopy[2];
+ objcopy_argv[8] = NULL;
+ fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false);
+
+ return target_so_filename;
+}
+
+int
+main (int argc, char **argv)
+{
+ progname = "mkoffload-intelmic";
+ gcc_init_libintl ();
+ diagnostic_initialize (global_dc, 0);
+
+ if (atexit (mkoffload_atexit) != 0)
+ fatal_error ("atexit failed");
+
+ const char *host_compiler = getenv ("COLLECT_GCC");
+ if (!host_compiler)
+ fatal_error ("COLLECT_GCC must be set");
+
+ const char *target_driver_name
+ = DEFAULT_REAL_TARGET_MACHINE "-accel-" DEFAULT_TARGET_MACHINE "-gcc";
+ char *target_compiler = find_target_compiler (target_driver_name);
+ if (target_compiler == NULL)
+ fatal_error ("offload compiler %s not found", target_driver_name);
+
+ /* We may be called with all the arguments stored in some file and
+ passed with @file. Expand them into argv before processing. */
+ expandargv (&argc, &argv);
+
+ /* Find out whether we should compile binaries for i386 or x86-64. */
+ for (int i = argc - 1; i > 0; i--)
+ if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0)
+ {
+ if (strstr (argv[i], "ilp32"))
+ target_ilp32 = true;
+ else if (!strstr (argv[i], "lp64"))
+ fatal_error ("unrecognizable argument of option -foffload-abi");
+ break;
+ }
+
+ const char *target_so_filename
+ = prepare_target_image (target_compiler, argc, argv);
+
+ const char *host_descr_filename = generate_host_descr_file (host_compiler);
+
+ /* Perform partial linking for the target image and host side descriptor.
+ As a result we'll get a finalized object file with all offload data. */
+ unsigned new_argc = 0;
+ const char *new_argv[9];
+ new_argv[new_argc++] = "ld";
+ if (target_ilp32)
+ {
+ new_argv[new_argc++] = "-m";
+ new_argv[new_argc++] = "elf_i386";
+ }
+ new_argv[new_argc++] = "--relocatable";
+ new_argv[new_argc++] = host_descr_filename;
+ new_argv[new_argc++] = target_so_filename;
+ new_argv[new_argc++] = "-o";
+ new_argv[new_argc++] = out_obj_filename;
+ new_argv[new_argc++] = NULL;
+ fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
+
+ /* Run objcopy on the resultant object file to localize generated symbols
+ to avoid conflicting between different DSO and an executable. */
+ new_argv[0] = "objcopy";
+ new_argv[1] = "-L";
+ new_argv[2] = symbols[0];
+ new_argv[3] = "-L";
+ new_argv[4] = symbols[1];
+ new_argv[5] = "-L";
+ new_argv[6] = symbols[2];
+ new_argv[7] = out_obj_filename;
+ new_argv[8] = NULL;
+ fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false);
+
+ return 0;
+}