1 /* Offload image generation tool for Intel MIC devices.
3 Copyright (C) 2014-2019 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/>. */
23 #define IN_TARGET_CODE 1
28 #include "coretypes.h"
31 #include "diagnostic.h"
32 #include "collect-utils.h"
33 #include "intelmic-offload.h"
35 const char tool_name
[] = "intelmic mkoffload";
37 const char image_section_name
[] = ".gnu.offload_images";
38 const char *symbols
[3] = { "__offload_image_intelmic_start",
39 "__offload_image_intelmic_end",
40 "__offload_image_intelmic_size" };
41 const char *out_obj_filename
= NULL
;
44 const int MAX_NUM_TEMPS
= 10;
45 const char *temp_files
[MAX_NUM_TEMPS
];
47 enum offload_abi offload_abi
= OFFLOAD_ABI_UNSET
;
49 /* Delete tempfiles and exit function. */
52 tool_cleanup (bool from_signal ATTRIBUTE_UNUSED
)
54 for (int i
= 0; i
< num_temps
; i
++)
55 maybe_unlink (temp_files
[i
]);
59 mkoffload_cleanup (void)
64 /* Unlink FILE unless requested otherwise. */
67 maybe_unlink (const char *file
)
71 if (unlink_if_ordinary (file
)
73 fatal_error (input_location
, "deleting file %s: %m", file
);
76 fprintf (stderr
, "[Leaving %s]\n", file
);
79 /* Add or change the value of an environment variable, outputting the
80 change to standard error if in verbose mode. */
82 xputenv (const char *string
)
85 fprintf (stderr
, "%s\n", string
);
86 putenv (CONST_CAST (char *, string
));
89 /* Parse STR, saving found tokens into PVALUES and return their number.
90 Tokens are assumed to be delimited by ':'. */
92 parse_env_var (const char *str
, char ***pvalues
)
94 const char *curval
, *nextval
;
98 curval
= strchr (str
, ':');
102 curval
= strchr (curval
+ 1, ':');
105 values
= (char **) xmalloc (num
* sizeof (char *));
107 nextval
= strchr (curval
, ':');
109 nextval
= strchr (curval
, '\0');
111 for (i
= 0; i
< num
; i
++)
113 int l
= nextval
- curval
;
114 values
[i
] = (char *) xmalloc (l
+ 1);
115 memcpy (values
[i
], curval
, l
);
117 curval
= nextval
+ 1;
118 nextval
= strchr (curval
, ':');
120 nextval
= strchr (curval
, '\0');
126 /* Auxiliary function that frees elements of PTR and PTR itself.
127 N is number of elements to be freed. If PTR is NULL, nothing is freed.
128 If an element is NULL, subsequent elements are not freed. */
130 free_array_of_ptrs (void **ptr
, unsigned n
)
135 for (i
= 0; i
< n
; i
++)
145 /* Check whether NAME can be accessed in MODE. This is like access,
146 except that it never considers directories to be executable. */
148 access_check (const char *name
, int mode
)
154 if (stat (name
, &st
) < 0 || S_ISDIR (st
.st_mode
))
158 return access (name
, mode
);
161 /* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH. */
163 find_target_compiler (const char *name
)
168 char *target_compiler
;
169 const char *collect_gcc
= getenv ("COLLECT_GCC");
170 const char *gcc_path
= dirname (ASTRDUP (collect_gcc
));
171 const char *gcc_exec
= basename (ASTRDUP (collect_gcc
));
173 if (strcmp (gcc_exec
, collect_gcc
) == 0)
175 /* collect_gcc has no path, so it was found in PATH. Make sure we also
176 find accel-gcc in PATH. */
177 target_compiler
= XDUPVEC (char, name
, strlen (name
) + 1);
182 target_compiler
= concat (gcc_path
, "/", name
, NULL
);
183 if (access_check (target_compiler
, X_OK
) == 0)
189 n_paths
= parse_env_var (getenv ("COMPILER_PATH"), &paths
);
190 for (i
= 0; i
< n_paths
; i
++)
192 size_t len
= strlen (paths
[i
]) + 1 + strlen (name
) + 1;
193 target_compiler
= XRESIZEVEC (char, target_compiler
, len
);
194 sprintf (target_compiler
, "%s/%s", paths
[i
], name
);
195 if (access_check (target_compiler
, X_OK
) == 0)
203 free_array_of_ptrs ((void **) paths
, n_paths
);
204 return found
? target_compiler
: NULL
;
208 compile_for_target (struct obstack
*argv_obstack
)
212 case OFFLOAD_ABI_LP64
:
213 obstack_ptr_grow (argv_obstack
, "-m64");
215 case OFFLOAD_ABI_ILP32
:
216 obstack_ptr_grow (argv_obstack
, "-m32");
221 obstack_ptr_grow (argv_obstack
, NULL
);
222 char **argv
= XOBFINISH (argv_obstack
, char **);
224 /* Save environment variables. */
225 const char *epath
= getenv ("GCC_EXEC_PREFIX");
226 const char *cpath
= getenv ("COMPILER_PATH");
227 const char *lpath
= getenv ("LIBRARY_PATH");
228 const char *rpath
= getenv ("LD_RUN_PATH");
229 unsetenv ("GCC_EXEC_PREFIX");
230 unsetenv ("COMPILER_PATH");
231 unsetenv ("LIBRARY_PATH");
232 unsetenv ("LD_RUN_PATH");
234 fork_execute (argv
[0], argv
, false);
235 obstack_free (argv_obstack
, NULL
);
237 /* Restore environment variables. */
238 xputenv (concat ("GCC_EXEC_PREFIX=", epath
, NULL
));
239 xputenv (concat ("COMPILER_PATH=", cpath
, NULL
));
240 xputenv (concat ("LIBRARY_PATH=", lpath
, NULL
));
241 xputenv (concat ("LD_RUN_PATH=", rpath
, NULL
));
244 /* Generates object file with the descriptor for the target library. */
246 generate_target_descr_file (const char *target_compiler
)
248 const char *src_filename
= make_temp_file ("_target_descr.c");
249 const char *obj_filename
= make_temp_file ("_target_descr.o");
250 temp_files
[num_temps
++] = src_filename
;
251 temp_files
[num_temps
++] = obj_filename
;
252 FILE *src_file
= fopen (src_filename
, "w");
255 fatal_error (input_location
, "cannot open '%s'", src_filename
);
258 "extern const void *const __offload_funcs_end[];\n"
259 "extern const void *const __offload_vars_end[];\n\n"
261 "const void *const __offload_func_table[0]\n"
262 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
263 "section (\".gnu.offload_funcs\"))) = { };\n\n"
265 "const void *const __offload_var_table[0]\n"
266 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
267 "section (\".gnu.offload_vars\"))) = { };\n\n"
269 "const void *const __OFFLOAD_TARGET_TABLE__[]\n"
270 "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n"
271 " &__offload_func_table, &__offload_funcs_end,\n"
272 " &__offload_var_table, &__offload_vars_end\n"
276 "#ifdef __cplusplus\n"
279 "void target_register_lib (const void *);\n\n"
281 "__attribute__((constructor))\n"
285 " target_register_lib (__OFFLOAD_TARGET_TABLE__);\n"
289 struct obstack argv_obstack
;
290 obstack_init (&argv_obstack
);
291 obstack_ptr_grow (&argv_obstack
, target_compiler
);
293 obstack_ptr_grow (&argv_obstack
, "-save-temps");
295 obstack_ptr_grow (&argv_obstack
, "-v");
296 obstack_ptr_grow (&argv_obstack
, "-c");
297 obstack_ptr_grow (&argv_obstack
, "-shared");
298 obstack_ptr_grow (&argv_obstack
, "-fPIC");
299 obstack_ptr_grow (&argv_obstack
, src_filename
);
300 obstack_ptr_grow (&argv_obstack
, "-o");
301 obstack_ptr_grow (&argv_obstack
, obj_filename
);
302 compile_for_target (&argv_obstack
);
307 /* Generates object file with __offload_*_end symbols for the target
310 generate_target_offloadend_file (const char *target_compiler
)
312 const char *src_filename
= make_temp_file ("_target_offloadend.c");
313 const char *obj_filename
= make_temp_file ("_target_offloadend.o");
314 temp_files
[num_temps
++] = src_filename
;
315 temp_files
[num_temps
++] = obj_filename
;
316 FILE *src_file
= fopen (src_filename
, "w");
319 fatal_error (input_location
, "cannot open '%s'", src_filename
);
322 "const void *const __offload_funcs_end[0]\n"
323 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
324 "section (\".gnu.offload_funcs\"))) = { };\n\n"
326 "const void *const __offload_vars_end[0]\n"
327 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
328 "section (\".gnu.offload_vars\"))) = { };\n");
331 struct obstack argv_obstack
;
332 obstack_init (&argv_obstack
);
333 obstack_ptr_grow (&argv_obstack
, target_compiler
);
335 obstack_ptr_grow (&argv_obstack
, "-save-temps");
337 obstack_ptr_grow (&argv_obstack
, "-v");
338 obstack_ptr_grow (&argv_obstack
, "-c");
339 obstack_ptr_grow (&argv_obstack
, "-shared");
340 obstack_ptr_grow (&argv_obstack
, "-fPIC");
341 obstack_ptr_grow (&argv_obstack
, src_filename
);
342 obstack_ptr_grow (&argv_obstack
, "-o");
343 obstack_ptr_grow (&argv_obstack
, obj_filename
);
344 compile_for_target (&argv_obstack
);
349 /* Generates object file with the host side descriptor. */
351 generate_host_descr_file (const char *host_compiler
)
353 const char *src_filename
= make_temp_file ("_host_descr.c");
354 const char *obj_filename
= make_temp_file ("_host_descr.o");
355 temp_files
[num_temps
++] = src_filename
;
356 temp_files
[num_temps
++] = obj_filename
;
357 FILE *src_file
= fopen (src_filename
, "w");
360 fatal_error (input_location
, "cannot open '%s'", src_filename
);
363 "extern const void *const __OFFLOAD_TABLE__;\n"
364 "extern const void *const __offload_image_intelmic_start;\n"
365 "extern const void *const __offload_image_intelmic_end;\n\n"
367 "static const void *const __offload_target_data[] = {\n"
368 " &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
372 "#ifdef __cplusplus\n"
375 "void GOMP_offload_register (const void *, int, const void *);\n"
376 "#ifdef __cplusplus\n"
379 "void GOMP_offload_unregister (const void *, int, const void *);\n\n"
381 "__attribute__((constructor))\n"
385 " GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
386 "}\n\n", GOMP_DEVICE_INTEL_MIC
);
389 "__attribute__((destructor))\n"
393 " GOMP_offload_unregister (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
394 "}\n", GOMP_DEVICE_INTEL_MIC
);
398 struct obstack argv_obstack
;
399 obstack_init (&argv_obstack
);
400 obstack_ptr_grow (&argv_obstack
, host_compiler
);
402 obstack_ptr_grow (&argv_obstack
, "-save-temps");
404 obstack_ptr_grow (&argv_obstack
, "-v");
405 obstack_ptr_grow (&argv_obstack
, "-c");
406 obstack_ptr_grow (&argv_obstack
, "-fPIC");
407 obstack_ptr_grow (&argv_obstack
, "-shared");
410 case OFFLOAD_ABI_LP64
:
411 obstack_ptr_grow (&argv_obstack
, "-m64");
413 case OFFLOAD_ABI_ILP32
:
414 obstack_ptr_grow (&argv_obstack
, "-m32");
419 obstack_ptr_grow (&argv_obstack
, src_filename
);
420 obstack_ptr_grow (&argv_obstack
, "-o");
421 obstack_ptr_grow (&argv_obstack
, obj_filename
);
422 obstack_ptr_grow (&argv_obstack
, NULL
);
424 char **argv
= XOBFINISH (&argv_obstack
, char **);
425 fork_execute (argv
[0], argv
, false);
426 obstack_free (&argv_obstack
, NULL
);
432 prepare_target_image (const char *target_compiler
, int argc
, char **argv
)
434 const char *target_descr_filename
435 = generate_target_descr_file (target_compiler
);
436 const char *target_offloadend_filename
437 = generate_target_offloadend_file (target_compiler
);
440 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename
));
442 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename
));
443 sprintf (opt1
, "-Wl,%s", target_descr_filename
);
444 sprintf (opt2
, "-Wl,%s", target_offloadend_filename
);
446 const char *target_so_filename
= make_temp_file ("_offload_intelmic.so");
447 temp_files
[num_temps
++] = target_so_filename
;
448 struct obstack argv_obstack
;
449 obstack_init (&argv_obstack
);
450 obstack_ptr_grow (&argv_obstack
, target_compiler
);
452 obstack_ptr_grow (&argv_obstack
, "-save-temps");
454 obstack_ptr_grow (&argv_obstack
, "-v");
455 obstack_ptr_grow (&argv_obstack
, "-xlto");
456 obstack_ptr_grow (&argv_obstack
, "-shared");
457 obstack_ptr_grow (&argv_obstack
, "-fPIC");
458 obstack_ptr_grow (&argv_obstack
, opt1
);
459 for (int i
= 1; i
< argc
; i
++)
461 if (!strcmp (argv
[i
], "-o") && i
+ 1 != argc
)
462 out_obj_filename
= argv
[++i
];
464 obstack_ptr_grow (&argv_obstack
, argv
[i
]);
466 if (!out_obj_filename
)
467 fatal_error (input_location
, "output file not specified");
468 obstack_ptr_grow (&argv_obstack
, opt2
);
469 obstack_ptr_grow (&argv_obstack
, "-o");
470 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
471 compile_for_target (&argv_obstack
);
474 char *rename_section_opt
475 = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name
));
476 sprintf (rename_section_opt
, ".data=%s", image_section_name
);
477 obstack_init (&argv_obstack
);
478 obstack_ptr_grow (&argv_obstack
, "objcopy");
479 obstack_ptr_grow (&argv_obstack
, "-B");
480 obstack_ptr_grow (&argv_obstack
, "i386");
481 obstack_ptr_grow (&argv_obstack
, "-I");
482 obstack_ptr_grow (&argv_obstack
, "binary");
483 obstack_ptr_grow (&argv_obstack
, "-O");
486 case OFFLOAD_ABI_LP64
:
487 obstack_ptr_grow (&argv_obstack
, "elf64-x86-64");
489 case OFFLOAD_ABI_ILP32
:
490 obstack_ptr_grow (&argv_obstack
, "elf32-i386");
495 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
496 obstack_ptr_grow (&argv_obstack
, "--rename-section");
497 obstack_ptr_grow (&argv_obstack
, rename_section_opt
);
498 obstack_ptr_grow (&argv_obstack
, NULL
);
499 char **new_argv
= XOBFINISH (&argv_obstack
, char **);
500 fork_execute (new_argv
[0], new_argv
, false);
501 obstack_free (&argv_obstack
, NULL
);
503 /* Objcopy has created symbols, containing the input file name with
504 non-alphanumeric characters replaced by underscores.
505 We are going to rename these new symbols. */
506 size_t symbol_name_len
= strlen (target_so_filename
);
507 char *symbol_name
= XALLOCAVEC (char, symbol_name_len
+ 1);
508 for (size_t i
= 0; i
< symbol_name_len
; i
++)
510 char c
= target_so_filename
[i
];
515 symbol_name
[symbol_name_len
] = '\0';
517 char *opt_for_objcopy
[3];
518 opt_for_objcopy
[0] = XALLOCAVEC (char, sizeof ("_binary__start=")
520 + strlen (symbols
[0]));
521 opt_for_objcopy
[1] = XALLOCAVEC (char, sizeof ("_binary__end=")
523 + strlen (symbols
[1]));
524 opt_for_objcopy
[2] = XALLOCAVEC (char, sizeof ("_binary__size=")
526 + strlen (symbols
[2]));
527 sprintf (opt_for_objcopy
[0], "_binary_%s_start=%s", symbol_name
, symbols
[0]);
528 sprintf (opt_for_objcopy
[1], "_binary_%s_end=%s", symbol_name
, symbols
[1]);
529 sprintf (opt_for_objcopy
[2], "_binary_%s_size=%s", symbol_name
, symbols
[2]);
531 obstack_init (&argv_obstack
);
532 obstack_ptr_grow (&argv_obstack
, "objcopy");
533 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
534 obstack_ptr_grow (&argv_obstack
, "--redefine-sym");
535 obstack_ptr_grow (&argv_obstack
, opt_for_objcopy
[0]);
536 obstack_ptr_grow (&argv_obstack
, "--redefine-sym");
537 obstack_ptr_grow (&argv_obstack
, opt_for_objcopy
[1]);
538 obstack_ptr_grow (&argv_obstack
, "--redefine-sym");
539 obstack_ptr_grow (&argv_obstack
, opt_for_objcopy
[2]);
540 obstack_ptr_grow (&argv_obstack
, NULL
);
541 new_argv
= XOBFINISH (&argv_obstack
, char **);
542 fork_execute (new_argv
[0], new_argv
, false);
543 obstack_free (&argv_obstack
, NULL
);
545 return target_so_filename
;
549 main (int argc
, char **argv
)
551 progname
= "mkoffload-intelmic";
553 diagnostic_initialize (global_dc
, 0);
555 if (atexit (mkoffload_cleanup
) != 0)
556 fatal_error (input_location
, "atexit failed");
558 const char *host_compiler
= getenv ("COLLECT_GCC");
560 fatal_error (input_location
, "COLLECT_GCC must be set");
562 const char *target_driver_name
= GCC_INSTALL_NAME
;
563 char *target_compiler
= find_target_compiler (target_driver_name
);
564 if (target_compiler
== NULL
)
565 fatal_error (input_location
, "offload compiler %s not found",
568 /* We may be called with all the arguments stored in some file and
569 passed with @file. Expand them into argv before processing. */
570 expandargv (&argc
, &argv
);
572 /* Scan the argument vector. */
573 for (int i
= 1; i
< argc
; i
++)
575 #define STR "-foffload-abi="
576 if (strncmp (argv
[i
], STR
, strlen (STR
)) == 0)
578 if (strcmp (argv
[i
] + strlen (STR
), "lp64") == 0)
579 offload_abi
= OFFLOAD_ABI_LP64
;
580 else if (strcmp (argv
[i
] + strlen (STR
), "ilp32") == 0)
581 offload_abi
= OFFLOAD_ABI_ILP32
;
583 fatal_error (input_location
,
584 "unrecognizable argument of option " STR
);
587 else if (strcmp (argv
[i
], "-save-temps") == 0)
589 else if (strcmp (argv
[i
], "-v") == 0)
593 const char *target_so_filename
594 = prepare_target_image (target_compiler
, argc
, argv
);
596 const char *host_descr_filename
= generate_host_descr_file (host_compiler
);
598 /* Perform partial linking for the target image and host side descriptor.
599 As a result we'll get a finalized object file with all offload data. */
600 struct obstack argv_obstack
;
601 obstack_init (&argv_obstack
);
602 obstack_ptr_grow (&argv_obstack
, "ld");
603 obstack_ptr_grow (&argv_obstack
, "-m");
606 case OFFLOAD_ABI_LP64
:
607 obstack_ptr_grow (&argv_obstack
, "elf_x86_64");
609 case OFFLOAD_ABI_ILP32
:
610 obstack_ptr_grow (&argv_obstack
, "elf_i386");
615 obstack_ptr_grow (&argv_obstack
, "--relocatable");
616 obstack_ptr_grow (&argv_obstack
, host_descr_filename
);
617 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
618 obstack_ptr_grow (&argv_obstack
, "-o");
619 obstack_ptr_grow (&argv_obstack
, out_obj_filename
);
620 obstack_ptr_grow (&argv_obstack
, NULL
);
621 char **new_argv
= XOBFINISH (&argv_obstack
, char **);
622 fork_execute (new_argv
[0], new_argv
, false);
623 obstack_free (&argv_obstack
, NULL
);
625 /* Run objcopy on the resultant object file to localize generated symbols
626 to avoid conflicting between different DSO and an executable. */
627 obstack_init (&argv_obstack
);
628 obstack_ptr_grow (&argv_obstack
, "objcopy");
629 obstack_ptr_grow (&argv_obstack
, "-L");
630 obstack_ptr_grow (&argv_obstack
, symbols
[0]);
631 obstack_ptr_grow (&argv_obstack
, "-L");
632 obstack_ptr_grow (&argv_obstack
, symbols
[1]);
633 obstack_ptr_grow (&argv_obstack
, "-L");
634 obstack_ptr_grow (&argv_obstack
, symbols
[2]);
635 obstack_ptr_grow (&argv_obstack
, out_obj_filename
);
636 obstack_ptr_grow (&argv_obstack
, NULL
);
637 new_argv
= XOBFINISH (&argv_obstack
, char **);
638 fork_execute (new_argv
[0], new_argv
, false);
639 obstack_free (&argv_obstack
, NULL
);