1 /* Offload image generation tool for Intel MIC devices.
3 Copyright (C) 2014-2015 Free Software Foundation, Inc.
5 Contributed by Ilya Verbin <ilya.verbin@intel.com>.
7 This file is part of GCC.
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)
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.
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/>. */
26 #include "coretypes.h"
29 #include "diagnostic.h"
30 #include "collect-utils.h"
31 #include "intelmic-offload.h"
33 const char tool_name
[] = "intelmic mkoffload";
35 const char image_section_name
[] = ".gnu.offload_images";
36 const char *symbols
[3] = { "__offload_image_intelmic_start",
37 "__offload_image_intelmic_end",
38 "__offload_image_intelmic_size" };
39 const char *out_obj_filename
= NULL
;
42 const int MAX_NUM_TEMPS
= 10;
43 const char *temp_files
[MAX_NUM_TEMPS
];
45 enum offload_abi offload_abi
= OFFLOAD_ABI_UNSET
;
47 /* Delete tempfiles and exit function. */
50 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED
)
52 for (int i
= 0; i
< num_temps
; i
++)
53 maybe_unlink (temp_files
[i
]);
57 mkoffload_cleanup (void)
62 /* Unlink FILE unless requested otherwise. */
65 maybe_unlink (const char *file
)
69 if (unlink_if_ordinary (file
)
71 fatal_error (input_location
, "deleting file %s: %m", file
);
74 fprintf (stderr
, "[Leaving %s]\n", file
);
77 /* Add or change the value of an environment variable, outputting the
78 change to standard error if in verbose mode. */
80 xputenv (const char *string
)
83 fprintf (stderr
, "%s\n", string
);
84 putenv (CONST_CAST (char *, string
));
87 /* Parse STR, saving found tokens into PVALUES and return their number.
88 Tokens are assumed to be delimited by ':'. */
90 parse_env_var (const char *str
, char ***pvalues
)
92 const char *curval
, *nextval
;
96 curval
= strchr (str
, ':');
100 curval
= strchr (curval
+ 1, ':');
103 values
= (char **) xmalloc (num
* sizeof (char *));
105 nextval
= strchr (curval
, ':');
107 nextval
= strchr (curval
, '\0');
109 for (i
= 0; i
< num
; i
++)
111 int l
= nextval
- curval
;
112 values
[i
] = (char *) xmalloc (l
+ 1);
113 memcpy (values
[i
], curval
, l
);
115 curval
= nextval
+ 1;
116 nextval
= strchr (curval
, ':');
118 nextval
= strchr (curval
, '\0');
124 /* Auxiliary function that frees elements of PTR and PTR itself.
125 N is number of elements to be freed. If PTR is NULL, nothing is freed.
126 If an element is NULL, subsequent elements are not freed. */
128 free_array_of_ptrs (void **ptr
, unsigned n
)
133 for (i
= 0; i
< n
; i
++)
143 /* Check whether NAME can be accessed in MODE. This is like access,
144 except that it never considers directories to be executable. */
146 access_check (const char *name
, int mode
)
152 if (stat (name
, &st
) < 0 || S_ISDIR (st
.st_mode
))
156 return access (name
, mode
);
159 /* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH. */
161 find_target_compiler (const char *name
)
166 char *target_compiler
;
167 const char *collect_gcc
= getenv ("COLLECT_GCC");
168 const char *gcc_path
= dirname (ASTRDUP (collect_gcc
));
169 const char *gcc_exec
= basename (ASTRDUP (collect_gcc
));
171 if (strcmp (gcc_exec
, collect_gcc
) == 0)
173 /* collect_gcc has no path, so it was found in PATH. Make sure we also
174 find accel-gcc in PATH. */
175 target_compiler
= XDUPVEC (char, name
, strlen (name
) + 1);
180 target_compiler
= concat (gcc_path
, "/", name
, NULL
);
181 if (access_check (target_compiler
, X_OK
) == 0)
187 n_paths
= parse_env_var (getenv ("COMPILER_PATH"), &paths
);
188 for (i
= 0; i
< n_paths
; i
++)
190 size_t len
= strlen (paths
[i
]) + 1 + strlen (name
) + 1;
191 target_compiler
= XRESIZEVEC (char, target_compiler
, len
);
192 sprintf (target_compiler
, "%s/%s", paths
[i
], name
);
193 if (access_check (target_compiler
, X_OK
) == 0)
201 free_array_of_ptrs ((void **) paths
, n_paths
);
202 return found
? target_compiler
: NULL
;
206 compile_for_target (struct obstack
*argv_obstack
)
210 case OFFLOAD_ABI_LP64
:
211 obstack_ptr_grow (argv_obstack
, "-m64");
213 case OFFLOAD_ABI_ILP32
:
214 obstack_ptr_grow (argv_obstack
, "-m32");
219 obstack_ptr_grow (argv_obstack
, NULL
);
220 char **argv
= XOBFINISH (argv_obstack
, char **);
222 /* Save environment variables. */
223 const char *epath
= getenv ("GCC_EXEC_PREFIX");
224 const char *cpath
= getenv ("COMPILER_PATH");
225 const char *lpath
= getenv ("LIBRARY_PATH");
226 const char *rpath
= getenv ("LD_RUN_PATH");
227 unsetenv ("GCC_EXEC_PREFIX");
228 unsetenv ("COMPILER_PATH");
229 unsetenv ("LIBRARY_PATH");
230 unsetenv ("LD_RUN_PATH");
232 fork_execute (argv
[0], argv
, false);
233 obstack_free (argv_obstack
, NULL
);
235 /* Restore environment variables. */
236 xputenv (concat ("GCC_EXEC_PREFIX=", epath
, NULL
));
237 xputenv (concat ("COMPILER_PATH=", cpath
, NULL
));
238 xputenv (concat ("LIBRARY_PATH=", lpath
, NULL
));
239 xputenv (concat ("LD_RUN_PATH=", rpath
, NULL
));
242 /* Generates object file with the descriptor for the target library. */
244 generate_target_descr_file (const char *target_compiler
)
246 const char *src_filename
= make_temp_file ("_target_descr.c");
247 const char *obj_filename
= make_temp_file ("_target_descr.o");
248 temp_files
[num_temps
++] = src_filename
;
249 temp_files
[num_temps
++] = obj_filename
;
250 FILE *src_file
= fopen (src_filename
, "w");
253 fatal_error (input_location
, "cannot open '%s'", src_filename
);
256 "extern const void *const __offload_funcs_end[];\n"
257 "extern const void *const __offload_vars_end[];\n\n"
259 "const void *const __offload_func_table[0]\n"
260 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
261 "section (\".gnu.offload_funcs\"))) = { };\n\n"
263 "const void *const __offload_var_table[0]\n"
264 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
265 "section (\".gnu.offload_vars\"))) = { };\n\n"
267 "const void *const __OFFLOAD_TARGET_TABLE__[]\n"
268 "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n"
269 " &__offload_func_table, &__offload_funcs_end,\n"
270 " &__offload_var_table, &__offload_vars_end\n"
274 "#ifdef __cplusplus\n"
277 "void target_register_lib (const void *);\n\n"
279 "__attribute__((constructor))\n"
283 " target_register_lib (__OFFLOAD_TARGET_TABLE__);\n"
287 struct obstack argv_obstack
;
288 obstack_init (&argv_obstack
);
289 obstack_ptr_grow (&argv_obstack
, target_compiler
);
291 obstack_ptr_grow (&argv_obstack
, "-save-temps");
293 obstack_ptr_grow (&argv_obstack
, "-v");
294 obstack_ptr_grow (&argv_obstack
, "-c");
295 obstack_ptr_grow (&argv_obstack
, "-shared");
296 obstack_ptr_grow (&argv_obstack
, "-fPIC");
297 obstack_ptr_grow (&argv_obstack
, src_filename
);
298 obstack_ptr_grow (&argv_obstack
, "-o");
299 obstack_ptr_grow (&argv_obstack
, obj_filename
);
300 compile_for_target (&argv_obstack
);
305 /* Generates object file with __offload_*_end symbols for the target
308 generate_target_offloadend_file (const char *target_compiler
)
310 const char *src_filename
= make_temp_file ("_target_offloadend.c");
311 const char *obj_filename
= make_temp_file ("_target_offloadend.o");
312 temp_files
[num_temps
++] = src_filename
;
313 temp_files
[num_temps
++] = obj_filename
;
314 FILE *src_file
= fopen (src_filename
, "w");
317 fatal_error (input_location
, "cannot open '%s'", src_filename
);
320 "const void *const __offload_funcs_end[0]\n"
321 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
322 "section (\".gnu.offload_funcs\"))) = { };\n\n"
324 "const void *const __offload_vars_end[0]\n"
325 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
326 "section (\".gnu.offload_vars\"))) = { };\n");
329 struct obstack argv_obstack
;
330 obstack_init (&argv_obstack
);
331 obstack_ptr_grow (&argv_obstack
, target_compiler
);
333 obstack_ptr_grow (&argv_obstack
, "-save-temps");
335 obstack_ptr_grow (&argv_obstack
, "-v");
336 obstack_ptr_grow (&argv_obstack
, "-c");
337 obstack_ptr_grow (&argv_obstack
, "-shared");
338 obstack_ptr_grow (&argv_obstack
, "-fPIC");
339 obstack_ptr_grow (&argv_obstack
, src_filename
);
340 obstack_ptr_grow (&argv_obstack
, "-o");
341 obstack_ptr_grow (&argv_obstack
, obj_filename
);
342 compile_for_target (&argv_obstack
);
347 /* Generates object file with the host side descriptor. */
349 generate_host_descr_file (const char *host_compiler
)
351 const char *src_filename
= make_temp_file ("_host_descr.c");
352 const char *obj_filename
= make_temp_file ("_host_descr.o");
353 temp_files
[num_temps
++] = src_filename
;
354 temp_files
[num_temps
++] = obj_filename
;
355 FILE *src_file
= fopen (src_filename
, "w");
358 fatal_error (input_location
, "cannot open '%s'", src_filename
);
361 "extern const void *const __OFFLOAD_TABLE__;\n"
362 "extern const void *const __offload_image_intelmic_start;\n"
363 "extern const void *const __offload_image_intelmic_end;\n\n"
365 "static const void *const __offload_target_data[] = {\n"
366 " &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
370 "#ifdef __cplusplus\n"
373 "void GOMP_offload_register (const void *, int, const void *);\n"
374 "#ifdef __cplusplus\n"
377 "void GOMP_offload_unregister (const void *, int, const void *);\n\n"
379 "__attribute__((constructor))\n"
383 " GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
384 "}\n\n", GOMP_DEVICE_INTEL_MIC
);
387 "__attribute__((destructor))\n"
391 " GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
392 "}\n", GOMP_DEVICE_INTEL_MIC
);
396 struct obstack argv_obstack
;
397 obstack_init (&argv_obstack
);
398 obstack_ptr_grow (&argv_obstack
, host_compiler
);
400 obstack_ptr_grow (&argv_obstack
, "-save-temps");
402 obstack_ptr_grow (&argv_obstack
, "-v");
403 obstack_ptr_grow (&argv_obstack
, "-c");
404 obstack_ptr_grow (&argv_obstack
, "-fPIC");
405 obstack_ptr_grow (&argv_obstack
, "-shared");
408 case OFFLOAD_ABI_LP64
:
409 obstack_ptr_grow (&argv_obstack
, "-m64");
411 case OFFLOAD_ABI_ILP32
:
412 obstack_ptr_grow (&argv_obstack
, "-m32");
417 obstack_ptr_grow (&argv_obstack
, src_filename
);
418 obstack_ptr_grow (&argv_obstack
, "-o");
419 obstack_ptr_grow (&argv_obstack
, obj_filename
);
420 obstack_ptr_grow (&argv_obstack
, NULL
);
422 char **argv
= XOBFINISH (&argv_obstack
, char **);
423 fork_execute (argv
[0], argv
, false);
424 obstack_free (&argv_obstack
, NULL
);
430 prepare_target_image (const char *target_compiler
, int argc
, char **argv
)
432 const char *target_descr_filename
433 = generate_target_descr_file (target_compiler
);
434 const char *target_offloadend_filename
435 = generate_target_offloadend_file (target_compiler
);
438 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename
));
440 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename
));
441 sprintf (opt1
, "-Wl,%s", target_descr_filename
);
442 sprintf (opt2
, "-Wl,%s", target_offloadend_filename
);
444 const char *target_so_filename
= make_temp_file ("_offload_intelmic.so");
445 temp_files
[num_temps
++] = target_so_filename
;
446 struct obstack argv_obstack
;
447 obstack_init (&argv_obstack
);
448 obstack_ptr_grow (&argv_obstack
, target_compiler
);
450 obstack_ptr_grow (&argv_obstack
, "-save-temps");
452 obstack_ptr_grow (&argv_obstack
, "-v");
453 obstack_ptr_grow (&argv_obstack
, "-xlto");
454 obstack_ptr_grow (&argv_obstack
, "-shared");
455 obstack_ptr_grow (&argv_obstack
, "-fPIC");
456 obstack_ptr_grow (&argv_obstack
, opt1
);
457 for (int i
= 1; i
< argc
; i
++)
459 if (!strcmp (argv
[i
], "-o") && i
+ 1 != argc
)
460 out_obj_filename
= argv
[++i
];
462 obstack_ptr_grow (&argv_obstack
, argv
[i
]);
464 if (!out_obj_filename
)
465 fatal_error (input_location
, "output file not specified");
466 obstack_ptr_grow (&argv_obstack
, opt2
);
467 obstack_ptr_grow (&argv_obstack
, "-o");
468 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
469 compile_for_target (&argv_obstack
);
472 char *rename_section_opt
473 = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name
));
474 sprintf (rename_section_opt
, ".data=%s", image_section_name
);
475 obstack_init (&argv_obstack
);
476 obstack_ptr_grow (&argv_obstack
, "objcopy");
477 obstack_ptr_grow (&argv_obstack
, "-B");
478 obstack_ptr_grow (&argv_obstack
, "i386");
479 obstack_ptr_grow (&argv_obstack
, "-I");
480 obstack_ptr_grow (&argv_obstack
, "binary");
481 obstack_ptr_grow (&argv_obstack
, "-O");
484 case OFFLOAD_ABI_LP64
:
485 obstack_ptr_grow (&argv_obstack
, "elf64-x86-64");
487 case OFFLOAD_ABI_ILP32
:
488 obstack_ptr_grow (&argv_obstack
, "elf32-i386");
493 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
494 obstack_ptr_grow (&argv_obstack
, "--rename-section");
495 obstack_ptr_grow (&argv_obstack
, rename_section_opt
);
496 obstack_ptr_grow (&argv_obstack
, NULL
);
497 char **new_argv
= XOBFINISH (&argv_obstack
, char **);
498 fork_execute (new_argv
[0], new_argv
, false);
499 obstack_free (&argv_obstack
, NULL
);
501 /* Objcopy has created symbols, containing the input file name with
502 non-alphanumeric characters replaced by underscores.
503 We are going to rename these new symbols. */
504 size_t symbol_name_len
= strlen (target_so_filename
);
505 char *symbol_name
= XALLOCAVEC (char, symbol_name_len
+ 1);
506 for (size_t i
= 0; i
< symbol_name_len
; i
++)
508 char c
= target_so_filename
[i
];
513 symbol_name
[symbol_name_len
] = '\0';
515 char *opt_for_objcopy
[3];
516 opt_for_objcopy
[0] = XALLOCAVEC (char, sizeof ("_binary__start=")
518 + strlen (symbols
[0]));
519 opt_for_objcopy
[1] = XALLOCAVEC (char, sizeof ("_binary__end=")
521 + strlen (symbols
[1]));
522 opt_for_objcopy
[2] = XALLOCAVEC (char, sizeof ("_binary__size=")
524 + strlen (symbols
[2]));
525 sprintf (opt_for_objcopy
[0], "_binary_%s_start=%s", symbol_name
, symbols
[0]);
526 sprintf (opt_for_objcopy
[1], "_binary_%s_end=%s", symbol_name
, symbols
[1]);
527 sprintf (opt_for_objcopy
[2], "_binary_%s_size=%s", symbol_name
, symbols
[2]);
529 obstack_init (&argv_obstack
);
530 obstack_ptr_grow (&argv_obstack
, "objcopy");
531 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
532 obstack_ptr_grow (&argv_obstack
, "--redefine-sym");
533 obstack_ptr_grow (&argv_obstack
, opt_for_objcopy
[0]);
534 obstack_ptr_grow (&argv_obstack
, "--redefine-sym");
535 obstack_ptr_grow (&argv_obstack
, opt_for_objcopy
[1]);
536 obstack_ptr_grow (&argv_obstack
, "--redefine-sym");
537 obstack_ptr_grow (&argv_obstack
, opt_for_objcopy
[2]);
538 obstack_ptr_grow (&argv_obstack
, NULL
);
539 new_argv
= XOBFINISH (&argv_obstack
, char **);
540 fork_execute (new_argv
[0], new_argv
, false);
541 obstack_free (&argv_obstack
, NULL
);
543 return target_so_filename
;
547 main (int argc
, char **argv
)
549 progname
= "mkoffload-intelmic";
551 diagnostic_initialize (global_dc
, 0);
553 if (atexit (mkoffload_cleanup
) != 0)
554 fatal_error (input_location
, "atexit failed");
556 const char *host_compiler
= getenv ("COLLECT_GCC");
558 fatal_error (input_location
, "COLLECT_GCC must be set");
560 const char *target_driver_name
= GCC_INSTALL_NAME
;
561 char *target_compiler
= find_target_compiler (target_driver_name
);
562 if (target_compiler
== NULL
)
563 fatal_error (input_location
, "offload compiler %s not found",
566 /* We may be called with all the arguments stored in some file and
567 passed with @file. Expand them into argv before processing. */
568 expandargv (&argc
, &argv
);
570 /* Scan the argument vector. */
571 for (int i
= 1; i
< argc
; i
++)
573 #define STR "-foffload-abi="
574 if (strncmp (argv
[i
], STR
, strlen (STR
)) == 0)
576 if (strcmp (argv
[i
] + strlen (STR
), "lp64") == 0)
577 offload_abi
= OFFLOAD_ABI_LP64
;
578 else if (strcmp (argv
[i
] + strlen (STR
), "ilp32") == 0)
579 offload_abi
= OFFLOAD_ABI_ILP32
;
581 fatal_error (input_location
,
582 "unrecognizable argument of option " STR
);
585 else if (strcmp (argv
[i
], "-save-temps") == 0)
587 else if (strcmp (argv
[i
], "-v") == 0)
591 const char *target_so_filename
592 = prepare_target_image (target_compiler
, argc
, argv
);
594 const char *host_descr_filename
= generate_host_descr_file (host_compiler
);
596 /* Perform partial linking for the target image and host side descriptor.
597 As a result we'll get a finalized object file with all offload data. */
598 struct obstack argv_obstack
;
599 obstack_init (&argv_obstack
);
600 obstack_ptr_grow (&argv_obstack
, "ld");
601 obstack_ptr_grow (&argv_obstack
, "-m");
604 case OFFLOAD_ABI_LP64
:
605 obstack_ptr_grow (&argv_obstack
, "elf_x86_64");
607 case OFFLOAD_ABI_ILP32
:
608 obstack_ptr_grow (&argv_obstack
, "elf_i386");
613 obstack_ptr_grow (&argv_obstack
, "--relocatable");
614 obstack_ptr_grow (&argv_obstack
, host_descr_filename
);
615 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
616 obstack_ptr_grow (&argv_obstack
, "-o");
617 obstack_ptr_grow (&argv_obstack
, out_obj_filename
);
618 obstack_ptr_grow (&argv_obstack
, NULL
);
619 char **new_argv
= XOBFINISH (&argv_obstack
, char **);
620 fork_execute (new_argv
[0], new_argv
, false);
621 obstack_free (&argv_obstack
, NULL
);
623 /* Run objcopy on the resultant object file to localize generated symbols
624 to avoid conflicting between different DSO and an executable. */
625 obstack_init (&argv_obstack
);
626 obstack_ptr_grow (&argv_obstack
, "objcopy");
627 obstack_ptr_grow (&argv_obstack
, "-L");
628 obstack_ptr_grow (&argv_obstack
, symbols
[0]);
629 obstack_ptr_grow (&argv_obstack
, "-L");
630 obstack_ptr_grow (&argv_obstack
, symbols
[1]);
631 obstack_ptr_grow (&argv_obstack
, "-L");
632 obstack_ptr_grow (&argv_obstack
, symbols
[2]);
633 obstack_ptr_grow (&argv_obstack
, out_obj_filename
);
634 obstack_ptr_grow (&argv_obstack
, NULL
);
635 new_argv
= XOBFINISH (&argv_obstack
, char **);
636 fork_execute (new_argv
[0], new_argv
, false);
637 obstack_free (&argv_obstack
, NULL
);