configure.ac (--enable-lto): Add x86_64-apple-darwin* as a platform that supports LTO.

ChangeLog:
	* configure.ac (--enable-lto): Add x86_64-apple-darwin* as
	a platform that supports LTO.
	* configure: Regenerate.

gcc/ChangeLog:
	* config.gcc (i[34567]86-*-darwin*, x86_64-*-darwin*): Add
	lto-macho as lto_binary_reader.
	* target.h (struct gcc_target): New hooks lto_start and	lto_end.
	* target-def.h (TARGET_ASM_LTO_START, TARGET_ASM_LTO_END): Define.
	* cgraphunit.c (ipa_passes): Wrap LTO assembler output generation
	in lto_start and lto_end calls.
	(is_elf_or_coff): Rename to maybe_lto_object_file.  Add Mach-O
	magic numbers.
	(scan_prog_file): Update is_elf_or_coff call.
	* doc/tm.text (TARGET_ASM_LTO_START, TARGET_ASM_LTO_END): Document.

	* collect2.c (main): Fix enum comparison.

	* config/darwin-protos.h (darwin_asm_lto_start, darwin_asm_lto_end):
	Add prototypes.
	* darwin9.h (LINK_COMMAND_SPEC): Pass -flto and -fwhopr to the linker.
	* darwin.h (LINK_COMMAND_SPEC): Likewise.  Define TARGET_ASM_LTO_START
	and TARGET_ASM_LTO_END.
	* darwin.c: Include obstack.h and lto-streamer.h.
	(lto_section_names_offset, lto_section_names_obstack,
	lto_asm_out_file, lto_asm_out_name, saved_asm_out_file): New static
	global variables.
	(LTO_SEGMENT_NAME, LTO_NAMES_SECTION): New defines.
	(darwin_asm_lto_start): New function.  Redirect output to asm_out_file
	to a temporary file.
	(darwin_asm_lto_end): New function.  Restore asm_out_file.
	(darwin_asm_named_section): For LTO sections, replace the name with
	the offset of the section name in a string table, and build this
	table.
	(darwin_file_start): Initialize global vars for LTO support.
	(darwin_file_end): If output to asm_out_file was redirected, append it
	to the proper asm_out_file here.  Add the section names section.

lto/ChangeLog:
	* lto.h (struct lto_file_struct): Document offset member.
	* lto-endian.h: New file.
	* lto-macho.h: New file.
	* lto-macho.c: New file.
	* Make-lang.in: Add rule for lto-macho.o.

From-SVN: r159173
This commit is contained in:
Steven Bosscher 2010-05-07 21:37:43 +00:00
parent 18bc5398c1
commit c082f9f317
20 changed files with 1704 additions and 14 deletions

View File

@ -1,3 +1,9 @@
2010-05-07 Steven Bosscher <steven@gcc.gnu.org>
* configure.ac (--enable-lto): Add x86_64-apple-darwin* as
a platform that supports LTO.
* configure: Regenerate.
2010-05-05 Sebastian Pop <sebastian.pop@amd.com>
* configure.ac: Allow all the versions greater than 0.10 of PPL.

1
configure vendored
View File

@ -6686,6 +6686,7 @@ else
# -flto it won't be needed until after installation anyway.
case $target in
*-cygwin*|*-mingw*) ;;
x86_64-apple-darwin*) ;;
*) if test x"$enable_lto" = x"yes"; then
as_fn_error "LTO support is not enabled for this target." "$LINENO" 5
fi

View File

@ -1803,6 +1803,7 @@ fi],[if test x"$default_enable_lto" = x"yes" ; then
# -flto it won't be needed until after installation anyway.
case $target in
*-cygwin*|*-mingw*) ;;
x86_64-apple-darwin*) ;;
*) if test x"$enable_lto" = x"yes"; then
AC_MSG_ERROR([LTO support is not enabled for this target.])
fi

View File

@ -1,3 +1,38 @@
2010-05-07 Steven Bosscher <steven@gcc.gnu.org>
* config.gcc (i[34567]86-*-darwin*, x86_64-*-darwin*): Add
lto-macho as lto_binary_reader.
* target.h (struct gcc_target): New hooks lto_start and lto_end.
* target-def.h (TARGET_ASM_LTO_START, TARGET_ASM_LTO_END): Define.
* cgraphunit.c (ipa_passes): Wrap LTO assembler output generation
in lto_start and lto_end calls.
(is_elf_or_coff): Rename to maybe_lto_object_file. Add Mach-O
magic numbers.
(scan_prog_file): Update is_elf_or_coff call.
* doc/tm.text (TARGET_ASM_LTO_START, TARGET_ASM_LTO_END): Document.
* collect2.c (main): Fix enum comparison.
* config/darwin-protos.h (darwin_asm_lto_start, darwin_asm_lto_end):
Add prototypes.
* darwin9.h (LINK_COMMAND_SPEC): Pass -flto and -fwhopr to the linker.
* darwin.h (LINK_COMMAND_SPEC): Likewise. Define TARGET_ASM_LTO_START
and TARGET_ASM_LTO_END.
* darwin.c: Include obstack.h and lto-streamer.h.
(lto_section_names_offset, lto_section_names_obstack,
lto_asm_out_file, lto_asm_out_name, saved_asm_out_file): New static
global variables.
(LTO_SEGMENT_NAME, LTO_NAMES_SECTION): New defines.
(darwin_asm_lto_start): New function. Redirect output to asm_out_file
to a temporary file.
(darwin_asm_lto_end): New function. Restore asm_out_file.
(darwin_asm_named_section): For LTO sections, replace the name with
the offset of the section name in a string table, and build this
table.
(darwin_file_start): Initialize global vars for LTO support.
(darwin_file_end): If output to asm_out_file was redirected, append it
to the proper asm_out_file here. Add the section names section.
2010-05-07 Steven Bosscher <steven@gcc.gnu.org>
* c-pragma.c (pending_weak_d, pending_weak): New.

View File

@ -1866,11 +1866,19 @@ ipa_passes (void)
execute_ipa_summary_passes
((struct ipa_opt_pass_d *) all_regular_ipa_passes);
}
/* Some targets need to handle LTO assembler output specially. */
if (flag_generate_lto)
targetm.asm_out.lto_start ();
execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes);
if (!in_lto_p)
ipa_write_summaries ();
if (flag_generate_lto)
targetm.asm_out.lto_end ();
if (!flag_ltrans)
execute_ipa_pass_list (all_regular_ipa_passes);
invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL);

View File

@ -1817,7 +1817,7 @@ main (int argc, char **argv)
if (export_file != 0 && export_file[0])
maybe_unlink (export_file);
#endif
if (lto_mode)
if (lto_mode != LTO_MODE_NONE)
maybe_run_lto_and_relink (ld1_argv, object_lst, object, false);
maybe_unlink (c_file);
@ -2563,16 +2563,23 @@ write_aix_file (FILE *stream, struct id *list)
#ifdef OBJECT_FORMAT_NONE
/* Check to make sure the file is an ELF file. LTO objects must
be in ELF format. */
/* Check to make sure the file is an LTO object file. */
static bool
is_elf_or_coff (const char *prog_name)
maybe_lto_object_file (const char *prog_name)
{
FILE *f;
char buf[4];
static char magic[4] = { 0x7f, 'E', 'L', 'F' };
static char coffmag[2] = { 0x4c, 0x01 };
unsigned char buf[4];
int i;
static unsigned char elfmagic[4] = { 0x7f, 'E', 'L', 'F' };
static unsigned char coffmagic[2] = { 0x4c, 0x01 };
static unsigned char machomagic[4][4] = {
{ 0xcf, 0xfa, 0xed, 0xfe },
{ 0xce, 0xfa, 0xed, 0xfe },
{ 0xfe, 0xed, 0xfa, 0xcf },
{ 0xfe, 0xed, 0xfa, 0xce }
};
f = fopen (prog_name, "rb");
if (f == NULL)
@ -2580,8 +2587,15 @@ is_elf_or_coff (const char *prog_name)
if (fread (buf, sizeof (buf), 1, f) != 1)
buf[0] = 0;
fclose (f);
return memcmp (buf, magic, sizeof (magic)) == 0
|| memcmp (buf, coffmag, sizeof (coffmag)) == 0;
if (memcmp (buf, elfmagic, sizeof (elfmagic)) == 0
|| memcmp (buf, coffmagic, sizeof (coffmagic)) == 0)
return true;
for (i = 0; i < 4; i++)
if (memcmp (buf, machomagic[i], sizeof (machomagic[i])) == 0)
return true;
return false;
}
/* Generic version to scan the name list of the loaded program for
@ -2611,7 +2625,7 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
/* LTO objects must be in a known format. This check prevents
us from accepting an archive containing LTO objects, which
gcc cannnot currently handle. */
if (which_pass == PASS_LTOINFO && !is_elf_or_coff (prog_name))
if (which_pass == PASS_LTOINFO && !maybe_lto_object_file (prog_name))
return;
/* If we do not have an `nm', complain. */

View File

@ -1077,11 +1077,13 @@ i[34567]86-*-darwin*)
# support.
with_cpu=${with_cpu:-generic}
tmake_file="${tmake_file} i386/t-crtpc i386/t-crtfm"
lto_binary_reader=lto-macho
;;
x86_64-*-darwin*)
with_cpu=${with_cpu:-generic}
tmake_file="${tmake_file} t-darwin ${cpu_type}/t-darwin64 t-slibgcc-darwin i386/t-crtpc i386/t-crtfm"
tm_file="${tm_file} ${cpu_type}/darwin64.h"
lto_binary_reader=lto-macho
;;
i[34567]86-*-elf*)
tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h newlib-stdint.h i386/i386elf.h"

View File

@ -71,6 +71,9 @@ extern void darwin_pragma_ms_struct (struct cpp_reader *);
extern void darwin_file_start (void);
extern void darwin_file_end (void);
extern void darwin_asm_lto_start (void);
extern void darwin_asm_lto_end (void);
extern void darwin_mark_decl_preserved (const char *);
extern tree darwin_handle_kext_attribute (tree *, tree, tree, int, bool *);

View File

@ -46,6 +46,8 @@ along with GCC; see the file COPYING3. If not see
#include "hashtab.h"
#include "df.h"
#include "debug.h"
#include "obstack.h"
#include "lto-streamer.h"
/* Darwin supports a feature called fix-and-continue, which is used
for rapid turn around debugging. When code is compiled with the
@ -1387,12 +1389,88 @@ darwin_label_is_anonymous_local_objc_name (const char *name)
return (!strncmp ((const char *)p, "_OBJC_", 6));
}
/* LTO support for Mach-O. */
/* Section names for LTO sections. */
static unsigned int lto_section_names_offset = 0;
/* This is the obstack which we use to allocate the many strings. */
static struct obstack lto_section_names_obstack;
/* Segment name for LTO sections. */
#define LTO_SEGMENT_NAME "__GNU_LTO"
/* Section name for LTO section names section. */
#define LTO_NAMES_SECTION "__section_names"
/* File to temporarily store LTO data. This is appended to asm_out_file
in darwin_end_file. */
static FILE *lto_asm_out_file, *saved_asm_out_file;
static char *lto_asm_out_name;
/* Prepare asm_out_file for LTO output. For darwin, this means hiding
asm_out_file and switching to an alternative output file. */
void
darwin_asm_lto_start (void)
{
gcc_assert (! saved_asm_out_file);
saved_asm_out_file = asm_out_file;
if (! lto_asm_out_name)
lto_asm_out_name = make_temp_file (".lto.s");
lto_asm_out_file = fopen (lto_asm_out_name, "a");
if (lto_asm_out_file == NULL)
fatal_error ("failed to open temporary file %s for LTO output",
lto_asm_out_name);
asm_out_file = lto_asm_out_file;
}
/* Restore asm_out_file. */
void
darwin_asm_lto_end (void)
{
gcc_assert (saved_asm_out_file);
fclose (lto_asm_out_file);
asm_out_file = saved_asm_out_file;
saved_asm_out_file = NULL;
}
void
darwin_asm_named_section (const char *name,
unsigned int flags ATTRIBUTE_UNUSED,
unsigned int flags,
tree decl ATTRIBUTE_UNUSED)
{
fprintf (asm_out_file, "\t.section %s\n", name);
/* LTO sections go in a special segment __GNU_LTO. We want to replace the
section name with something we can use to represent arbitrary-length
names (section names in Mach-O are at most 16 characters long). */
if (strncmp (name, LTO_SECTION_NAME_PREFIX,
strlen (LTO_SECTION_NAME_PREFIX)) == 0)
{
/* We expect certain flags to be set... */
gcc_assert ((flags & (SECTION_DEBUG | SECTION_NAMED))
== (SECTION_DEBUG | SECTION_NAMED));
/* Add the section name to the things to output when we end the
current assembler output file.
This is all not very efficient, but that doesn't matter -- this
shouldn't be a hot path in the compiler... */
obstack_1grow (&lto_section_names_obstack, '\t');
obstack_grow (&lto_section_names_obstack, ".ascii ", 7);
obstack_1grow (&lto_section_names_obstack, '"');
obstack_grow (&lto_section_names_obstack, name, strlen (name));
obstack_grow (&lto_section_names_obstack, "\\0\"\n", 4);
/* Output the dummy section name. */
fprintf (asm_out_file, "\t.section %s,__%08X,regular,debug\t# %s\n",
LTO_SEGMENT_NAME, lto_section_names_offset, name);
/* Update the offset for the next section name. Make sure we stay
within reasonable length. */
lto_section_names_offset += strlen (name) + 1;
gcc_assert (lto_section_names_offset > 0
&& lto_section_names_offset < ((unsigned) 1 << 31));
}
else
fprintf (asm_out_file, "\t.section %s\n", name);
}
void
@ -1585,7 +1663,8 @@ darwin_asm_output_dwarf_delta (FILE *file, int size,
fprintf (file, "\n\t%s L$set$%d", directive, darwin_dwarf_label_counter++);
}
/* Output labels for the start of the DWARF sections if necessary. */
/* Output labels for the start of the DWARF sections if necessary.
Initialize the stuff we need for LTO long section names support. */
void
darwin_file_start (void)
{
@ -1620,6 +1699,11 @@ darwin_file_start (void)
fprintf (asm_out_file, "Lsection%.*s:\n", namelen, debugnames[i] + 8);
}
}
/* We fill this obstack with the complete section text for the lto section
names to write in darwin_file_end. */
obstack_init (&lto_section_names_obstack);
lto_section_names_offset = 0;
}
/* Output an offset in a DWARF section on Darwin. On Darwin, DWARF section
@ -1646,6 +1730,8 @@ darwin_asm_output_dwarf_offset (FILE *file, int size, const char * lab,
void
darwin_file_end (void)
{
const char *lto_section_names;
machopic_finish (asm_out_file);
if (strcmp (lang_hooks.name, "GNU C++") == 0)
{
@ -1653,6 +1739,52 @@ darwin_file_end (void)
switch_to_section (darwin_sections[destructor_section]);
ASM_OUTPUT_ALIGN (asm_out_file, 1);
}
/* If there was LTO assembler output, append it to asm_out_file. */
if (lto_asm_out_name)
{
int n;
char *buf, *lto_asm_txt;
/* Shouldn't be here if we failed to switch back. */
gcc_assert (! saved_asm_out_file);
lto_asm_out_file = fopen (lto_asm_out_name, "r");
if (lto_asm_out_file == NULL)
fatal_error ("failed to open temporary file %s with LTO output",
lto_asm_out_name);
fseek (lto_asm_out_file, 0, SEEK_END);
n = ftell (lto_asm_out_file);
if (n > 0)
{
fseek (lto_asm_out_file, 0, SEEK_SET);
lto_asm_txt = buf = (char *) xmalloc (n + 1);
while (fgets (lto_asm_txt, n, lto_asm_out_file))
fputs (lto_asm_txt, asm_out_file);
}
/* Remove the temporary file. */
fclose (lto_asm_out_file);
unlink_if_ordinary (lto_asm_out_name);
free (lto_asm_out_name);
}
/* Finish the LTO section names obstack. Don't output anything if
there are no recorded section names. */
obstack_1grow (&lto_section_names_obstack, '\0');
lto_section_names = XOBFINISH (&lto_section_names_obstack, const char *);
if (strlen (lto_section_names) > 0)
{
fprintf (asm_out_file,
"\t.section %s,%s,regular,debug\n",
LTO_SEGMENT_NAME, LTO_NAMES_SECTION);
fprintf (asm_out_file,
"\t# Section names in %s are offsets into this table\n",
LTO_SEGMENT_NAME);
fprintf (asm_out_file, "%s\n", lto_section_names);
}
obstack_free (&lto_section_names_obstack, NULL);
fprintf (asm_out_file, "\t.subsections_via_symbols\n");
}

View File

@ -273,6 +273,7 @@ extern GTY(()) int darwin_ms_struct;
%{o*}%{!o:-o a.out} \
%{!A:%{!nostdlib:%{!nostartfiles:%S}}} \
%{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \
%{flto} %{fwhopr} \
%{fopenmp|ftree-parallelize-loops=*: \
%{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \
%{!nostdlib:%{!nodefaultlibs: %(link_ssp) %G %L }} \
@ -581,6 +582,16 @@ extern GTY(()) int darwin_ms_struct;
#undef TARGET_ASM_FILE_END
#define TARGET_ASM_FILE_END darwin_file_end
/* Because Mach-O relocations have a counter from 1 to 255 for the
section number they apply to, it is necessary to output all
normal sections before the LTO sections, to make sure that the
sections that may have relocations always have a section number
smaller than 255. */
#undef TARGET_ASM_LTO_START
#define TARGET_ASM_LTO_START darwin_asm_lto_start
#undef TARGET_ASM_LTO_END
#define TARGET_ASM_LTO_END darwin_asm_lto_end
#define ASM_OUTPUT_SKIP(FILE,SIZE) \
fprintf (FILE, "\t.space "HOST_WIDE_INT_PRINT_UNSIGNED"\n", SIZE)

View File

@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
%{o*}%{!o:-o a.out} \
%{!A:%{!nostdlib:%{!nostartfiles:%S}}} \
%{L*} %(link_libgcc) %o %{fprofile-arcs|fprofile-generate*|coverage:-lgcov} \
%{flto} %{fwhopr} \
%{fopenmp|ftree-parallelize-loops=*: \
%{static|static-libgcc|static-libstdc++|static-libgfortran: libgomp.a%s; : -lgomp } } \
%{!nostdlib:%{!nodefaultlibs: %(link_ssp) %G %L }} \

View File

@ -7184,6 +7184,18 @@ need to do other things in that hook, have your hook function call
this function.
@end deftypefun
@deftypefn {Target Hook} void TARGET_ASM_LTO_START (void)
Output to @code{asm_out_file} any text which the assembler expects
to find at the start of an LTO section. The default is to output
nothing.
@end deftypefn
@deftypefn {Target Hook} void TARGET_ASM_LTO_END (void)
Output to @code{asm_out_file} any text which the assembler expects
to find at the end of an LTO section. The default is to output
nothing.
@end deftypefn
@deftypefn {Target Hook} void TARGET_ASM_CODE_END (void)
Output to @code{asm_out_file} any text which is needed before emitting
unwind info and debug info at the end of a file. Some targets emit

View File

@ -1,3 +1,11 @@
2010-05-07 Steven Bosscher <steven@gcc.gnu.org>
* lto.h (struct lto_file_struct): Document offset member.
* lto-endian.h: New file.
* lto-macho.h: New file.
* lto-macho.c: New file.
* Make-lang.in: Add rule for lto-macho.o.
2010-05-07 Richard Guenther <rguenther@suse.de>
PR lto/43857

View File

@ -91,3 +91,6 @@ lto/lto-elf.o: lto/lto-elf.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
lto/lto-coff.o: lto/lto-coff.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
toplev.h $(LTO_H) $(TM_H) $(LIBIBERTY_H) $(GGC_H) $(LTO_STREAMER_H) \
lto/lto-coff.h
lto/lto-macho.o: lto/lto-macho.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
toplev.h $(LTO_H) $(TM_H) $(LIBIBERTY_H) $(GGC_H) $(LTO_STREAMER_H) \
lto/lto-macho.h lto/lto-endian.h

205
gcc/lto/lto-endian.h Normal file
View File

@ -0,0 +1,205 @@
/* Very simple endian-ness layer for LTO object file handling
Copyright 2010 Free Software Foundation, Inc.
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/>. */
/* This header file provides a simple way to handle object files in
another endian-ness than the host machine. This is necesarry to
enable cross-compilation with LTO enabled. Targets that use the
ELF binary object format do not need this (libelf already handles
endian-ness) but for COFF and Mach-O the functions in this header
are used in the minimal binary object reader/writer.
For all functions in this header, the user is responsible for
making sure that the memory accesses are valid. */
#ifndef GCC_LTO_ENDIAN_H
#define GCC_LTO_ENDIAN_H
#include <stdint.h>
#include <inttypes.h>
static inline uint16_t
get_uint16_le (const unsigned char *ptr)
{
return ptr[0] | (ptr[1] << 8);
}
static inline uint32_t
get_uint32_le (const unsigned char *ptr)
{
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
}
static inline uint64_t
get_uint64_le (const unsigned char *ptr_)
{
#define ptr (uint64_t) ptr_
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)
| (ptr[4] << 32) | (ptr[5] << 40) | (ptr[6] << 48) | (ptr[7] << 56);
#undef ptr
}
static inline uint16_t
get_uint16_be (const unsigned char *ptr)
{
return ptr[1] | (ptr[2] << 8);
}
static inline uint32_t
get_uint32_be (const unsigned char *ptr)
{
return ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24);
}
static inline uint64_t
get_uint64_be (const unsigned char *ptr_)
{
#define ptr (uint64_t) ptr_
return ptr[7] | (ptr[6] << 8) | (ptr[5] << 16) | (ptr[4] << 24)
| (ptr[3] << 32) | (ptr[2] << 40) | (ptr[1] << 48) | (ptr[0] << 56);
#undef ptr
}
static inline void
put_uint16_le (unsigned char *ptr, uint16_t data)
{
ptr[0] = data & 0xff;
ptr[1] = (data >> 8) & 0xff;
}
static inline void
put_uint32_le (unsigned char *ptr, uint32_t data)
{
ptr[0] = data & 0xff;
ptr[1] = (data >> 8) & 0xff;
ptr[2] = (data >> 16) & 0xff;
ptr[3] = (data >> 24) & 0xff;
}
static inline void
put_uint64_le (unsigned char *ptr, uint64_t data)
{
ptr[0] = data & 0xff;
ptr[1] = (data >> 8) & 0xff;
ptr[2] = (data >> 16) & 0xff;
ptr[3] = (data >> 24) & 0xff;
ptr[4] = (data >> 32) & 0xff;
ptr[5] = (data >> 40) & 0xff;
ptr[6] = (data >> 48) & 0xff;
ptr[7] = (data >> 56) & 0xff;
}
static inline void
put_uint16_be (unsigned char *ptr, uint16_t data)
{
ptr[1] = data & 0xff;
ptr[0] = (data >> 8) & 0xff;
}
static inline void
put_uint32_be (unsigned char *ptr, uint32_t data)
{
ptr[3] = data & 0xff;
ptr[2] = (data >> 8) & 0xff;
ptr[1] = (data >> 16) & 0xff;
ptr[0] = (data >> 24) & 0xff;
}
static inline void
put_uint64_be (unsigned char *ptr, uint64_t data)
{
ptr[7] = data & 0xff;
ptr[6] = (data >> 8) & 0xff;
ptr[5] = (data >> 16) & 0xff;
ptr[4] = (data >> 24) & 0xff;
ptr[3] = (data >> 32) & 0xff;
ptr[2] = (data >> 40) & 0xff;
ptr[1] = (data >> 48) & 0xff;
ptr[0] = (data >> 56) & 0xff;
}
static inline void
get_string (unsigned char *ptr, char *dest, size_t len)
{
memcpy (dest, ptr, len);
}
static inline void
put_string (unsigned char *ptr, char *src, size_t len)
{
memcpy (ptr, src, len);
}
/* Use the target macro BYTES_BIG_ENDIAN to choose. */
static inline uint16_t
get_uint16 (const unsigned char *ptr)
{
if (BYTES_BIG_ENDIAN)
return get_uint16_be (ptr);
else
return get_uint16_le (ptr);
}
static inline uint32_t
get_uint32 (const unsigned char *ptr)
{
if (BYTES_BIG_ENDIAN)
return get_uint32_be (ptr);
else
return get_uint32_le (ptr);
}
static inline uint64_t
get_uint64 (const unsigned char *ptr)
{
if (BYTES_BIG_ENDIAN)
return get_uint64_be (ptr);
else
return get_uint64_le (ptr);
}
static inline void
put_uint16 (unsigned char *ptr, uint16_t data)
{
if (BYTES_BIG_ENDIAN)
put_uint16_be (ptr, data);
else
put_uint16_le (ptr, data);
}
static inline void
put_uint32 (unsigned char *ptr, uint32_t data)
{
if (BYTES_BIG_ENDIAN)
put_uint32_be (ptr, data);
else
put_uint32_le (ptr, data);
}
static inline void
put_uint64 (unsigned char *ptr, uint64_t data)
{
if (BYTES_BIG_ENDIAN)
put_uint64_be (ptr, data);
else
put_uint64_le (ptr, data);
}
#endif /* GCC_LTO_ENDIAN_H */

978
gcc/lto/lto-macho.c Normal file
View File

@ -0,0 +1,978 @@
/* LTO routines for Mach-O object files.
Copyright 2010 Free Software Foundation, Inc.
Contributed by Steven Bosscher.
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 "system.h"
#include "coretypes.h"
#include "toplev.h"
#include "lto.h"
#include "tm.h"
#include "libiberty.h"
#include "lto-streamer.h"
#include "lto/lto-endian.h"
#include "lto/lto-macho.h"
/* Rather than implementing a libmacho to match libelf, or attempting to
integrate libbfd into GCC, this file is a self-contained (and very
minimal) Mach-O format object file reader/writer. The generated files
will contain a Mach-O header, a number of Mach-O load commands an
section headers, the section data itself, and a trailing string table
for section names. */
/* This needs to be kept in sync with darwin.c. Better yet, lto-macho.c
and lto-macho.h should be moved to config/, and likewise for lto-coff.*
and lto-elf.*. */
/* Segment name for LTO sections. */
#define LTO_SEGMENT_NAME "__GNU_LTO"
/* Section name for LTO section names section. */
#define LTO_NAMES_SECTION "__section_names"
/* Handle opening elf files on hosts, such as Windows, that may use
text file handling that will break binary access. */
#ifndef O_BINARY
# define O_BINARY 0
#endif
/* Cached object file header. We use a header_64 for this, since all
the fields we need are in there, in the same position as header_32. */
mach_o_header_64 cached_mach_o_header;
uint32_t cached_mach_o_magic;
/* The current output file. */
static lto_file *current_out_file;
/* Is this a 32-bits or 64-bits Mach-O object file? */
static int
mach_o_word_size (void)
{
gcc_assert (cached_mach_o_magic != 0);
return (cached_mach_o_magic == MACH_O_MH_MAGIC_64
|| cached_mach_o_magic == MACH_O_MH_CIGAM_64) ? 64 : 32;
}
/* Sets the current output file to FILE. Returns the old output file or
NULL. */
lto_file *
lto_set_current_out_file (lto_file *file)
{
lto_file *old_file = current_out_file;
current_out_file = file;
return old_file;
}
/* Returns the current output file. */
lto_file *
lto_get_current_out_file (void)
{
return current_out_file;
}
/* Mach-O section structure constructor. */
static lto_mach_o_section
mach_o_new_section (lto_mach_o_file *mach_o_file, const char *name)
{
lto_mach_o_section ptr;
/* FIXME We could allocate these things on an obstack. */
ptr = XCNEW (struct lto_mach_o_section_d);
if (name)
{
if (strncmp (name, LTO_SECTION_NAME_PREFIX,
strlen(LTO_SECTION_NAME_PREFIX)) != 0)
sorry ("not implemented: Mach-O writer for non-LTO sections");
ptr->name = xstrdup (name);
}
VEC_safe_push (lto_mach_o_section, heap, mach_o_file->section_vec, ptr);
return ptr;
}
/* Mach-O section data block structure constructor. */
static lto_mach_o_data
mach_o_new_data (lto_mach_o_section sec)
{
lto_mach_o_data ptr, *chain_ptr_ptr;
/* FIXME We could allocate these things on an obstack. */
ptr = XCNEW (struct lto_mach_o_data_d);
chain_ptr_ptr = &sec->data_chain;
while (*chain_ptr_ptr)
chain_ptr_ptr = &(*chain_ptr_ptr)->next;
*chain_ptr_ptr = ptr;
return ptr;
}
/* Initialize FILE, an LTO file object for FILENAME. Offset is the
offset into FILE where the object is located (e.g. in an archive). */
static void
lto_file_init (lto_file *file, const char *filename, off_t offset)
{
file->filename = filename;
file->offset = offset;
}
/* Return an error string after an error, or a predetermined one
if ERRCODE is not -1. */
static const char *
mach_o_errmsg (int errcode)
{
return strerror (errcode == -1 ? errno : errcode);
}
/* Returns a hash code for P. */
static hashval_t
hash_name (const void *p)
{
const struct lto_section_slot *s = (const struct lto_section_slot *) p;
return (hashval_t) htab_hash_string (s->name);
}
/* Returns nonzero if P1 and P2 are equal. */
static int
eq_name (const void *p1, const void *p2)
{
const struct lto_section_slot *s1 =
(const struct lto_section_slot *) p1;
const struct lto_section_slot *s2 =
(const struct lto_section_slot *) p2;
return strcmp (s1->name, s2->name) == 0;
}
/* Build a hash table whose key is the section names and whose data is
the start and size of each section in the .o file. */
htab_t
lto_obj_build_section_table (lto_file *lto_file)
{
lto_mach_o_file *mach_o_file = (lto_mach_o_file *)lto_file;
lto_mach_o_section sec;
htab_t section_hash_table;
off_t strtab_offs;
ssize_t strtab_size;
char *strtab = NULL;
int i;
section_hash_table = htab_create (37, hash_name, eq_name, free);
/* Seek the string table. */
/* FIXME The segment name should be in darwin.h, but can we include it
here in this file? */
for (i = 0;
VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
{
if (strncmp (sec->u.section.segname, "__GNU_LTO", 16) != 0)
continue;
if (strncmp (sec->u.section.sectname, "__section_names", 16) == 0)
break;
}
if (! sec)
{
error ("invalid Mach-O LTO object file: no __section_names section found");
goto done;
}
mach_o_file->section_names_section = sec;
if (mach_o_word_size () == 64)
{
strtab_offs = (off_t) get_uint32 (&sec->u.section_64.offset[0]);
strtab_size = (size_t) get_uint64 (&sec->u.section_64.size[0]);
}
else
{
strtab_offs = (off_t) get_uint32 (&sec->u.section_32.offset[0]);
strtab_size = (size_t) get_uint32 (&sec->u.section_32.size[0]);
}
/* Seek to start of string table. */
if (strtab_offs != lseek (mach_o_file->fd,
mach_o_file->base.offset + strtab_offs,
SEEK_SET))
{
error ("altered or invalid Mach-O object file");
goto done;
}
strtab = XNEWVEC (char, strtab_size);
if (read (mach_o_file->fd, strtab, strtab_size) != strtab_size)
{
error ("invalid Mach-O LTO object file __section_names section");
goto done;
}
/* Scan sections looking at names. */
for (i = 0;
VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
{
struct lto_section_slot s_slot;
void **slot;
char *new_name;
unsigned long stringoffset;
char name[17];
/* Ignore non-LTO sections. Also ignore the __section_names section
which does not need renaming. */
if (strncmp (sec->u.section.segname, "__GNU_LTO", 16) != 0)
continue;
if (sec == mach_o_file->section_names_section)
continue;
/* Try to extract the offset of the real name for this section from
__section_names. */
memcpy (&name[0], sec->u.section.sectname, 16);
name[16] = '\0';
if (name[0] != '_' || name[1] != '_'
|| sscanf (&name[2], "%08lX", &stringoffset) != 1
|| strtab_size < (ssize_t) stringoffset)
{
error ("invalid Mach-O LTO section name string: %s", name);
continue;
}
new_name = XNEWVEC (char, strlen (strtab + stringoffset) + 1);
strcpy (new_name, strtab + stringoffset);
s_slot.name = new_name;
slot = htab_find_slot (section_hash_table, &s_slot, INSERT);
if (*slot == NULL)
{
struct lto_section_slot *new_slot = XNEW (struct lto_section_slot);
new_slot->name = new_name;
if (mach_o_word_size() == 64)
{
new_slot->start =
(intptr_t) get_uint32 (&sec->u.section_64.offset[0]);
new_slot->len =
(size_t) get_uint64 (&sec->u.section_64.size[0]);
}
else
{
new_slot->start =
(intptr_t) get_uint32 (&sec->u.section_32.offset[0]);
new_slot->len =
(size_t) get_uint32 (&sec->u.section_32.size[0]);
}
*slot = new_slot;
}
else
{
error ("two or more sections for %s:", new_name);
goto done;
}
}
done:
if (strtab)
free (strtab);
return section_hash_table;
}
/* Begin a new Mach-O section named NAME in the current output file. */
void
lto_obj_begin_section (const char *name)
{
lto_mach_o_file *file;
if (strncmp (name, LTO_SECTION_NAME_PREFIX,
strlen(LTO_SECTION_NAME_PREFIX)) != 0)
sorry ("not implemented: Mach-O writer for non-LTO sections");
/* Grab the current output file and do some basic assertion checking. */
file = (lto_mach_o_file *) lto_get_current_out_file (),
gcc_assert (file && file->writable && !file->scn);
/* Create a new section. */
file->scn = mach_o_new_section (file, name);
if (!file->scn)
fatal_error ("could not create a new Mach-O section: %s", mach_o_errmsg (-1));
}
/* Append DATA of length LEN to the current output section. BASE is a pointer
to the output page containing DATA. It is freed once the output file has
been written. */
void
lto_obj_append_data (const void *data, size_t len, void *block)
{
lto_mach_o_file *file;
lto_mach_o_data mach_o_data;
struct lto_char_ptr_base *base = (struct lto_char_ptr_base *) block;
/* Grab the current output file and do some basic assertion checking. */
file = (lto_mach_o_file *) lto_get_current_out_file ();
gcc_assert (file);
gcc_assert (file->scn);
mach_o_data = mach_o_new_data (file->scn);
if (!mach_o_data)
fatal_error ("could not append data to Mach-O section: %s", mach_o_errmsg (-1));
mach_o_data->d_buf = CONST_CAST (void *, data);
mach_o_data->d_size = len;
/* Chain all data blocks (from all sections) on one singly-linked
list for freeing en masse after the file is closed. */
base->ptr = (char *)file->data;
file->data = base;
}
/* End the current output section. This just does some assertion checking
and sets the current output file's scn member to NULL. */
void
lto_obj_end_section (void)
{
lto_mach_o_file *file;
/* Grab the current output file and validate some basic assertions. */
file = (lto_mach_o_file *) lto_get_current_out_file ();
gcc_assert (file);
gcc_assert (file->scn);
file->scn = NULL;
}
/* Read a Mach-O header from MACH_O_FILE and validate it.
The file descriptor in MACH_O_FILE points at the start of the file.
If cached_mach_o_header is uninitialized, caches the results.
On succes, returns true and moves file pointer to the start of the
load commands. On failure, returns false. */
static bool
validate_mach_o_header (lto_mach_o_file *mach_o_file)
{
ssize_t i, n;
unsigned char magic[4];
uint32_t cputype;
off_t startpos;
/* Known header magics for validation, as an array. */
static const unsigned int mach_o_known_formats[] = {
MACH_O_MH_MAGIC,
MACH_O_MH_CIGAM,
MACH_O_MH_MAGIC_64,
MACH_O_MH_CIGAM_64,
};
#define MACH_O_NUM_KNOWN_FORMATS \
((ssize_t) ARRAY_SIZE (mach_o_known_formats))
startpos = lseek (mach_o_file->fd, 0, SEEK_CUR);
if (read (mach_o_file->fd, &magic, sizeof (magic)) != 4
|| lseek (mach_o_file->fd, -4, SEEK_CUR) != startpos)
{
error ("cannot read file %s", mach_o_file->base.filename);
return false;
}
for (i = 0; i < MACH_O_NUM_KNOWN_FORMATS; ++i)
if (get_uint32 (&magic[0]) == mach_o_known_formats[i])
break;
if (i == MACH_O_NUM_KNOWN_FORMATS)
goto not_for_target;
/* Check the endian-ness. */
if (BYTES_BIG_ENDIAN && magic[0] != 0xfe)
goto not_for_target;
/* Set or check cached magic number. */
if (cached_mach_o_magic == 0)
cached_mach_o_magic = get_uint32 (&magic[0]);
else if (cached_mach_o_magic != get_uint32 (&magic[0]))
goto not_for_target;
n = mach_o_word_size () == 64
? sizeof (mach_o_header_64) : sizeof (mach_o_header_32);
if (read (mach_o_file->fd, &mach_o_file->u.header, n) != n)
goto not_for_target;
/* Is this a supported CPU? */
/* ??? Would be nice to validate the exact target architecture. */
cputype = get_uint32 (&mach_o_file->u.header.cputype[0]);
if (cputype == MACH_O_CPU_TYPE_I386
|| cputype == MACH_O_CPU_TYPE_POWERPC)
{
if (mach_o_word_size () != 32)
goto not_for_target;
}
else if (cputype == MACH_O_CPU_TYPE_X86_64
|| cputype == MACH_O_CPU_TYPE_POWERPC_64)
{
if (mach_o_word_size () != 64)
goto not_for_target;
}
/* Is this an MH_OBJECT file? */
if (get_uint32 (&mach_o_file->u.header.filetype[0]) != MACH_O_MH_OBJECT)
error ("Mach-O file %s is not an MH_OBJECT file",
mach_o_file->base.filename);
/* Save the header for future use. */
memcpy (&cached_mach_o_header, &mach_o_file->u.header,
sizeof (cached_mach_o_header));
return true;
not_for_target:
error ("file %s is not a Mach-O object file for target",
mach_o_file->base.filename);
return false;
}
/* Read a Mach-O LC_SEGMENT command (32 bits) from MACH_O_FILE and
validate it.
The file descriptor in MACH_O_FILE points at the start of the load
command. On sucess, returns true and advances the file pointer
past the end of the load command. On failure, returns false. */
static bool
validate_mach_o_segment_command_32 (lto_mach_o_file *mach_o_file)
{
mach_o_segment_command_32 seg_cmd_32;
unsigned int i;
ssize_t n;
off_t startpos;
/* Fields we're interested in. */
uint32_t cmd;
uint32_t cmdsize;
uint32_t nsects;
startpos = lseek (mach_o_file->fd, 0, SEEK_CUR);
n = sizeof (mach_o_segment_command_32);
if (read (mach_o_file->fd, (void *) &seg_cmd_32, n) != n)
goto fail;
cmd = get_uint32 (&seg_cmd_32.cmd[0]);
cmdsize = get_uint32 (&seg_cmd_32.cmdsize[0]);
nsects = get_uint32 (&seg_cmd_32.nsects[0]);
gcc_assert (cmd == MACH_O_LC_SEGMENT);
/* Validate section table entries. */
for (i = 0; i < nsects; i++)
{
mach_o_section_32 sec_32;
lto_mach_o_section ltosec;
n = sizeof (mach_o_section_32);
if (read (mach_o_file->fd, &sec_32, n) != n)
goto fail;
/* ??? Perform some checks. */
/* Looks ok, so record its details. We don't read the
string table or set up names yet; we'll do that when
we build the hash table. */
ltosec = mach_o_new_section (mach_o_file, NULL);
memcpy (&ltosec->u.section_32, &sec_32, sizeof (sec_32));
}
if (lseek (mach_o_file->fd, 0, SEEK_CUR) != startpos + cmdsize)
goto fail;
return true;
fail:
error ("could not read LC_SEGMENT command in Mach-O file %s",
mach_o_file->base.filename);
return false;
}
/* Read a Mach-O LC_SEGMENT_64 command from MACH_O_FILE and validate it.
The file descriptor in MACH_O_FILE points at the start of the load
command. On sucess, returns true and advances the file pointer
past the end of the load command. On failure, returns false. */
static bool
validate_mach_o_segment_command_64 (lto_mach_o_file *mach_o_file)
{
mach_o_segment_command_64 seg_cmd_64;
unsigned int i;
ssize_t n;
off_t startpos;
/* Fields we're interested in. */
uint32_t cmd;
uint32_t cmdsize;
uint32_t nsects;
startpos = lseek (mach_o_file->fd, 0, SEEK_CUR);
n = sizeof (mach_o_segment_command_64);
if (read (mach_o_file->fd, (void *) &seg_cmd_64, n) != n)
goto fail;
cmd = get_uint32 (&seg_cmd_64.cmd[0]);
cmdsize = get_uint32 (&seg_cmd_64.cmdsize[0]);
nsects = get_uint32 (&seg_cmd_64.nsects[0]);
gcc_assert (cmd == MACH_O_LC_SEGMENT_64);
/* Validate section table entries. */
for (i = 0; i < nsects; i++)
{
mach_o_section_64 sec_64;
lto_mach_o_section ltosec;
n = sizeof (mach_o_section_64);
if (read (mach_o_file->fd, &sec_64, n) != n)
goto fail;
/* ??? Perform some checks. */
/* Looks ok, so record its details. We don't read the
string table or set up names yet; we'll do that when
we build the hash table. */
ltosec = mach_o_new_section (mach_o_file, NULL);
memcpy (&ltosec->u.section_64, &sec_64, sizeof (sec_64));
}
if (lseek (mach_o_file->fd, 0, SEEK_CUR) != startpos + cmdsize)
goto fail;
return true;
fail:
error ("could not read LC_SEGMENT_64 command in Mach-O file %s",
mach_o_file->base.filename);
return false;
}
/* Read a Mach-O load commands from MACH_O_FILE and validate it.
The file descriptor in MACH_O_FILE points at the start of the load
command. On sucess, returns true and advances the file pointer
past the end of the load command. On failure, returns false. */
static bool
validate_mach_o_load_command (lto_mach_o_file *mach_o_file)
{
mach_o_load_command load_command;
uint32_t cmd;
uint32_t cmdsize;
ssize_t n;
n = sizeof (load_command);
if (read (mach_o_file->fd, &load_command, n) != n)
{
error ("could not read load commands in Mach-O file %s",
mach_o_file->base.filename);
return false;
}
lseek (mach_o_file->fd, -1 * (off_t) sizeof (load_command), SEEK_CUR);
cmd = get_uint32 (&load_command.cmd[0]);
cmdsize = get_uint32 (&load_command.cmdsize[0]);
switch (cmd)
{
case MACH_O_LC_SEGMENT:
return validate_mach_o_segment_command_32 (mach_o_file);
case MACH_O_LC_SEGMENT_64:
return validate_mach_o_segment_command_64 (mach_o_file);
default:
/* Just skip over it. */
lseek (mach_o_file->fd, cmdsize, SEEK_CUR);
return true;
}
}
/* Validate's MACH_O_FILE's executable header and, if cached_mach_o_header is
uninitialized, caches the results. Also records the section header string
table's section index. Returns true on success, false on failure. */
static bool
validate_file (lto_mach_o_file *mach_o_file)
{
uint32_t i, ncmds;
/* Read and sanity check the raw header. */
if (! validate_mach_o_header (mach_o_file))
return false;
ncmds = get_uint32 (&mach_o_file->u.header.ncmds[0]);
for (i = 0; i < ncmds; ++i)
if (! validate_mach_o_load_command (mach_o_file))
return false;
return true;
}
/* Initialize MACH_O_FILE's executable header using cached data from previously
read files. */
static void
init_mach_o_header (lto_mach_o_file *mach_o_file)
{
gcc_assert (cached_mach_o_magic != 0);
memcpy (&mach_o_file->u.header,
&cached_mach_o_header,
sizeof (mach_o_file->u.header));
put_uint32 (&mach_o_file->u.header.ncmds[0], 0);
put_uint32 (&mach_o_file->u.header.sizeofcmds[0], 0);
}
/* Open Mach-O file FILENAME. If WRITABLE is true, the file is opened for write
and, if necessary, created. Otherwise, the file is opened for reading.
Returns the opened file. */
lto_file *
lto_obj_file_open (const char *filename, bool writable)
{
lto_mach_o_file *mach_o_file;
lto_file *result = NULL;
off_t offset;
const char *offset_p;
char *fname;
struct stat statbuf;
offset_p = strchr (filename, '@');
if (!offset_p)
{
fname = xstrdup (filename);
offset = 0;
}
else
{
/* The file started with '@' is a file containing command line
options. Stop if it doesn't exist. */
if (offset_p == filename)
fatal_error ("command line option file '%s' does not exist",
filename);
fname = (char *) xmalloc (offset_p - filename + 1);
memcpy (fname, filename, offset_p - filename);
fname[offset_p - filename] = '\0';
offset_p += 3; /* skip the @0x */
offset = lto_parse_hex (offset_p);
}
/* Set up. */
mach_o_file = XCNEW (lto_mach_o_file);
result = (lto_file *) mach_o_file;
lto_file_init (result, fname, offset);
mach_o_file->fd = -1;
mach_o_file->writable = writable;
/* Open the file. */
mach_o_file->fd = open (fname,
O_BINARY | (writable ? O_WRONLY | O_CREAT | O_TRUNC : O_RDONLY), 0666);
if (mach_o_file->fd == -1)
{
error ("could not open file %s", fname);
goto fail;
}
if (stat (fname, &statbuf) < 0)
{
error ("could not stat file %s", fname);
goto fail;
}
mach_o_file->file_size = statbuf.st_size;
/* If the object is in an archive, get it out. */
if (offset != 0)
{
char ar_tail[12];
int size;
/* Surely not? */
gcc_assert (!writable);
/* Seek to offset, or error. */
if (lseek (mach_o_file->fd, offset, SEEK_SET) != (ssize_t) offset)
{
error ("could not find archive member @0x%lx", (long) offset);
goto fail;
}
/* Now seek back 12 chars and read the tail of the AR header to
find the length of the member file. */
if (lseek (mach_o_file->fd, -12, SEEK_CUR) < 0
|| read (mach_o_file->fd, ar_tail, 12) != 12
|| lseek (mach_o_file->fd, 0, SEEK_CUR) != (ssize_t) offset
|| ar_tail[10] != '`' || ar_tail[11] != '\n')
{
error ("could not find archive header @0x%lx", (long) offset);
goto fail;
}
ar_tail[11] = 0;
if (sscanf (ar_tail, "%d", &size) != 1)
{
error ("invalid archive header @0x%lx", (long) offset);
goto fail;
}
mach_o_file->file_size = size;
}
if (writable)
{
init_mach_o_header (mach_o_file);
}
else
if (! validate_file (mach_o_file))
goto fail;
return result;
fail:
if (result)
lto_obj_file_close (result);
return NULL;
}
/* Write the data in MACH_O_FILE to a real Mach-O binary object.
We write a header, a segment load command, and section data. */
static bool
mach_o_write_object_file (lto_mach_o_file *mach_o_file)
{
lto_mach_o_section sec, snsec;
lto_mach_o_data snsec_data;
ssize_t hdrsize, cmdsize, secsize;
size_t num_sections, snsec_size, total_sec_size;
unsigned int sec_offs, strtab_offs;
int i;
bool write_err = false;
/* The number of sections we will write is the number of sections added by
the streamer, plus 1 for the section names section. */
num_sections = VEC_length (lto_mach_o_section, mach_o_file->section_vec) + 1;
/* Calculate the size of the basic data structures on disk. */
if (mach_o_word_size () == 64)
{
hdrsize = sizeof (mach_o_header_64);
secsize = sizeof (mach_o_section_64);
cmdsize = sizeof (mach_o_segment_command_64) + num_sections * secsize;
}
else
{
hdrsize = sizeof (mach_o_header_32);
secsize = sizeof (mach_o_section_32);
cmdsize = sizeof (mach_o_segment_command_32) + num_sections * secsize;
}
/* Allocate the section names section. */
snsec_size = 0;
for (i = 0;
VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
snsec_size += strlen (sec->name) + 1;
snsec = mach_o_new_section (mach_o_file, NULL);
snsec->name = LTO_NAMES_SECTION;
snsec_data = mach_o_new_data (snsec);
snsec_data->d_buf = XCNEWVEC (char, snsec_size);
snsec_data->d_size = snsec_size;
/* Position all the sections, and fill out their headers. */
sec_offs = hdrsize + cmdsize;
strtab_offs = 0;
total_sec_size = 0;
for (i = 0;
VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
{
lto_mach_o_data data;
size_t data_size;
/* Put the section and segment names. Add the section name to the
section names section (unless, of course, this *is* the section
names section). */
if (sec == snsec)
snprintf (sec->u.section.sectname, 16, "%s", LTO_NAMES_SECTION);
else
{
sprintf (sec->u.section.sectname, "__%08X", strtab_offs);
memcpy ((char *) snsec_data->d_buf + strtab_offs, sec->name, strlen (sec->name));
}
memcpy (&sec->u.section.segname[0],
LTO_SEGMENT_NAME, strlen (LTO_SEGMENT_NAME));
/* Add layout and attributes. */
for (data = sec->data_chain, data_size = 0; data; data = data->next)
data_size += data->d_size;
if (mach_o_word_size () == 64)
{
put_uint64 (&sec->u.section_64.addr[0], total_sec_size);
put_uint64 (&sec->u.section_64.size[0], data_size);
put_uint32 (&sec->u.section_64.offset[0], sec_offs);
put_uint32 (&sec->u.section_64.flags[0], MACH_O_S_ATTR_DEBUG);
}
else
{
put_uint32 (&sec->u.section_64.addr[0], total_sec_size);
put_uint32 (&sec->u.section_32.size[0], data_size);
put_uint32 (&sec->u.section_32.offset[0], sec_offs);
put_uint32 (&sec->u.section_32.flags[0], MACH_O_S_ATTR_DEBUG);
}
sec_offs += data_size;
total_sec_size += data_size;
strtab_offs += strlen (sec->name) + 1;
}
/* We can write the data now. As there's no way to indicate an error return
from this hook, error handling is limited to not wasting our time doing
any more writes in the event that any one fails. */
/* Write the header. */
put_uint32 (&mach_o_file->u.header.ncmds[0], 1);
put_uint32 (&mach_o_file->u.header.sizeofcmds[0], cmdsize);
write_err = (write (mach_o_file->fd,
&mach_o_file->u.header, hdrsize) != hdrsize);
/* Write the segment load command. */
if (mach_o_word_size () == 64)
{
mach_o_segment_command_64 lc;
ssize_t lc_size = sizeof (lc);
memset (&lc, 0, lc_size);
put_uint32 (&lc.cmd[0], MACH_O_LC_SEGMENT_64);
put_uint32 (&lc.cmdsize[0], cmdsize);
put_uint64 (&lc.fileoff[0], hdrsize + cmdsize);
put_uint64 (&lc.filesize[0], total_sec_size);
put_uint32 (&lc.nsects[0], num_sections);
write_err = (write (mach_o_file->fd, &lc, lc_size) != lc_size);
}
else
{
mach_o_segment_command_32 lc;
ssize_t lc_size = sizeof (lc);
memset (&lc, 0, lc_size);
put_uint32 (&lc.cmd[0], MACH_O_LC_SEGMENT);
put_uint32 (&lc.cmdsize[0], cmdsize);
put_uint32 (&lc.fileoff[0], hdrsize + cmdsize);
put_uint32 (&lc.filesize[0], total_sec_size);
put_uint32 (&lc.nsects[0], num_sections);
write_err = (write (mach_o_file->fd, &lc, lc_size) != lc_size);
}
for (i = 0;
!write_err
&& VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
write_err = (write (mach_o_file->fd,
&sec->u.section, secsize) != secsize);
gcc_assert (lseek (mach_o_file->fd, 0, SEEK_CUR) == hdrsize + cmdsize);
/* Write the section data. */
for (i = 0;
!write_err
&& VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
{
lto_mach_o_data data;
for (data = sec->data_chain; data; data = data->next)
{
if (!write_err)
write_err = (write (mach_o_file->fd, data->d_buf, data->d_size)
!= data->d_size);
else
break;
}
}
return !write_err;
}
/* Close Mach-O file FILE and clean up any associated data structures. If FILE
was opened for writing, the file's Mach-O data is written at this time. Any
cached data buffers are freed. */
void
lto_obj_file_close (lto_file *file)
{
lto_mach_o_file *mach_o_file = (lto_mach_o_file *) file;
struct lto_char_ptr_base *cur, *tmp;
lto_mach_o_section sec;
bool write_err = false;
int i;
/* If this file is open for writing, write a Mach-O object file. */
if (mach_o_file->writable)
{
if (! mach_o_write_object_file (mach_o_file))
fatal_error ("cannot write Mach-O object file");
}
/* Close the file, we're done. */
if (mach_o_file->fd != -1)
close (mach_o_file->fd);
/* Free any data buffers. */
cur = mach_o_file->data;
while (cur)
{
tmp = cur;
cur = (struct lto_char_ptr_base *) cur->ptr;
free (tmp);
}
/* Free any sections and their data chains. */
for (i = 0;
VEC_iterate (lto_mach_o_section, mach_o_file->section_vec, i, sec);
i++)
{
lto_mach_o_data curdata, nextdata;
curdata = sec->data_chain;
while (curdata)
{
nextdata = curdata->next;
free (curdata);
curdata = nextdata;
}
free (sec);
}
VEC_free (lto_mach_o_section, heap, mach_o_file->section_vec);
free (file);
/* If there was an error, mention it. */
if (write_err)
error ("I/O error writing Mach-O output file");
}

251
gcc/lto/lto-macho.h Normal file
View File

@ -0,0 +1,251 @@
/* LTO routines for Mach-O object files.
Copyright 2010 Free Software Foundation, Inc.
Contributed by Steven Bosscher.
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/>. */
#ifndef LTO_MACH_O_H
#define LTO_MACH_O_H
/* On-disk file structures. */
/* Mach-O header (32 bits version). */
struct mach_o_header_32
{
unsigned char magic[4]; /* Magic number. */
unsigned char cputype[4]; /* CPU that this object is for. */
unsigned char cpusubtype[4]; /* CPU subtype. */
unsigned char filetype[4]; /* Type of file. */
unsigned char ncmds[4]; /* Number of load commands. */
unsigned char sizeofcmds[4]; /* Total size of load commands. */
unsigned char flags[4]; /* Flags for special featues. */
};
typedef struct mach_o_header_32 mach_o_header_32;
/* Mach-O header (64 bits version). */
struct mach_o_header_64
{
unsigned char magic[4]; /* Magic number. */
unsigned char cputype[4]; /* CPU that this object is for. */
unsigned char cpusubtype[4]; /* CPU subtype. */
unsigned char filetype[4]; /* Type of file. */
unsigned char ncmds[4]; /* Number of load commands. */
unsigned char sizeofcmds[4]; /* Total size of load commands. */
unsigned char flags[4]; /* Flags for special featues. */
unsigned char reserved[4]; /* Reserved. Duh. */
};
typedef struct mach_o_header_64 mach_o_header_64;
/* Magic number. */
#define MACH_O_MH_MAGIC 0xfeedface
#define MACH_O_MH_CIGAM 0xcefaedfe
#define MACH_O_MH_MAGIC_64 0xfeedfacf
#define MACH_O_MH_CIGAM_64 0xcffaedfe
/* Supported CPU types. */
#define MACH_O_CPU_TYPE_I386 7
#define MACH_O_CPU_TYPE_X86_64 7 + 0x1000000
#define MACH_O_CPU_TYPE_POWERPC 18
#define MACH_O_CPU_TYPE_POWERPC_64 18 + 0x1000000
/* Supported file types. */
#define MACH_O_MH_OBJECT 0x01
/* Mach-O load command data structure. */
struct mach_o_load_command
{
unsigned char cmd[4]; /* The type of load command. */
unsigned char cmdsize[4]; /* Size in bytes of load command data structure. */
};
typedef struct mach_o_load_command mach_o_load_command;
/* Supported load commands. We support only the segment load commands. */
#define MACH_O_LC_SEGMENT 0x01
#define MACH_O_LC_SEGMENT_64 0x19
/* LC_SEGMENT load command. */
struct mach_o_segment_command_32
{
unsigned char cmd[4]; /* The type of load command (LC_SEGMENT). */
unsigned char cmdsize[4]; /* Size in bytes of load command data structure. */
unsigned char segname[16]; /* Name of this segment. */
unsigned char vmaddr[4]; /* Virtual memory address of this segment. */
unsigned char vmsize[4]; /* Size there, in bytes. */
unsigned char fileoff[4]; /* Offset in bytes of the data to be mapped. */
unsigned char filesize[4]; /* Size in bytes on disk. */
unsigned char maxprot[4]; /* Maximum permitted vmem protection. */
unsigned char initprot[4]; /* Initial vmem protection. */
unsigned char nsects[4]; /* Number of sections in this segment. */
unsigned char flags[4]; /* Flags that affect the loading. */
};
typedef struct mach_o_segment_command_32 mach_o_segment_command_32;
/* LC_SEGMENT_64 load command. Only nsects matters for us, really. */
struct mach_o_segment_command_64
{
unsigned char cmd[4]; /* The type of load command (LC_SEGMENT_64). */
unsigned char cmdsize[4]; /* Size in bytes of load command data structure. */
unsigned char segname[16]; /* Name of this segment. */
unsigned char vmaddr[8]; /* Virtual memory address of this segment. */
unsigned char vmsize[8]; /* Size there, in bytes. */
unsigned char fileoff[8]; /* Offset in bytes of the data to be mapped. */
unsigned char filesize[8]; /* Size in bytes on disk. */
unsigned char maxprot[4]; /* Maximum permitted vmem protection. */
unsigned char initprot[4]; /* Initial vmem protection. */
unsigned char nsects[4]; /* Number of sections in this segment. */
unsigned char flags[4]; /* Flags that affect the loading. */
};
typedef struct mach_o_segment_command_64 mach_o_segment_command_64;
/* A Mach-O 32-bits section. */
struct mach_o_section_32
{
unsigned char sectname[16]; /* Section name. */
unsigned char segname[16]; /* Segment that the section belongs to. */
unsigned char addr[4]; /* Address of this section in memory. */
unsigned char size[4]; /* Size in bytes of this section. */
unsigned char offset[4]; /* File offset of this section. */
unsigned char align[4]; /* log2 of this section's alignment. */
unsigned char reloff[4]; /* File offset of this section's relocs. */
unsigned char nreloc[4]; /* Number of relocs for this section. */
unsigned char flags[4]; /* Section flags/attributes. */
unsigned char reserved1[4];
unsigned char reserved2[4];
};
typedef struct mach_o_section_32 mach_o_section_32;
/* A Mach-O 64-bits section. */
struct mach_o_section_64
{
unsigned char sectname[16]; /* Section name. */
unsigned char segname[16]; /* Segment that the section belongs to. */
unsigned char addr[8]; /* Address of this section in memory. */
unsigned char size[8]; /* Size in bytes of this section. */
unsigned char offset[4]; /* File offset of this section. */
unsigned char align[4]; /* log2 of this section's alignment. */
unsigned char reloff[4]; /* File offset of this section's relocs. */
unsigned char nreloc[4]; /* Number of relocs for this section. */
unsigned char flags[4]; /* Section flags/attributes. */
unsigned char reserved1[4];
unsigned char reserved2[4];
unsigned char reserved3[4];
};
typedef struct mach_o_section_64 mach_o_section_64;
/* Flags for Mach-O sections. LTO sections are marked with S_ATTR_DEBUG
to instruct the linker to ignore the sections. */
#define MACH_O_S_ATTR_DEBUG 0x02000000
/* In-memory file structures. */
/* Section data in output files is made of these. */
struct lto_mach_o_data_d
{
/* Pointer to data block. */
void *d_buf;
/* Size of data block. */
ssize_t d_size;
/* Next data block for this section. */
struct lto_mach_o_data_d *next;
};
typedef struct lto_mach_o_data_d *lto_mach_o_data;
/* This struct tracks the data for a section. */
struct lto_mach_o_section_d
{
/* Singly-linked list of section's data blocks. */
lto_mach_o_data data_chain;
/* Offset in string table of the section name. */
size_t strtab_offs;
/* Section name. */
const char *name;
/* Number of trailing padding bytes needed. */
ssize_t pad_needed;
/* Raw section header data. */
size_t section_size;
union {
struct {
char sectname[16];
char segname[16];
} section;
mach_o_section_32 section_32;
mach_o_section_64 section_64;
} u;
/* Next section for this file. */
struct lto_mach_o_section_d *next;
};
typedef struct lto_mach_o_section_d *lto_mach_o_section;
DEF_VEC_P (lto_mach_o_section);
DEF_VEC_ALLOC_P (lto_mach_o_section, heap);
/* A Mach-O file. */
struct lto_mach_o_file_d
{
/* The base information. */
lto_file base;
/* Common file members: */
/* The system file descriptor for the file. */
int fd;
/* The file's overall header. */
union {
/* We make use here of the fact that section_32 and section_64
have the same layout (except for section_64.reserved3). We
read the struct of proper size, but only address the first
member of this union. */
mach_o_header_64 header;
mach_o_header_32 header_32;
mach_o_header_64 header_64;
} u;
/* All sections in a varray. */
VEC(lto_mach_o_section, heap) *section_vec;
/* Readable file members: */
/* File total size. */
off_t file_size;
/* True if this file is open for writing. */
bool writable;
/* Section containing the __section_names section. */
lto_mach_o_section section_names_section;
/* Writable file members: */
/* The currently active section. */
lto_mach_o_section scn;
/* Linked list of data which must be freed *after* the file has been
closed. This is an annoying limitation of libelf. Which has been
faithfully reproduced here. */
struct lto_char_ptr_base *data;
};
typedef struct lto_mach_o_file_d lto_mach_o_file;
#endif /* LTO_MACH_O_H */

View File

@ -1,5 +1,5 @@
/* LTO declarations.
Copyright 2009 Free Software Foundation, Inc.
Copyright 2009, 2010 Free Software Foundation, Inc.
Contributed by CodeSourcery, Inc.
This file is part of GCC.
@ -28,6 +28,7 @@ typedef struct lto_file_struct
{
/* The name of the file. */
const char *filename;
/* The offset for the object inside an ar archive file (or zero). */
off_t offset;
} lto_file;

View File

@ -204,6 +204,14 @@
#define TARGET_ASM_FILE_END hook_void_void
#endif
#ifndef TARGET_ASM_LTO_START
#define TARGET_ASM_LTO_START hook_void_void
#endif
#ifndef TARGET_ASM_LTO_END
#define TARGET_ASM_LTO_END hook_void_void
#endif
#ifndef TARGET_ASM_CODE_END
#define TARGET_ASM_CODE_END hook_void_void
#endif
@ -296,6 +304,8 @@
TARGET_ASM_CAN_OUTPUT_MI_THUNK, \
TARGET_ASM_FILE_START, \
TARGET_ASM_FILE_END, \
TARGET_ASM_LTO_START, \
TARGET_ASM_LTO_END, \
TARGET_ASM_CODE_END, \
TARGET_ASM_EXTERNAL_LIBCALL, \
TARGET_ASM_MARK_DECL_PRESERVED, \

View File

@ -238,6 +238,14 @@ struct gcc_target
translation unit. */
void (*file_end) (void);
/* Output any boilerplate text needed at the beginning of an
LTO output stream. */
void (*lto_start) (void);
/* Output any boilerplate text needed at the end of an
LTO output stream. */
void (*lto_end) (void);
/* Output any boilerplace text needed at the end of a
translation unit before debug and unwind info is emitted. */
void (*code_end) (void);