diff mbox

[1/3] Add native ELF and LTO support in collect2

Message ID 1286792263-9244-1-git-send-email-andi@firstfloor.org
State New
Headers show

Commit Message

Andi Kleen Oct. 11, 2010, 10:17 a.m. UTC
From: Andi Kleen <ak@linux.intel.com>

Change collect2 to read the symbol table directly on ELF systems
using libelf. Also add support for the LTO symbol table.
This way collect2 can resolve symbols in a object file
that only has LTO information.

The LTO parser is closely patterned after the code
in the lto-plugin.

Controlled by a define in the OS specific config file.
I only enabled this on x86 Linux for now.

gcc/

2010-10-07  Andi Kleen  <ak@linux.intel.com>

	* collect2.c: Add ifdefs for OBJECT_FORMAT_ELF.
	(main): Move use_plugin to top level.
	(scan_prog_file): Move switch statement to ..
	(handle_pass): Separate function here.
	(LTO_SYMTAB_NAME, scan_lto_symtab, scan_elf_symtab, is_ar,
	 scan_prog_file): Add.
	* config.in: Regenerate.
	* config/i386/linux.h (OBJECT_FORMAT_ELF): Define.
	* configure: Regenerate.
	* configure.ac: Check for libelf.h and gelf.h. Adjust
	libelf test.
---
 gcc/collect2.c          |  312 +++++++++++++++++++++++++++++++++++++++--------
 gcc/config.in           |   12 ++
 gcc/config/i386/linux.h |    2 +
 gcc/configure           |   30 ++++-
 gcc/configure.ac        |    8 +-
 5 files changed, 310 insertions(+), 54 deletions(-)

Comments

Joseph Myers Oct. 11, 2010, 2:33 p.m. UTC | #1
On Mon, 11 Oct 2010, Andi Kleen wrote:

> Controlled by a define in the OS specific config file.
> I only enabled this on x86 Linux for now.

OBJECT_FORMAT_ELF is already defined in elfos.h where it belongs (and in 
several target-specific headers where it doesn't belong - some duplicating 
the definition in elfos.h, others ELF targets that don't use elfos.h for 
whatever reason).

> +#if  !defined (HAVE_LIBELF_H) || !defined (HAVE_GELF_H)  \
> +  || !defined (HAVE_UNISTD_H) || !defined (HAVE_FCNTL_H) \
> +  || defined (CROSS_DIRECTORY_STRUCTURE)

Why the CROSS_DIRECTORY_STRUCTURE conditional?
Andi Kleen Oct. 11, 2010, 2:56 p.m. UTC | #2
On Mon, Oct 11, 2010 at 02:33:07PM +0000, Joseph S. Myers wrote:
> On Mon, 11 Oct 2010, Andi Kleen wrote:
> 
> > Controlled by a define in the OS specific config file.
> > I only enabled this on x86 Linux for now.
> 
> OBJECT_FORMAT_ELF is already defined in elfos.h where it belongs (and in 
> several target-specific headers where it doesn't belong - some duplicating 
> the definition in elfos.h, others ELF targets that don't use elfos.h for 
> whatever reason).

What do you suggest? Use a different name?

> 
> > +#if  !defined (HAVE_LIBELF_H) || !defined (HAVE_GELF_H)  \
> > +  || !defined (HAVE_UNISTD_H) || !defined (HAVE_FCNTL_H) \
> > +  || defined (CROSS_DIRECTORY_STRUCTURE)
> 
> Why the CROSS_DIRECTORY_STRUCTURE conditional?

I wasn't sure if the host libelf works from cross compilation.
Also some of the other elf format code had checks for it so
I thought it was safer to exclude.

-Andi
Joseph Myers Oct. 11, 2010, 3:09 p.m. UTC | #3
On Mon, 11 Oct 2010, Andi Kleen wrote:

> On Mon, Oct 11, 2010 at 02:33:07PM +0000, Joseph S. Myers wrote:
> > On Mon, 11 Oct 2010, Andi Kleen wrote:
> > 
> > > Controlled by a define in the OS specific config file.
> > > I only enabled this on x86 Linux for now.
> > 
> > OBJECT_FORMAT_ELF is already defined in elfos.h where it belongs (and in 
> > several target-specific headers where it doesn't belong - some duplicating 
> > the definition in elfos.h, others ELF targets that don't use elfos.h for 
> > whatever reason).
> 
> What do you suggest? Use a different name?

No.  Just allow this code to apply for all ELF targets like it should, 
rather than trying to enable it specially only for one particular target.

> > > +#if  !defined (HAVE_LIBELF_H) || !defined (HAVE_GELF_H)  \
> > > +  || !defined (HAVE_UNISTD_H) || !defined (HAVE_FCNTL_H) \
> > > +  || defined (CROSS_DIRECTORY_STRUCTURE)
> > 
> > Why the CROSS_DIRECTORY_STRUCTURE conditional?
> 
> I wasn't sure if the host libelf works from cross compilation.

libelf should work for all targets for any host.

> Also some of the other elf format code had checks for it so
> I thought it was safer to exclude.

What code are you talking about?  The compiler should never behave 
differently for a particular target depending on the host unless you have 
a properly understood reason for it; such a difference certainly isn't 
"safer", it's dangerous.  If it's a matter of non-GNU native linkers, the 
conditional should be on the choice of linker, not on whether it's a cross 
compiler.  If it's a matter of native system headers describing an object 
file format, that won't apply with libelf (and in any case, adding 
equivalent GNU headers describing the format is the correct approach).  
CROSS_DIRECTORY_STRUCTURE should only ever be used when it is genuinely 
the *directory structure* - the locations of various files - that is 
relevant.
Andi Kleen Oct. 11, 2010, 3:21 p.m. UTC | #4
"Joseph S. Myers" <joseph@codesourcery.com> writes:

> On Mon, 11 Oct 2010, Andi Kleen wrote:
>
>> On Mon, Oct 11, 2010 at 02:33:07PM +0000, Joseph S. Myers wrote:
>> > On Mon, 11 Oct 2010, Andi Kleen wrote:
>> > 
>> > > Controlled by a define in the OS specific config file.
>> > > I only enabled this on x86 Linux for now.
>> > 
>> > OBJECT_FORMAT_ELF is already defined in elfos.h where it belongs (and in 
>> > several target-specific headers where it doesn't belong - some duplicating 
>> > the definition in elfos.h, others ELF targets that don't use elfos.h for 
>> > whatever reason).
>> 
>> What do you suggest? Use a different name?
>
> No.  Just allow this code to apply for all ELF targets like it should, 
> rather than trying to enable it specially only for one particular target.

Ok I can do that and drop the cross compile check, but I am only able to
test all that on Linux.

-Andi
Andi Kleen Oct. 11, 2010, 3:34 p.m. UTC | #5
On Mon, Oct 11, 2010 at 04:54:07PM +0100, Dave Korn wrote:
> On 11/10/2010 11:17, Andi Kleen wrote:
> > From: Andi Kleen <ak@linux.intel.com>
> > 
> > Change collect2 to read the symbol table directly on ELF systems
> > using libelf. Also add support for the LTO symbol table.
> > This way collect2 can resolve symbols in a object file
> > that only has LTO information.
> > 
> > The LTO parser is closely patterned after the code
> > in the lto-plugin.
> 
>   Rather than duplicating that code, did you consider implementing the plugin
> API into collect2?

No I didn't consider that and I don't think it makes a lot of sense,
because you would need a ELF plugin too.

One thing I considered was calling nm with plugin or calling
a new lto-nm I wrote, but just doing it directly was much nicer.

-Andi
Dave Korn Oct. 11, 2010, 3:54 p.m. UTC | #6
On 11/10/2010 11:17, Andi Kleen wrote:
> From: Andi Kleen <ak@linux.intel.com>
> 
> Change collect2 to read the symbol table directly on ELF systems
> using libelf. Also add support for the LTO symbol table.
> This way collect2 can resolve symbols in a object file
> that only has LTO information.
> 
> The LTO parser is closely patterned after the code
> in the lto-plugin.

  Rather than duplicating that code, did you consider implementing the plugin
API into collect2?

  I think this might be quite easy (based on the partial implementation in
lto-plugin/lto-symtab.c), and I think if you did it that way you would gain
object file format neutrality "for free" as a side-effect.

+  /* REMOVEME */

  You didn't removeme that one!

    cheers,
      DaveK
Andi Kleen Oct. 11, 2010, 3:54 p.m. UTC | #7
>   I'm not clear here: by "an ELF plugin", do you mean one that would return
> the native ELF symbols rather than LTO ones?  Wouldn't need to be a new plugin
> at all, that could easily be done by sending a plugin option to the existing one.

The existing plugin only does LTO, not native ELF.
 
> > One thing I considered was calling nm with plugin or calling
> > a new lto-nm I wrote, but just doing it directly was much nicer.
> 
>   Or, as you say, could use nm with the plugin.  I think reusing code is
> "nicer" than copy-pasting it with minor variations into multiple locations.  I
> think it would be really nice clean design if we had just one plugin, capable
> of doing "whatever we want", and used it in as many locations as we can, using
> a consistent plugin API.

I think you're missing the point. collect2 does far more than the linker plugin 
does.

e.g. it scans for all kinds of different sections and does other special
magic, which is not done by the LTO plugin which just looks through the 
symbol table.

The code to read the LTO symbol table is also quite small, about 40 lines
or so. I don't think it makes sense to go through connotations just
to share it.  The code to dynamically link the plugin would be also far
from being system independent.

-Andi
Dave Korn Oct. 11, 2010, 4:12 p.m. UTC | #8
On 11/10/2010 16:34, Andi Kleen wrote:
> On Mon, Oct 11, 2010 at 04:54:07PM +0100, Dave Korn wrote:
>> On 11/10/2010 11:17, Andi Kleen wrote:
>>>
>>> Change collect2 to read the symbol table directly on ELF systems
>>> using libelf. Also add support for the LTO symbol table.
>>> This way collect2 can resolve symbols in a object file
>>> that only has LTO information.
>>>
>>> The LTO parser is closely patterned after the code
>>> in the lto-plugin.
>>   Rather than duplicating that code, did you consider implementing the plugin
>> API into collect2?
> 
> No I didn't consider that and I don't think it makes a lot of sense,
> because you would need a ELF plugin too.

  I'm not clear here: by "an ELF plugin", do you mean one that would return
the native ELF symbols rather than LTO ones?  Wouldn't need to be a new plugin
at all, that could easily be done by sending a plugin option to the existing one.

> One thing I considered was calling nm with plugin or calling
> a new lto-nm I wrote, but just doing it directly was much nicer.

  Or, as you say, could use nm with the plugin.  I think reusing code is
"nicer" than copy-pasting it with minor variations into multiple locations.  I
think it would be really nice clean design if we had just one plugin, capable
of doing "whatever we want", and used it in as many locations as we can, using
a consistent plugin API.

    cheers,
      DaveK
Andi Kleen Oct. 11, 2010, 4:28 p.m. UTC | #9
Dave Korn <dave.korn.cygwin@gmail.com> writes:

> On 11/10/2010 16:54, Andi Kleen wrote:
>>>   I'm not clear here: by "an ELF plugin", do you mean one that would return
>>> the native ELF symbols rather than LTO ones?  Wouldn't need to be a new plugin
>>> at all, that could easily be done by sending a plugin option to the existing one.
>> 
>> The existing plugin only does LTO, not native ELF.
>
>   That didn't make anything clearer, I already understood that you were
> talking about a plugin that "does" something with ELF, I was trying to get you
> to tell me what that "something" was.  I'm going to assume the answer
> to my

Well you're making the suggestions, so I assume you know what the code
you're suggesting to use, actually does.


> first question was "yes", and your phrase "an ELF plugin" does indeed mean one
> that would read the ELF native symbol table rather than the LTO symtab section.

Yes.

>   But I think you're missing my point: collect2 "does" all those other things
> *by means of* looking at the symbol table, doesn't it?  Which is what
> the

Actually no: it has some more knowledge, although it's mostly symbol
table today. At least some of the changes I plan to implement -- and why 
I wrote all this in the first place -- will require looking at all
sections.

>   Ah, that's actually a problem that would require libltdl, and even then
> might not be possible on all host platforms.  Which is a shame, because I'd
> really like it if we could control the proliferation of target object-format
> dependent code and have it somehow centralized or abstracted, and the
> linker

If you're worrying about COFF: collect2 already has native COFF support.

-Andi
Dave Korn Oct. 11, 2010, 4:41 p.m. UTC | #10
On 11/10/2010 16:54, Andi Kleen wrote:
>>   I'm not clear here: by "an ELF plugin", do you mean one that would return
>> the native ELF symbols rather than LTO ones?  Wouldn't need to be a new plugin
>> at all, that could easily be done by sending a plugin option to the existing one.
> 
> The existing plugin only does LTO, not native ELF.

  That didn't make anything clearer, I already understood that you were
talking about a plugin that "does" something with ELF, I was trying to get you
to tell me what that "something" was.  I'm going to assume the answer to my
first question was "yes", and your phrase "an ELF plugin" does indeed mean one
that would read the ELF native symbol table rather than the LTO symtab section.

>>> One thing I considered was calling nm with plugin or calling
>>> a new lto-nm I wrote, but just doing it directly was much nicer.
>>   Or, as you say, could use nm with the plugin.  I think reusing code is
>> "nicer" than copy-pasting it with minor variations into multiple locations.  I
>> think it would be really nice clean design if we had just one plugin, capable
>> of doing "whatever we want", and used it in as many locations as we can, using
>> a consistent plugin API.
> 
> I think you're missing the point. collect2 does far more than the linker plugin 
> does.
> 
> e.g. it scans for all kinds of different sections and does other special
> magic, which is not done by the LTO plugin which just looks through the 
> symbol table.

  But I think you're missing my point: collect2 "does" all those other things
*by means of* looking at the symbol table, doesn't it?  Which is what the
lto-plugin a) already does for LTO symbols and b) could trivially easily do
likewise for the native ELF symbols.  Unless I've overlooked something?

> The code to read the LTO symbol table is also quite small, about 40 lines
> or so. I don't think it makes sense to go through connotations just
> to share it.  The code to dynamically link the plugin would be also far
> from being system independent.

  Ah, that's actually a problem that would require libltdl, and even then
might not be possible on all host platforms.  Which is a shame, because I'd
really like it if we could control the proliferation of target object-format
dependent code and have it somehow centralized or abstracted, and the linker
plugin seemed like a good place to do so.  We could then have started getting
rid of the existing OBJECT_FORMAT_xxx code in collect2.  I have to admit
though that I don't know enough about the entire range of GCC targets to know
if this would be a sufficiently portable solution, but it ought to work for
ELF and COFF.

    cheers,
      DaveK
Dave Korn Oct. 11, 2010, 5:12 p.m. UTC | #11
On 11/10/2010 17:28, Andi Kleen wrote:
> Dave Korn <dave.korn.cygwin@gmail.com> writes:

>>   But I think you're missing my point: collect2 "does" all those other things
>> *by means of* looking at the symbol table, doesn't it?  Which is what
>> the
> 
> Actually no: it has some more knowledge, although it's mostly symbol
> table today. 

  I think the only other thing apart from symbols is the dynamic library
dependencies that it gathers using ldd on some systems.

> At least some of the changes I plan to implement -- and why 
> I wrote all this in the first place -- will require looking at all
> sections.

  Ah well, that's new and certainly doesn't fit with the existing plugin api,
otherwise it could have been a good idea.

  What (out of curiosity and to help with long-term planning) are the new
changes you're looking to implement?

>>   Ah, that's actually a problem that would require libltdl, and even then
>> might not be possible on all host platforms.  Which is a shame, because I'd
>> really like it if we could control the proliferation of target object-format
>> dependent code and have it somehow centralized or abstracted, and the
>> linker
> 
> If you're worrying about COFF: collect2 already has native COFF support.

  Yes, I was thinking how nice it would be to remove that!

    cheers,
      DaveK
Andi Kleen Oct. 11, 2010, 5:32 p.m. UTC | #12
On Mon, Oct 11, 2010 at 06:12:30PM +0100, Dave Korn wrote:
> On 11/10/2010 17:28, Andi Kleen wrote:
> > Dave Korn <dave.korn.cygwin@gmail.com> writes:
> 
> >>   But I think you're missing my point: collect2 "does" all those other things
> >> *by means of* looking at the symbol table, doesn't it?  Which is what
> >> the
> > 
> > Actually no: it has some more knowledge, although it's mostly symbol
> > table today. 
> 
>   I think the only other thing apart from symbols is the dynamic library
> dependencies that it gathers using ldd on some systems.

It also checks the type for once.

>   Ah well, that's new and certainly doesn't fit with the existing plugin api,
> otherwise it could have been a good idea.
> 
>   What (out of curiosity and to help with long-term planning) are the new
> changes you're looking to implement?

The two changes (which will come soon) are: detecting automatically when
-fwhopr is needed and in slim mode pass through data that is non LTOed,
but mixed in a object file. This can happen with ld -r, e.g. if some
of the input files are assembler.

-Andi
diff mbox

Patch

diff --git a/gcc/collect2.c b/gcc/collect2.c
index a8cd232..cc85dad 100644
--- a/gcc/collect2.c
+++ b/gcc/collect2.c
@@ -68,12 +68,20 @@  along with GCC; see the file COPYING3.  If not see
 #undef REAL_STRIP_FILE_NAME
 #endif
 
+#if  !defined (HAVE_LIBELF_H) || !defined (HAVE_GELF_H)  \
+  || !defined (HAVE_UNISTD_H) || !defined (HAVE_FCNTL_H) \
+  || defined (CROSS_DIRECTORY_STRUCTURE)
+#undef OBJECT_FORMAT_ELF
+#else
+#undef REAL_NM_FILE_NAME
+#endif
+
 /* If we cannot use a special method, use the ordinary one:
    run nm to find what symbols are present.
    In a cross-compiler, this means you need a cross nm,
    but that is not quite as unpleasant as special headers.  */
 
-#if !defined (OBJECT_FORMAT_COFF)
+#if !defined (OBJECT_FORMAT_COFF) && !defined(OBJECT_FORMAT_ELF)
 #define OBJECT_FORMAT_NONE
 #endif
 
@@ -869,7 +877,7 @@  prefix_from_string (const char *p, struct path_prefix *pprefix)
   free (nstore);
 }
 
-#ifdef OBJECT_FORMAT_NONE
+#if defined(OBJECT_FORMAT_NONE) || defined(OBJECT_FORMAT_ELF)
 
 /* Add an entry for the object file NAME to object file list LIST.
    New entries are added at the end of the list. The original pointer
@@ -889,7 +897,7 @@  add_lto_object (struct lto_object_list *list, const char *name)
 
   list->last = n;
 }
-#endif /* OBJECT_FORMAT_NONE */
+#endif /* OBJECT_FORMAT_NONE || OBJECT_FORMAT_ELF */
 
 
 /* Perform a link-time recompilation and relink if any of the object
@@ -1070,6 +1078,8 @@  maybe_run_lto_and_relink (char **lto_ld_argv, char **object_lst,
     }
 }
 
+bool use_plugin = false;
+
 /* Main program.  */
 
 int
@@ -1132,7 +1142,6 @@  main (int argc, char **argv)
   const char **c_ptr;
   char **ld1_argv;
   const char **ld1;
-  bool use_plugin = false;
 
   /* The kinds of symbols we will have to consider when scanning the
      outcome of a first pass link.  This is ALL to start with, then might
@@ -2521,6 +2530,63 @@  write_aix_file (FILE *stream, struct id *list)
 }
 #endif
 
+#if defined (OBJECT_FORMAT_NONE) || defined (OBJECT_FORMAT_ELF)
+
+/* Handle a defined symbol */
+
+static void
+handle_pass (const char *name, scanpass which_pass, scanfilter filter,
+	     const char *prog_name)
+{
+  switch (is_ctor_dtor (name))
+    {
+    case SYM_CTOR:
+      if (! (filter & SCAN_CTOR))
+	break;
+      if (which_pass != PASS_LIB)
+	add_to_list (&constructors, name);
+      break;
+      
+    case SYM_DTOR:
+      if (! (filter & SCAN_DTOR))
+	break;
+      if (which_pass != PASS_LIB)
+	add_to_list (&destructors, name);
+      break;
+      
+    case SYM_INIT:
+      if (! (filter & SCAN_INIT))
+	break;
+      if (which_pass != PASS_LIB)
+	fatal ("init function found in object %s", prog_name);
+#ifndef LD_INIT_SWITCH
+      add_to_list (&constructors, name);
+#endif
+      break;
+      
+    case SYM_FINI:
+      if (! (filter & SCAN_FINI))
+	break;
+      if (which_pass != PASS_LIB)
+	fatal ("fini function found in object %s", prog_name);
+#ifndef LD_FINI_SWITCH
+      add_to_list (&destructors, name);
+#endif
+      break;
+      
+    case SYM_DWEH:
+      if (! (filter & SCAN_DWEH))
+	break;
+      if (which_pass != PASS_LIB)
+	add_to_list (&frame_tables, name);
+      break;
+
+    case SYM_REGULAR:
+      break;
+    }
+}
+#endif
+
 #ifdef OBJECT_FORMAT_NONE
 
 /* Check to make sure the file is an LTO object file.  */
@@ -2703,52 +2769,7 @@  scan_prog_file (const char *prog_name, scanpass which_pass,
 
 
       *end = '\0';
-      switch (is_ctor_dtor (name))
-	{
-	case SYM_CTOR:
-	  if (! (filter & SCAN_CTOR))
-	    break;
-	  if (which_pass != PASS_LIB)
-	    add_to_list (&constructors, name);
-	  break;
-
-	case SYM_DTOR:
-	  if (! (filter & SCAN_DTOR))
-	    break;
-	  if (which_pass != PASS_LIB)
-	    add_to_list (&destructors, name);
-	  break;
-
-	case SYM_INIT:
-	  if (! (filter & SCAN_INIT))
-	    break;
-	  if (which_pass != PASS_LIB)
-	    fatal ("init function found in object %s", prog_name);
-#ifndef LD_INIT_SWITCH
-	  add_to_list (&constructors, name);
-#endif
-	  break;
-
-	case SYM_FINI:
-	  if (! (filter & SCAN_FINI))
-	    break;
-	  if (which_pass != PASS_LIB)
-	    fatal ("fini function found in object %s", prog_name);
-#ifndef LD_FINI_SWITCH
-	  add_to_list (&destructors, name);
-#endif
-	  break;
-
-	case SYM_DWEH:
-	  if (! (filter & SCAN_DWEH))
-	    break;
-	  if (which_pass != PASS_LIB)
-	    add_to_list (&frame_tables, name);
-	  break;
-
-	default:		/* not a constructor or destructor */
-	  continue;
-	}
+      handle_pass (name, which_pass, filter, prog_name);
     }
 
   if (debug)
@@ -3218,3 +3239,194 @@  resolve_lib_name (const char *name)
   return (NULL);
 }
 #endif /* COLLECT_EXPORT_LIST */
+
+#ifdef OBJECT_FORMAT_ELF
+#include <libelf.h>
+#include <gelf.h>
+#include <plugin-api.h>
+
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+#define LTO_SYMTAB_NAME ".gnu.lto_.symtab"
+
+/* Scan a LTO symbol table section. */
+
+static unsigned
+scan_lto_symtab (Elf_Data *tab, scanpass which_pass, 
+		 scanfilter filter, const char *prog_name)
+{
+  unsigned nsyms = 0;
+  char *p;
+
+  for (p = (char *)tab->d_buf; p < (char *)tab->d_buf + tab->d_size; ) 
+    {
+      const char *name;
+      int skip = 0;
+
+      /* Done in the same way as the lto-plugin. */
+
+      /* name */
+      name = p;
+      while (*p++)
+	;
+      /* comdat */
+      while (*p++)
+	;
+      /* translate */
+      if (*p == LDPK_UNDEF || *p == LDPK_WEAKUNDEF)
+	skip = 1;
+      p++;
+      /* visibility */
+      p++;
+      /* size */
+      p += 8;
+      /* slot */
+      p += 4;
+
+      if (!skip)
+	handle_pass (name, which_pass, filter, prog_name);
+      nsyms++;
+    }
+  return nsyms;
+}
+
+/* Scan a ELF symbol table */
+
+static unsigned
+scan_elf_symtab (Elf *elf, Elf_Data *data, GElf_Shdr *shdr,
+		 scanpass which_pass, scanfilter filter, const char *prog_name)
+{
+  unsigned i;
+  unsigned nsyms = shdr->sh_size / shdr->sh_entsize;
+  unsigned proc = 0;
+
+  for (i = 0; i < nsyms; i++) 
+    {
+      GElf_Sym sym;
+      const char *name;
+
+      gelf_getsym (data, i, &sym);
+
+      if (ELF32_ST_TYPE (sym.st_info) >= STT_SECTION)
+	continue;
+      if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= SHN_LORESERVE)
+	continue;
+      name = elf_strptr (elf, shdr->sh_link, sym.st_name);
+      handle_pass (name, which_pass, filter, prog_name);
+      proc++;
+    }
+
+  /* REMOVEME */
+    fprintf (stderr, "nsyms = %u skipped = %u\n", nsyms, nsyms - proc);
+  return nsyms;
+}
+
+/* Is FD an ar file? */
+
+static int
+is_ar (int fd)
+{
+  char buf[8];
+  bool isar = false;
+ 
+  if (read (fd, buf, 8) == 8) 
+    isar = !memcmp (buf, "!<arch>\r", 8);
+  lseek (fd, 0, SEEK_SET);
+  return isar;
+}
+
+/* ELF version to scan the name list of the loaded program for
+   the symbols g++ uses for static constructors and destructors.
+   This also supports LTO symbol tables. */
+
+static void
+scan_prog_file (const char *prog_name, scanpass which_pass,
+		scanfilter filter)
+{
+  int fd;
+  GElf_Ehdr header;
+  Elf *elf;
+  Elf_Scn *section;
+  unsigned syms = 0;
+  unsigned sections = 0;
+  unsigned lto = 0;
+
+  if (which_pass == PASS_SECOND)
+    return;
+
+  if (debug)
+    fprintf (stderr, "Scanning file '%s'\n", prog_name);
+
+  elf_version (EV_CURRENT);
+  fd = open (prog_name, O_RDONLY);
+  if (fd < 0)
+    {
+      fprintf (stderr, "Cannot open %s\n", prog_name);
+      return;
+    }
+
+  /* It is difficult to figure out if an ar file needs LTO or not.
+     If we use the linker plugin and it is an ar file just handle
+     it like a LTO file unconditionally.  */
+  if (which_pass == PASS_LTOINFO && use_plugin && is_ar (fd)) 
+    {
+      if (debug)
+        fprintf (stderr, "Handling ar file %s as LTO\n", prog_name);
+      add_lto_object (&lto_objects, prog_name);
+      return;
+    }
+
+  elf = elf_begin (fd, ELF_C_READ, NULL);
+  if (elf == NULL)
+    {
+      fprintf (stderr, "Cannot run elf_begin on %s: %s\n", prog_name,
+                       elf_errmsg (0));
+      close (fd);
+      return;
+    }
+  if (!gelf_getehdr (elf, &header))
+    fatal ("Cannot find EHDR in %s: %s", prog_name, elf_errmsg (0));
+
+  section = NULL;
+  while ((section = elf_nextscn (elf, section)) != 0)
+    {
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (section, &shdr_mem);
+      char *name;
+      int islto;
+
+      sections++;
+
+      if (!shdr)
+        fatal("Cannot read SHDR for section %d: %s", sections, elf_errmsg (0));
+
+      name = elf_strptr (elf, header.e_shstrndx, shdr->sh_name);
+      islto = !strncmp (name, LTO_SYMTAB_NAME, strlen (LTO_SYMTAB_NAME));
+      lto += islto;
+
+      if (which_pass == PASS_LTOINFO)
+	{
+	  if (!islto)
+	    continue;
+	  add_lto_object (&lto_objects, prog_name);
+	  break;
+	}
+
+      if (islto)
+	syms += scan_lto_symtab (elf_getdata (section, NULL), which_pass, 
+				 filter, prog_name);
+      else if (shdr->sh_type == SHT_SYMTAB && shdr->sh_entsize > 0)
+	syms += scan_elf_symtab (elf, elf_getdata (section, NULL), shdr,
+				 which_pass, filter, prog_name);
+    }
+
+  if (debug)
+    fprintf (stderr, "Scanned %u symbols, %u sections, %u LTO\n", syms, 
+                     sections, lto);
+
+  elf_end (elf);
+  close (fd);
+}
+
+#endif
diff --git a/gcc/config.in b/gcc/config.in
index 4576de0..d141e6a 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1017,6 +1017,12 @@ 
 #endif
 
 
+/* Define to 1 if you have the <gelf.h> header file. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_GELF_H
+#endif
+
+
 /* Define to 1 if you have the `getchar_unlocked' function. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_GETCHAR_UNLOCKED
@@ -1215,6 +1221,12 @@ 
 #endif
 
 
+/* Define to 1 if you have the <libelf.h> header file. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LIBELF_H
+#endif
+
+
 /* Define to 1 if you have the <limits.h> header file. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_LIMITS_H
diff --git a/gcc/config/i386/linux.h b/gcc/config/i386/linux.h
index 7564c70..4ef951e 100644
--- a/gcc/config/i386/linux.h
+++ b/gcc/config/i386/linux.h
@@ -223,3 +223,5 @@  along with GCC; see the file COPYING3.  If not see
 #define TARGET_CAN_SPLIT_STACK
 #define TARGET_THREAD_SPLIT_STACK_OFFSET 0x30
 #endif
+
+#define OBJECT_FORMAT_ELF 1
diff --git a/gcc/configure b/gcc/configure
index 27962d5..babda31 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -7870,7 +7870,8 @@  fi
 for ac_header in limits.h stddef.h string.h strings.h stdlib.h time.h iconv.h \
 		 fcntl.h unistd.h sys/file.h sys/time.h sys/mman.h \
 		 sys/resource.h sys/param.h sys/times.h sys/stat.h \
-		 direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h
+		 direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h \
+                 libelf.h gelf.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_preproc "$LINENO" "$ac_header" "$as_ac_Header"
@@ -8209,7 +8210,7 @@  if test "${gcc_cv_collect2_libs+set}" = set; then :
   $as_echo_n "(cached) " >&6
 else
   save_LIBS="$LIBS"
-for libs in '' -lld -lmld \
+for libs in '' -lelf -lld -lmld \
 		'-L/usr/lib/cmplrs/cc2.11 -lmld' \
 		'-L/usr/lib/cmplrs/cc3.11 -lmld'
 do
@@ -8238,6 +8239,31 @@  fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 done
+LIBS=-lelf
+test -z "$gcc_cv_collect2_libs" &&
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char elf_version ();
+int
+main ()
+{
+return elf_version ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  gcc_cv_collect2_libs=-lelf
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
 LIBS="$save_LIBS"
 test -z "$gcc_cv_collect2_libs" && gcc_cv_collect2_libs='none required'
 fi
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 1534f7f..e87b86b 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -893,7 +893,8 @@  AC_HEADER_SYS_WAIT
 AC_CHECK_HEADERS(limits.h stddef.h string.h strings.h stdlib.h time.h iconv.h \
 		 fcntl.h unistd.h sys/file.h sys/time.h sys/mman.h \
 		 sys/resource.h sys/param.h sys/times.h sys/stat.h \
-		 direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h)
+		 direct.h malloc.h langinfo.h ldfcn.h locale.h wchar.h \
+                 libelf.h gelf.h)
 
 # Check for thread headers.
 AC_CHECK_HEADER(thread.h, [have_thread_h=yes], [have_thread_h=])
@@ -912,7 +913,7 @@  AC_C_BIGENDIAN
 # We may need a special search path to get them linked.
 AC_CACHE_CHECK(for collect2 libraries, gcc_cv_collect2_libs,
 [save_LIBS="$LIBS"
-for libs in '' -lld -lmld \
+for libs in '' -lelf -lld -lmld \
 		'-L/usr/lib/cmplrs/cc2.11 -lmld' \
 		'-L/usr/lib/cmplrs/cc3.11 -lmld'
 do
@@ -920,6 +921,9 @@  do
 	AC_TRY_LINK_FUNC(ldopen,
 		[gcc_cv_collect2_libs="$libs"; break])
 done
+LIBS=-lelf
+test -z "$gcc_cv_collect2_libs" && 
+AC_TRY_LINK_FUNC(elf_version, [gcc_cv_collect2_libs=-lelf])
 LIBS="$save_LIBS"
 test -z "$gcc_cv_collect2_libs" && gcc_cv_collect2_libs='none required'])
 case $gcc_cv_collect2_libs in