1/* Offload image generation tool for Intel MIC devices. 2 3 Copyright (C) 2014-2015 Free Software Foundation, Inc. 4 5 Contributed by Ilya Verbin <ilya.verbin@intel.com>. 6 7 This file is part of GCC. 8 9 GCC is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3, or (at your option) 12 any later version. 13 14 GCC is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with GCC; see the file COPYING3. If not see 21 <http://www.gnu.org/licenses/>. */ 22 23#include "config.h" 24#include <libgen.h> 25#include "system.h" 26#include "coretypes.h" 27#include "obstack.h" 28#include "intl.h" 29#include "diagnostic.h" 30#include "collect-utils.h" 31#include "intelmic-offload.h" 32 33const char tool_name[] = "intelmic mkoffload"; 34 35const char image_section_name[] = ".gnu.offload_images"; 36const char *symbols[3] = { "__offload_image_intelmic_start", 37 "__offload_image_intelmic_end", 38 "__offload_image_intelmic_size" }; 39const char *out_obj_filename = NULL; 40 41int num_temps = 0; 42const int MAX_NUM_TEMPS = 10; 43const char *temp_files[MAX_NUM_TEMPS]; 44 45/* Shows if we should compile binaries for i386 instead of x86-64. */ 46bool target_ilp32 = false; 47 48/* Delete tempfiles and exit function. */ 49void 50tool_cleanup (bool from_signal ATTRIBUTE_UNUSED) 51{ 52 for (int i = 0; i < num_temps; i++) 53 maybe_unlink (temp_files[i]); 54} 55 56static void 57mkoffload_atexit (void) 58{ 59 tool_cleanup (false); 60} 61 62/* Unlink FILE unless we are debugging. */ 63void 64maybe_unlink (const char *file) 65{ 66 if (debug) 67 notice ("[Leaving %s]\n", file); 68 else 69 unlink_if_ordinary (file); 70} 71 72/* Add or change the value of an environment variable, outputting the 73 change to standard error if in verbose mode. */ 74static void 75xputenv (const char *string) 76{ 77 if (verbose) 78 fprintf (stderr, "%s\n", string); 79 putenv (CONST_CAST (char *, string)); 80} 81 82/* Parse STR, saving found tokens into PVALUES and return their number. 83 Tokens are assumed to be delimited by ':'. */ 84static unsigned 85parse_env_var (const char *str, char ***pvalues) 86{ 87 const char *curval, *nextval; 88 char **values; 89 unsigned num = 1, i; 90 91 curval = strchr (str, ':'); 92 while (curval) 93 { 94 num++; 95 curval = strchr (curval + 1, ':'); 96 } 97 98 values = (char **) xmalloc (num * sizeof (char *)); 99 curval = str; 100 nextval = strchr (curval, ':'); 101 if (nextval == NULL) 102 nextval = strchr (curval, '\0'); 103 104 for (i = 0; i < num; i++) 105 { 106 int l = nextval - curval; 107 values[i] = (char *) xmalloc (l + 1); 108 memcpy (values[i], curval, l); 109 values[i][l] = 0; 110 curval = nextval + 1; 111 nextval = strchr (curval, ':'); 112 if (nextval == NULL) 113 nextval = strchr (curval, '\0'); 114 } 115 *pvalues = values; 116 return num; 117} 118 119/* Auxiliary function that frees elements of PTR and PTR itself. 120 N is number of elements to be freed. If PTR is NULL, nothing is freed. 121 If an element is NULL, subsequent elements are not freed. */ 122static void 123free_array_of_ptrs (void **ptr, unsigned n) 124{ 125 unsigned i; 126 if (!ptr) 127 return; 128 for (i = 0; i < n; i++) 129 { 130 if (!ptr[i]) 131 break; 132 free (ptr[i]); 133 } 134 free (ptr); 135 return; 136} 137 138/* Check whether NAME can be accessed in MODE. This is like access, 139 except that it never considers directories to be executable. */ 140static int 141access_check (const char *name, int mode) 142{ 143 if (mode == X_OK) 144 { 145 struct stat st; 146 147 if (stat (name, &st) < 0 || S_ISDIR (st.st_mode)) 148 return -1; 149 } 150 151 return access (name, mode); 152} 153 154/* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH. */ 155static char * 156find_target_compiler (const char *name) 157{ 158 bool found = false; 159 char **paths = NULL; 160 unsigned n_paths, i; 161 char *target_compiler; 162 const char *collect_gcc = getenv ("COLLECT_GCC"); 163 const char *gcc_path = dirname (ASTRDUP (collect_gcc)); 164 const char *gcc_exec = basename (ASTRDUP (collect_gcc)); 165 166 if (strcmp (gcc_exec, collect_gcc) == 0) 167 { 168 /* collect_gcc has no path, so it was found in PATH. Make sure we also 169 find accel-gcc in PATH. */ 170 target_compiler = XDUPVEC (char, name, strlen (name) + 1); 171 found = true; 172 goto out; 173 } 174 175 target_compiler = concat (gcc_path, "/", name, NULL); 176 if (access_check (target_compiler, X_OK) == 0) 177 { 178 found = true; 179 goto out; 180 } 181 182 n_paths = parse_env_var (getenv ("COMPILER_PATH"), &paths); 183 for (i = 0; i < n_paths; i++) 184 { 185 size_t len = strlen (paths[i]) + 1 + strlen (name) + 1; 186 target_compiler = XRESIZEVEC (char, target_compiler, len); 187 sprintf (target_compiler, "%s/%s", paths[i], name); 188 if (access_check (target_compiler, X_OK) == 0) 189 { 190 found = true; 191 break; 192 } 193 } 194 195out: 196 free_array_of_ptrs ((void **) paths, n_paths); 197 return found ? target_compiler : NULL; 198} 199 200static void 201compile_for_target (struct obstack *argv_obstack) 202{ 203 if (target_ilp32) 204 obstack_ptr_grow (argv_obstack, "-m32"); 205 else 206 obstack_ptr_grow (argv_obstack, "-m64"); 207 obstack_ptr_grow (argv_obstack, NULL); 208 char **argv = XOBFINISH (argv_obstack, char **); 209 210 /* Save environment variables. */ 211 const char *epath = getenv ("GCC_EXEC_PREFIX"); 212 const char *cpath = getenv ("COMPILER_PATH"); 213 const char *lpath = getenv ("LIBRARY_PATH"); 214 const char *rpath = getenv ("LD_RUN_PATH"); 215 unsetenv ("GCC_EXEC_PREFIX"); 216 unsetenv ("COMPILER_PATH"); 217 unsetenv ("LIBRARY_PATH"); 218 unsetenv ("LD_RUN_PATH"); 219 220 fork_execute (argv[0], argv, false); 221 obstack_free (argv_obstack, NULL); 222 223 /* Restore environment variables. */ 224 xputenv (concat ("GCC_EXEC_PREFIX=", epath, NULL)); 225 xputenv (concat ("COMPILER_PATH=", cpath, NULL)); 226 xputenv (concat ("LIBRARY_PATH=", lpath, NULL)); 227 xputenv (concat ("LD_RUN_PATH=", rpath, NULL)); 228} 229 230/* Generates object file with the descriptor for the target library. */ 231static const char * 232generate_target_descr_file (const char *target_compiler) 233{ 234 const char *src_filename = make_temp_file ("_target_descr.c"); 235 const char *obj_filename = make_temp_file ("_target_descr.o"); 236 temp_files[num_temps++] = src_filename; 237 temp_files[num_temps++] = obj_filename; 238 FILE *src_file = fopen (src_filename, "w"); 239 240 if (!src_file) 241 fatal_error (input_location, "cannot open '%s'", src_filename); 242 243 fprintf (src_file, 244 "extern void *__offload_funcs_end[];\n" 245 "extern void *__offload_vars_end[];\n\n" 246 247 "void *__offload_func_table[0]\n" 248 "__attribute__ ((__used__, visibility (\"hidden\"),\n" 249 "section (\".gnu.offload_funcs\"))) = { };\n\n" 250 251 "void *__offload_var_table[0]\n" 252 "__attribute__ ((__used__, visibility (\"hidden\"),\n" 253 "section (\".gnu.offload_vars\"))) = { };\n\n" 254 255 "void *__OFFLOAD_TARGET_TABLE__[]\n" 256 "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n" 257 " &__offload_func_table, &__offload_funcs_end,\n" 258 " &__offload_var_table, &__offload_vars_end\n" 259 "};\n\n"); 260 261 fprintf (src_file, 262 "#ifdef __cplusplus\n" 263 "extern \"C\"\n" 264 "#endif\n" 265 "void target_register_lib (const void *);\n\n" 266 267 "__attribute__((constructor))\n" 268 "static void\n" 269 "init (void)\n" 270 "{\n" 271 " target_register_lib (__OFFLOAD_TARGET_TABLE__);\n" 272 "}\n"); 273 fclose (src_file); 274 275 struct obstack argv_obstack; 276 obstack_init (&argv_obstack); 277 obstack_ptr_grow (&argv_obstack, target_compiler); 278 obstack_ptr_grow (&argv_obstack, "-c"); 279 obstack_ptr_grow (&argv_obstack, "-shared"); 280 obstack_ptr_grow (&argv_obstack, "-fPIC"); 281 obstack_ptr_grow (&argv_obstack, src_filename); 282 obstack_ptr_grow (&argv_obstack, "-o"); 283 obstack_ptr_grow (&argv_obstack, obj_filename); 284 compile_for_target (&argv_obstack); 285 286 return obj_filename; 287} 288 289/* Generates object file with __offload_*_end symbols for the target 290 library. */ 291static const char * 292generate_target_offloadend_file (const char *target_compiler) 293{ 294 const char *src_filename = make_temp_file ("_target_offloadend.c"); 295 const char *obj_filename = make_temp_file ("_target_offloadend.o"); 296 temp_files[num_temps++] = src_filename; 297 temp_files[num_temps++] = obj_filename; 298 FILE *src_file = fopen (src_filename, "w"); 299 300 if (!src_file) 301 fatal_error (input_location, "cannot open '%s'", src_filename); 302 303 fprintf (src_file, 304 "void *__offload_funcs_end[0]\n" 305 "__attribute__ ((__used__, visibility (\"hidden\"),\n" 306 "section (\".gnu.offload_funcs\"))) = { };\n\n" 307 308 "void *__offload_vars_end[0]\n" 309 "__attribute__ ((__used__, visibility (\"hidden\"),\n" 310 "section (\".gnu.offload_vars\"))) = { };\n"); 311 fclose (src_file); 312 313 struct obstack argv_obstack; 314 obstack_init (&argv_obstack); 315 obstack_ptr_grow (&argv_obstack, target_compiler); 316 obstack_ptr_grow (&argv_obstack, "-c"); 317 obstack_ptr_grow (&argv_obstack, "-shared"); 318 obstack_ptr_grow (&argv_obstack, "-fPIC"); 319 obstack_ptr_grow (&argv_obstack, src_filename); 320 obstack_ptr_grow (&argv_obstack, "-o"); 321 obstack_ptr_grow (&argv_obstack, obj_filename); 322 compile_for_target (&argv_obstack); 323 324 return obj_filename; 325} 326 327/* Generates object file with the host side descriptor. */ 328static const char * 329generate_host_descr_file (const char *host_compiler) 330{ 331 const char *src_filename = make_temp_file ("_host_descr.c"); 332 const char *obj_filename = make_temp_file ("_host_descr.o"); 333 temp_files[num_temps++] = src_filename; 334 temp_files[num_temps++] = obj_filename; 335 FILE *src_file = fopen (src_filename, "w"); 336 337 if (!src_file) 338 fatal_error (input_location, "cannot open '%s'", src_filename); 339 340 fprintf (src_file, 341 "extern void *__OFFLOAD_TABLE__;\n" 342 "extern void *__offload_image_intelmic_start;\n" 343 "extern void *__offload_image_intelmic_end;\n\n" 344 345 "static const void *__offload_target_data[] = {\n" 346 " &__offload_image_intelmic_start, &__offload_image_intelmic_end\n" 347 "};\n\n"); 348 349 fprintf (src_file, 350 "#ifdef __cplusplus\n" 351 "extern \"C\"\n" 352 "#endif\n" 353 "void GOMP_offload_register (void *, int, void *);\n" 354 "#ifdef __cplusplus\n" 355 "extern \"C\"\n" 356 "#endif\n" 357 "void GOMP_offload_unregister (void *, int, void *);\n\n" 358 359 "__attribute__((constructor))\n" 360 "static void\n" 361 "init (void)\n" 362 "{\n" 363 " GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n" 364 "}\n\n", GOMP_DEVICE_INTEL_MIC); 365 366 fprintf (src_file, 367 "__attribute__((destructor))\n" 368 "static void\n" 369 "fini (void)\n" 370 "{\n" 371 " GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n" 372 "}\n", GOMP_DEVICE_INTEL_MIC); 373 374 fclose (src_file); 375 376 unsigned new_argc = 0; 377 const char *new_argv[9]; 378 new_argv[new_argc++] = host_compiler; 379 new_argv[new_argc++] = "-c"; 380 new_argv[new_argc++] = "-fPIC"; 381 new_argv[new_argc++] = "-shared"; 382 if (target_ilp32) 383 new_argv[new_argc++] = "-m32"; 384 else 385 new_argv[new_argc++] = "-m64"; 386 new_argv[new_argc++] = src_filename; 387 new_argv[new_argc++] = "-o"; 388 new_argv[new_argc++] = obj_filename; 389 new_argv[new_argc++] = NULL; 390 391 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false); 392 393 return obj_filename; 394} 395 396static const char * 397prepare_target_image (const char *target_compiler, int argc, char **argv) 398{ 399 const char *target_descr_filename 400 = generate_target_descr_file (target_compiler); 401 const char *target_offloadend_filename 402 = generate_target_offloadend_file (target_compiler); 403 404 char *opt1 405 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename)); 406 char *opt2 407 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename)); 408 sprintf (opt1, "-Wl,%s", target_descr_filename); 409 sprintf (opt2, "-Wl,%s", target_offloadend_filename); 410 411 const char *target_so_filename = make_temp_file ("_offload_intelmic.so"); 412 temp_files[num_temps++] = target_so_filename; 413 struct obstack argv_obstack; 414 obstack_init (&argv_obstack); 415 obstack_ptr_grow (&argv_obstack, target_compiler); 416 obstack_ptr_grow (&argv_obstack, "-xlto"); 417 obstack_ptr_grow (&argv_obstack, "-shared"); 418 obstack_ptr_grow (&argv_obstack, "-fPIC"); 419 obstack_ptr_grow (&argv_obstack, opt1); 420 for (int i = 1; i < argc; i++) 421 { 422 if (!strcmp (argv[i], "-o") && i + 1 != argc) 423 out_obj_filename = argv[++i]; 424 else 425 obstack_ptr_grow (&argv_obstack, argv[i]); 426 } 427 if (!out_obj_filename) 428 fatal_error (input_location, "output file not specified"); 429 obstack_ptr_grow (&argv_obstack, opt2); 430 obstack_ptr_grow (&argv_obstack, "-o"); 431 obstack_ptr_grow (&argv_obstack, target_so_filename); 432 compile_for_target (&argv_obstack); 433 434 /* Run objcopy. */ 435 char *rename_section_opt 436 = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name)); 437 sprintf (rename_section_opt, ".data=%s", image_section_name); 438 const char *objcopy_argv[11]; 439 objcopy_argv[0] = "objcopy"; 440 objcopy_argv[1] = "-B"; 441 objcopy_argv[2] = "i386"; 442 objcopy_argv[3] = "-I"; 443 objcopy_argv[4] = "binary"; 444 objcopy_argv[5] = "-O"; 445 if (target_ilp32) 446 objcopy_argv[6] = "elf32-i386"; 447 else 448 objcopy_argv[6] = "elf64-x86-64"; 449 objcopy_argv[7] = target_so_filename; 450 objcopy_argv[8] = "--rename-section"; 451 objcopy_argv[9] = rename_section_opt; 452 objcopy_argv[10] = NULL; 453 fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false); 454 455 /* Objcopy has created symbols, containing the input file name with 456 non-alphanumeric characters replaced by underscores. 457 We are going to rename these new symbols. */ 458 size_t symbol_name_len = strlen (target_so_filename); 459 char *symbol_name = XALLOCAVEC (char, symbol_name_len + 1); 460 for (size_t i = 0; i < symbol_name_len; i++) 461 { 462 char c = target_so_filename[i]; 463 if (!ISALNUM (c)) 464 c = '_'; 465 symbol_name[i] = c; 466 } 467 symbol_name[symbol_name_len] = '\0'; 468 469 char *opt_for_objcopy[3]; 470 opt_for_objcopy[0] = XALLOCAVEC (char, sizeof ("_binary__start=") 471 + symbol_name_len 472 + strlen (symbols[0])); 473 opt_for_objcopy[1] = XALLOCAVEC (char, sizeof ("_binary__end=") 474 + symbol_name_len 475 + strlen (symbols[1])); 476 opt_for_objcopy[2] = XALLOCAVEC (char, sizeof ("_binary__size=") 477 + symbol_name_len 478 + strlen (symbols[2])); 479 sprintf (opt_for_objcopy[0], "_binary_%s_start=%s", symbol_name, symbols[0]); 480 sprintf (opt_for_objcopy[1], "_binary_%s_end=%s", symbol_name, symbols[1]); 481 sprintf (opt_for_objcopy[2], "_binary_%s_size=%s", symbol_name, symbols[2]); 482 483 objcopy_argv[0] = "objcopy"; 484 objcopy_argv[1] = target_so_filename; 485 objcopy_argv[2] = "--redefine-sym"; 486 objcopy_argv[3] = opt_for_objcopy[0]; 487 objcopy_argv[4] = "--redefine-sym"; 488 objcopy_argv[5] = opt_for_objcopy[1]; 489 objcopy_argv[6] = "--redefine-sym"; 490 objcopy_argv[7] = opt_for_objcopy[2]; 491 objcopy_argv[8] = NULL; 492 fork_execute (objcopy_argv[0], CONST_CAST (char **, objcopy_argv), false); 493 494 return target_so_filename; 495} 496 497int 498main (int argc, char **argv) 499{ 500 progname = "mkoffload-intelmic"; 501 gcc_init_libintl (); 502 diagnostic_initialize (global_dc, 0); 503 504 if (atexit (mkoffload_atexit) != 0) 505 fatal_error (input_location, "atexit failed"); 506 507 const char *host_compiler = getenv ("COLLECT_GCC"); 508 if (!host_compiler) 509 fatal_error (input_location, "COLLECT_GCC must be set"); 510 511 const char *target_driver_name = GCC_INSTALL_NAME; 512 char *target_compiler = find_target_compiler (target_driver_name); 513 if (target_compiler == NULL) 514 fatal_error (input_location, "offload compiler %s not found", 515 target_driver_name); 516 517 /* We may be called with all the arguments stored in some file and 518 passed with @file. Expand them into argv before processing. */ 519 expandargv (&argc, &argv); 520 521 /* Find out whether we should compile binaries for i386 or x86-64. */ 522 for (int i = argc - 1; i > 0; i--) 523 if (strncmp (argv[i], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0) 524 { 525 if (strstr (argv[i], "ilp32")) 526 target_ilp32 = true; 527 else if (!strstr (argv[i], "lp64")) 528 fatal_error (input_location, 529 "unrecognizable argument of option -foffload-abi"); 530 break; 531 } 532 533 const char *target_so_filename 534 = prepare_target_image (target_compiler, argc, argv); 535 536 const char *host_descr_filename = generate_host_descr_file (host_compiler); 537 538 /* Perform partial linking for the target image and host side descriptor. 539 As a result we'll get a finalized object file with all offload data. */ 540 unsigned new_argc = 0; 541 const char *new_argv[9]; 542 new_argv[new_argc++] = "ld"; 543 new_argv[new_argc++] = "-m"; 544 if (target_ilp32) 545 new_argv[new_argc++] = "elf_i386"; 546 else 547 new_argv[new_argc++] = "elf_x86_64"; 548 new_argv[new_argc++] = "--relocatable"; 549 new_argv[new_argc++] = host_descr_filename; 550 new_argv[new_argc++] = target_so_filename; 551 new_argv[new_argc++] = "-o"; 552 new_argv[new_argc++] = out_obj_filename; 553 new_argv[new_argc++] = NULL; 554 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false); 555 556 /* Run objcopy on the resultant object file to localize generated symbols 557 to avoid conflicting between different DSO and an executable. */ 558 new_argv[0] = "objcopy"; 559 new_argv[1] = "-L"; 560 new_argv[2] = symbols[0]; 561 new_argv[3] = "-L"; 562 new_argv[4] = symbols[1]; 563 new_argv[5] = "-L"; 564 new_argv[6] = symbols[2]; 565 new_argv[7] = out_obj_filename; 566 new_argv[8] = NULL; 567 fork_execute (new_argv[0], CONST_CAST (char **, new_argv), false); 568 569 return 0; 570} 571