mirror of git://gcc.gnu.org/git/gcc.git
re PR lto/42776 (LTO doesn't work on non-ELF platforms.)
ChangeLog: PR lto/42776 * configure.ac (--enable-lto): Refactor handling so libelf tests are only performed inside then-clause of ACX_ELF_TARGET_IFELSE, and allow LTO to be explicitly enabled on non-ELF platforms that are known to support it inside else-clause. * configure: Regenerate. gcc/ChangeLog: PR lto/42776 * configure.ac (gcc_cv_as_section_has_align): Set if installed binutils supports extended .section directive needed by LTO, or warn if older binutils found. (LTO_BINARY_READER): New AC_SUBST'd variable. (LTO_USE_LIBELF): Likewise. * gcc/config.gcc (lto_binary_reader): New target-specific configure variable. * gcc/Makefile.in (LTO_BINARY_READER): Import AC_SUBST'd autoconf var. (LTO_USE_LIBELF): Likewise. * configure: Regenerate. * collect2.c (is_elf): Rename from this ... (is_elf_or_coff): ... to this, and recognize and allow i386 COFF object files in addition to ELF-formatted ones. (scan_prog_file): Caller updated. Also allow for LTO info marker symbol to be prefixed or not by an extra underscore. * config/i386/t-cygming (winnt.o): Also depend on LTO_STREAMER_H. * config/i386/winnt.c: Also #include lto-streamer.h (i386_pe_asm_named_section): Specify 1-byte section alignment for LTO named sections. (i386_pe_asm_output_aligned_decl_common): Add comment. (i386_pe_maybe_record_exported_symbol): Allow decl arg to be NULL. gcc/lto/ChangeLog: PR lto/42776 * Make-lang.in (LTO_OBJS): Use LTO_BINARY_READER instead of hardcoding 'lto-elf.o'. ($(LTO_EXE)): Use LTO_USE_LIBELF instead of hardcoding '-lelf'. * lto-coff.h: New file. * lto-coff.c: Likewise. gcc/testsuite/ChangeLog: PR lto/42776 * lib/lto.exp (lto_prune_vis_warns): New function. (lto-link-and-maybe-run): Call it. From-SVN: r158762
This commit is contained in:
parent
45c384e375
commit
3bec79c52e
|
|
@ -1,3 +1,12 @@
|
||||||
|
2010-04-27 Dave Korn <dave.korn.cygwin@gmail.com>
|
||||||
|
|
||||||
|
PR lto/42776
|
||||||
|
* configure.ac (--enable-lto): Refactor handling so libelf tests
|
||||||
|
are only performed inside then-clause of ACX_ELF_TARGET_IFELSE,
|
||||||
|
and allow LTO to be explicitly enabled on non-ELF platforms that
|
||||||
|
are known to support it inside else-clause.
|
||||||
|
* configure: Regenerate.
|
||||||
|
|
||||||
2010-04-20 Eric Botcazou <ebotcazou@adacore.com>
|
2010-04-20 Eric Botcazou <ebotcazou@adacore.com>
|
||||||
|
|
||||||
* configure.ac (BUILD_CONFIG): Redirect output to /dev/null.
|
* configure.ac (BUILD_CONFIG): Redirect output to /dev/null.
|
||||||
|
|
|
||||||
30
configure.ac
30
configure.ac
|
|
@ -1637,17 +1637,8 @@ AC_ARG_ENABLE(lto,
|
||||||
enable_lto=$enableval,
|
enable_lto=$enableval,
|
||||||
enable_lto=yes; default_enable_lto=yes)
|
enable_lto=yes; default_enable_lto=yes)
|
||||||
|
|
||||||
ACX_ELF_TARGET_IFELSE([],
|
|
||||||
if test x"$default_enable_lto" = x"yes" ; then
|
|
||||||
enable_lto=no
|
|
||||||
else
|
|
||||||
if test x"$enable_lto" = x"yes"; then
|
|
||||||
AC_MSG_ERROR([LTO support requires an ELF target.])
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
default_enable_lto=no)
|
|
||||||
|
|
||||||
if test x"$enable_lto" = x"yes" ; then
|
ACX_ELF_TARGET_IFELSE([if test x"$enable_lto" = x"yes" ; then
|
||||||
# Make sure that libelf.h and gelf.h are available.
|
# Make sure that libelf.h and gelf.h are available.
|
||||||
AC_ARG_WITH(libelf, [ --with-libelf=PATH Specify prefix directory for the installed libelf package
|
AC_ARG_WITH(libelf, [ --with-libelf=PATH Specify prefix directory for the installed libelf package
|
||||||
Equivalent to --with-libelf-include=PATH/include
|
Equivalent to --with-libelf-include=PATH/include
|
||||||
|
|
@ -1783,7 +1774,24 @@ to specify its location.])
|
||||||
# Flags needed for libelf.
|
# Flags needed for libelf.
|
||||||
AC_SUBST(libelflibs)
|
AC_SUBST(libelflibs)
|
||||||
AC_SUBST(libelfinc)
|
AC_SUBST(libelfinc)
|
||||||
fi
|
fi],[if test x"$default_enable_lto" = x"yes" ; then
|
||||||
|
# On non-ELF platforms, LTO must be explicitly enabled.
|
||||||
|
enable_lto=no
|
||||||
|
else
|
||||||
|
# Apart from ELF platforms, only Windows supports LTO so far. It
|
||||||
|
# would also be nice to check the binutils support, but we don't
|
||||||
|
# have gcc_GAS_CHECK_FEATURE available here. For now, we'll just
|
||||||
|
# warn during gcc/ subconfigure; unless you're bootstrapping with
|
||||||
|
# -flto it won't be needed until after installation anyway.
|
||||||
|
case $target in
|
||||||
|
*-cygwin*|*-mingw*) ;;
|
||||||
|
*) if test x"$enable_lto" = x"yes"; then
|
||||||
|
AC_MSG_ERROR([LTO support is not enabled for this target.])
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
default_enable_lto=no])
|
||||||
|
|
||||||
|
|
||||||
# By default, C is the only stage 1 language.
|
# By default, C is the only stage 1 language.
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,10 @@ LIBELFINC = @LIBELFINC@
|
||||||
# Set to 'yes' if the LTO front end is enabled.
|
# Set to 'yes' if the LTO front end is enabled.
|
||||||
enable_lto = @enable_lto@
|
enable_lto = @enable_lto@
|
||||||
|
|
||||||
|
# Set according to LTO object file format.
|
||||||
|
LTO_BINARY_READER = @LTO_BINARY_READER@
|
||||||
|
LTO_USE_LIBELF = @LTO_USE_LIBELF@
|
||||||
|
|
||||||
# Compiler needed for plugin support
|
# Compiler needed for plugin support
|
||||||
PLUGINCC = @CC@
|
PLUGINCC = @CC@
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2548,19 +2548,21 @@ write_aix_file (FILE *stream, struct id *list)
|
||||||
be in ELF format. */
|
be in ELF format. */
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_elf (const char *prog_name)
|
is_elf_or_coff (const char *prog_name)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
char buf[4];
|
char buf[4];
|
||||||
static char magic[4] = { 0x7f, 'E', 'L', 'F' };
|
static char magic[4] = { 0x7f, 'E', 'L', 'F' };
|
||||||
|
static char coffmag[2] = { 0x4c, 0x01 };
|
||||||
|
|
||||||
f = fopen (prog_name, "r");
|
f = fopen (prog_name, "rb");
|
||||||
if (f == NULL)
|
if (f == NULL)
|
||||||
return false;
|
return false;
|
||||||
if (fread (buf, sizeof (buf), 1, f) != 1)
|
if (fread (buf, sizeof (buf), 1, f) != 1)
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
fclose (f);
|
fclose (f);
|
||||||
return memcmp (buf, magic, sizeof (magic)) == 0;
|
return memcmp (buf, magic, sizeof (magic)) == 0
|
||||||
|
|| memcmp (buf, coffmag, sizeof (coffmag)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generic version to scan the name list of the loaded program for
|
/* Generic version to scan the name list of the loaded program for
|
||||||
|
|
@ -2587,10 +2589,10 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
|
||||||
if (which_pass == PASS_SECOND)
|
if (which_pass == PASS_SECOND)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* LTO objects must be in ELF format. This check prevents
|
/* LTO objects must be in a known format. This check prevents
|
||||||
us from accepting an archive containing LTO objects, which
|
us from accepting an archive containing LTO objects, which
|
||||||
gcc cannnot currently handle. */
|
gcc cannnot currently handle. */
|
||||||
if (which_pass == PASS_LTOINFO && !is_elf (prog_name))
|
if (which_pass == PASS_LTOINFO && !is_elf_or_coff (prog_name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If we do not have an `nm', complain. */
|
/* If we do not have an `nm', complain. */
|
||||||
|
|
@ -2670,9 +2672,9 @@ scan_prog_file (const char *prog_name, scanpass which_pass,
|
||||||
/* Look for the LTO info marker symbol, and add filename to
|
/* Look for the LTO info marker symbol, and add filename to
|
||||||
the LTO objects list if found. */
|
the LTO objects list if found. */
|
||||||
for (p = buf; (ch = *p) != '\0' && ch != '\n'; p++)
|
for (p = buf; (ch = *p) != '\0' && ch != '\n'; p++)
|
||||||
if (ch == ' '
|
if (ch == ' ' && p[1] == '_' && p[2] == '_'
|
||||||
&& (strncmp (p + 1, "__gnu_lto_v1", 12) == 0)
|
&& (strncmp (p + (p[3] == '_' ? 2 : 1), "__gnu_lto_v1", 12) == 0)
|
||||||
&& ISSPACE (p[13]))
|
&& ISSPACE (p[p[3] == '_' ? 14 : 13]))
|
||||||
{
|
{
|
||||||
add_lto_object (<o_objects, prog_name);
|
add_lto_object (<o_objects, prog_name);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,8 @@ default_use_cxa_atexit=no
|
||||||
target_gtfiles=
|
target_gtfiles=
|
||||||
need_64bit_hwint=
|
need_64bit_hwint=
|
||||||
need_64bit_isa=
|
need_64bit_isa=
|
||||||
|
# Selects the object file format reader/writer used by LTO.
|
||||||
|
lto_binary_reader=lto-elf
|
||||||
|
|
||||||
# Don't carry these over build->host->target. Please.
|
# Don't carry these over build->host->target. Please.
|
||||||
xm_file=
|
xm_file=
|
||||||
|
|
@ -1324,6 +1326,7 @@ i[34567]86-*-pe | i[34567]86-*-cygwin*)
|
||||||
thread_file='posix'
|
thread_file='posix'
|
||||||
fi
|
fi
|
||||||
use_gcc_stdint=wrap
|
use_gcc_stdint=wrap
|
||||||
|
lto_binary_reader=lto-coff
|
||||||
;;
|
;;
|
||||||
i[34567]86-*-mingw* | x86_64-*-mingw*)
|
i[34567]86-*-mingw* | x86_64-*-mingw*)
|
||||||
tm_file="${tm_file} i386/unix.h i386/bsd.h i386/gas.h dbxcoff.h i386/cygming.h i386/mingw32.h"
|
tm_file="${tm_file} i386/unix.h i386/bsd.h i386/gas.h dbxcoff.h i386/cygming.h i386/mingw32.h"
|
||||||
|
|
@ -1391,6 +1394,7 @@ i[34567]86-*-mingw* | x86_64-*-mingw*)
|
||||||
cxx_target_objs="${cxx_target_objs} winnt-cxx.o msformat-c.o"
|
cxx_target_objs="${cxx_target_objs} winnt-cxx.o msformat-c.o"
|
||||||
default_use_cxa_atexit=yes
|
default_use_cxa_atexit=yes
|
||||||
use_gcc_stdint=wrap
|
use_gcc_stdint=wrap
|
||||||
|
lto_binary_reader=lto-coff
|
||||||
case ${enable_threads} in
|
case ${enable_threads} in
|
||||||
"" | yes | win32) thread_file='win32'
|
"" | yes | win32) thread_file='win32'
|
||||||
tmake_file="${tmake_file} i386/t-gthr-win32"
|
tmake_file="${tmake_file} i386/t-gthr-win32"
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ LIBGCC2_INCLUDES = -I$(srcdir)/../winsup/w32api/include
|
||||||
|
|
||||||
winnt.o: $(srcdir)/config/i386/winnt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
winnt.o: $(srcdir)/config/i386/winnt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
|
||||||
$(TM_H) $(RTL_H) $(REGS_H) hard-reg-set.h output.h $(TREE_H) flags.h \
|
$(TM_H) $(RTL_H) $(REGS_H) hard-reg-set.h output.h $(TREE_H) flags.h \
|
||||||
$(TM_P_H) toplev.h $(HASHTAB_H) $(GGC_H)
|
$(TM_P_H) toplev.h $(HASHTAB_H) $(GGC_H) $(LTO_STREAMER_H)
|
||||||
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
|
$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
|
||||||
$(srcdir)/config/i386/winnt.c
|
$(srcdir)/config/i386/winnt.c
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
|
||||||
#include "langhooks.h"
|
#include "langhooks.h"
|
||||||
#include "ggc.h"
|
#include "ggc.h"
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
|
#include "lto-streamer.h"
|
||||||
|
|
||||||
/* i386/PE specific attribute support.
|
/* i386/PE specific attribute support.
|
||||||
|
|
||||||
|
|
@ -465,6 +466,12 @@ i386_pe_asm_named_section (const char *name, unsigned int flags,
|
||||||
*f++ = 's';
|
*f++ = 's';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* LTO sections need 1-byte alignment to avoid confusing the
|
||||||
|
zlib decompression algorithm with trailing zero pad bytes. */
|
||||||
|
if (strncmp (name, LTO_SECTION_NAME_PREFIX,
|
||||||
|
strlen (LTO_SECTION_NAME_PREFIX)) == 0)
|
||||||
|
*f++ = '0';
|
||||||
|
|
||||||
*f = '\0';
|
*f = '\0';
|
||||||
|
|
||||||
fprintf (asm_out_file, "\t.section\t%s,\"%s\"\n", name, flagchars);
|
fprintf (asm_out_file, "\t.section\t%s,\"%s\"\n", name, flagchars);
|
||||||
|
|
@ -485,6 +492,8 @@ i386_pe_asm_named_section (const char *name, unsigned int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Beware, DECL may be NULL if compile_file() is emitting the LTO marker. */
|
||||||
|
|
||||||
void
|
void
|
||||||
i386_pe_asm_output_aligned_decl_common (FILE *stream, tree decl,
|
i386_pe_asm_output_aligned_decl_common (FILE *stream, tree decl,
|
||||||
const char *name, HOST_WIDE_INT size,
|
const char *name, HOST_WIDE_INT size,
|
||||||
|
|
@ -581,7 +590,8 @@ static GTY(()) struct export_list *export_head;
|
||||||
these, so that we can output the export list at the end of the
|
these, so that we can output the export list at the end of the
|
||||||
assembly. We used to output these export symbols in each function,
|
assembly. We used to output these export symbols in each function,
|
||||||
but that causes problems with GNU ld when the sections are
|
but that causes problems with GNU ld when the sections are
|
||||||
linkonce. */
|
linkonce. Beware, DECL may be NULL if compile_file() is emitting
|
||||||
|
the LTO marker. */
|
||||||
|
|
||||||
void
|
void
|
||||||
i386_pe_maybe_record_exported_symbol (tree decl, const char *name, int is_data)
|
i386_pe_maybe_record_exported_symbol (tree decl, const char *name, int is_data)
|
||||||
|
|
@ -589,6 +599,9 @@ i386_pe_maybe_record_exported_symbol (tree decl, const char *name, int is_data)
|
||||||
rtx symbol;
|
rtx symbol;
|
||||||
struct export_list *p;
|
struct export_list *p;
|
||||||
|
|
||||||
|
if (!decl)
|
||||||
|
return;
|
||||||
|
|
||||||
symbol = XEXP (DECL_RTL (decl), 0);
|
symbol = XEXP (DECL_RTL (decl), 0);
|
||||||
gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
|
gcc_assert (GET_CODE (symbol) == SYMBOL_REF);
|
||||||
if (!SYMBOL_REF_DLLEXPORT_P (symbol))
|
if (!SYMBOL_REF_DLLEXPORT_P (symbol))
|
||||||
|
|
|
||||||
|
|
@ -671,6 +671,8 @@ subdirs
|
||||||
slibdir
|
slibdir
|
||||||
dollar
|
dollar
|
||||||
gcc_tooldir
|
gcc_tooldir
|
||||||
|
LTO_USE_LIBELF
|
||||||
|
LTO_BINARY_READER
|
||||||
enable_lto
|
enable_lto
|
||||||
MAINT
|
MAINT
|
||||||
zlibinc
|
zlibinc
|
||||||
|
|
@ -17092,7 +17094,7 @@ else
|
||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 17095 "configure"
|
#line 17097 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
|
|
@ -17198,7 +17200,7 @@ else
|
||||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||||
lt_status=$lt_dlunknown
|
lt_status=$lt_dlunknown
|
||||||
cat > conftest.$ac_ext <<_LT_EOF
|
cat > conftest.$ac_ext <<_LT_EOF
|
||||||
#line 17201 "configure"
|
#line 17203 "configure"
|
||||||
#include "confdefs.h"
|
#include "confdefs.h"
|
||||||
|
|
||||||
#if HAVE_DLFCN_H
|
#if HAVE_DLFCN_H
|
||||||
|
|
@ -22942,6 +22944,48 @@ if test $gcc_cv_as_ix86_pe_secrel32 = yes; then
|
||||||
$as_echo "#define HAVE_GAS_PE_SECREL32_RELOC 1" >>confdefs.h
|
$as_echo "#define HAVE_GAS_PE_SECREL32_RELOC 1" >>confdefs.h
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
# Test if the assembler supports the extended form of the .section
|
||||||
|
# directive that specifies section alignment. LTO support uses this,
|
||||||
|
# but normally only after installation, so we warn but don't fail the
|
||||||
|
# configure if LTO is enabled but the assembler does not support it.
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .section with alignment" >&5
|
||||||
|
$as_echo_n "checking assembler for .section with alignment... " >&6; }
|
||||||
|
if test "${gcc_cv_as_section_has_align+set}" = set; then :
|
||||||
|
$as_echo_n "(cached) " >&6
|
||||||
|
else
|
||||||
|
gcc_cv_as_section_has_align=no
|
||||||
|
if test $in_tree_gas = yes; then
|
||||||
|
if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 20 \) \* 1000 + 1`
|
||||||
|
then gcc_cv_as_section_has_align=yes
|
||||||
|
fi
|
||||||
|
elif test x$gcc_cv_as != x; then
|
||||||
|
echo '.section lto_test,"dr0"' > conftest.s
|
||||||
|
if { ac_try='$gcc_cv_as $gcc_cv_as_flags -fatal-warnings -o conftest.o conftest.s >&5'
|
||||||
|
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
|
||||||
|
(eval $ac_try) 2>&5
|
||||||
|
ac_status=$?
|
||||||
|
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||||
|
test $ac_status = 0; }; }
|
||||||
|
then
|
||||||
|
gcc_cv_as_section_has_align=yes
|
||||||
|
else
|
||||||
|
echo "configure: failed program was" >&5
|
||||||
|
cat conftest.s >&5
|
||||||
|
fi
|
||||||
|
rm -f conftest.o conftest.s
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_section_has_align" >&5
|
||||||
|
$as_echo "$gcc_cv_as_section_has_align" >&6; }
|
||||||
|
|
||||||
|
if test x$gcc_cv_as_section_has_align != xyes; then
|
||||||
|
case ",$enable_languages," in
|
||||||
|
*,lto,*)
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: LTO for $target requires binutils >= 2.20.1, but version found appears insufficient; LTO will not work until binutils is upgraded." >&5
|
||||||
|
$as_echo "$as_me: WARNING: LTO for $target requires binutils >= 2.20.1, but version found appears insufficient; LTO will not work until binutils is upgraded." >&2;}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -25078,6 +25122,17 @@ $as_echo "#define ENABLE_LTO 1" >>confdefs.h
|
||||||
|
|
||||||
enable_lto=yes
|
enable_lto=yes
|
||||||
|
|
||||||
|
# LTO needs to speak the platform's object file format, and has a
|
||||||
|
# number of implementations of the required binary file access APIs.
|
||||||
|
# ELF is the most common, and default. We only link libelf if ELF
|
||||||
|
# is indeed the selected format.
|
||||||
|
LTO_BINARY_READER=${lto_binary_reader}
|
||||||
|
LTO_USE_LIBELF=-lelf
|
||||||
|
if test "x$lto_binary_reader" != "xlto-elf" ; then
|
||||||
|
LTO_USE_LIBELF=
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
;;
|
;;
|
||||||
*) ;;
|
*) ;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -3202,6 +3202,19 @@ foo: nop
|
||||||
rm -f conftest],
|
rm -f conftest],
|
||||||
[AC_DEFINE(HAVE_GAS_PE_SECREL32_RELOC, 1,
|
[AC_DEFINE(HAVE_GAS_PE_SECREL32_RELOC, 1,
|
||||||
[Define if your assembler and linker support 32-bit section relative relocs via '.secrel32 label'.])])
|
[Define if your assembler and linker support 32-bit section relative relocs via '.secrel32 label'.])])
|
||||||
|
# Test if the assembler supports the extended form of the .section
|
||||||
|
# directive that specifies section alignment. LTO support uses this,
|
||||||
|
# but normally only after installation, so we warn but don't fail the
|
||||||
|
# configure if LTO is enabled but the assembler does not support it.
|
||||||
|
gcc_GAS_CHECK_FEATURE([.section with alignment], gcc_cv_as_section_has_align,
|
||||||
|
[2,20,1],-fatal-warnings,[.section lto_test,"dr0"])
|
||||||
|
if test x$gcc_cv_as_section_has_align != xyes; then
|
||||||
|
case ",$enable_languages," in
|
||||||
|
*,lto,*)
|
||||||
|
AC_MSG_WARN([LTO for $target requires binutils >= 2.20.1, but version found appears insufficient; LTO will not work until binutils is upgraded.])
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -4270,6 +4283,17 @@ changequote([,])dnl
|
||||||
AC_DEFINE(ENABLE_LTO, 1, [Define to enable LTO support.])
|
AC_DEFINE(ENABLE_LTO, 1, [Define to enable LTO support.])
|
||||||
enable_lto=yes
|
enable_lto=yes
|
||||||
AC_SUBST(enable_lto)
|
AC_SUBST(enable_lto)
|
||||||
|
# LTO needs to speak the platform's object file format, and has a
|
||||||
|
# number of implementations of the required binary file access APIs.
|
||||||
|
# ELF is the most common, and default. We only link libelf if ELF
|
||||||
|
# is indeed the selected format.
|
||||||
|
LTO_BINARY_READER=${lto_binary_reader}
|
||||||
|
LTO_USE_LIBELF=-lelf
|
||||||
|
if test "x$lto_binary_reader" != "xlto-elf" ; then
|
||||||
|
LTO_USE_LIBELF=
|
||||||
|
fi
|
||||||
|
AC_SUBST(LTO_BINARY_READER)
|
||||||
|
AC_SUBST(LTO_USE_LIBELF)
|
||||||
;;
|
;;
|
||||||
*) ;;
|
*) ;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,13 @@
|
||||||
|
2010-04-27 Dave Korn <dave.korn.cygwin@gmail.com>
|
||||||
|
|
||||||
|
PR lto/42776
|
||||||
|
* Make-lang.in (LTO_OBJS): Use LTO_BINARY_READER instead of
|
||||||
|
hardcoding 'lto-elf.o'.
|
||||||
|
($(LTO_EXE)): Use LTO_USE_LIBELF instead of hardcoding '-lelf'.
|
||||||
|
|
||||||
|
* lto-coff.h: New file.
|
||||||
|
* lto-coff.c: Likewise.
|
||||||
|
|
||||||
2010-04-26 Richard Guenther <rguenther@suse.de>
|
2010-04-26 Richard Guenther <rguenther@suse.de>
|
||||||
|
|
||||||
* lto.c (lto_fixup_type): Deal with non-type TYPE_CONTEXT.
|
* lto.c (lto_fixup_type): Deal with non-type TYPE_CONTEXT.
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
# The name of the LTO compiler.
|
# The name of the LTO compiler.
|
||||||
LTO_EXE = lto1$(exeext)
|
LTO_EXE = lto1$(exeext)
|
||||||
# The LTO-specific object files inclued in $(LTO_EXE).
|
# The LTO-specific object files inclued in $(LTO_EXE).
|
||||||
LTO_OBJS = lto/lto-lang.o lto/lto.o lto/lto-elf.o attribs.o
|
LTO_OBJS = lto/lto-lang.o lto/lto.o lto/$(LTO_BINARY_READER).o attribs.o
|
||||||
LTO_H = lto/lto.h $(HASHTAB_H)
|
LTO_H = lto/lto.h $(HASHTAB_H)
|
||||||
LINKER_PLUGIN_API_H = $(srcdir)/../include/plugin-api.h
|
LINKER_PLUGIN_API_H = $(srcdir)/../include/plugin-api.h
|
||||||
LTO_TREE_H = lto/lto-tree.h $(LINKER_PLUGIN_API_H)
|
LTO_TREE_H = lto/lto-tree.h $(LINKER_PLUGIN_API_H)
|
||||||
|
|
@ -73,7 +73,7 @@ lto-warn = $(STRICT_WARN)
|
||||||
|
|
||||||
$(LTO_EXE): $(LTO_OBJS) $(BACKEND) $(LIBDEPS)
|
$(LTO_EXE): $(LTO_OBJS) $(BACKEND) $(LIBDEPS)
|
||||||
$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
|
$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
|
||||||
$(LTO_OBJS) $(BACKEND) $(BACKENDLIBS) $(LIBS) -lelf
|
$(LTO_OBJS) $(BACKEND) $(BACKENDLIBS) $(LIBS) $(LTO_USE_LIBELF)
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
lto/lto-lang.o: lto/lto-lang.c $(CONFIG_H) coretypes.h debug.h \
|
lto/lto-lang.o: lto/lto-lang.c $(CONFIG_H) coretypes.h debug.h \
|
||||||
|
|
@ -88,3 +88,6 @@ lto/lto.o: lto/lto.c $(CONFIG_H) $(SYSTEM_H) coretypes.h opts.h \
|
||||||
$(LTO_TAGS_H) $(LTO_STREAMER_H)
|
$(LTO_TAGS_H) $(LTO_STREAMER_H)
|
||||||
lto/lto-elf.o: lto/lto-elf.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
|
lto/lto-elf.o: lto/lto-elf.c $(CONFIG_H) coretypes.h $(SYSTEM_H) \
|
||||||
toplev.h $(LTO_H) $(TM_H) $(LIBIBERTY_H) $(GGC_H) $(LTO_STREAMER_H)
|
toplev.h $(LTO_H) $(TM_H) $(LIBIBERTY_H) $(GGC_H) $(LTO_STREAMER_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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,845 @@
|
||||||
|
/* LTO routines for COFF object files.
|
||||||
|
Copyright 2009 Free Software Foundation, Inc.
|
||||||
|
Contributed by Dave Korn.
|
||||||
|
|
||||||
|
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 "ggc.h"
|
||||||
|
#include "lto-streamer.h"
|
||||||
|
#include "lto/lto-coff.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Rather than implementing a libcoff to match libelf, or attempting to
|
||||||
|
integrate libbfd into GCC, this file is a self-contained (and very
|
||||||
|
minimal) COFF format object file reader/writer. The generated files
|
||||||
|
will contain a COFF header, a number of COFF section headers, the
|
||||||
|
section data itself, and a trailing string table for 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
|
||||||
|
|
||||||
|
/* Known header magics for validation, as an array. */
|
||||||
|
|
||||||
|
static const unsigned int coff_machine_array[] = COFF_KNOWN_MACHINES;
|
||||||
|
|
||||||
|
/* Number of valid entries (no sentinel) in array. */
|
||||||
|
|
||||||
|
#define NUM_COFF_KNOWN_MACHINES \
|
||||||
|
(sizeof (coff_machine_array) / sizeof (coff_machine_array[0]))
|
||||||
|
|
||||||
|
/* Cached object file header. */
|
||||||
|
|
||||||
|
static Coff_header cached_coff_hdr;
|
||||||
|
|
||||||
|
/* Flag to indicate if we have read and cached any header yet. */
|
||||||
|
|
||||||
|
static bool cached_coff_hdr_valid = false;
|
||||||
|
|
||||||
|
/* The current output file. */
|
||||||
|
|
||||||
|
static lto_file *current_out_file;
|
||||||
|
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* COFF section structure constructor. */
|
||||||
|
|
||||||
|
static lto_coff_section *
|
||||||
|
coff_newsection (lto_coff_file *file, const char *name, size_t type)
|
||||||
|
{
|
||||||
|
lto_coff_section *ptr, **chain_ptr_ptr;
|
||||||
|
|
||||||
|
ptr = XCNEW (lto_coff_section);
|
||||||
|
ptr->name = name;
|
||||||
|
ptr->type = type;
|
||||||
|
|
||||||
|
chain_ptr_ptr = &file->section_chain;
|
||||||
|
while (*chain_ptr_ptr)
|
||||||
|
chain_ptr_ptr = &(*chain_ptr_ptr)->next;
|
||||||
|
*chain_ptr_ptr = ptr;
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* COFF section data block structure constructor. */
|
||||||
|
|
||||||
|
static lto_coff_data *
|
||||||
|
coff_newdata (lto_coff_section *sec)
|
||||||
|
{
|
||||||
|
lto_coff_data *ptr, **chain_ptr_ptr;
|
||||||
|
|
||||||
|
ptr = XCNEW (lto_coff_data);
|
||||||
|
|
||||||
|
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. */
|
||||||
|
|
||||||
|
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 *
|
||||||
|
coff_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 *ds = (const struct lto_section_slot *) p;
|
||||||
|
return (hashval_t) htab_hash_string (ds->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_coff_file *coff_file = (lto_coff_file *)lto_file;
|
||||||
|
lto_coff_section *sec;
|
||||||
|
htab_t section_hash_table;
|
||||||
|
ssize_t strtab_size;
|
||||||
|
char *strtab;
|
||||||
|
|
||||||
|
section_hash_table = htab_create (37, hash_name, eq_name, free);
|
||||||
|
|
||||||
|
/* Seek to start of string table. */
|
||||||
|
if (coff_file->strtab_offs != lseek (coff_file->fd,
|
||||||
|
coff_file->base.offset + coff_file->strtab_offs, SEEK_SET))
|
||||||
|
{
|
||||||
|
error ("altered or invalid COFF object file");
|
||||||
|
return section_hash_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
strtab_size = coff_file->file_size - coff_file->strtab_offs;
|
||||||
|
strtab = XNEWVEC (char, strtab_size);
|
||||||
|
if (read (coff_file->fd, strtab, strtab_size) != strtab_size)
|
||||||
|
{
|
||||||
|
error ("invalid COFF object file string table");
|
||||||
|
return section_hash_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan sections looking at names. */
|
||||||
|
COFF_FOR_ALL_SECTIONS(coff_file, sec)
|
||||||
|
{
|
||||||
|
struct lto_section_slot s_slot;
|
||||||
|
void **slot;
|
||||||
|
char *new_name;
|
||||||
|
int stringoffset;
|
||||||
|
char *name = (char *) &sec->coffsec.Name[0];
|
||||||
|
|
||||||
|
/* Skip dummy string section if by any chance we see it. */
|
||||||
|
if (sec->type == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (name[0] == '/')
|
||||||
|
{
|
||||||
|
if (1 != sscanf (&name[1], "%d", &stringoffset)
|
||||||
|
|| stringoffset < 0 || stringoffset >= strtab_size)
|
||||||
|
{
|
||||||
|
error ("invalid COFF section name string");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
name = strtab + stringoffset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* If we cared about the VirtualSize field, we couldn't
|
||||||
|
crudely trash it like this to guarantee nul-termination
|
||||||
|
of the Name field. But we don't, so we do. */
|
||||||
|
name[8] = 0;
|
||||||
|
}
|
||||||
|
if (strncmp (name, LTO_SECTION_NAME_PREFIX,
|
||||||
|
strlen (LTO_SECTION_NAME_PREFIX)) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
new_name = XNEWVEC (char, strlen (name) + 1);
|
||||||
|
strcpy (new_name, name);
|
||||||
|
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;
|
||||||
|
/* The offset into the file for this section. */
|
||||||
|
new_slot->start = coff_file->base.offset
|
||||||
|
+ COFF_GET(&sec->coffsec,PointerToRawData);
|
||||||
|
new_slot->len = COFF_GET(&sec->coffsec,SizeOfRawData);
|
||||||
|
*slot = new_slot;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error ("two or more sections for %s:", new_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free (strtab);
|
||||||
|
return section_hash_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Begin a new COFF section named NAME with type TYPE in the current output
|
||||||
|
file. TYPE is an SHT_* macro from the libelf headers. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
lto_coff_begin_section_with_type (const char *name, size_t type)
|
||||||
|
{
|
||||||
|
lto_coff_file *file;
|
||||||
|
size_t sh_name;
|
||||||
|
|
||||||
|
/* Grab the current output file and do some basic assertion checking. */
|
||||||
|
file = (lto_coff_file *) lto_get_current_out_file (),
|
||||||
|
gcc_assert (file);
|
||||||
|
gcc_assert (!file->scn);
|
||||||
|
|
||||||
|
/* Create a new section. */
|
||||||
|
file->scn = coff_newsection (file, name, type);
|
||||||
|
if (!file->scn)
|
||||||
|
fatal_error ("could not create a new COFF section: %s", coff_errmsg (-1));
|
||||||
|
|
||||||
|
/* Add a string table entry and record the offset. */
|
||||||
|
gcc_assert (file->shstrtab_stream);
|
||||||
|
sh_name = file->shstrtab_stream->total_size;
|
||||||
|
lto_output_data_stream (file->shstrtab_stream, name, strlen (name) + 1);
|
||||||
|
|
||||||
|
/* Initialize the section header. */
|
||||||
|
file->scn->strtab_offs = sh_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Begin a new COFF section named NAME in the current output file. */
|
||||||
|
|
||||||
|
void
|
||||||
|
lto_obj_begin_section (const char *name)
|
||||||
|
{
|
||||||
|
lto_coff_begin_section_with_type (name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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_coff_file *file;
|
||||||
|
lto_coff_data *coff_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_coff_file *) lto_get_current_out_file ();
|
||||||
|
gcc_assert (file);
|
||||||
|
gcc_assert (file->scn);
|
||||||
|
|
||||||
|
coff_data = coff_newdata (file->scn);
|
||||||
|
if (!coff_data)
|
||||||
|
fatal_error ("could not append data to COFF section: %s", coff_errmsg (-1));
|
||||||
|
|
||||||
|
coff_data->d_buf = CONST_CAST (void *, data);
|
||||||
|
coff_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_coff_file *file;
|
||||||
|
|
||||||
|
/* Grab the current output file and validate some basic assertions. */
|
||||||
|
file = (lto_coff_file *) lto_get_current_out_file ();
|
||||||
|
gcc_assert (file);
|
||||||
|
gcc_assert (file->scn);
|
||||||
|
|
||||||
|
file->scn = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Validate's COFF_FILE's executable header and, if cached_coff_hdr is
|
||||||
|
uninitialized, caches the results. Also records the section header string
|
||||||
|
table's section index. Returns true on success or false on failure. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
validate_file (lto_coff_file *coff_file)
|
||||||
|
{
|
||||||
|
size_t n, secnum;
|
||||||
|
unsigned int numsections, secheaderssize, numsyms;
|
||||||
|
off_t sectionsstart, symbolsstart, stringsstart;
|
||||||
|
unsigned int mach, charact;
|
||||||
|
|
||||||
|
/* Read and sanity check the raw header. */
|
||||||
|
n = read (coff_file->fd, &coff_file->coffhdr, sizeof (coff_file->coffhdr));
|
||||||
|
if (n != sizeof (coff_file->coffhdr))
|
||||||
|
{
|
||||||
|
error ("not a COFF object file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mach = COFF_GET(&coff_file->coffhdr, Machine);
|
||||||
|
for (n = 0; n < NUM_COFF_KNOWN_MACHINES; n++)
|
||||||
|
if (mach == coff_machine_array[n])
|
||||||
|
break;
|
||||||
|
if (n == NUM_COFF_KNOWN_MACHINES)
|
||||||
|
{
|
||||||
|
error ("not a recognized COFF object file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
charact = COFF_GET(&coff_file->coffhdr, Characteristics);
|
||||||
|
if (COFF_NOT_CHARACTERISTICS & charact)
|
||||||
|
{
|
||||||
|
/* DLL, EXE or SYS file. */
|
||||||
|
error ("not a relocatable COFF object file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COFF_CHARACTERISTICS != (COFF_CHARACTERISTICS & charact))
|
||||||
|
{
|
||||||
|
/* ECOFF/XCOFF/PE+ support not implemented. */
|
||||||
|
error ("not a 32-bit COFF object file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It validated OK, so cached it if we don't already have one. */
|
||||||
|
if (!cached_coff_hdr_valid)
|
||||||
|
{
|
||||||
|
cached_coff_hdr_valid = true;
|
||||||
|
memcpy (&cached_coff_hdr, &coff_file->coffhdr, sizeof (cached_coff_hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mach != COFF_GET(&cached_coff_hdr, Machine))
|
||||||
|
{
|
||||||
|
error ("inconsistent file architecture detected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read section headers and string table? */
|
||||||
|
|
||||||
|
numsections = COFF_GET(&coff_file->coffhdr, NumberOfSections);
|
||||||
|
secheaderssize = numsections * sizeof (Coff_section);
|
||||||
|
sectionsstart = sizeof (Coff_header) + secheaderssize;
|
||||||
|
symbolsstart = COFF_GET(&coff_file->coffhdr, PointerToSymbolTable);
|
||||||
|
numsyms = COFF_GET(&coff_file->coffhdr, NumberOfSymbols);
|
||||||
|
stringsstart = (symbolsstart + COFF_SYMBOL_SIZE * numsyms);
|
||||||
|
|
||||||
|
#define CVOFFSETTTED(x) (coff_file->base.offset + (x))
|
||||||
|
|
||||||
|
if (numsections <= 0 || symbolsstart <= 0 || numsyms <= 0
|
||||||
|
|| (CVOFFSETTTED(sectionsstart) >= coff_file->file_size)
|
||||||
|
|| (CVOFFSETTTED(symbolsstart) >= coff_file->file_size)
|
||||||
|
|| (CVOFFSETTTED(stringsstart) >= coff_file->file_size))
|
||||||
|
{
|
||||||
|
error ("not a valid COFF object file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CVOFFSETTTED
|
||||||
|
|
||||||
|
/* Record start of string table. */
|
||||||
|
coff_file->strtab_offs = stringsstart;
|
||||||
|
|
||||||
|
/* Validate section table entries. */
|
||||||
|
for (secnum = 0; secnum < numsections; secnum++)
|
||||||
|
{
|
||||||
|
Coff_section coffsec;
|
||||||
|
lto_coff_section *ltosec;
|
||||||
|
off_t size_raw, offs_raw, offs_relocs, offs_lines;
|
||||||
|
off_t num_relocs, num_lines;
|
||||||
|
|
||||||
|
n = read (coff_file->fd, &coffsec, sizeof (coffsec));
|
||||||
|
if (n != sizeof (coffsec))
|
||||||
|
{
|
||||||
|
error ("short/missing COFF section table");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_raw = COFF_GET(&coffsec, SizeOfRawData);
|
||||||
|
offs_raw = COFF_GET(&coffsec, PointerToRawData);
|
||||||
|
offs_relocs = COFF_GET(&coffsec, PointerToRelocations);
|
||||||
|
offs_lines = COFF_GET(&coffsec, PointerToLinenumbers);
|
||||||
|
num_relocs = COFF_GET(&coffsec, NumberOfRelocations);
|
||||||
|
num_lines = COFF_GET(&coffsec, NumberOfLinenumbers);
|
||||||
|
|
||||||
|
if (size_raw < 0 || num_relocs < 0 || num_lines < 0
|
||||||
|
|| (size_raw
|
||||||
|
&& ((COFF_GET(&coffsec, Characteristics)
|
||||||
|
& IMAGE_SCN_CNT_UNINITIALIZED_DATA)
|
||||||
|
? (offs_raw != 0)
|
||||||
|
: (offs_raw < sectionsstart || offs_raw >= coff_file->file_size)))
|
||||||
|
|| (num_relocs
|
||||||
|
&& (offs_relocs < sectionsstart
|
||||||
|
|| offs_relocs >= coff_file->file_size))
|
||||||
|
|| (num_lines
|
||||||
|
&& (offs_lines < sectionsstart
|
||||||
|
|| offs_lines >= coff_file->file_size)))
|
||||||
|
{
|
||||||
|
error ("invalid COFF section table");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 = coff_newsection (coff_file, NULL, 0);
|
||||||
|
memcpy (<osec->coffsec, &coffsec, sizeof (ltosec->coffsec));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize COFF_FILE's executable header using cached data from previously
|
||||||
|
read files. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_coffhdr (lto_coff_file *coff_file)
|
||||||
|
{
|
||||||
|
gcc_assert (cached_coff_hdr_valid);
|
||||||
|
memset (&coff_file->coffhdr, 0, sizeof (coff_file->coffhdr));
|
||||||
|
COFF_PUT(&coff_file->coffhdr, Machine, COFF_GET(&cached_coff_hdr, Machine));
|
||||||
|
COFF_PUT(&coff_file->coffhdr, Characteristics, COFF_GET(&cached_coff_hdr, Characteristics));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open COFF 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_coff_file *coff_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. */
|
||||||
|
coff_file = XCNEW (lto_coff_file);
|
||||||
|
result = (lto_file *) coff_file;
|
||||||
|
lto_file_init (result, fname, offset);
|
||||||
|
coff_file->fd = -1;
|
||||||
|
|
||||||
|
/* Open the file. */
|
||||||
|
coff_file->fd = open (fname,
|
||||||
|
O_BINARY | (writable ? O_WRONLY | O_CREAT | O_TRUNC : O_RDONLY), 0666);
|
||||||
|
|
||||||
|
if (coff_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
coff_file->file_size = statbuf.st_size;
|
||||||
|
|
||||||
|
if (offset != 0)
|
||||||
|
{
|
||||||
|
char ar_tail[12];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
/* Surely not? */
|
||||||
|
gcc_assert (!writable);
|
||||||
|
|
||||||
|
/* Seek to offset, or error. */
|
||||||
|
if (lseek (coff_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 (coff_file->fd, -12, SEEK_CUR) < 0
|
||||||
|
|| read (coff_file->fd, ar_tail, 12) != 12
|
||||||
|
|| lseek (coff_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;
|
||||||
|
}
|
||||||
|
coff_file->file_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writable)
|
||||||
|
{
|
||||||
|
init_coffhdr (coff_file);
|
||||||
|
coff_file->shstrtab_stream = XCNEW (struct lto_output_stream);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (!validate_file (coff_file))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (result)
|
||||||
|
lto_obj_file_close (result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Close COFF file FILE and clean up any associated data structures. If FILE
|
||||||
|
was opened for writing, the file's COFF data is written at this time, and
|
||||||
|
any cached data buffers are freed. Return TRUE if there was an error. */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
coff_write_object_file (lto_coff_file *coff_file)
|
||||||
|
{
|
||||||
|
lto_coff_section *cursec, *stringsec;
|
||||||
|
lto_coff_data *data;
|
||||||
|
size_t fileoffset, numsections, totalsecsize, numsyms, stringssize;
|
||||||
|
bool write_err = false;
|
||||||
|
int secnum;
|
||||||
|
|
||||||
|
/* Infer whether this file was opened for reading or writing from the
|
||||||
|
presence or absense of an initialised stream for the string table;
|
||||||
|
do nothing if it was opened for reading. */
|
||||||
|
if (!coff_file->shstrtab_stream)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Write the COFF string table into a dummy new section that
|
||||||
|
we will not write a header for. */
|
||||||
|
lto_file *old_file = lto_set_current_out_file (&coff_file->base);
|
||||||
|
/* This recursively feeds in the data to a new section. */
|
||||||
|
lto_coff_begin_section_with_type (".strtab", 1);
|
||||||
|
lto_write_stream (coff_file->shstrtab_stream);
|
||||||
|
lto_obj_end_section ();
|
||||||
|
lto_set_current_out_file (old_file);
|
||||||
|
free (coff_file->shstrtab_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout the file. Count sections (not dummy string section) and calculate
|
||||||
|
data size for all of them. */
|
||||||
|
numsections = 0;
|
||||||
|
totalsecsize = 0;
|
||||||
|
stringssize = 0;
|
||||||
|
stringsec = NULL;
|
||||||
|
COFF_FOR_ALL_SECTIONS(coff_file, cursec)
|
||||||
|
{
|
||||||
|
lto_coff_data *data;
|
||||||
|
size_t cursecsize;
|
||||||
|
cursecsize = 0;
|
||||||
|
COFF_FOR_ALL_DATA(cursec,data)
|
||||||
|
cursecsize += data->d_size;
|
||||||
|
if (cursec->type == 0)
|
||||||
|
{
|
||||||
|
++numsections;
|
||||||
|
totalsecsize += COFF_ALIGN(cursecsize);
|
||||||
|
#if COFF_ALIGNMENT > 1
|
||||||
|
cursec->pad_needed = COFF_ALIGN(cursecsize) - cursecsize;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stringssize = cursecsize;
|
||||||
|
stringsec = cursec;
|
||||||
|
}
|
||||||
|
COFF_PUT(&cursec->coffsec, SizeOfRawData, cursecsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There is a file symbol and a section symbol per section,
|
||||||
|
and each of these has a single auxiliary symbol following. */
|
||||||
|
numsyms = 2 * (1 + numsections);
|
||||||
|
|
||||||
|
/* Great! Now we have enough info to fill out the file header. */
|
||||||
|
COFF_PUT(&coff_file->coffhdr, NumberOfSections, numsections);
|
||||||
|
COFF_PUT(&coff_file->coffhdr, NumberOfSymbols, numsyms);
|
||||||
|
COFF_PUT(&coff_file->coffhdr, PointerToSymbolTable, sizeof (Coff_header)
|
||||||
|
+ numsections * sizeof (Coff_section) + totalsecsize);
|
||||||
|
/* The remaining members were initialised to zero or copied from
|
||||||
|
a cached header, so we leave them alone here. */
|
||||||
|
|
||||||
|
/* Now position all the sections, and fill out their headers. */
|
||||||
|
fileoffset = sizeof (Coff_header) + numsections * sizeof (Coff_section);
|
||||||
|
COFF_FOR_ALL_SECTIONS(coff_file, cursec)
|
||||||
|
{
|
||||||
|
/* Skip dummy string section. */
|
||||||
|
if (cursec->type == 1)
|
||||||
|
continue;
|
||||||
|
COFF_PUT(&cursec->coffsec, PointerToRawData, fileoffset);
|
||||||
|
fileoffset += COFF_ALIGN (COFF_GET(&cursec->coffsec, SizeOfRawData));
|
||||||
|
COFF_PUT(&cursec->coffsec, Characteristics, COFF_SECTION_CHARACTERISTICS);
|
||||||
|
snprintf ((char *)&cursec->coffsec.Name[0], 8, "/%d", cursec->strtab_offs + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 COFF header. */
|
||||||
|
write_err = (write (coff_file->fd, &coff_file->coffhdr,
|
||||||
|
sizeof (coff_file->coffhdr)) != sizeof (coff_file->coffhdr));
|
||||||
|
|
||||||
|
/* Write the COFF section headers. */
|
||||||
|
COFF_FOR_ALL_SECTIONS(coff_file, cursec)
|
||||||
|
if (cursec->type == 1) /* Skip dummy string section. */
|
||||||
|
continue;
|
||||||
|
else if (!write_err)
|
||||||
|
write_err = (write (coff_file->fd, &cursec->coffsec,
|
||||||
|
sizeof (cursec->coffsec)) != sizeof (cursec->coffsec));
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Write the COFF sections. */
|
||||||
|
COFF_FOR_ALL_SECTIONS(coff_file, cursec)
|
||||||
|
{
|
||||||
|
#if COFF_ALIGNMENT > 1
|
||||||
|
static const char padzeros[COFF_ALIGNMENT] = { 0 };
|
||||||
|
#endif
|
||||||
|
/* Skip dummy string section. */
|
||||||
|
if (cursec->type == 1)
|
||||||
|
continue;
|
||||||
|
COFF_FOR_ALL_DATA(cursec, data)
|
||||||
|
if (!write_err)
|
||||||
|
write_err = (write (coff_file->fd, data->d_buf, data->d_size)
|
||||||
|
!= data->d_size);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
#if COFF_ALIGNMENT > 1
|
||||||
|
if (!write_err && cursec->pad_needed)
|
||||||
|
write_err = (write (coff_file->fd, padzeros, cursec->pad_needed)
|
||||||
|
!= cursec->pad_needed);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the COFF symbol table. */
|
||||||
|
if (!write_err)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Coff_symbol sym;
|
||||||
|
Coff_aux_sym_file file;
|
||||||
|
Coff_aux_sym_section sec;
|
||||||
|
} symbols[2];
|
||||||
|
memset (&symbols[0], 0, sizeof (symbols));
|
||||||
|
strcpy ((char *) &symbols[0].sym.Name[0], ".file");
|
||||||
|
COFF_PUT(&symbols[0].sym, SectionNumber, IMAGE_SYM_DEBUG);
|
||||||
|
COFF_PUT(&symbols[0].sym, Type, IMAGE_SYM_TYPE);
|
||||||
|
symbols[0].sym.StorageClass[0] = IMAGE_SYM_CLASS_FILE;
|
||||||
|
symbols[0].sym.NumberOfAuxSymbols[0] = 1;
|
||||||
|
snprintf ((char *)symbols[1].file.FileName,
|
||||||
|
sizeof (symbols[1].file.FileName),
|
||||||
|
"%s", lbasename (coff_file->base.filename));
|
||||||
|
write_err = (write (coff_file->fd, &symbols[0], sizeof (symbols))
|
||||||
|
!= (2 * COFF_SYMBOL_SIZE));
|
||||||
|
|
||||||
|
/* Set up constant parts for section sym loop. */
|
||||||
|
memset (&symbols[0], 0, sizeof (symbols));
|
||||||
|
COFF_PUT(&symbols[0].sym, Type, IMAGE_SYM_TYPE);
|
||||||
|
symbols[0].sym.StorageClass[0] = IMAGE_SYM_CLASS_STATIC;
|
||||||
|
symbols[0].sym.NumberOfAuxSymbols[0] = 1;
|
||||||
|
|
||||||
|
secnum = 1;
|
||||||
|
if (!write_err)
|
||||||
|
COFF_FOR_ALL_SECTIONS(coff_file, cursec)
|
||||||
|
{
|
||||||
|
/* Skip dummy string section. */
|
||||||
|
if (cursec->type == 1)
|
||||||
|
continue;
|
||||||
|
/* Reuse section name string for section symbol name. */
|
||||||
|
COFF_PUT_NDXSZ(&symbols[0].sym, Name, 0, 0, 4);
|
||||||
|
COFF_PUT_NDXSZ(&symbols[0].sym, Name, cursec->strtab_offs + 4, 4, 4);
|
||||||
|
COFF_PUT(&symbols[0].sym, SectionNumber, secnum++);
|
||||||
|
COFF_PUT(&symbols[1].sec, Length,
|
||||||
|
COFF_GET(&cursec->coffsec, SizeOfRawData));
|
||||||
|
if (!write_err)
|
||||||
|
write_err = (write (coff_file->fd, &symbols[0], sizeof (symbols))
|
||||||
|
!= (2 * COFF_SYMBOL_SIZE));
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write the COFF string table. */
|
||||||
|
if (!write_err)
|
||||||
|
{
|
||||||
|
unsigned char outlen[4];
|
||||||
|
COFF_PUT4(outlen, stringssize + 4);
|
||||||
|
if (!write_err)
|
||||||
|
write_err = (write (coff_file->fd, outlen, 4) != 4);
|
||||||
|
if (stringsec)
|
||||||
|
COFF_FOR_ALL_DATA(stringsec, data)
|
||||||
|
if (!write_err)
|
||||||
|
write_err = (write (coff_file->fd, data->d_buf, data->d_size)
|
||||||
|
!= data->d_size);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return write_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close COFF file FILE and clean up any associated data structures. If FILE
|
||||||
|
was opened for writing, the file's COFF data is written at this time, and
|
||||||
|
any cached data buffers are freed. */
|
||||||
|
|
||||||
|
void
|
||||||
|
lto_obj_file_close (lto_file *file)
|
||||||
|
{
|
||||||
|
lto_coff_file *coff_file = (lto_coff_file *) file;
|
||||||
|
struct lto_char_ptr_base *cur, *tmp;
|
||||||
|
lto_coff_section *cursec, *nextsec;
|
||||||
|
bool write_err = false;
|
||||||
|
|
||||||
|
/* Write the COFF string table into a dummy new section that
|
||||||
|
we will not write a header for. */
|
||||||
|
if (coff_file->shstrtab_stream)
|
||||||
|
coff_write_object_file (coff_file);
|
||||||
|
|
||||||
|
/* Close the file, we're done. */
|
||||||
|
if (coff_file->fd != -1)
|
||||||
|
close (coff_file->fd);
|
||||||
|
|
||||||
|
/* Free any data buffers. */
|
||||||
|
cur = coff_file->data;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
tmp = cur;
|
||||||
|
cur = (struct lto_char_ptr_base *) cur->ptr;
|
||||||
|
free (tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free any sections and their data chains. */
|
||||||
|
cursec = coff_file->section_chain;
|
||||||
|
while (cursec)
|
||||||
|
{
|
||||||
|
lto_coff_data *curdata, *nextdata;
|
||||||
|
nextsec = cursec->next;
|
||||||
|
curdata = cursec->data_chain;
|
||||||
|
while (curdata)
|
||||||
|
{
|
||||||
|
nextdata = curdata->next;
|
||||||
|
free (curdata);
|
||||||
|
curdata = nextdata;
|
||||||
|
}
|
||||||
|
free (cursec);
|
||||||
|
cursec = nextsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
free (file);
|
||||||
|
|
||||||
|
/* If there was an error, mention it. */
|
||||||
|
if (write_err)
|
||||||
|
error ("I/O error writing COFF output file");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,406 @@
|
||||||
|
/* LTO routines for COFF object files.
|
||||||
|
Copyright 2009 Free Software Foundation, Inc.
|
||||||
|
Contributed by Dave Korn.
|
||||||
|
|
||||||
|
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_COFF_H
|
||||||
|
#define LTO_COFF_H
|
||||||
|
|
||||||
|
/* Rather than implementing a libcoff to match libelf, or attempting to
|
||||||
|
integrate libbfd into GCC, this file is a self-contained (and very
|
||||||
|
minimal) COFF format object file reader/writer. The generated files
|
||||||
|
will contain a COFF header, a number of COFF section headers, the
|
||||||
|
section data itself, and a trailing string table for section names. */
|
||||||
|
|
||||||
|
/* Alignment of sections in a COFF object file.
|
||||||
|
|
||||||
|
The LTO writer uses zlib compression on the data that it streams into
|
||||||
|
LTO sections in the output object file. Because these streams don't
|
||||||
|
have any embedded size information, the section in the object file must
|
||||||
|
be exactly sized to the data emitted; any trailing padding bytes will
|
||||||
|
be interpreted as partial and/or corrupt compressed data.
|
||||||
|
|
||||||
|
This is easy enough to do on COFF targets (with binutils 2.20.1 or
|
||||||
|
above) because we can specify 1-byte alignment for the LTO sections.
|
||||||
|
They are then emitted precisely-sized and byte-packed into the object
|
||||||
|
and the reader is happy when it parses them later. This is currently
|
||||||
|
implemented in the x86/windows backed in i386_pe_asm_named_section()
|
||||||
|
in config/i386/winnt.c by detecting the LTO section name prefix,
|
||||||
|
|
||||||
|
That would be sufficient, but for one thing. At the start of the LTO
|
||||||
|
data is a header struct with (currently) a couple of version numbers and
|
||||||
|
some type info; see struct lto_header in lto-streamer.h. If the sections
|
||||||
|
are byte-packed, this header will not necessarily be correctly-aligned
|
||||||
|
when it is read back into memory.
|
||||||
|
|
||||||
|
On x86 targets, which are currently the only LTO-COFF targets, misaligned
|
||||||
|
memory accesses aren't problematic (okay, inefficient, but not worth
|
||||||
|
worrying about two half-word memory reads per section in the context of
|
||||||
|
everything else the compiler has to do at the time!), but RISC targets may
|
||||||
|
fail on trying to access the header struct. In this case, it will be
|
||||||
|
necessary to enable (preferably in a target-dependent fashion, but a few
|
||||||
|
bytes of padding are hardly an important issue if it comes down to it) the
|
||||||
|
COFF_ALIGNMENT macros below.
|
||||||
|
|
||||||
|
As currently implemented, this will emit padding to the necessary number
|
||||||
|
of bytes after each LTO section. These bytes will constitute 'gaps' in
|
||||||
|
the object file structure, as they won't be covered by any section header.
|
||||||
|
This hasn't yet been tested, because no such RISC LTO-COFF target yet
|
||||||
|
exists. If it causes problems further down the toolchain, it will be
|
||||||
|
necessary to adapt the code to emit additional section headers for these
|
||||||
|
padding bytes, but the odds are that it will "just work".
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define COFF_ALIGNMENT (4)
|
||||||
|
#define COFF_ALIGNMENTM1 (COFF_ALIGNMENT - 1)
|
||||||
|
#define COFF_ALIGN(x) (((x) + COFF_ALIGNMENTM1) & ~COFF_ALIGNMENTM1)
|
||||||
|
#else
|
||||||
|
#define COFF_ALIGNMENT (1)
|
||||||
|
#define COFF_ALIGN(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* COFF header machine codes. */
|
||||||
|
|
||||||
|
#define IMAGE_FILE_MACHINE_I386 (0x014c)
|
||||||
|
|
||||||
|
/* Known header magics for validation, as an array initialiser. */
|
||||||
|
|
||||||
|
#define COFF_KNOWN_MACHINES \
|
||||||
|
{ IMAGE_FILE_MACHINE_I386/*, ... add more here when working. */ }
|
||||||
|
|
||||||
|
/* COFF object file header, section and symbol flags and types. These are
|
||||||
|
currently specific to PE-COFF, which is the only LTO-COFF format at the
|
||||||
|
time of writing. Maintainers adding support for new COFF formats will
|
||||||
|
need to make these into target macros of some kind. */
|
||||||
|
|
||||||
|
/* COFF header characteristics. */
|
||||||
|
|
||||||
|
#define IMAGE_FILE_EXECUTABLE_IMAGE (1 << 1)
|
||||||
|
#define IMAGE_FILE_32BIT_MACHINE (1 << 8)
|
||||||
|
#define IMAGE_FILE_SYSTEM (1 << 12)
|
||||||
|
#define IMAGE_FILE_DLL (1 << 13)
|
||||||
|
|
||||||
|
/* Desired characteristics (for validation). */
|
||||||
|
|
||||||
|
#define COFF_CHARACTERISTICS \
|
||||||
|
(IMAGE_FILE_32BIT_MACHINE)
|
||||||
|
|
||||||
|
/* Unwanted characteristics (for validation). */
|
||||||
|
|
||||||
|
#define COFF_NOT_CHARACTERISTICS \
|
||||||
|
(IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_SYSTEM | IMAGE_FILE_DLL)
|
||||||
|
|
||||||
|
/* Section flags. LTO emits byte-aligned read-only loadable data sections. */
|
||||||
|
|
||||||
|
#define IMAGE_SCN_CNT_INITIALIZED_DATA (1 << 6)
|
||||||
|
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA (1 << 7)
|
||||||
|
#define IMAGE_SCN_ALIGN_1BYTES (0x1 << 20)
|
||||||
|
#define IMAGE_SCN_MEM_DISCARDABLE (1 << 25)
|
||||||
|
#define IMAGE_SCN_MEM_SHARED (1 << 28)
|
||||||
|
#define IMAGE_SCN_MEM_READ (1 << 30)
|
||||||
|
|
||||||
|
#define COFF_SECTION_CHARACTERISTICS \
|
||||||
|
(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | \
|
||||||
|
IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ)
|
||||||
|
|
||||||
|
/* Symbol-related constants. */
|
||||||
|
|
||||||
|
#define IMAGE_SYM_DEBUG (-2)
|
||||||
|
#define IMAGE_SYM_TYPE_NULL (0)
|
||||||
|
#define IMAGE_SYM_DTYPE_NULL (0)
|
||||||
|
#define IMAGE_SYM_CLASS_STATIC (3)
|
||||||
|
#define IMAGE_SYM_CLASS_FILE (103)
|
||||||
|
|
||||||
|
#define IMAGE_SYM_TYPE \
|
||||||
|
((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
|
||||||
|
|
||||||
|
/* Size of a COFF symbol in bytes. */
|
||||||
|
|
||||||
|
#define COFF_SYMBOL_SIZE (18)
|
||||||
|
|
||||||
|
/* On-disk file structures. */
|
||||||
|
|
||||||
|
struct Coff_header
|
||||||
|
{
|
||||||
|
unsigned char Machine[2];
|
||||||
|
unsigned char NumberOfSections[2];
|
||||||
|
unsigned char TimeDateStamp[4];
|
||||||
|
unsigned char PointerToSymbolTable[4];
|
||||||
|
unsigned char NumberOfSymbols[4];
|
||||||
|
unsigned char SizeOfOptionalHeader[2];
|
||||||
|
unsigned char Characteristics[2];
|
||||||
|
};
|
||||||
|
typedef struct Coff_header Coff_header;
|
||||||
|
|
||||||
|
struct Coff_section
|
||||||
|
{
|
||||||
|
unsigned char Name[8];
|
||||||
|
unsigned char VirtualSize[4];
|
||||||
|
unsigned char VirtualAddress[4];
|
||||||
|
unsigned char SizeOfRawData[4];
|
||||||
|
unsigned char PointerToRawData[4];
|
||||||
|
unsigned char PointerToRelocations[4];
|
||||||
|
unsigned char PointerToLinenumbers[4];
|
||||||
|
unsigned char NumberOfRelocations[2];
|
||||||
|
unsigned char NumberOfLinenumbers[2];
|
||||||
|
unsigned char Characteristics[4];
|
||||||
|
};
|
||||||
|
typedef struct Coff_section Coff_section;
|
||||||
|
|
||||||
|
struct Coff_symbol
|
||||||
|
{
|
||||||
|
unsigned char Name[8];
|
||||||
|
unsigned char Value[4];
|
||||||
|
unsigned char SectionNumber[2];
|
||||||
|
unsigned char Type[2];
|
||||||
|
unsigned char StorageClass[1];
|
||||||
|
unsigned char NumberOfAuxSymbols[1];
|
||||||
|
};
|
||||||
|
typedef struct Coff_symbol Coff_symbol;
|
||||||
|
|
||||||
|
struct Coff_aux_sym_file
|
||||||
|
{
|
||||||
|
unsigned char FileName[18];
|
||||||
|
};
|
||||||
|
typedef struct Coff_aux_sym_file Coff_aux_sym_file;
|
||||||
|
|
||||||
|
struct Coff_aux_sym_section
|
||||||
|
{
|
||||||
|
unsigned char Length[4];
|
||||||
|
unsigned char NumberOfRelocations[2];
|
||||||
|
unsigned char NumberOfLineNumbers[2];
|
||||||
|
unsigned char Checksum[4];
|
||||||
|
unsigned char Number[2];
|
||||||
|
unsigned char Selection[1];
|
||||||
|
unsigned char Unused[3];
|
||||||
|
};
|
||||||
|
typedef struct Coff_aux_sym_section Coff_aux_sym_section;
|
||||||
|
|
||||||
|
/* Accessor macros for the above structures. */
|
||||||
|
|
||||||
|
#define COFF_GET(struc,memb) \
|
||||||
|
((COFFENDIAN ? get_be : get_le) (&(struc)->memb[0], sizeof ((struc)->memb)))
|
||||||
|
|
||||||
|
#define COFF_PUT(struc,memb,val) \
|
||||||
|
((COFFENDIAN ? put_be : put_le) (&(struc)->memb[0], sizeof ((struc)->memb), val))
|
||||||
|
|
||||||
|
#define COFF_PUT_NDXSZ(struc,memb,val,ndx,sz) \
|
||||||
|
((COFFENDIAN ? put_be : put_le) (&(struc)->memb[ndx], sz, val))
|
||||||
|
|
||||||
|
/* In-memory file structures. */
|
||||||
|
|
||||||
|
/* Forward declared structs. */
|
||||||
|
|
||||||
|
struct lto_coff_data;
|
||||||
|
struct lto_coff_section;
|
||||||
|
struct lto_coff_file;
|
||||||
|
|
||||||
|
/* Section data in output files is made of these. */
|
||||||
|
|
||||||
|
struct lto_coff_data
|
||||||
|
{
|
||||||
|
/* Pointer to data block. */
|
||||||
|
void *d_buf;
|
||||||
|
|
||||||
|
/* Size of data block. */
|
||||||
|
ssize_t d_size;
|
||||||
|
|
||||||
|
/* Next data block for this section. */
|
||||||
|
struct lto_coff_data *next;
|
||||||
|
};
|
||||||
|
typedef struct lto_coff_data lto_coff_data;
|
||||||
|
|
||||||
|
/* This struct tracks the data for a section. */
|
||||||
|
|
||||||
|
struct lto_coff_section
|
||||||
|
{
|
||||||
|
/* Singly-linked list of section's data blocks. */
|
||||||
|
lto_coff_data *data_chain;
|
||||||
|
|
||||||
|
/* Offset in string table of name. */
|
||||||
|
size_t strtab_offs;
|
||||||
|
|
||||||
|
/* Section type: 0 = real, 1 = dummy. */
|
||||||
|
size_t type;
|
||||||
|
|
||||||
|
/* Section name. */
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
#if COFF_ALIGNMENT > 1
|
||||||
|
/* Number of trailing padding bytes needed. */
|
||||||
|
ssize_t pad_needed;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Raw section header data. */
|
||||||
|
Coff_section coffsec;
|
||||||
|
|
||||||
|
/* Next section for this file. */
|
||||||
|
struct lto_coff_section *next;
|
||||||
|
};
|
||||||
|
typedef struct lto_coff_section lto_coff_section;
|
||||||
|
|
||||||
|
/* A COFF file. */
|
||||||
|
|
||||||
|
struct lto_coff_file
|
||||||
|
{
|
||||||
|
/* The base information. */
|
||||||
|
lto_file base;
|
||||||
|
|
||||||
|
/* Common file members: */
|
||||||
|
|
||||||
|
/* The system file descriptor for the file. */
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/* The file's overall header. */
|
||||||
|
Coff_header coffhdr;
|
||||||
|
|
||||||
|
/* All sections in a singly-linked list. */
|
||||||
|
lto_coff_section *section_chain;
|
||||||
|
|
||||||
|
/* Readable file members: */
|
||||||
|
|
||||||
|
/* File total size. */
|
||||||
|
off_t file_size;
|
||||||
|
|
||||||
|
/* String table file offset, relative to base.offset. */
|
||||||
|
off_t strtab_offs;
|
||||||
|
|
||||||
|
/* Writable file members: */
|
||||||
|
|
||||||
|
/* The currently active section. */
|
||||||
|
lto_coff_section *scn;
|
||||||
|
|
||||||
|
/* The output stream for section header names. */
|
||||||
|
struct lto_output_stream *shstrtab_stream;
|
||||||
|
|
||||||
|
/* 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_coff_file lto_coff_file;
|
||||||
|
|
||||||
|
/* Data hunk iterator. */
|
||||||
|
|
||||||
|
#define COFF_FOR_ALL_DATA(sec,var) \
|
||||||
|
for (var = sec->data_chain; var; var = var->next)
|
||||||
|
|
||||||
|
/* Section list iterator. */
|
||||||
|
|
||||||
|
#define COFF_FOR_ALL_SECTIONS(file,var) \
|
||||||
|
for (var = file->section_chain; var; var = var->next)
|
||||||
|
|
||||||
|
/* Very simple endian-ness layer. */
|
||||||
|
|
||||||
|
#ifndef COFFENDIAN
|
||||||
|
#define COFFENDIAN (BYTES_BIG_ENDIAN)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
get_2_le (const unsigned char *ptr)
|
||||||
|
{
|
||||||
|
return ptr[0] | (ptr[1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
get_4_le (const unsigned char *ptr)
|
||||||
|
{
|
||||||
|
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
get_2_be (const unsigned char *ptr)
|
||||||
|
{
|
||||||
|
return ptr[1] | (ptr[0] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
get_4_be (const unsigned char *ptr)
|
||||||
|
{
|
||||||
|
return ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
get_be (const unsigned char *ptr, size_t size)
|
||||||
|
{
|
||||||
|
gcc_assert (size == 4 || size == 2);
|
||||||
|
return (size == 2) ? get_2_be (ptr) : get_4_be (ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int
|
||||||
|
get_le (const unsigned char *ptr, size_t size)
|
||||||
|
{
|
||||||
|
gcc_assert (size == 4 || size == 2);
|
||||||
|
return (size == 2) ? get_2_le (ptr) : get_4_le (ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_2_le (unsigned char *ptr, unsigned int data)
|
||||||
|
{
|
||||||
|
ptr[0] = data & 0xff;
|
||||||
|
ptr[1] = (data >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_4_le (unsigned char *ptr, unsigned int data)
|
||||||
|
{
|
||||||
|
ptr[0] = data & 0xff;
|
||||||
|
ptr[1] = (data >> 8) & 0xff;
|
||||||
|
ptr[2] = (data >> 16) & 0xff;
|
||||||
|
ptr[3] = (data >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_2_be (unsigned char *ptr, unsigned int data)
|
||||||
|
{
|
||||||
|
ptr[1] = data & 0xff;
|
||||||
|
ptr[0] = (data >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_4_be (unsigned char *ptr, unsigned int data)
|
||||||
|
{
|
||||||
|
ptr[3] = data & 0xff;
|
||||||
|
ptr[2] = (data >> 8) & 0xff;
|
||||||
|
ptr[1] = (data >> 16) & 0xff;
|
||||||
|
ptr[0] = (data >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_le (unsigned char *ptr, size_t size, unsigned int data)
|
||||||
|
{
|
||||||
|
gcc_assert (size == 4 || size == 2);
|
||||||
|
(void) (size == 2 ? put_2_le : put_4_le) (ptr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
put_be (unsigned char *ptr, size_t size, unsigned int data)
|
||||||
|
{
|
||||||
|
gcc_assert (size == 4 || size == 2);
|
||||||
|
(void) (size == 2 ? put_2_be : put_4_be) (ptr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We use this for putting the string table size. */
|
||||||
|
|
||||||
|
#define COFF_PUT4(ptr, data) \
|
||||||
|
((COFFENDIAN ? put_4_be : put_4_le) (ptr, data))
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LTO_COFF_H */
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
2010-04-27 Dave Korn <dave.korn.cygwin@gmail.com>
|
||||||
|
|
||||||
|
PR lto/42776
|
||||||
|
* lib/lto.exp (lto_prune_vis_warns): New function.
|
||||||
|
(lto-link-and-maybe-run): Call it.
|
||||||
|
|
||||||
2010-04-26 H.J. Lu <hongjiu.lu@intel.com>
|
2010-04-26 H.J. Lu <hongjiu.lu@intel.com>
|
||||||
|
|
||||||
PR tree-optimization/43904
|
PR tree-optimization/43904
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,19 @@
|
||||||
|
|
||||||
# Contributed by Diego Novillo <dnovillo@google.com>
|
# Contributed by Diego Novillo <dnovillo@google.com>
|
||||||
|
|
||||||
|
# Prune messages from gcc that aren't useful.
|
||||||
|
|
||||||
|
proc lto_prune_vis_warns { text } {
|
||||||
|
|
||||||
|
# Many tests that use visibility will still pass on platforms that don't support it.
|
||||||
|
regsub -all "(^|\n)\[^\n\]*: warning: visibility attribute not supported in this configuration; ignored\[^\n\]*" $text "" text
|
||||||
|
|
||||||
|
# And any stray location lines.
|
||||||
|
regsub -all "(^|\n)\[^\n\]*: In function \[^\n\]*" $text "" text
|
||||||
|
regsub -all "(^|\n)In file included from :\[^\n\]*" $text "" text
|
||||||
|
|
||||||
|
return $text
|
||||||
|
}
|
||||||
|
|
||||||
# lto_init -- called at the start of each subdir of tests
|
# lto_init -- called at the start of each subdir of tests
|
||||||
|
|
||||||
|
|
@ -147,6 +160,10 @@ proc lto-link-and-maybe-run { testname objlist dest optall optfile optstr } {
|
||||||
# Link the objects into an executable.
|
# Link the objects into an executable.
|
||||||
set comp_output [${tool}_target_compile "$objlist" $dest executable \
|
set comp_output [${tool}_target_compile "$objlist" $dest executable \
|
||||||
"$options"]
|
"$options"]
|
||||||
|
|
||||||
|
# Prune unimportant visibility warnings before checking output.
|
||||||
|
set comp_output [lto_prune_vis_warns $comp_output]
|
||||||
|
|
||||||
if ![${tool}_check_compile "$testcase $testname link" "" \
|
if ![${tool}_check_compile "$testcase $testname link" "" \
|
||||||
$dest $comp_output] then {
|
$dest $comp_output] then {
|
||||||
unresolved "$testcase $testname execute $optstr"
|
unresolved "$testcase $testname execute $optstr"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue