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 <libgomp_target.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 /* Shows if we should compile binaries for i386 instead of x86-64. */
46 bool target_ilp32
= false;
48 /* 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_atexit (void)
62 /* Unlink FILE unless we are debugging. */
64 maybe_unlink (const char *file
)
67 notice ("[Leaving %s]\n", file
);
69 unlink_if_ordinary (file
);
72 /* Add or change the value of an environment variable, outputting the
73 change to standard error if in verbose mode. */
75 xputenv (const char *string
)
78 fprintf (stderr
, "%s\n", string
);
79 putenv (CONST_CAST (char *, string
));
82 /* Parse STR, saving found tokens into PVALUES and return their number.
83 Tokens are assumed to be delimited by ':'. */
85 parse_env_var (const char *str
, char ***pvalues
)
87 const char *curval
, *nextval
;
91 curval
= strchr (str
, ':');
95 curval
= strchr (curval
+ 1, ':');
98 values
= (char **) xmalloc (num
* sizeof (char *));
100 nextval
= strchr (curval
, ':');
102 nextval
= strchr (curval
, '\0');
104 for (i
= 0; i
< num
; i
++)
106 int l
= nextval
- curval
;
107 values
[i
] = (char *) xmalloc (l
+ 1);
108 memcpy (values
[i
], curval
, l
);
110 curval
= nextval
+ 1;
111 nextval
= strchr (curval
, ':');
113 nextval
= strchr (curval
, '\0');
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. */
123 free_array_of_ptrs (void **ptr
, unsigned n
)
128 for (i
= 0; i
< n
; i
++)
138 /* Check whether NAME can be accessed in MODE. This is like access,
139 except that it never considers directories to be executable. */
141 access_check (const char *name
, int mode
)
147 if (stat (name
, &st
) < 0 || S_ISDIR (st
.st_mode
))
151 return access (name
, mode
);
154 /* Find target compiler using a path from COLLECT_GCC or COMPILER_PATH. */
156 find_target_compiler (const char *name
)
161 const char *collect_path
= dirname (ASTRDUP (getenv ("COLLECT_GCC")));
162 size_t len
= strlen (collect_path
) + 1 + strlen (name
) + 1;
163 char *target_compiler
= XNEWVEC (char, len
);
164 sprintf (target_compiler
, "%s/%s", collect_path
, name
);
165 if (access_check (target_compiler
, X_OK
) == 0)
171 n_paths
= parse_env_var (getenv ("COMPILER_PATH"), &paths
);
172 for (i
= 0; i
< n_paths
; i
++)
174 len
= strlen (paths
[i
]) + 1 + strlen (name
) + 1;
175 target_compiler
= XRESIZEVEC (char, target_compiler
, len
);
176 sprintf (target_compiler
, "%s/%s", paths
[i
], name
);
177 if (access_check (target_compiler
, X_OK
) == 0)
185 free_array_of_ptrs ((void **) paths
, n_paths
);
186 return found
? target_compiler
: NULL
;
190 compile_for_target (struct obstack
*argv_obstack
)
193 obstack_ptr_grow (argv_obstack
, "-m32");
195 obstack_ptr_grow (argv_obstack
, "-m64");
196 obstack_ptr_grow (argv_obstack
, NULL
);
197 char **argv
= XOBFINISH (argv_obstack
, char **);
199 /* Save environment variables. */
200 const char *epath
= getenv ("GCC_EXEC_PREFIX");
201 const char *cpath
= getenv ("COMPILER_PATH");
202 const char *lpath
= getenv ("LIBRARY_PATH");
203 const char *rpath
= getenv ("LD_RUN_PATH");
204 unsetenv ("GCC_EXEC_PREFIX");
205 unsetenv ("COMPILER_PATH");
206 unsetenv ("LIBRARY_PATH");
207 unsetenv ("LD_RUN_PATH");
209 fork_execute (argv
[0], argv
, false);
210 obstack_free (argv_obstack
, NULL
);
212 /* Restore environment variables. */
213 xputenv (concat ("GCC_EXEC_PREFIX=", epath
, NULL
));
214 xputenv (concat ("COMPILER_PATH=", cpath
, NULL
));
215 xputenv (concat ("LIBRARY_PATH=", lpath
, NULL
));
216 xputenv (concat ("LD_RUN_PATH=", rpath
, NULL
));
219 /* Generates object file with the descriptor for the target library. */
221 generate_target_descr_file (const char *target_compiler
)
223 const char *src_filename
= make_temp_file ("_target_descr.c");
224 const char *obj_filename
= make_temp_file ("_target_descr.o");
225 temp_files
[num_temps
++] = src_filename
;
226 temp_files
[num_temps
++] = obj_filename
;
227 FILE *src_file
= fopen (src_filename
, "w");
230 fatal_error ("cannot open '%s'", src_filename
);
233 "extern void *__offload_funcs_end[];\n"
234 "extern void *__offload_vars_end[];\n\n"
236 "void *__offload_func_table[0]\n"
237 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
238 "section (\".gnu.offload_funcs\"))) = { };\n\n"
240 "void *__offload_var_table[0]\n"
241 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
242 "section (\".gnu.offload_vars\"))) = { };\n\n"
244 "void *__OFFLOAD_TARGET_TABLE__[]\n"
245 "__attribute__ ((__used__, visibility (\"hidden\"))) = {\n"
246 " &__offload_func_table, &__offload_funcs_end,\n"
247 " &__offload_var_table, &__offload_vars_end\n"
251 "#ifdef __cplusplus\n"
254 "void target_register_lib (const void *);\n\n"
256 "__attribute__((constructor))\n"
260 " target_register_lib (__OFFLOAD_TARGET_TABLE__);\n"
264 struct obstack argv_obstack
;
265 obstack_init (&argv_obstack
);
266 obstack_ptr_grow (&argv_obstack
, target_compiler
);
267 obstack_ptr_grow (&argv_obstack
, "-c");
268 obstack_ptr_grow (&argv_obstack
, "-shared");
269 obstack_ptr_grow (&argv_obstack
, "-fPIC");
270 obstack_ptr_grow (&argv_obstack
, src_filename
);
271 obstack_ptr_grow (&argv_obstack
, "-o");
272 obstack_ptr_grow (&argv_obstack
, obj_filename
);
273 compile_for_target (&argv_obstack
);
278 /* Generates object file with __offload_*_end symbols for the target
281 generate_target_offloadend_file (const char *target_compiler
)
283 const char *src_filename
= make_temp_file ("_target_offloadend.c");
284 const char *obj_filename
= make_temp_file ("_target_offloadend.o");
285 temp_files
[num_temps
++] = src_filename
;
286 temp_files
[num_temps
++] = obj_filename
;
287 FILE *src_file
= fopen (src_filename
, "w");
290 fatal_error ("cannot open '%s'", src_filename
);
293 "void *__offload_funcs_end[0]\n"
294 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
295 "section (\".gnu.offload_funcs\"))) = { };\n\n"
297 "void *__offload_vars_end[0]\n"
298 "__attribute__ ((__used__, visibility (\"hidden\"),\n"
299 "section (\".gnu.offload_vars\"))) = { };\n");
302 struct obstack argv_obstack
;
303 obstack_init (&argv_obstack
);
304 obstack_ptr_grow (&argv_obstack
, target_compiler
);
305 obstack_ptr_grow (&argv_obstack
, "-c");
306 obstack_ptr_grow (&argv_obstack
, "-shared");
307 obstack_ptr_grow (&argv_obstack
, "-fPIC");
308 obstack_ptr_grow (&argv_obstack
, src_filename
);
309 obstack_ptr_grow (&argv_obstack
, "-o");
310 obstack_ptr_grow (&argv_obstack
, obj_filename
);
311 compile_for_target (&argv_obstack
);
316 /* Generates object file with the host side descriptor. */
318 generate_host_descr_file (const char *host_compiler
)
320 const char *src_filename
= make_temp_file ("_host_descr.c");
321 const char *obj_filename
= make_temp_file ("_host_descr.o");
322 temp_files
[num_temps
++] = src_filename
;
323 temp_files
[num_temps
++] = obj_filename
;
324 FILE *src_file
= fopen (src_filename
, "w");
327 fatal_error ("cannot open '%s'", src_filename
);
330 "extern void *__OFFLOAD_TABLE__;\n"
331 "extern void *__offload_image_intelmic_start;\n"
332 "extern void *__offload_image_intelmic_end;\n\n"
334 "static const void *__offload_target_data[] = {\n"
335 " &__offload_image_intelmic_start, &__offload_image_intelmic_end\n"
339 "#ifdef __cplusplus\n"
342 "void GOMP_offload_register (void *, int, void *);\n\n"
344 "__attribute__((constructor))\n"
348 " GOMP_offload_register (&__OFFLOAD_TABLE__, %d, __offload_target_data);\n"
349 "}\n", OFFLOAD_TARGET_TYPE_INTEL_MIC
);
352 unsigned new_argc
= 0;
353 const char *new_argv
[9];
354 new_argv
[new_argc
++] = host_compiler
;
355 new_argv
[new_argc
++] = "-c";
356 new_argv
[new_argc
++] = "-fPIC";
357 new_argv
[new_argc
++] = "-shared";
359 new_argv
[new_argc
++] = "-m32";
361 new_argv
[new_argc
++] = "-m64";
362 new_argv
[new_argc
++] = src_filename
;
363 new_argv
[new_argc
++] = "-o";
364 new_argv
[new_argc
++] = obj_filename
;
365 new_argv
[new_argc
++] = NULL
;
367 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), false);
373 prepare_target_image (const char *target_compiler
, int argc
, char **argv
)
375 const char *target_descr_filename
376 = generate_target_descr_file (target_compiler
);
377 const char *target_offloadend_filename
378 = generate_target_offloadend_file (target_compiler
);
381 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_descr_filename
));
383 = XALLOCAVEC (char, sizeof ("-Wl,") + strlen (target_offloadend_filename
));
384 sprintf (opt1
, "-Wl,%s", target_descr_filename
);
385 sprintf (opt2
, "-Wl,%s", target_offloadend_filename
);
387 const char *target_so_filename
= make_temp_file ("_offload_intelmic.so");
388 temp_files
[num_temps
++] = target_so_filename
;
389 struct obstack argv_obstack
;
390 obstack_init (&argv_obstack
);
391 obstack_ptr_grow (&argv_obstack
, target_compiler
);
392 obstack_ptr_grow (&argv_obstack
, "-xlto");
393 obstack_ptr_grow (&argv_obstack
, "-fopenmp");
394 obstack_ptr_grow (&argv_obstack
, "-shared");
395 obstack_ptr_grow (&argv_obstack
, "-fPIC");
396 obstack_ptr_grow (&argv_obstack
, opt1
);
397 for (int i
= 1; i
< argc
; i
++)
399 if (!strcmp (argv
[i
], "-o") && i
+ 1 != argc
)
400 out_obj_filename
= argv
[++i
];
402 obstack_ptr_grow (&argv_obstack
, argv
[i
]);
404 if (!out_obj_filename
)
405 fatal_error ("output file not specified");
406 obstack_ptr_grow (&argv_obstack
, opt2
);
407 obstack_ptr_grow (&argv_obstack
, "-o");
408 obstack_ptr_grow (&argv_obstack
, target_so_filename
);
409 compile_for_target (&argv_obstack
);
412 char *rename_section_opt
413 = XALLOCAVEC (char, sizeof (".data=") + strlen (image_section_name
));
414 sprintf (rename_section_opt
, ".data=%s", image_section_name
);
415 const char *objcopy_argv
[11];
416 objcopy_argv
[0] = "objcopy";
417 objcopy_argv
[1] = "-B";
418 objcopy_argv
[2] = "i386";
419 objcopy_argv
[3] = "-I";
420 objcopy_argv
[4] = "binary";
421 objcopy_argv
[5] = "-O";
423 objcopy_argv
[6] = "elf32-i386";
425 objcopy_argv
[6] = "elf64-x86-64";
426 objcopy_argv
[7] = target_so_filename
;
427 objcopy_argv
[8] = "--rename-section";
428 objcopy_argv
[9] = rename_section_opt
;
429 objcopy_argv
[10] = NULL
;
430 fork_execute (objcopy_argv
[0], CONST_CAST (char **, objcopy_argv
), false);
432 /* Objcopy has created symbols, containing the input file name with
433 special characters replaced with '_'. We are going to rename these
435 size_t symbol_name_len
= strlen (target_so_filename
);
436 char *symbol_name
= XALLOCAVEC (char, symbol_name_len
+ 1);
437 for (size_t i
= 0; i
<= symbol_name_len
; i
++)
439 char c
= target_so_filename
[i
];
440 if ((c
== '/') || (c
== '.'))
445 char *opt_for_objcopy
[3];
446 opt_for_objcopy
[0] = XALLOCAVEC (char, sizeof ("_binary__start=")
448 + strlen (symbols
[0]));
449 opt_for_objcopy
[1] = XALLOCAVEC (char, sizeof ("_binary__end=")
451 + strlen (symbols
[1]));
452 opt_for_objcopy
[2] = XALLOCAVEC (char, sizeof ("_binary__size=")
454 + strlen (symbols
[2]));
455 sprintf (opt_for_objcopy
[0], "_binary_%s_start=%s", symbol_name
, symbols
[0]);
456 sprintf (opt_for_objcopy
[1], "_binary_%s_end=%s", symbol_name
, symbols
[1]);
457 sprintf (opt_for_objcopy
[2], "_binary_%s_size=%s", symbol_name
, symbols
[2]);
459 objcopy_argv
[0] = "objcopy";
460 objcopy_argv
[1] = target_so_filename
;
461 objcopy_argv
[2] = "--redefine-sym";
462 objcopy_argv
[3] = opt_for_objcopy
[0];
463 objcopy_argv
[4] = "--redefine-sym";
464 objcopy_argv
[5] = opt_for_objcopy
[1];
465 objcopy_argv
[6] = "--redefine-sym";
466 objcopy_argv
[7] = opt_for_objcopy
[2];
467 objcopy_argv
[8] = NULL
;
468 fork_execute (objcopy_argv
[0], CONST_CAST (char **, objcopy_argv
), false);
470 return target_so_filename
;
474 main (int argc
, char **argv
)
476 progname
= "mkoffload-intelmic";
478 diagnostic_initialize (global_dc
, 0);
480 if (atexit (mkoffload_atexit
) != 0)
481 fatal_error ("atexit failed");
483 const char *host_compiler
= getenv ("COLLECT_GCC");
485 fatal_error ("COLLECT_GCC must be set");
487 const char *target_driver_name
488 = DEFAULT_REAL_TARGET_MACHINE
"-accel-" DEFAULT_TARGET_MACHINE
"-gcc";
489 char *target_compiler
= find_target_compiler (target_driver_name
);
490 if (target_compiler
== NULL
)
491 fatal_error ("offload compiler %s not found", target_driver_name
);
493 /* We may be called with all the arguments stored in some file and
494 passed with @file. Expand them into argv before processing. */
495 expandargv (&argc
, &argv
);
497 /* Find out whether we should compile binaries for i386 or x86-64. */
498 for (int i
= argc
- 1; i
> 0; i
--)
499 if (strncmp (argv
[i
], "-foffload-abi=", sizeof ("-foffload-abi=") - 1) == 0)
501 if (strstr (argv
[i
], "ilp32"))
503 else if (!strstr (argv
[i
], "lp64"))
504 fatal_error ("unrecognizable argument of option -foffload-abi");
508 const char *target_so_filename
509 = prepare_target_image (target_compiler
, argc
, argv
);
511 const char *host_descr_filename
= generate_host_descr_file (host_compiler
);
513 /* Perform partial linking for the target image and host side descriptor.
514 As a result we'll get a finalized object file with all offload data. */
515 unsigned new_argc
= 0;
516 const char *new_argv
[9];
517 new_argv
[new_argc
++] = "ld";
518 new_argv
[new_argc
++] = "-m";
520 new_argv
[new_argc
++] = "elf_i386";
522 new_argv
[new_argc
++] = "elf_x86_64";
523 new_argv
[new_argc
++] = "--relocatable";
524 new_argv
[new_argc
++] = host_descr_filename
;
525 new_argv
[new_argc
++] = target_so_filename
;
526 new_argv
[new_argc
++] = "-o";
527 new_argv
[new_argc
++] = out_obj_filename
;
528 new_argv
[new_argc
++] = NULL
;
529 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), false);
531 /* Run objcopy on the resultant object file to localize generated symbols
532 to avoid conflicting between different DSO and an executable. */
533 new_argv
[0] = "objcopy";
535 new_argv
[2] = symbols
[0];
537 new_argv
[4] = symbols
[1];
539 new_argv
[6] = symbols
[2];
540 new_argv
[7] = out_obj_filename
;
542 fork_execute (new_argv
[0], CONST_CAST (char **, new_argv
), false);