aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
Diffstat (limited to 'gold')
-rw-r--r--gold/Makefile.am4
-rw-r--r--gold/Makefile.in32
-rw-r--r--gold/aclocal.m412
-rw-r--r--gold/archive.cc43
-rw-r--r--gold/archive.h20
-rw-r--r--gold/common.cc208
-rw-r--r--gold/common.h49
-rw-r--r--gold/defstd.cc127
-rw-r--r--gold/defstd.h16
-rw-r--r--gold/dirsearch.h3
-rw-r--r--gold/fileread.cc104
-rw-r--r--gold/fileread.h43
-rw-r--r--gold/gold.cc28
-rw-r--r--gold/i386.cc312
-rw-r--r--gold/layout.cc259
-rw-r--r--gold/layout.h66
-rw-r--r--gold/object.cc54
-rw-r--r--gold/object.h30
-rw-r--r--gold/options.cc78
-rw-r--r--gold/options.h146
-rw-r--r--gold/output.cc390
-rw-r--r--gold/output.h415
-rw-r--r--gold/po/POTFILES.in4
-rw-r--r--gold/po/gold.pot202
-rw-r--r--gold/readsyms.cc137
-rw-r--r--gold/readsyms.h105
-rw-r--r--gold/reloc.cc34
-rw-r--r--gold/reloc.h15
-rw-r--r--gold/resolve.cc71
-rw-r--r--gold/symtab.cc624
-rw-r--r--gold/symtab.h422
-rw-r--r--gold/target-reloc.h53
-rw-r--r--gold/target-select.cc2
-rw-r--r--gold/target-select.h2
-rw-r--r--gold/target.h1
-rw-r--r--gold/workqueue.h6
36 files changed, 3545 insertions, 572 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am
index 8545a77..13aae26 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -19,6 +19,8 @@ noinst_PROGRAMS = ld-new
CCFILES = \
archive.cc \
+ common.cc \
+ defstd.cc \
dirsearch.cc \
fileread.cc \
gold.cc \
@@ -37,6 +39,8 @@ CCFILES = \
HFILES = \
archive.h \
+ common.h \
+ defstd.h \
dirsearch.h \
fileread.h \
gold.h \
diff --git a/gold/Makefile.in b/gold/Makefile.in
index a9d6073..d4c0c67 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.9.5 from Makefile.am.
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@@ -16,8 +16,6 @@
# Process this file with automake to generate Makefile.in
-SOURCES = $(ld_new_SOURCES)
-
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
@@ -52,10 +50,9 @@ subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \
$(top_srcdir)/../config/lead-dot.m4 \
- $(top_srcdir)/../bfd/../config/progtest.m4 \
- $(top_srcdir)/../bfd/../config/po.m4 \
- $(top_srcdir)/../bfd/../config/nls.m4 \
- $(top_srcdir)/../bfd/../config/gettext-sister.m4 \
+ $(top_srcdir)/../config/progtest.m4 \
+ $(top_srcdir)/../config/po.m4 $(top_srcdir)/../config/nls.m4 \
+ $(top_srcdir)/../config/gettext-sister.m4 \
$(top_srcdir)/../bfd/warning.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
@@ -65,12 +62,13 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES = po/Makefile.in
PROGRAMS = $(noinst_PROGRAMS)
-am__objects_1 = archive.$(OBJEXT) dirsearch.$(OBJEXT) \
- fileread.$(OBJEXT) gold.$(OBJEXT) gold-threads.$(OBJEXT) \
- layout.$(OBJEXT) object.$(OBJEXT) options.$(OBJEXT) \
- output.$(OBJEXT) readsyms.$(OBJEXT) reloc.$(OBJEXT) \
- resolve.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \
- target-select.$(OBJEXT) workqueue.$(OBJEXT)
+am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \
+ dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \
+ gold-threads.$(OBJEXT) layout.$(OBJEXT) object.$(OBJEXT) \
+ options.$(OBJEXT) output.$(OBJEXT) readsyms.$(OBJEXT) \
+ reloc.$(OBJEXT) resolve.$(OBJEXT) symtab.$(OBJEXT) \
+ stringpool.$(OBJEXT) target-select.$(OBJEXT) \
+ workqueue.$(OBJEXT)
am__objects_2 =
am__objects_3 = i386.$(OBJEXT)
am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3)
@@ -233,6 +231,8 @@ INCLUDES = -D_GNU_SOURCE \
CCFILES = \
archive.cc \
+ common.cc \
+ defstd.cc \
dirsearch.cc \
fileread.cc \
gold.cc \
@@ -251,6 +251,8 @@ CCFILES = \
HFILES = \
archive.h \
+ common.h \
+ defstd.h \
dirsearch.h \
fileread.h \
gold.h \
@@ -347,6 +349,8 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defstd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirsearch.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@
@@ -514,7 +518,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
- $(mkdir_p) $(distdir)/.. $(distdir)/../bfd $(distdir)/../bfd/../config $(distdir)/../config $(distdir)/po
+ $(mkdir_p) $(distdir)/.. $(distdir)/../bfd $(distdir)/../config $(distdir)/po
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
diff --git a/gold/aclocal.m4 b/gold/aclocal.m4
index 1385aed..4438a34 100644
--- a/gold/aclocal.m4
+++ b/gold/aclocal.m4
@@ -1,4 +1,4 @@
-# generated automatically by aclocal 1.9.5 -*- Autoconf -*-
+# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005 Free Software Foundation, Inc.
@@ -28,7 +28,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
# Call AM_AUTOMAKE_VERSION so it can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
- [AM_AUTOMAKE_VERSION([1.9.5])])
+ [AM_AUTOMAKE_VERSION([1.9.6])])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
@@ -870,8 +870,8 @@ AC_SUBST([am__untar])
m4_include([../config/depstand.m4])
m4_include([../config/lead-dot.m4])
-m4_include([../bfd/../config/progtest.m4])
-m4_include([../bfd/../config/po.m4])
-m4_include([../bfd/../config/nls.m4])
-m4_include([../bfd/../config/gettext-sister.m4])
+m4_include([../config/progtest.m4])
+m4_include([../config/po.m4])
+m4_include([../config/nls.m4])
+m4_include([../config/gettext-sister.m4])
m4_include([../bfd/warning.m4])
diff --git a/gold/archive.cc b/gold/archive.cc
index a64109a..031ead0 100644
--- a/gold/archive.cc
+++ b/gold/archive.cc
@@ -9,6 +9,7 @@
#include "elfcpp.h"
#include "fileread.h"
+#include "readsyms.h"
#include "symtab.h"
#include "object.h"
#include "archive.h"
@@ -47,14 +48,6 @@ const char Archive::armag[sarmag] =
const char Archive::arfmag[2] = { '`', '\n' };
-// Get a view into the underlying file.
-
-const unsigned char*
-Archive::get_view(off_t start, off_t size)
-{
- return this->input_file_->file().get_view(start, size);
-}
-
// Set up the archive: read the symbol map and the extended name
// table.
@@ -113,6 +106,10 @@ Archive::setup()
this->extended_names_.assign(px, extended_size);
}
+ // This array keeps track of which symbols are for archive elements
+ // which we have already included in the link.
+ this->seen_.resize(nsyms);
+
// Opening the file locked it. Unlock it now.
this->input_file_->file().unlock();
}
@@ -221,10 +218,7 @@ void
Archive::add_symbols(Symbol_table* symtab, Layout* layout,
Input_objects* input_objects)
{
- size_t armap_size = this->armap_.size();
- std::vector<bool> seen;
- seen.resize(this->armap_.size());
- seen.clear();
+ const size_t armap_size = this->armap_.size();
bool added_new_object;
do
@@ -233,20 +227,20 @@ Archive::add_symbols(Symbol_table* symtab, Layout* layout,
off_t last = -1;
for (size_t i = 0; i < armap_size; ++i)
{
- if (seen[i])
+ if (this->seen_[i])
continue;
if (this->armap_[i].offset == last)
{
- seen[i] = true;
+ this->seen_[i] = true;
continue;
}
Symbol* sym = symtab->lookup(this->armap_[i].name);
if (sym == NULL)
continue;
- else if (sym->shnum() != elfcpp::SHN_UNDEF)
+ else if (!sym->is_undefined())
{
- seen[i] = true;
+ this->seen_[i] = true;
continue;
}
else if (sym->binding() == elfcpp::STB_WEAK)
@@ -255,6 +249,7 @@ Archive::add_symbols(Symbol_table* symtab, Layout* layout,
// We want to include this object in the link.
last = this->armap_[i].offset;
this->include_member(symtab, layout, input_objects, last);
+ this->seen_[i] = true;
added_new_object = true;
}
}
@@ -337,13 +332,13 @@ class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker
{
public:
Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue,
- Archive* archive)
- : blocker_(token, workqueue), archlock_(*archive)
+ File_read& file)
+ : blocker_(token, workqueue), filelock_(file)
{ }
private:
Task_locker_block blocker_;
- Task_locker_obj<Archive> archlock_;
+ Task_locker_obj<File_read> filelock_;
};
Task_locker*
@@ -351,7 +346,7 @@ Add_archive_symbols::locks(Workqueue* workqueue)
{
return new Add_archive_symbols_locker(*this->next_blocker_,
workqueue,
- this->archive_);
+ this->archive_->file());
}
void
@@ -359,6 +354,14 @@ Add_archive_symbols::run(Workqueue*)
{
this->archive_->add_symbols(this->symtab_, this->layout_,
this->input_objects_);
+
+ if (this->input_group_ != NULL)
+ this->input_group_->add_archive(this->archive_);
+ else
+ {
+ // We no longer need to know about this archive.
+ delete this->archive_;
+ }
}
} // End namespace gold.
diff --git a/gold/archive.h b/gold/archive.h
index b2d4c46..f0edfcb 100644
--- a/gold/archive.h
+++ b/gold/archive.h
@@ -13,6 +13,7 @@ namespace gold
class Input_file;
class Input_objects;
+class Input_group;
class Layout;
class Symbol_table;
@@ -44,6 +45,11 @@ class Archive
void
setup();
+ // Get a reference to the underlying file.
+ File_read&
+ file()
+ { return this->input_file_->file(); }
+
// Lock the underlying file.
void
lock()
@@ -73,7 +79,8 @@ class Archive
// Get a view into the underlying file.
const unsigned char*
- get_view(off_t start, off_t size);
+ get_view(off_t start, off_t size)
+ { return this->input_file_->file().get_view(start, size); }
// Read an archive member header at OFF. Return the size of the
// member, and set *PNAME to the name.
@@ -101,6 +108,9 @@ class Archive
std::vector<Armap_entry> armap_;
// The extended name table.
std::string extended_names_;
+ // Track which symbols in the archive map are for elements which
+ // have already been included in the link.
+ std::vector<bool> seen_;
};
// This class is used to read an archive and pick out the desired
@@ -111,11 +121,12 @@ class Add_archive_symbols : public Task
public:
Add_archive_symbols(Symbol_table* symtab, Layout* layout,
Input_objects* input_objects,
- Archive* archive, Task_token* this_blocker,
+ Archive* archive, Input_group* input_group,
+ Task_token* this_blocker,
Task_token* next_blocker)
: symtab_(symtab), layout_(layout), input_objects_(input_objects),
- archive_(archive), this_blocker_(this_blocker),
- next_blocker_(next_blocker)
+ archive_(archive), input_group_(input_group),
+ this_blocker_(this_blocker), next_blocker_(next_blocker)
{ }
~Add_archive_symbols();
@@ -138,6 +149,7 @@ class Add_archive_symbols : public Task
Layout* layout_;
Input_objects* input_objects_;
Archive* archive_;
+ Input_group* input_group_;
Task_token* this_blocker_;
Task_token* next_blocker_;
};
diff --git a/gold/common.cc b/gold/common.cc
new file mode 100644
index 0000000..9738f98
--- /dev/null
+++ b/gold/common.cc
@@ -0,0 +1,208 @@
+// common.cc -- handle common symbols for gold
+
+#include "gold.h"
+
+#include <algorithm>
+
+#include "workqueue.h"
+#include "layout.h"
+#include "output.h"
+#include "common.h"
+
+namespace gold
+{
+
+// Allocate_commons_task methods.
+
+// This task allocates the common symbols. We need a lock on the
+// symbol table.
+
+Task::Is_runnable_type
+Allocate_commons_task::is_runnable(Workqueue*)
+{
+ if (!this->symtab_lock_->is_writable())
+ return IS_LOCKED;
+ return IS_RUNNABLE;
+}
+
+// Return the locks we hold: one on the symbol table, and one blocker.
+
+class Allocate_commons_task::Allocate_commons_locker : public Task_locker
+{
+ public:
+ Allocate_commons_locker(Task_token& symtab_lock, Task* task,
+ Task_token& blocker, Workqueue* workqueue)
+ : symtab_locker_(symtab_lock, task),
+ blocker_(blocker, workqueue)
+ { }
+
+ private:
+ Task_locker_write symtab_locker_;
+ Task_locker_block blocker_;
+};
+
+Task_locker*
+Allocate_commons_task::locks(Workqueue* workqueue)
+{
+ return new Allocate_commons_locker(*this->symtab_lock_, this,
+ *this->blocker_, workqueue);
+}
+
+// Allocate the common symbols.
+
+void
+Allocate_commons_task::run(Workqueue*)
+{
+ this->symtab_->allocate_commons(this->options_, this->layout_);
+}
+
+// This class is used to sort the common symbol by size. We put the
+// larger common symbols first.
+
+template<int size>
+class Sort_commons
+{
+ public:
+ Sort_commons(const Symbol_table* symtab)
+ : symtab_(symtab)
+ { }
+
+ bool operator()(const Symbol* a, const Symbol* b) const;
+
+ private:
+ const Symbol_table* symtab_;
+};
+
+template<int size>
+bool
+Sort_commons<size>::operator()(const Symbol* pa, const Symbol* pb) const
+{
+ if (pa == NULL)
+ return false;
+ if (pb == NULL)
+ return true;
+
+ const Symbol_table* symtab = this->symtab_;
+ const Sized_symbol<size>* psa;
+ psa = symtab->get_sized_symbol SELECT_SIZE_NAME (pa
+ SELECT_SIZE(size));
+ const Sized_symbol<size>* psb;
+ psb = symtab->get_sized_symbol SELECT_SIZE_NAME (pb
+ SELECT_SIZE(size));
+
+ typename Sized_symbol<size>::Size_type sa = psa->symsize();
+ typename Sized_symbol<size>::Size_type sb = psb->symsize();
+ if (sa < sb)
+ return false;
+ else if (sb > sa)
+ return true;
+
+ // When the symbols are the same size, we sort them by alignment.
+ typename Sized_symbol<size>::Value_type va = psa->value();
+ typename Sized_symbol<size>::Value_type vb = psb->value();
+ if (va < vb)
+ return false;
+ else if (vb > va)
+ return true;
+
+ // Otherwise we stabilize the sort by sorting by name.
+ return strcmp(psa->name(), psb->name()) < 0;
+}
+
+// Allocate the common symbols.
+
+void
+Symbol_table::allocate_commons(const General_options& options, Layout* layout)
+{
+ if (this->get_size() == 32)
+ this->do_allocate_commons<32>(options, layout);
+ else if (this->get_size() == 64)
+ this->do_allocate_commons<64>(options, layout);
+ else
+ abort();
+}
+
+// Allocated the common symbols, sized version.
+
+template<int size>
+void
+Symbol_table::do_allocate_commons(const General_options&,
+ Layout* layout)
+{
+ typedef typename Sized_symbol<size>::Value_type Value_type;
+ typedef typename Sized_symbol<size>::Size_type Size_type;
+
+ // We've kept a list of all the common symbols. But the symbol may
+ // have been resolved to a defined symbol by now. And it may be a
+ // forwarder. First remove all non-common symbols.
+ bool any = false;
+ uint64_t addralign = 0;
+ for (Commons_type::iterator p = this->commons_.begin();
+ p != this->commons_.end();
+ ++p)
+ {
+ Symbol* sym = *p;
+ if (sym->is_forwarder())
+ {
+ sym = this->resolve_forwards(sym);
+ *p = sym;
+ }
+ if (!sym->is_common())
+ *p = NULL;
+ else
+ {
+ any = true;
+ Sized_symbol<size>* ssym;
+ ssym = this->get_sized_symbol SELECT_SIZE_NAME (sym
+ SELECT_SIZE(size));
+ if (ssym->value() > addralign)
+ addralign = ssym->value();
+ }
+ }
+ if (!any)
+ return;
+
+ // Sort the common symbols by size, so that they pack better into
+ // memory.
+ std::sort(this->commons_.begin(), this->commons_.end(),
+ Sort_commons<size>(this));
+
+ // Place them in a newly allocated .bss section.
+
+ Output_section_common *poc = new Output_section_common(addralign);
+
+ layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS,
+ elfcpp::SHF_WRITE | elfcpp::SHF_ALLOC,
+ poc);
+
+ // Allocate them all.
+
+ off_t off = 0;
+ for (Commons_type::iterator p = this->commons_.begin();
+ p != this->commons_.end();
+ ++p)
+ {
+ Symbol* sym = *p;
+ if (sym == NULL)
+ break;
+
+ Sized_symbol<size>* ssym;
+ ssym = this->get_sized_symbol SELECT_SIZE_NAME (sym
+ SELECT_SIZE(size));
+
+ off = align_address(off, ssym->value());
+
+ Size_type symsize = ssym->symsize();
+ ssym->init(ssym->name(), poc, off, symsize, ssym->type(),
+ ssym->binding(), ssym->visibility(), ssym->nonvis(),
+ false);
+
+ off += symsize;
+ }
+
+ poc->set_common_size(off);
+
+ this->commons_.clear();
+}
+
+} // End namespace gold.
diff --git a/gold/common.h b/gold/common.h
new file mode 100644
index 0000000..75237a6
--- /dev/null
+++ b/gold/common.h
@@ -0,0 +1,49 @@
+// common.h -- handle common symbols for gold -*- C++ -*-
+
+#ifndef GOLD_COMMON_H
+#define GOLD_COMMON_H
+
+#include "workqueue.h"
+
+namespace gold
+{
+
+class General_options;
+class Symbol_table;
+
+// This task is used to allocate the common symbols.
+
+class Allocate_commons_task : public Task
+{
+ public:
+ Allocate_commons_task(const General_options& options, Symbol_table* symtab,
+ Layout* layout, Task_token* symtab_lock,
+ Task_token* blocker)
+ : options_(options), symtab_(symtab), layout_(layout),
+ symtab_lock_(symtab_lock), blocker_(blocker)
+ { }
+
+ // The standard Task methods.
+
+ Is_runnable_type
+ is_runnable(Workqueue*);
+
+ Task_locker*
+ locks(Workqueue*);
+
+ void
+ run(Workqueue*);
+
+ private:
+ class Allocate_commons_locker;
+
+ const General_options& options_;
+ Symbol_table* symtab_;
+ Layout* layout_;
+ Task_token* symtab_lock_;
+ Task_token* blocker_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_COMMON_H)
diff --git a/gold/defstd.cc b/gold/defstd.cc
new file mode 100644
index 0000000..29fd2cd
--- /dev/null
+++ b/gold/defstd.cc
@@ -0,0 +1,127 @@
+// defstd.cc -- define standard symbols for gold.
+
+#include "gold.h"
+
+#include "symtab.h"
+#include "defstd.h"
+
+// This is a simple file which defines the standard symbols like
+// "_end".
+
+namespace
+{
+
+using namespace gold;
+
+const Define_symbol_in_section in_section[] =
+{
+ {
+ "__preinit_array_start", // name
+ ".preinit_array", // output_section
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_HIDDEN, // visibility
+ 0, // nonvis
+ false, // offset_is_from_end
+ true // only_if_ref
+ },
+ {
+ "__preinit_array_end", // name
+ ".preinit_array", // output_section
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_HIDDEN, // visibility
+ 0, // nonvis
+ true, // offset_is_from_end
+ true // only_if_ref
+ },
+ {
+ "__init_array_start", // name
+ ".init_array", // output_section
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_HIDDEN, // visibility
+ 0, // nonvis
+ false, // offset_is_from_end
+ true // only_if_ref
+ },
+ {
+ "__init_array_end", // name
+ ".init_array", // output_section
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_HIDDEN, // visibility
+ 0, // nonvis
+ true, // offset_is_from_end
+ true // only_if_ref
+ },
+ {
+ "__fini_array_start", // name
+ ".fini_array", // output_section
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_HIDDEN, // visibility
+ 0, // nonvis
+ false, // offset_is_from_end
+ true // only_if_ref
+ },
+ {
+ "__fini_array_end", // name
+ ".fini_array", // output_section
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_HIDDEN, // visibility
+ 0, // nonvis
+ true, // offset_is_from_end
+ true // only_if_ref
+ }
+};
+
+const int in_section_count = sizeof in_section / sizeof in_section[0];
+
+const Define_symbol_in_segment in_segment[] =
+{
+ {
+ "_end", // name
+ elfcpp::PT_LOAD, // segment_type
+ elfcpp::PF_W, // segment_flags_set
+ elfcpp::PF(0), // segment_flags_clear
+ 0, // value
+ 0, // size
+ elfcpp::STT_NOTYPE, // type
+ elfcpp::STB_GLOBAL, // binding
+ elfcpp::STV_DEFAULT, // visibility
+ 0, // nonvis
+ Symbol::SEGMENT_START, // offset_from_bas
+ false // only_if_ref
+ }
+};
+
+const int in_segment_count = sizeof in_segment / sizeof in_segment[0];
+
+} // End anonymous namespace.
+
+namespace gold
+{
+
+void
+define_standard_symbols(Symbol_table* symtab, const Layout* layout,
+ Target* target)
+{
+ symtab->define_symbols(layout, target, in_section_count, in_section);
+ symtab->define_symbols(layout, target, in_segment_count, in_segment);
+}
+
+} // End namespace gold.
diff --git a/gold/defstd.h b/gold/defstd.h
new file mode 100644
index 0000000..f578b49
--- /dev/null
+++ b/gold/defstd.h
@@ -0,0 +1,16 @@
+// defstd.h -- define standard symbols for gold -*- C++ -*-
+
+#ifndef GOLD_DEFSTD_H
+#define GOLD_DEFSTD_H
+
+#include "symtab.h"
+
+namespace gold
+{
+
+extern void
+define_standard_symbols(Symbol_table*, const Layout*, Target*);
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_DEFSTD_H)
diff --git a/gold/dirsearch.h b/gold/dirsearch.h
index 3beb143..8b6ba59 100644
--- a/gold/dirsearch.h
+++ b/gold/dirsearch.h
@@ -6,12 +6,13 @@
#include <string>
#include <list>
-#include "options.h"
#include "workqueue.h"
namespace gold
{
+class General_options;
+
// A simple interface to manage directories to be searched for
// libraries.
diff --git a/gold/fileread.cc b/gold/fileread.cc
index 987408e..00971a7 100644
--- a/gold/fileread.cc
+++ b/gold/fileread.cc
@@ -102,18 +102,16 @@ File_read::is_locked()
// See if we have a view which covers the file starting at START for
// SIZE bytes. Return a pointer to the View if found, NULL if not.
-File_read::View*
+inline File_read::View*
File_read::find_view(off_t start, off_t size)
{
- for (std::list<File_read::View*>::iterator p = this->view_list_.begin();
- p != this->view_list_.end();
- ++p)
- {
- if ((*p)->start() <= start
- && (*p)->start() + (*p)->size() >= start + size)
- return *p;
- }
- return NULL;
+ off_t page = File_read::page_offset(start);
+ Views::iterator p = this->views_.find(page);
+ if (p == this->views_.end())
+ return NULL;
+ if (p->second->size() - (start - page) < size)
+ return NULL;
+ return p->second;
}
// Read data from the file. Return the number of bytes read. If
@@ -184,15 +182,59 @@ File_read::find_or_make_view(off_t start, off_t size, off_t* pbytes)
{
assert(this->lock_count_ > 0);
- File_read::View* pv = this->find_view(start, size);
- if (pv != NULL)
- return pv;
+ off_t poff = File_read::page_offset(start);
+
+ File_read::View* const vnull = NULL;
+ std::pair<Views::iterator, bool> ins =
+ this->views_.insert(std::make_pair(poff, vnull));
+
+ if (!ins.second)
+ {
+ // There was an existing view at this offset.
+ File_read::View* v = ins.first->second;
+ if (v->size() - (start - v->start()) >= size)
+ {
+ if (pbytes != NULL)
+ *pbytes = size;
+ return v;
+ }
+
+ // This view is not large enough.
+ this->saved_views_.push_back(v);
+ }
+
+ // We need to read data from the file.
+
+ off_t psize = File_read::pages(size + (start - poff));
+ unsigned char* p = new unsigned char[psize];
- unsigned char* p = new unsigned char[size];
- off_t bytes = this->do_read(start, size, p, pbytes);
- pv = new File_read::View(start, bytes, p);
- this->view_list_.push_back(pv);
- return pv;
+ off_t got_bytes;
+ off_t bytes = this->do_read(poff, psize, p, &got_bytes);
+
+ File_read::View* v = new File_read::View(poff, bytes, p);
+
+ ins.first->second = v;
+
+ if (bytes - (start - poff) >= size)
+ {
+ if (pbytes != NULL)
+ *pbytes = size;
+ return v;
+ }
+
+ if (pbytes != NULL)
+ {
+ *pbytes = bytes - (start - poff);
+ return v;
+ }
+
+ fprintf(stderr,
+ _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"),
+ program_name, this->filename().c_str(),
+ static_cast<long long>(bytes - (start - poff)),
+ static_cast<long long>(size),
+ static_cast<long long>(start));
+ gold_exit(false);
}
// This implementation of get_view just reads into a memory buffer,
@@ -221,18 +263,32 @@ File_read::get_lasting_view(off_t start, off_t size, off_t* pbytes)
void
File_read::clear_views(bool destroying)
{
- std::list<File_read::View*>::iterator p = this->view_list_.begin();
- while (p != this->view_list_.end())
+ for (Views::iterator p = this->views_.begin();
+ p != this->views_.end();
+ ++p)
{
- if ((*p)->is_locked())
+ if (!p->second->is_locked())
+ delete p->second;
+ else
{
assert(!destroying);
- ++p;
+ this->saved_views_.push_back(p->second);
}
- else
+ }
+ this->views_.clear();
+
+ Saved_views::iterator p = this->saved_views_.begin();
+ while (p != this->saved_views_.end())
+ {
+ if (!(*p)->is_locked())
{
delete *p;
- p = this->view_list_.erase(p);
+ p = this->saved_views_.erase(p);
+ }
+ else
+ {
+ assert(!destroying);
+ ++p;
}
}
}
diff --git a/gold/fileread.h b/gold/fileread.h
index b65a86b..6e49324 100644
--- a/gold/fileread.h
+++ b/gold/fileread.h
@@ -5,8 +5,9 @@
#ifndef GOLD_FILEREAD_H
#define GOLD_FILEREAD_H
-#include <string>
#include <list>
+#include <map>
+#include <string>
#include "options.h"
@@ -14,7 +15,6 @@ namespace gold
{
class Dirsearch;
-
class File_view;
// File_read manages a file descriptor for a file we are reading. We
@@ -123,22 +123,52 @@ class File_read
friend class File_view;
+ // Find a view into the file.
View*
find_view(off_t start, off_t size);
+ // Read data from the file into a buffer.
off_t
do_read(off_t start, off_t size, void* p, off_t* pbytes);
+ // Find or make a view into the file.
View*
find_or_make_view(off_t start, off_t size, off_t* pbytes);
+ // Clear the file views.
void
clear_views(bool);
+ // The size of a file page for buffering data.
+ static const off_t page_size = 8192;
+
+ // Given a file offset, return the page offset.
+ static off_t
+ page_offset(off_t file_offset)
+ { return file_offset & ~ (page_size - 1); }
+
+ // Given a file size, return the size to read integral pages.
+ static off_t
+ pages(off_t file_size)
+ { return (file_size + (page_size - 1)) & ~ (page_size - 1); }
+
+ // The type of a mapping from page start to views.
+ typedef std::map<off_t, View*> Views;
+
+ // A simple list of Views.
+ typedef std::list<View*> Saved_views;
+
+ // File name.
std::string name_;
+ // File descriptor.
int descriptor_;
+ // Number of locks on the file.
int lock_count_;
- std::list<View*> view_list_;
+ // Buffered views into the file.
+ Views views_;
+ // List of views which were locked but had to be removed from views_
+ // because they were not large enough.
+ Saved_views saved_views_;
};
// A view of file data that persists even when the file is unlocked.
@@ -179,7 +209,7 @@ class File_view
class Input_file
{
public:
- Input_file(const Input_argument& input_argument)
+ Input_file(const Input_file_argument& input_argument)
: input_argument_(input_argument)
{ }
@@ -201,7 +231,10 @@ class Input_file
{ return this->file_; }
private:
- const Input_argument& input_argument_;
+ Input_file(const Input_file&);
+ Input_file& operator=(const Input_file&);
+
+ const Input_file_argument& input_argument_;
File_read file_;
};
diff --git a/gold/gold.cc b/gold/gold.cc
index c39f999..31598dc 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -12,9 +12,11 @@
#include "dirsearch.h"
#include "readsyms.h"
#include "symtab.h"
+#include "common.h"
#include "object.h"
#include "layout.h"
#include "reloc.h"
+#include "defstd.h"
namespace gold
{
@@ -92,11 +94,11 @@ Middle_runner::run(Workqueue* workqueue)
void
queue_initial_tasks(const General_options& options,
const Dirsearch& search_path,
- const Input_argument_list& inputs,
+ const Command_line& cmdline,
Workqueue* workqueue, Input_objects* input_objects,
Symbol_table* symtab, Layout* layout)
{
- if (inputs.empty())
+ if (cmdline.begin() == cmdline.end())
gold_fatal(_("no input files"), false);
// Read the input files. We have to add the symbols to the symbol
@@ -104,14 +106,14 @@ queue_initial_tasks(const General_options& options,
// each input file. We associate the blocker with the following
// input file, to give us a convenient place to delete it.
Task_token* this_blocker = NULL;
- for (Input_argument_list::const_iterator p = inputs.begin();
- p != inputs.end();
+ for (Command_line::const_iterator p = cmdline.begin();
+ p != cmdline.end();
++p)
{
Task_token* next_blocker = new Task_token();
next_blocker->add_blocker();
workqueue->queue(new Read_symbols(options, input_objects, symtab, layout,
- search_path, *p, this_blocker,
+ search_path, *p, NULL, this_blocker,
next_blocker));
this_blocker = next_blocker;
}
@@ -134,6 +136,10 @@ queue_middle_tasks(const General_options& options,
Layout* layout,
Workqueue* workqueue)
{
+ // Predefine standard symbols. This should be fast, so we don't
+ // bother to create a task for it.
+ define_standard_symbols(symtab, layout, input_objects->target());
+
// Read the relocations of the input files. We do this to find
// which symbols are used by relocations which require a GOT and/or
// a PLT entry, or a COPY reloc. When we implement garbage
@@ -157,15 +163,15 @@ queue_middle_tasks(const General_options& options,
// relocations. That task will in turn queue a task to wait
// until it can write to the symbol table.
blocker->add_blocker();
- workqueue->queue(new Read_relocs(options, symtab, *p, symtab_lock,
- blocker));
+ workqueue->queue(new Read_relocs(options, symtab, layout, *p,
+ symtab_lock, blocker));
}
// Allocate common symbols. This requires write access to the
// symbol table, but is independent of the relocation processing.
- // blocker->add_blocker();
- // workqueue->queue(new Allocate_commons_task(options, symtab, layout,
- // symtab_lock, blocker));
+ blocker->add_blocker();
+ workqueue->queue(new Allocate_commons_task(options, symtab, layout,
+ symtab_lock, blocker));
// When all those tasks are complete, we can start laying out the
// output file.
@@ -257,7 +263,7 @@ main(int argc, char** argv)
// Queue up the first set of tasks.
queue_initial_tasks(command_line.options(), search_path,
- command_line.inputs(), &workqueue, &input_objects,
+ command_line, &workqueue, &input_objects,
&symtab, &layout);
// Run the main task processing loop.
diff --git a/gold/i386.cc b/gold/i386.cc
index 468cb90..53af5ee 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -1,10 +1,14 @@
// i386.cc -- i386 target support for gold.
#include "gold.h"
+
+#include <cstring>
+
#include "elfcpp.h"
#include "reloc.h"
#include "i386.h"
#include "object.h"
+#include "symtab.h"
#include "layout.h"
#include "output.h"
#include "target.h"
@@ -22,13 +26,15 @@ class Target_i386 : public Sized_target<32, false>
{
public:
Target_i386()
- : Sized_target<32, false>(&i386_info)
+ : Sized_target<32, false>(&i386_info),
+ got_(NULL)
{ }
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Sized_object<32, false>* object,
unsigned int sh_type,
const unsigned char* prelocs,
@@ -52,12 +58,16 @@ class Target_i386 : public Sized_target<32, false>
struct Scan
{
inline void
- local(const General_options& options, Sized_object<32, false>* object,
+ local(const General_options& options, Symbol_table* symtab,
+ Layout* layout, Target_i386* target,
+ Sized_object<32, false>* object,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
const elfcpp::Sym<32, false>& lsym);
inline void
- global(const General_options& options, Sized_object<32, false>* object,
+ global(const General_options& options, Symbol_table* symtab,
+ Layout* layout, Target_i386* target,
+ Sized_object<32, false>* object,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
Symbol* gsym);
};
@@ -66,9 +76,25 @@ class Target_i386 : public Sized_target<32, false>
class Relocate
{
public:
- // Do a relocation.
- static inline void
- relocate(const Relocate_info<32, false>*, size_t relnum,
+ Relocate()
+ : skip_call_tls_get_addr_(false)
+ { }
+
+ ~Relocate()
+ {
+ if (this->skip_call_tls_get_addr_)
+ {
+ // FIXME: This needs to specify the location somehow.
+ fprintf(stderr, _("%s: missing expected TLS relocation\n"),
+ program_name);
+ gold_exit(false);
+ }
+ }
+
+ // Do a relocation. Return false if the caller should not issue
+ // any warnings about this relocation.
+ inline bool
+ relocate(const Relocate_info<32, false>*, Target_i386*, size_t relnum,
const elfcpp::Rel<32, false>&,
unsigned int r_type, Sized_symbol<32>*,
elfcpp::Elf_types<32>::Elf_Addr,
@@ -77,7 +103,7 @@ class Target_i386 : public Sized_target<32, false>
private:
// Do a TLS relocation.
- static inline void
+ inline void
relocate_tls(const Relocate_info<32, false>*, size_t relnum,
const elfcpp::Rel<32, false>&,
unsigned int r_type, Sized_symbol<32>*,
@@ -93,6 +119,15 @@ class Target_i386 : public Sized_target<32, false>
unsigned char* view,
off_t view_size);
+ // Do a TLS Global-Dynamic to Local-Exec transition.
+ inline void
+ tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>&, unsigned int r_type,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ off_t view_size);
+
// Check the range for a TLS relocation.
static inline void
check_range(const Relocate_info<32, false>*, size_t relnum,
@@ -102,6 +137,10 @@ class Target_i386 : public Sized_target<32, false>
static inline void
check_tls(const Relocate_info<32, false>*, size_t relnum,
const elfcpp::Rel<32, false>&, bool);
+
+ // This is set if we should skip the next reloc, which should be a
+ // PLT32 reloc against ___tls_get_addr.
+ bool skip_call_tls_get_addr_;
};
// Adjust TLS relocation type based on the options and whether this
@@ -109,9 +148,16 @@ class Target_i386 : public Sized_target<32, false>
static unsigned int
optimize_tls_reloc(const General_options*, bool is_local, int r_type);
+ // Get the GOT section, creating it if necessary.
+ Output_section_got<32, false>*
+ got_section(Symbol_table*, Layout*);
+
// Information about this specific target which we pass to the
// general Target structure.
static const Target::Target_info i386_info;
+
+ // The GOT section.
+ Output_section_got<32, false>* got_;
};
const Target::Target_info Target_i386::i386_info =
@@ -126,6 +172,34 @@ const Target::Target_info Target_i386::i386_info =
0x1000 // common_pagesize
};
+// Get the GOT section, creating it if necessary.
+
+Output_section_got<32, false>*
+Target_i386::got_section(Symbol_table* symtab, Layout* layout)
+{
+ if (this->got_ == NULL)
+ {
+ this->got_ = new Output_section_got<32, false>();
+
+ assert(symtab != NULL && layout != NULL);
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC, this->got_);
+
+ // The first three entries are reserved.
+ this->got_->add_constant(0);
+ this->got_->add_constant(0);
+ this->got_->add_constant(0);
+
+ // Define _GLOBAL_OFFSET_TABLE_ at the start of the section.
+ symtab->define_in_output_data(this, "_GLOBAL_OFFSET_TABLE_", this->got_,
+ 0, 0, elfcpp::STT_OBJECT,
+ elfcpp::STB_GLOBAL,
+ elfcpp::STV_HIDDEN, 0,
+ false, false);
+ }
+ return this->got_;
+}
+
// Optimize the TLS relocation type based on what we know about the
// symbol. IS_LOCAL is true if this symbol can be resolved entirely
// locally--i.e., does not have to be in the dynamic symbol table.
@@ -188,6 +262,9 @@ Target_i386::optimize_tls_reloc(const General_options* options, bool is_local,
inline void
Target_i386::Scan::local(const General_options& options,
+ Symbol_table* symtab,
+ Layout* layout,
+ Target_i386* target,
Sized_object<32, false>* object,
const elfcpp::Rel<32, false>&, unsigned int r_type,
const elfcpp::Sym<32, false>&)
@@ -211,6 +288,12 @@ Target_i386::Scan::local(const General_options& options,
case elfcpp::R_386_PC8:
break;
+ case elfcpp::R_386_GOTOFF:
+ case elfcpp::R_386_GOTPC:
+ // We need a GOT section.
+ target->got_section(symtab, layout);
+ break;
+
case elfcpp::R_386_COPY:
case elfcpp::R_386_GLOB_DAT:
case elfcpp::R_386_JUMP_SLOT:
@@ -261,8 +344,6 @@ Target_i386::Scan::local(const General_options& options,
case elfcpp::R_386_GOT32:
case elfcpp::R_386_PLT32:
- case elfcpp::R_386_GOTOFF:
- case elfcpp::R_386_GOTPC:
case elfcpp::R_386_32PLT:
case elfcpp::R_386_TLS_GD_32:
case elfcpp::R_386_TLS_GD_PUSH:
@@ -284,6 +365,9 @@ Target_i386::Scan::local(const General_options& options,
inline void
Target_i386::Scan::global(const General_options& options,
+ Symbol_table* symtab,
+ Layout* layout,
+ Target_i386* target,
Sized_object<32, false>* object,
const elfcpp::Rel<32, false>&, unsigned int r_type,
Symbol* gsym)
@@ -307,6 +391,37 @@ Target_i386::Scan::global(const General_options& options,
// relocation in order to avoid a COPY relocation.
break;
+ case elfcpp::R_386_GOT32:
+ // The symbol requires a GOT entry.
+ if (!gsym->has_got_offset())
+ {
+ Output_section_got<32, false>* got = target->got_section(symtab,
+ layout);
+ const unsigned int got_offset = got->add_global(gsym);
+ gsym->set_got_offset(got_offset);
+
+ // If this symbol is not resolved locally, we need to add a
+ // dynamic relocation for it.
+ if (!gsym->is_resolved_locally())
+ abort();
+ }
+ break;
+
+ case elfcpp::R_386_PLT32:
+ // If the symbol is resolved locally, this is just a PC32 reloc.
+ if (gsym->is_resolved_locally())
+ break;
+ fprintf(stderr,
+ _("%s: %s: unsupported reloc %u against global symbol %s\n"),
+ program_name, object->name().c_str(), r_type, gsym->name());
+ break;
+
+ case elfcpp::R_386_GOTOFF:
+ case elfcpp::R_386_GOTPC:
+ // We need a GOT section.
+ target->got_section(symtab, layout);
+ break;
+
case elfcpp::R_386_COPY:
case elfcpp::R_386_GLOB_DAT:
case elfcpp::R_386_JUMP_SLOT:
@@ -332,7 +447,7 @@ Target_i386::Scan::global(const General_options& options,
case elfcpp::R_386_TLS_GOTDESC:
case elfcpp::R_386_TLS_DESC_CALL:
r_type = Target_i386::optimize_tls_reloc(&options,
- !gsym->in_dynsym(),
+ gsym->is_resolved_locally(),
r_type);
switch (r_type)
{
@@ -357,10 +472,6 @@ Target_i386::Scan::global(const General_options& options,
}
break;
- case elfcpp::R_386_GOT32:
- case elfcpp::R_386_PLT32:
- case elfcpp::R_386_GOTOFF:
- case elfcpp::R_386_GOTPC:
case elfcpp::R_386_32PLT:
case elfcpp::R_386_TLS_GD_32:
case elfcpp::R_386_TLS_GD_PUSH:
@@ -384,6 +495,7 @@ Target_i386::Scan::global(const General_options& options,
void
Target_i386::scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Sized_object<32, false>* object,
unsigned int sh_type,
const unsigned char* prelocs,
@@ -399,9 +511,12 @@ Target_i386::scan_relocs(const General_options& options,
gold_exit(false);
}
- gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>(
+ gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL,
+ Target_i386::Scan>(
options,
symtab,
+ layout,
+ this,
object,
prelocs,
reloc_count,
@@ -412,8 +527,9 @@ Target_i386::scan_relocs(const General_options& options,
// Perform a relocation.
-inline void
+inline bool
Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
+ Target_i386* target,
size_t relnum,
const elfcpp::Rel<32, false>& rel,
unsigned int r_type,
@@ -423,6 +539,23 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
elfcpp::Elf_types<32>::Elf_Addr address,
off_t view_size)
{
+ if (this->skip_call_tls_get_addr_)
+ {
+ if (r_type != elfcpp::R_386_PLT32
+ || gsym == NULL
+ || strcmp(gsym->name(), "___tls_get_addr") != 0)
+ {
+ fprintf(stderr, _("%s: %s: missing expected TLS relocation\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str());
+ gold_exit(false);
+ }
+
+ this->skip_call_tls_get_addr_ = false;
+
+ return false;
+ }
+
switch (r_type)
{
case elfcpp::R_386_NONE:
@@ -454,6 +587,34 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
Relocate_functions<32, false>::pcrel8(view, value, address);
break;
+ case elfcpp::R_386_PLT32:
+ if (gsym->is_resolved_locally())
+ Relocate_functions<32, false>::pcrel32(view, value, address);
+ else
+ fprintf(stderr, _("%s: %s: unsupported reloc %u\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
+ break;
+
+ case elfcpp::R_386_GOT32:
+ // Local GOT offsets not yet supported.
+ assert(gsym);
+ assert(gsym->has_got_offset());
+ value = gsym->got_offset();
+ Relocate_functions<32, false>::rel32(view, value);
+ break;
+
+ case elfcpp::R_386_GOTOFF:
+ value -= target->got_section(NULL, NULL)->address();
+ Relocate_functions<32, false>::rel32(view, value);
+ break;
+
+ case elfcpp::R_386_GOTPC:
+ value = target->got_section(NULL, NULL)->address();
+ Relocate_functions<32, false>::pcrel32(view, value, address);
+ break;
+
case elfcpp::R_386_COPY:
case elfcpp::R_386_GLOB_DAT:
case elfcpp::R_386_JUMP_SLOT:
@@ -480,15 +641,10 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
case elfcpp::R_386_TLS_LE_32:
case elfcpp::R_386_TLS_GOTDESC:
case elfcpp::R_386_TLS_DESC_CALL:
- Target_i386::Relocate::relocate_tls(relinfo, relnum, rel, r_type,
- gsym, value, view, address,
- view_size);
+ this->relocate_tls(relinfo, relnum, rel, r_type, gsym, value, view,
+ address, view_size);
break;
- case elfcpp::R_386_GOT32:
- case elfcpp::R_386_PLT32:
- case elfcpp::R_386_GOTOFF:
- case elfcpp::R_386_GOTPC:
case elfcpp::R_386_32PLT:
case elfcpp::R_386_TLS_GD_32:
case elfcpp::R_386_TLS_GD_PUSH:
@@ -507,6 +663,8 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
// gold_exit(false);
break;
}
+
+ return true;
}
// Perform a TLS relocation.
@@ -531,7 +689,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
gold_exit(false);
}
- const bool is_local = gsym == NULL || !gsym->in_dynsym();
+ const bool is_local = gsym == NULL || gsym->is_resolved_locally();
const unsigned int opt_r_type =
Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type);
switch (r_type)
@@ -564,6 +722,20 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
break;
case elfcpp::R_386_TLS_GD:
+ if (opt_r_type == elfcpp::R_386_TLS_LE_32)
+ {
+ this->tls_gd_to_le(relinfo, relnum, tls_segment,
+ rel, r_type, value, view,
+ view_size);
+ break;
+ }
+ fprintf(stderr, _("%s: %s: unsupported reloc %u\n"),
+ program_name,
+ relinfo->location(relnum, rel.get_r_offset()).c_str(),
+ r_type);
+ // gold_exit(false);
+ break;
+
case elfcpp::R_386_TLS_LDM:
case elfcpp::R_386_TLS_LDO_32:
case elfcpp::R_386_TLS_GOTDESC:
@@ -667,14 +839,79 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo,
Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0);
}
- if (r_type == elfcpp::R_386_TLS_IE_32)
- value = tls_segment->vaddr() + tls_segment->memsz() - value;
- else // elfcpp::R_386_TLS_IE, elfcpp::R_386_TLS_GOTIE
- value = value - (tls_segment->vaddr() + tls_segment->memsz());
+ value = tls_segment->vaddr() + tls_segment->memsz() - value;
+ if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE)
+ value = - value;
Relocate_functions<32, false>::rel32(view, value);
}
+// Do a relocation in which we convert a TLS Global-Dynamic to a
+// Local-Exec.
+
+inline void
+Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo,
+ size_t relnum,
+ Output_segment* tls_segment,
+ const elfcpp::Rel<32, false>& rel,
+ unsigned int,
+ elfcpp::Elf_types<32>::Elf_Addr value,
+ unsigned char* view,
+ off_t view_size)
+{
+ // leal foo(,%reg,1),%eax; call ___tls_get_addr
+ // ==> movl %gs,0,%eax; subl $foo@tpoff,%eax
+ // leal foo(%reg),%eax; call ___tls_get_addr
+ // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax
+
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2);
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9);
+
+ unsigned char op1 = view[-1];
+ unsigned char op2 = view[-2];
+
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ op2 == 0x8d || op2 == 0x04);
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ view[4] == 0xe8);
+
+ int roff = 5;
+
+ if (op2 == 0x04)
+ {
+ Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -3);
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ view[-3] == 0x8d);
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ ((op1 & 0xc7) == 0x05
+ && op1 != (4 << 3)));
+ memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+ }
+ else
+ {
+ Target_i386::Relocate::check_tls(relinfo, relnum, rel,
+ (op1 & 0xf8) == 0x80 && (op1 & 7) != 4);
+ if (rel.get_r_offset() + 9 < view_size && view[9] == 0x90)
+ {
+ // There is a trailing nop. Use the size byte subl.
+ memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12);
+ roff = 6;
+ }
+ else
+ {
+ // Use the five byte subl.
+ memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11);
+ }
+ }
+
+ value = tls_segment->vaddr() + tls_segment->memsz() - value;
+ Relocate_functions<32, false>::rel32(view + roff, value);
+
+ // The next reloc should be a PLT32 reloc against __tls_get_addr.
+ // We can skip it.
+ this->skip_call_tls_get_addr_ = true;
+}
+
// Check the range for a TLS relocation.
inline void
@@ -724,8 +961,10 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
{
assert(sh_type == elfcpp::SHT_REL);
- gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>(
+ gold::relocate_section<32, false, Target_i386, elfcpp::SHT_REL,
+ Target_i386::Relocate>(
relinfo,
+ this,
prelocs,
reloc_count,
view,
@@ -733,10 +972,6 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo,
view_size);
}
-// The i386 target.
-
-Target_i386 target_i386;
-
// The selector for i386 object files.
class Target_selector_i386 : public Target_selector
@@ -747,16 +982,21 @@ public:
{ }
Target*
- recognize(int machine, int osabi, int abiversion) const;
+ recognize(int machine, int osabi, int abiversion);
+
+ private:
+ Target_i386* target_;
};
// Recognize an i386 object file when we already know that the machine
// number is EM_386.
Target*
-Target_selector_i386::recognize(int, int, int) const
+Target_selector_i386::recognize(int, int, int)
{
- return &target_i386;
+ if (this->target_ == NULL)
+ this->target_ = new Target_i386();
+ return this->target_;
}
Target_selector_i386 target_selector_i386;
diff --git a/gold/layout.cc b/gold/layout.cc
index a81fd43..9e85f19 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -38,7 +38,7 @@ Layout_task_runner::run(Workqueue* workqueue)
// Layout methods.
Layout::Layout(const General_options& options)
- : options_(options), last_shndx_(0), namepool_(), sympool_(), signatures_(),
+ : options_(options), namepool_(), sympool_(), signatures_(),
section_name_map_(), segment_list_(), section_list_(),
special_output_list_(), tls_segment_(NULL)
{
@@ -86,66 +86,114 @@ Layout::include_section(Object*, const char*,
}
}
-// Return the output section to use for input section NAME, with
-// header HEADER, from object OBJECT. Set *OFF to the offset of this
-// input section without the output section.
+// Return an output section named NAME, or NULL if there is none.
-template<int size, bool big_endian>
Output_section*
-Layout::layout(Object* object, const char* name,
- const elfcpp::Shdr<size, big_endian>& shdr, off_t* off)
+Layout::find_output_section(const char* name) const
{
- // We discard empty input sections.
- if (shdr.get_sh_size() == 0)
- return NULL;
-
- if (!this->include_section(object, name, shdr))
- return NULL;
-
- // Unless we are doing a relocateable link, .gnu.linkonce sections
- // are laid out as though they were named for the sections are
- // placed into.
- if (!this->options_.is_relocatable() && Layout::is_linkonce(name))
- name = Layout::linkonce_output_name(name);
+ for (Section_name_map::const_iterator p = this->section_name_map_.begin();
+ p != this->section_name_map_.end();
+ ++p)
+ if (strcmp(p->first.first, name) == 0)
+ return p->second;
+ return NULL;
+}
- // FIXME: Handle SHF_OS_NONCONFORMING here.
+// Return an output segment of type TYPE, with segment flags SET set
+// and segment flags CLEAR clear. Return NULL if there is none.
- // Canonicalize the section name.
- name = this->namepool_.add(name);
+Output_segment*
+Layout::find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set,
+ elfcpp::Elf_Word clear) const
+{
+ for (Segment_list::const_iterator p = this->segment_list_.begin();
+ p != this->segment_list_.end();
+ ++p)
+ if (static_cast<elfcpp::PT>((*p)->type()) == type
+ && ((*p)->flags() & set) == set
+ && ((*p)->flags() & clear) == 0)
+ return *p;
+ return NULL;
+}
- // Find the output section. The output section is selected based on
- // the section name, type, and flags.
+// Return the output section to use for section NAME with type TYPE
+// and section flags FLAGS.
- // FIXME: If we want to do relaxation, we need to modify this
- // algorithm. We also build a list of input sections for each
- // output section. Then we relax all the input sections. Then we
- // walk down the list and adjust all the offsets.
+Output_section*
+Layout::get_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags)
+{
+ // We should ignore some flags.
+ flags &= ~ (elfcpp::SHF_INFO_LINK
+ | elfcpp::SHF_LINK_ORDER
+ | elfcpp::SHF_GROUP);
- elfcpp::Elf_Word type = shdr.get_sh_type();
- elfcpp::Elf_Xword flags = shdr.get_sh_flags();
const Key key(name, std::make_pair(type, flags));
const std::pair<Key, Output_section*> v(key, NULL);
std::pair<Section_name_map::iterator, bool> ins(
this->section_name_map_.insert(v));
- Output_section* os;
if (!ins.second)
- os = ins.first->second;
+ return ins.first->second;
else
{
// This is the first time we've seen this name/type/flags
// combination.
- os = this->make_output_section(name, type, flags);
+ Output_section* os = this->make_output_section(name, type, flags);
ins.first->second = os;
+ return os;
}
+}
+
+// Return the output section to use for input section SHNDX, with name
+// NAME, with header HEADER, from object OBJECT. Set *OFF to the
+// offset of this input section without the output section.
+
+template<int size, bool big_endian>
+Output_section*
+Layout::layout(Object* object, unsigned int shndx, const char* name,
+ const elfcpp::Shdr<size, big_endian>& shdr, off_t* off)
+{
+ if (!this->include_section(object, name, shdr))
+ return NULL;
+
+ // If we are not doing a relocateable link, choose the name to use
+ // for the output section.
+ size_t len = strlen(name);
+ if (!this->options_.is_relocatable())
+ name = Layout::output_section_name(name, &len);
+
+ // FIXME: Handle SHF_OS_NONCONFORMING here.
+
+ // Canonicalize the section name.
+ name = this->namepool_.add(name, len);
+
+ // Find the output section. The output section is selected based on
+ // the section name, type, and flags.
+ Output_section* os = this->get_output_section(name, shdr.get_sh_type(),
+ shdr.get_sh_flags());
// FIXME: Handle SHF_LINK_ORDER somewhere.
- *off = os->add_input_section(object, name, shdr);
+ *off = os->add_input_section(object, shndx, name, shdr);
return os;
}
+// Add POSD to an output section using NAME, TYPE, and FLAGS.
+
+void
+Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags,
+ Output_section_data* posd)
+{
+ // Canonicalize the name.
+ name = this->namepool_.add(name);
+
+ Output_section* os = this->get_output_section(name, type, flags);
+ os->add_output_section_data(posd);
+}
+
// Map section flags to segment flags.
elfcpp::Elf_Word
@@ -166,9 +214,7 @@ Output_section*
Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags)
{
- ++this->last_shndx_;
- Output_section* os = new Output_section(name, type, flags,
- this->last_shndx_);
+ Output_section* os = new Output_section(name, type, flags, true);
if ((flags & elfcpp::SHF_ALLOC) == 0)
this->section_list_.push_back(os);
@@ -339,8 +385,14 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
load_seg->add_initial_output_data(file_header);
this->special_output_list_.push_back(file_header);
- // Set the file offsets of all the segments.
- off_t off = this->set_segment_offsets(input_objects->target(), load_seg);
+ // We set the output section indexes in set_segment_offsets and
+ // set_section_offsets.
+ unsigned int shndx = 1;
+
+ // Set the file offsets of all the segments, and all the sections
+ // they contain.
+ off_t off = this->set_segment_offsets(input_objects->target(), load_seg,
+ &shndx);
// Create the symbol table sections.
// FIXME: We don't need to do this if we are stripping symbols.
@@ -354,7 +406,10 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
// Set the file offsets of all the sections not associated with
// segments.
- off = this->set_section_offsets(off);
+ off = this->set_section_offsets(off, &shndx);
+
+ // Now the section index of OSTRTAB is set.
+ osymtab->set_link(ostrtab->out_shndx());
// Create the section table header.
Output_section_headers* oshdrs = this->create_shdrs(size, big_endian, &off);
@@ -450,12 +505,13 @@ Layout::segment_precedes(const Output_segment* seg1,
return paddr1 < paddr2;
}
-// Set the file offsets of all the segments. They have all been
-// created. LOAD_SEG must be be laid out first. Return the offset of
-// the data to follow.
+// Set the file offsets of all the segments, and all the sections they
+// contain. They have all been created. LOAD_SEG must be be laid out
+// first. Return the offset of the data to follow.
off_t
-Layout::set_segment_offsets(const Target* target, Output_segment* load_seg)
+Layout::set_segment_offsets(const Target* target, Output_segment* load_seg,
+ unsigned int *pshndx)
{
// Sort them into the final order.
std::sort(this->segment_list_.begin(), this->segment_list_.end(),
@@ -489,16 +545,17 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg)
uint64_t abi_pagesize = target->abi_pagesize();
if (was_readonly && ((*p)->flags() & elfcpp::PF_W) != 0)
{
- uint64_t align = (*p)->max_data_align();
+ uint64_t align = (*p)->addralign();
- addr = (addr + align - 1) & ~ (align - 1);
+ addr = align_address(addr, align);
aligned_addr = addr;
if ((addr & (abi_pagesize - 1)) != 0)
addr = addr + abi_pagesize;
}
+ unsigned int shndx_hold = *pshndx;
off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1));
- uint64_t new_addr = (*p)->set_section_addresses(addr, &off);
+ uint64_t new_addr = (*p)->set_section_addresses(addr, &off, pshndx);
// Now that we know the size of this segment, we may be able
// to save a page in memory, at the cost of wasting some
@@ -519,10 +576,10 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg)
!= (new_addr & ~ (common_pagesize - 1)))
&& first_off + last_off <= common_pagesize)
{
- addr = ((aligned_addr + common_pagesize - 1)
- & ~ (common_pagesize - 1));
+ *pshndx = shndx_hold;
+ addr = align_address(aligned_addr, common_pagesize);
off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1));
- new_addr = (*p)->set_section_addresses(addr, &off);
+ new_addr = (*p)->set_section_addresses(addr, &off, pshndx);
}
}
@@ -550,17 +607,17 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg)
// segment.
off_t
-Layout::set_section_offsets(off_t off)
+Layout::set_section_offsets(off_t off, unsigned int* pshndx)
{
for (Layout::Section_list::iterator p = this->section_list_.begin();
p != this->section_list_.end();
++p)
{
+ (*p)->set_out_shndx(*pshndx);
+ ++*pshndx;
if ((*p)->offset() != -1)
continue;
- uint64_t addralign = (*p)->addralign();
- if (addralign != 0)
- off = (off + addralign - 1) & ~ (addralign - 1);
+ off = align_address(off, (*p)->addralign());
(*p)->set_address(0, off);
off += (*p)->data_size();
}
@@ -592,7 +649,7 @@ Layout::create_symtab_sections(int size, const Input_objects* input_objects,
abort();
off_t off = *poff;
- off = (off + align - 1) & ~ (align - 1);
+ off = align_address(off, align);
off_t startoff = off;
// Save space for the dummy symbol at the start of the section. We
@@ -614,23 +671,18 @@ Layout::create_symtab_sections(int size, const Input_objects* input_objects,
this->sympool_.set_string_offsets();
- ++this->last_shndx_;
const char* symtab_name = this->namepool_.add(".symtab");
Output_section* osymtab = new Output_section_symtab(symtab_name,
- off - startoff,
- this->last_shndx_);
+ off - startoff);
this->section_list_.push_back(osymtab);
- ++this->last_shndx_;
const char* strtab_name = this->namepool_.add(".strtab");
Output_section *ostrtab = new Output_section_strtab(strtab_name,
- &this->sympool_,
- this->last_shndx_);
+ &this->sympool_);
this->section_list_.push_back(ostrtab);
this->special_output_list_.push_back(ostrtab);
osymtab->set_address(0, startoff);
- osymtab->set_link(ostrtab->shndx());
osymtab->set_info(local_symcount);
osymtab->set_entsize(symsize);
osymtab->set_addralign(align);
@@ -654,10 +706,7 @@ Layout::create_shstrtab()
this->namepool_.set_string_offsets();
- ++this->last_shndx_;
- Output_section* os = new Output_section_strtab(name,
- &this->namepool_,
- this->last_shndx_);
+ Output_section* os = new Output_section_strtab(name, &this->namepool_);
this->section_list_.push_back(os);
this->special_output_list_.push_back(os);
@@ -675,8 +724,7 @@ Layout::create_shdrs(int size, bool big_endian, off_t* poff)
oshdrs = new Output_section_headers(size, big_endian, this->segment_list_,
this->section_list_,
&this->namepool_);
- uint64_t addralign = oshdrs->addralign();
- off_t off = (*poff + addralign - 1) & ~ (addralign - 1);
+ off_t off = align_address(*poff, oshdrs->addralign());
oshdrs->set_address(0, off);
off += oshdrs->data_size();
*poff = off;
@@ -686,7 +734,7 @@ Layout::create_shdrs(int size, bool big_endian, off_t* poff)
// The mapping of .gnu.linkonce section names to real section names.
-#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t }
+#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t, sizeof(t) - 1 }
const Layout::Linkonce_mapping Layout::linkonce_mapping[] =
{
MAPPING_INIT("d.rel.ro", ".data.rel.ro"), // Must be before "d".
@@ -713,10 +761,11 @@ const int Layout::linkonce_mapping_count =
// Return the name of the output section to use for a .gnu.linkonce
// section. This is based on the default ELF linker script of the old
// GNU linker. For example, we map a name like ".gnu.linkonce.t.foo"
-// to ".text".
+// to ".text". Set *PLEN to the length of the name. *PLEN is
+// initialized to the length of NAME.
const char*
-Layout::linkonce_output_name(const char* name)
+Layout::linkonce_output_name(const char* name, size_t *plen)
{
const char* s = name + sizeof(".gnu.linkonce") - 1;
if (*s != '.')
@@ -726,11 +775,67 @@ Layout::linkonce_output_name(const char* name)
for (int i = 0; i < linkonce_mapping_count; ++i, ++plm)
{
if (strncmp(s, plm->from, plm->fromlen) == 0 && s[plm->fromlen] == '.')
- return plm->to;
+ {
+ *plen = plm->tolen;
+ return plm->to;
+ }
}
return name;
}
+// Choose the output section name to use given an input section name.
+// Set *PLEN to the length of the name. *PLEN is initialized to the
+// length of NAME.
+
+const char*
+Layout::output_section_name(const char* name, size_t* plen)
+{
+ if (Layout::is_linkonce(name))
+ {
+ // .gnu.linkonce sections are laid out as though they were named
+ // for the sections are placed into.
+ return Layout::linkonce_output_name(name, plen);
+ }
+
+ // If the section name has no '.', or only an initial '.', we use
+ // the name unchanged (i.e., ".text" is unchanged).
+
+ // Otherwise, if the section name does not include ".rel", we drop
+ // the last '.' and everything that follows (i.e., ".text.XXX"
+ // becomes ".text").
+
+ // Otherwise, if the section name has zero or one '.' after the
+ // ".rel", we use the name unchanged (i.e., ".rel.text" is
+ // unchanged).
+
+ // Otherwise, we drop the last '.' and everything that follows
+ // (i.e., ".rel.text.XXX" becomes ".rel.text").
+
+ const char* s = name;
+ if (*s == '.')
+ ++s;
+ const char* sdot = strchr(s, '.');
+ if (sdot == NULL)
+ return name;
+
+ const char* srel = strstr(s, ".rel");
+ if (srel == NULL)
+ {
+ *plen = sdot - name;
+ return name;
+ }
+
+ sdot = strchr(srel + 1, '.');
+ if (sdot == NULL)
+ return name;
+ sdot = strchr(sdot + 1, '.');
+ if (sdot == NULL)
+ return name;
+
+ *plen = sdot - name;
+ return name;
+}
+
// Record the signature of a comdat section, and return whether to
// include it in the link. If GROUP is true, this is a regular
// section group. If GROUP is false, this is a group signature
@@ -743,7 +848,7 @@ Layout::add_comdat(const char* signature, bool group)
{
std::string sig(signature);
std::pair<Signatures::iterator, bool> ins(
- this->signatures_.insert(std::make_pair(signature, group)));
+ this->signatures_.insert(std::make_pair(sig, group)));
if (ins.second)
{
@@ -851,22 +956,22 @@ Close_task_runner::run(Workqueue*)
template
Output_section*
-Layout::layout<32, false>(Object* object, const char* name,
+Layout::layout<32, false>(Object* object, unsigned int shndx, const char* name,
const elfcpp::Shdr<32, false>& shdr, off_t*);
template
Output_section*
-Layout::layout<32, true>(Object* object, const char* name,
+Layout::layout<32, true>(Object* object, unsigned int shndx, const char* name,
const elfcpp::Shdr<32, true>& shdr, off_t*);
template
Output_section*
-Layout::layout<64, false>(Object* object, const char* name,
+Layout::layout<64, false>(Object* object, unsigned int shndx, const char* name,
const elfcpp::Shdr<64, false>& shdr, off_t*);
template
Output_section*
-Layout::layout<64, true>(Object* object, const char* name,
+Layout::layout<64, true>(Object* object, unsigned int shndx, const char* name,
const elfcpp::Shdr<64, true>& shdr, off_t*);
diff --git a/gold/layout.h b/gold/layout.h
index 4ec2a4a..c96d47d 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -8,7 +8,6 @@
#include <utility>
#include <vector>
-#include "options.h"
#include "workqueue.h"
#include "object.h"
#include "stringpool.h"
@@ -16,8 +15,10 @@
namespace gold
{
+class General_options;
class Input_objects;
class Symbol_table;
+class Output_section_data;
class Output_section;
class Output_section_symtab;
class Output_section_headers;
@@ -63,15 +64,22 @@ class Layout
public:
Layout(const General_options& options);
- // Given an input section named NAME with data in SHDR from the
- // object file OBJECT, return the output section where this input
- // section should go. Set *OFFSET to the offset within the output
- // section.
+ // Given an input section SHNDX, named NAME, with data in SHDR, from
+ // the object file OBJECT, return the output section where this
+ // input section should go. Set *OFFSET to the offset within the
+ // output section.
template<int size, bool big_endian>
Output_section*
- layout(Object *object, const char* name,
+ layout(Object *object, unsigned int shndx, const char* name,
const elfcpp::Shdr<size, big_endian>& shdr, off_t* offset);
+ // Add an Output_section_data to the layout. This is used for
+ // special sections like the GOT section.
+ void
+ add_output_section_data(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags,
+ Output_section_data*);
+
// Return the Stringpool used for symbol names.
const Stringpool*
sympool() const
@@ -104,6 +112,16 @@ class Layout
void
write_data(Output_file*) const;
+ // Return an output section named NAME, or NULL if there is none.
+ Output_section*
+ find_output_section(const char* name) const;
+
+ // Return an output segment of type TYPE, with segment flags SET set
+ // and segment flags CLEAR clear. Return NULL if there is none.
+ Output_segment*
+ find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set,
+ elfcpp::Elf_Word clear) const;
+
// The list of segments.
typedef std::vector<Output_segment*> Segment_list;
@@ -126,6 +144,7 @@ class Layout
const char* from;
int fromlen;
const char* to;
+ int tolen;
};
static const Linkonce_mapping linkonce_mapping[];
static const int linkonce_mapping_count;
@@ -137,12 +156,12 @@ class Layout
// Set the final file offsets of all the segments.
off_t
- set_segment_offsets(const Target*, Output_segment*);
+ set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx);
- // Set the final file offsets of all the sections not associated
- // with a segment.
+ // Set the final file offsets and section indices of all the
+ // sections not associated with a segment.
off_t
- set_section_offsets(off_t);
+ set_section_offsets(off_t, unsigned int *pshndx);
// Create the output sections for the symbol table.
void
@@ -164,10 +183,21 @@ class Layout
include_section(Object* object, const char* name,
const elfcpp::Shdr<size, big_endian>&);
+ // Return the output section name to use given an input section
+ // name. Set *PLEN to the length of the name. *PLEN must be
+ // initialized to the length of NAME.
+ static const char*
+ output_section_name(const char* name, size_t* plen);
+
// Return the output section name to use for a linkonce section
- // name.
+ // name. PLEN is as for output_section_name.
static const char*
- linkonce_output_name(const char* name);
+ linkonce_output_name(const char* name, size_t* plen);
+
+ // Return the output section for NAME, TYPE and FLAGS.
+ Output_section*
+ get_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags);
// Create a new Output_section.
Output_section*
@@ -210,8 +240,6 @@ class Layout
// A reference to the options on the command line.
const General_options& options_;
- // The index of the last output section.
- unsigned int last_shndx_;
// The output section names.
Stringpool namepool_;
// The output symbol names.
@@ -308,6 +336,16 @@ class Close_task_runner : public Task_function_runner
Output_file* of_;
};
+// A small helper function to align an address.
+
+inline uint64_t
+align_address(uint64_t address, uint64_t addralign)
+{
+ if (addralign != 0)
+ address = (address + addralign - 1) &~ (addralign - 1);
+ return address;
+}
+
} // End namespace gold.
#endif // !defined(GOLD_LAYOUT_H)
diff --git a/gold/object.cc b/gold/object.cc
index 5efb3cb..4e7f04c 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -16,25 +16,6 @@ namespace gold
// Class Object.
-const unsigned char*
-Object::get_view(off_t start, off_t size)
-{
- return this->input_file_->file().get_view(start + this->offset_, size);
-}
-
-void
-Object::read(off_t start, off_t size, void* p)
-{
- this->input_file_->file().read(start + this->offset_, size, p);
-}
-
-File_view*
-Object::get_lasting_view(off_t start, off_t size)
-{
- return this->input_file_->file().get_lasting_view(start + this->offset_,
- size);
-}
-
// Class Sized_object.
template<int size, bool big_endian>
@@ -79,7 +60,7 @@ Sized_object<size, big_endian>::~Sized_object()
// Read the section header for section SHNUM.
template<int size, bool big_endian>
-const unsigned char*
+inline const unsigned char*
Sized_object<size, big_endian>::section_header(unsigned int shnum)
{
assert(shnum < this->shnum());
@@ -328,6 +309,32 @@ Sized_object<size, big_endian>::include_section_group(
const char* signature = psymnames + sym.get_st_name();
+ // It seems that some versions of gas will create a section group
+ // associated with a section symbol, and then fail to give a name to
+ // the section symbol. In such a case, use the name of the section.
+ // FIXME.
+ if (signature[0] == '\0'
+ && sym.get_st_type() == elfcpp::STT_SECTION
+ && sym.get_st_shndx() < this->shnum())
+ {
+ typename This::Shdr shdrnames(this->section_header(this->shstrndx_));
+ const unsigned char* pnamesu = this->get_view(shdrnames.get_sh_offset(),
+ shdrnames.get_sh_size());
+ const char* pnames = reinterpret_cast<const char*>(pnamesu);
+
+ typename This::Shdr sechdr(this->section_header(sym.get_st_shndx()));
+ if (sechdr.get_sh_name() >= shdrnames.get_sh_size())
+ {
+ fprintf(stderr,
+ _("%s: %s: bad section name offset for section %u: %lu\n"),
+ program_name, this->name().c_str(), sym.get_st_shndx(),
+ static_cast<unsigned long>(sechdr.get_sh_name()));
+ gold_exit(false);
+ }
+
+ signature = pnames + sechdr.get_sh_name();
+ }
+
// Record this section group, and see whether we've already seen one
// with the same signature.
if (layout->add_comdat(signature, true))
@@ -446,7 +453,7 @@ Sized_object<size, big_endian>::do_layout(Layout* layout,
}
off_t offset;
- Output_section* os = layout->layout(this, name, shdr, &offset);
+ Output_section* os = layout->layout(this, i, name, shdr, &offset);
map_sections[i].output_section = os;
map_sections[i].offset = offset;
@@ -514,7 +521,7 @@ Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off,
return off;
}
- off = (off + (size >> 3) - 1) & ~ ((off_t) (size >> 3) - 1);
+ off = align_address(off, size >> 3);
this->local_symbol_offset_ = off;
@@ -587,6 +594,7 @@ Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off,
}
this->values_[i] = (mo[shndx].output_section->address()
+ + mo[shndx].offset
+ sym.get_st_value());
}
@@ -655,7 +663,7 @@ Sized_object<size, big_endian>::write_local_symbols(Output_file* of,
assert(st_shndx < mo.size());
if (mo[st_shndx].output_section == NULL)
continue;
- st_shndx = mo[st_shndx].output_section->shndx();
+ st_shndx = mo[st_shndx].output_section->out_shndx();
}
osym.put_st_name(sympool->get_offset(pnames + isym.get_st_name()));
diff --git a/gold/object.h b/gold/object.h
index 49a1ce9..5e57039 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -154,8 +154,8 @@ class Object
// Scan the relocs and adjust the symbol table.
void
scan_relocs(const General_options& options, Symbol_table* symtab,
- Read_relocs_data* rd)
- { return this->do_scan_relocs(options, symtab, rd); }
+ Layout* layout, Read_relocs_data* rd)
+ { return this->do_scan_relocs(options, symtab, layout, rd); }
// Initial local symbol processing: set the offset where local
// symbol information will be stored; add local symbol names to
@@ -184,6 +184,14 @@ class Object
inline Output_section*
output_section(unsigned int shnum, off_t* poff);
+ // Set the offset of an input section within its output section.
+ void
+ set_section_offset(unsigned int shndx, off_t off)
+ {
+ assert(shndx < this->map_to_output_.size());
+ this->map_to_output_[shndx].offset = off;
+ }
+
// Return the name of a section given a section index. This is only
// used for error messages.
std::string
@@ -218,7 +226,8 @@ class Object
// Scan the relocs--implemented by child class.
virtual void
- do_scan_relocs(const General_options&, Symbol_table*, Read_relocs_data*) = 0;
+ do_scan_relocs(const General_options&, Symbol_table*, Layout*,
+ Read_relocs_data*) = 0;
// Lay out sections--implemented by child class.
virtual void
@@ -250,7 +259,8 @@ class Object
// Get a view into the underlying file.
const unsigned char*
- get_view(off_t start, off_t size);
+ get_view(off_t start, off_t size)
+ { return this->input_file_->file().get_view(start + this->offset_, size); }
// Get the number of sections.
unsigned int
@@ -269,11 +279,16 @@ class Object
// Read data from the underlying file.
void
- read(off_t start, off_t size, void* p);
+ read(off_t start, off_t size, void* p)
+ { this->input_file_->file().read(start + this->offset_, size, p); }
// Get a lasting view into the underlying file.
File_view*
- get_lasting_view(off_t start, off_t size);
+ get_lasting_view(off_t start, off_t size)
+ {
+ return this->input_file_->file().get_lasting_view(start + this->offset_,
+ size);
+ }
// Return the vector mapping input sections to output sections.
std::vector<Map_to_output>&
@@ -353,7 +368,8 @@ class Sized_object : public Object
// Scan the relocs and adjust the symbol table.
void
- do_scan_relocs(const General_options&, Symbol_table*, Read_relocs_data*);
+ do_scan_relocs(const General_options&, Symbol_table*, Layout*,
+ Read_relocs_data*);
// Lay out the input sections.
void
diff --git a/gold/options.cc b/gold/options.cc
index 8e0465f..397259e 100644
--- a/gold/options.cc
+++ b/gold/options.cc
@@ -89,6 +89,24 @@ library(int argc, char** argv, char* arg, gold::Command_line* cmdline)
return cmdline->process_l_option(argc, argv, arg);
}
+// Handle the special --start-group option.
+
+int
+start_group(int, char**, char* arg, gold::Command_line* cmdline)
+{
+ cmdline->start_group(arg);
+ return 1;
+}
+
+// Handle the special --end-group option.
+
+int
+end_group(int, char**, char* arg, gold::Command_line* cmdline)
+{
+ cmdline->end_group(arg);
+ return 1;
+}
+
// Report usage information for ld --help, and exit.
int
@@ -209,6 +227,10 @@ options::Command_line_options::options[] =
SPECIAL('l', "library", N_("Search for library LIBNAME"),
N_("-lLIBNAME --library LIBNAME"), TWO_DASHES,
&library),
+ SPECIAL('(', "start-group", N_("Start a library search group"), NULL,
+ TWO_DASHES, &start_group),
+ SPECIAL(')', "end-group", N_("End a library search group"), NULL,
+ TWO_DASHES, &end_group),
GENERAL_ARG('L', "library-path", N_("Add directory to search path"),
N_("-L DIR, --library-path DIR"), TWO_DASHES,
&General_options::add_to_search_path),
@@ -246,9 +268,10 @@ Position_dependent_options::Position_dependent_options()
{
}
-// Construct a Command_line.
+// Command_line options.
Command_line::Command_line()
+ : options_(), position_options_(), inputs_(), in_group_(false)
{
}
@@ -266,8 +289,7 @@ Command_line::process(int argc, char** argv)
{
if (argv[i][0] != '-' || no_more_options)
{
- this->inputs_.push_back(Input_argument(argv[i], false,
- this->position_options_));
+ this->add_file(argv[i], false);
++i;
continue;
}
@@ -385,6 +407,12 @@ Command_line::process(int argc, char** argv)
}
}
+ if (this->in_group_)
+ {
+ fprintf(stderr, _("%s: missing group end"), program_name);
+ this->usage();
+ }
+
// FIXME: We should only do this when configured in native mode.
this->options_.add_to_search_path("/lib");
this->options_.add_to_search_path("/usr/lib");
@@ -416,6 +444,22 @@ Command_line::apply_option(const options::One_option& opt,
}
}
+// Add an input file or library.
+
+void
+Command_line::add_file(const char* name, bool is_lib)
+{
+ Input_file_argument file(name, is_lib, this->position_options_);
+ if (!this->in_group_)
+ this->inputs_.push_back(Input_argument(file));
+ else
+ {
+ assert(!this->inputs_.empty());
+ assert(this->inputs_.back().is_group());
+ this->inputs_.back().group()->add_file(file);
+ }
+}
+
// Handle the -l option, which requires special treatment.
int
@@ -436,12 +480,36 @@ Command_line::process_l_option(int argc, char** argv, char* arg)
else
this->usage(_("missing argument"), arg);
- this->inputs_.push_back(Input_argument(libname, true,
- this->position_options_));
+ this->add_file(libname, true);
return ret;
}
+// Handle the --start-group option.
+
+void
+Command_line::start_group(const char* arg)
+{
+ if (this->in_group_)
+ this->usage(_("may not nest groups"), arg);
+
+ // This object is leaked.
+ Input_file_group* group = new Input_file_group();
+ this->inputs_.push_back(Input_argument(group));
+
+ this->in_group_ = true;
+}
+
+// Handle the --end-group option.
+
+void
+Command_line::end_group(const char* arg)
+{
+ if (!this->in_group_)
+ this->usage(_("group end without group start"), arg);
+ this->in_group_ = false;
+}
+
// Report a usage error. */
void
diff --git a/gold/options.h b/gold/options.h
index 7e890fa..3b54c49 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -15,11 +15,13 @@
#include <list>
#include <string>
#include <vector>
+#include <cassert>
namespace gold
{
class Command_line;
+class Input_file_group;
namespace options {
@@ -128,11 +130,15 @@ class Position_dependent_options
// A single file or library argument from the command line.
-class Input_argument
+class Input_file_argument
{
public:
- Input_argument(const char* name, bool is_lib,
- const Position_dependent_options& options)
+ Input_file_argument()
+ : name_(NULL), is_lib_(false), options_()
+ { }
+
+ Input_file_argument(const char* name, bool is_lib,
+ const Position_dependent_options& options)
: name_(name), is_lib_(is_lib), options_(options)
{ }
@@ -154,9 +160,90 @@ class Input_argument
Position_dependent_options options_;
};
-// A list of input files.
-class Input_argument_list : public std::vector<Input_argument>
+// A file or library, or a group, from the command line.
+
+class Input_argument
+{
+ public:
+ // Create a file or library argument.
+ explicit Input_argument(Input_file_argument file)
+ : is_file_(true), file_(file), group_(NULL)
+ { }
+
+ // Create a group argument.
+ explicit Input_argument(Input_file_group* group)
+ : is_file_(false), group_(group)
+ { }
+
+ // Return whether this is a file.
+ bool
+ is_file() const
+ { return this->is_file_; }
+
+ // Return whether this is a group.
+ bool
+ is_group() const
+ { return !this->is_file_; }
+
+ // Return the information about the file.
+ const Input_file_argument&
+ file() const
+ {
+ assert(this->is_file_);
+ return this->file_;
+ }
+
+ // Return the information about the group.
+ const Input_file_group*
+ group() const
+ {
+ assert(!this->is_file_);
+ return this->group_;
+ }
+
+ Input_file_group*
+ group()
+ {
+ assert(!this->is_file_);
+ return this->group_;
+ }
+
+ private:
+ bool is_file_;
+ Input_file_argument file_;
+ Input_file_group* group_;
+};
+
+// A group from the command line. This is a set of arguments within
+// --start-group ... --end-group.
+
+class Input_file_group
{
+ public:
+ typedef std::vector<Input_argument> Files;
+ typedef Files::const_iterator const_iterator;
+
+ Input_file_group()
+ : files_()
+ { }
+
+ // Add a file to the end of the group.
+ void
+ add_file(const Input_file_argument& arg)
+ { this->files_.push_back(Input_argument(arg)); }
+
+ // Iterators to iterate over the group contents.
+
+ const_iterator
+ begin() const
+ { return this->files_.begin(); }
+
+ const_iterator
+ end() const
+ { return this->files_.end(); }
+
+ private:
+ Files files_;
};
// All the information read from the command line.
@@ -164,6 +251,9 @@ class Input_argument_list : public std::vector<Input_argument>
class Command_line
{
public:
+ typedef std::vector<Input_argument> Input_arguments;
+ typedef Input_arguments::const_iterator const_iterator;
+
Command_line();
// Process the command line options. This will exit with an
@@ -175,25 +265,53 @@ class Command_line
int
process_l_option(int, char**, char*);
+ // Handle a --start-group option.
+ void
+ start_group(const char* arg);
+
+ // Handle a --end-group option.
+ void
+ end_group(const char* arg);
+
// Get the general options.
const General_options&
options() const
{ return this->options_; }
- // Get the list of input files.
- const Input_argument_list&
- inputs() const
- { return this->inputs_; }
+ // Iterators to iterate over the list of input files.
+
+ const_iterator
+ begin() const
+ { return this->inputs_.begin(); }
+
+ const_iterator
+ end() const
+ { return this->inputs_.end(); }
private:
- void usage() ATTRIBUTE_NORETURN;
- void usage(const char* msg, const char* opt) ATTRIBUTE_NORETURN;
- void usage(const char* msg, char opt) ATTRIBUTE_NORETURN;
- void apply_option(const gold::options::One_option&, const char*);
+ Command_line(const Command_line&);
+ Command_line& operator=(const Command_line&);
+
+ // Report usage error.
+ void
+ usage() ATTRIBUTE_NORETURN;
+ void
+ usage(const char* msg, const char* opt) ATTRIBUTE_NORETURN;
+ void
+ usage(const char* msg, char opt) ATTRIBUTE_NORETURN;
+
+ // Apply a command line option.
+ void
+ apply_option(const gold::options::One_option&, const char*);
+
+ // Add a file.
+ void
+ add_file(const char* name, bool is_lib);
General_options options_;
Position_dependent_options position_options_;
- Input_argument_list inputs_;
+ Input_arguments inputs_;
+ bool in_group_;
};
} // End namespace gold.
diff --git a/gold/output.cc b/gold/output.cc
index 82e2ca5..703a560 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -10,6 +10,8 @@
#include <algorithm>
#include "object.h"
+#include "symtab.h"
+#include "reloc.h"
#include "output.h"
namespace gold
@@ -74,7 +76,8 @@ Output_section_headers::Output_section_headers(
for (Layout::Segment_list::const_iterator p = segment_list.begin();
p != segment_list.end();
++p)
- count += (*p)->output_section_count();
+ if ((*p)->type() == elfcpp::PT_LOAD)
+ count += (*p)->output_section_count();
count += section_list.size();
int shdr_size;
@@ -137,18 +140,22 @@ Output_section_headers::do_sized_write(Output_file* of)
v += shdr_size;
+ unsigned shndx = 1;
for (Layout::Segment_list::const_iterator p = this->segment_list_.begin();
p != this->segment_list_.end();
++p)
v = (*p)->write_section_headers SELECT_SIZE_ENDIAN_NAME (
- this->secnamepool_, v SELECT_SIZE_ENDIAN(size, big_endian));
+ this->secnamepool_, v, &shndx
+ SELECT_SIZE_ENDIAN(size, big_endian));
for (Layout::Section_list::const_iterator p = this->section_list_.begin();
p != this->section_list_.end();
++p)
{
+ assert(shndx == (*p)->out_shndx());
elfcpp::Shdr_write<size, big_endian> oshdr(v);
(*p)->write_header(this->secnamepool_, &oshdr);
v += shdr_size;
+ ++shndx;
}
of->write_output_view(this->offset(), all_shdrs_size, view);
@@ -318,6 +325,7 @@ Output_file_header::do_sized_write(Output_file* of)
oehdr.put_e_machine(this->target_->machine_code());
oehdr.put_e_version(elfcpp::EV_CURRENT);
+ // FIXME: Need to support -e, and target specific entry symbol.
Symbol* sym = this->symtab_->lookup("_start");
typename Sized_symbol<size>::Value_type v;
if (sym == NULL)
@@ -344,17 +352,137 @@ Output_file_header::do_sized_write(Output_file* of)
oehdr.put_e_shentsize(elfcpp::Elf_sizes<size>::shdr_size);
oehdr.put_e_shnum(this->section_header_->data_size()
/ elfcpp::Elf_sizes<size>::shdr_size);
- oehdr.put_e_shstrndx(this->shstrtab_->shndx());
+ oehdr.put_e_shstrndx(this->shstrtab_->out_shndx());
of->write_output_view(0, ehdr_size, view);
}
+// Output_section_got::Got_entry methods.
+
+// Write out the entry.
+
+template<int size, bool big_endian>
+void
+Output_section_got<size, big_endian>::Got_entry::write(unsigned char* pov)
+ const
+{
+ Valtype val = 0;
+
+ switch (this->local_sym_index_)
+ {
+ case GSYM_CODE:
+ {
+ Symbol* gsym = this->u_.gsym;
+
+ // If the symbol is resolved locally, we need to write out its
+ // value. Otherwise we just write zero. The target code is
+ // responsible for creating a relocation entry to fill in the
+ // value at runtime.
+ if (gsym->is_resolved_locally())
+ {
+ Sized_symbol<size>* sgsym;
+ // This cast is a bit ugly. We don't want to put a
+ // virtual method in Symbol, because we want Symbol to be
+ // as small as possible.
+ sgsym = static_cast<Sized_symbol<size>*>(gsym);
+ val = sgsym->value();
+ }
+ }
+ break;
+
+ case CONSTANT_CODE:
+ val = this->u_.constant;
+ break;
+
+ default:
+ abort();
+ }
+
+ Valtype* povv = reinterpret_cast<Valtype*>(pov);
+ Swap<size, big_endian>::writeval(povv, val);
+}
+
+// Output_section_data methods.
+
+unsigned int
+Output_section_data::do_out_shndx() const
+{
+ assert(this->output_section_ != NULL);
+ return this->output_section_->out_shndx();
+}
+
+// Output_section_got methods.
+
+// Write out the GOT.
+
+template<int size, bool big_endian>
+void
+Output_section_got<size, big_endian>::do_write(Output_file* of)
+{
+ const int add = size / 8;
+
+ const off_t off = this->offset();
+ const off_t oview_size = this->entries_.size() * add;
+ unsigned char* const oview = of->get_output_view(off, oview_size);
+
+ unsigned char* pov = oview;
+ for (typename Got_entries::const_iterator p = this->entries_.begin();
+ p != this->entries_.end();
+ ++p)
+ {
+ p->write(pov);
+ pov += add;
+ }
+
+ of->write_output_view(off, oview_size, oview);
+
+ // We no longer need the GOT entries.
+ this->entries_.clear();
+}
+
+// Output_section::Input_section methods.
+
+// Return the data size. For an input section we store the size here.
+// For an Output_section_data, we have to ask it for the size.
+
+off_t
+Output_section::Input_section::data_size() const
+{
+ if (this->is_input_section())
+ return this->data_size_;
+ else
+ return this->u_.posd->data_size();
+}
+
+// Set the address and file offset.
+
+void
+Output_section::Input_section::set_address(uint64_t addr, off_t off,
+ off_t secoff)
+{
+ if (this->is_input_section())
+ this->u_.object->set_section_offset(this->shndx_, off - secoff);
+ else
+ this->u_.posd->set_address(addr, off);
+}
+
+// Write out the data. We don't have to do anything for an input
+// section--they are handled via Object::relocate--but this is where
+// we write out the data for an Output_section_data.
+
+void
+Output_section::Input_section::write(Output_file* of)
+{
+ if (!this->is_input_section())
+ this->u_.posd->write(of);
+}
+
// Output_section methods.
// Construct an Output_section. NAME will point into a Stringpool.
Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
- elfcpp::Elf_Xword flags, unsigned int shndx)
+ elfcpp::Elf_Xword flags, bool may_add_data)
: name_(name),
addralign_(0),
entsize_(0),
@@ -362,7 +490,10 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
info_(0),
type_(type),
flags_(flags),
- shndx_(shndx)
+ out_shndx_(0),
+ input_sections_(),
+ first_input_offset_(0),
+ may_add_data_(may_add_data)
{
}
@@ -370,15 +501,20 @@ Output_section::~Output_section()
{
}
-// Add an input section to an Output_section. We don't keep track of
+// Add the input section SHNDX, with header SHDR, named SECNAME, in
+// OBJECT, to the Output_section. Return the offset of the input
+// section within the output section. We don't always keep track of
// input sections for an Output_section. Instead, each Object keeps
// track of the Output_section for each of its input sections.
template<int size, bool big_endian>
off_t
-Output_section::add_input_section(Object* object, const char* secname,
+Output_section::add_input_section(Object* object, unsigned int shndx,
+ const char* secname,
const elfcpp::Shdr<size, big_endian>& shdr)
{
+ assert(this->may_add_data_);
+
elfcpp::Elf_Xword addralign = shdr.get_sh_addralign();
if ((addralign & (addralign - 1)) != 0)
{
@@ -392,19 +528,56 @@ Output_section::add_input_section(Object* object, const char* secname,
this->addralign_ = addralign;
off_t ssize = this->data_size();
- ssize = (ssize + addralign - 1) &~ (addralign - 1);
+ ssize = align_address(ssize, addralign);
+ this->set_data_size(ssize + shdr.get_sh_size());
- // SHF_TLS/SHT_NOBITS sections are handled specially: they are
- // treated as having no size and taking up no space. We only use
- // the real size when setting the pt_memsz field of the PT_TLS
- // segment.
- if ((this->flags_ & elfcpp::SHF_TLS) == 0
- || this->type_ != elfcpp::SHT_NOBITS)
- this->set_data_size(ssize + shdr.get_sh_size());
+ // We need to keep track of this section if we are already keeping
+ // track of sections, or if we are relaxing. FIXME: Add test for
+ // relaxing.
+ if (! this->input_sections_.empty())
+ this->input_sections_.push_back(Input_section(object, shndx,
+ shdr.get_sh_size(),
+ addralign));
return ssize;
}
+// Add arbitrary data to an output section.
+
+void
+Output_section::add_output_section_data(Output_section_data* posd)
+{
+ if (this->input_sections_.empty())
+ this->first_input_offset_ = this->data_size();
+ this->input_sections_.push_back(Input_section(posd));
+ uint64_t addralign = posd->addralign();
+ if (addralign > this->addralign_)
+ this->addralign_ = addralign;
+ posd->set_output_section(this);
+}
+
+// Set the address of an Output_section. This is where we handle
+// setting the addresses of any Output_section_data objects.
+
+void
+Output_section::do_set_address(uint64_t address, off_t startoff)
+{
+ if (this->input_sections_.empty())
+ return;
+
+ off_t off = startoff + this->first_input_offset_;
+ for (Input_section_list::iterator p = this->input_sections_.begin();
+ p != this->input_sections_.end();
+ ++p)
+ {
+ off = align_address(off, p->addralign());
+ p->set_address(address + (off - startoff), off, startoff);
+ off += p->data_size();
+ }
+
+ this->set_data_size(off - startoff);
+}
+
// Write the section header to *OSHDR.
template<int size, bool big_endian>
@@ -424,11 +597,23 @@ Output_section::write_header(const Stringpool* secnamepool,
oshdr->put_sh_entsize(this->entsize_);
}
+// Write out the data. For input sections the data is written out by
+// Object::relocate, but we have to handle Output_section_data objects
+// here.
+
+void
+Output_section::do_write(Output_file* of)
+{
+ for (Input_section_list::iterator p = this->input_sections_.begin();
+ p != this->input_sections_.end();
+ ++p)
+ p->write(of);
+}
+
// Output_section_symtab methods.
-Output_section_symtab::Output_section_symtab(const char* name, off_t size,
- unsigned int shndx)
- : Output_section(name, elfcpp::SHT_SYMTAB, 0, shndx)
+Output_section_symtab::Output_section_symtab(const char* name, off_t size)
+ : Output_section(name, elfcpp::SHT_SYMTAB, 0, false)
{
this->set_data_size(size);
}
@@ -436,9 +621,8 @@ Output_section_symtab::Output_section_symtab(const char* name, off_t size,
// Output_section_strtab methods.
Output_section_strtab::Output_section_strtab(const char* name,
- Stringpool* contents,
- unsigned int shndx)
- : Output_section(name, elfcpp::SHT_STRTAB, 0, shndx),
+ Stringpool* contents)
+ : Output_section(name, elfcpp::SHT_STRTAB, 0, false),
contents_(contents)
{
this->set_data_size(contents->get_strtab_size());
@@ -462,7 +646,8 @@ Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags)
offset_(0),
filesz_(0),
type_(type),
- flags_(flags)
+ flags_(flags),
+ is_align_known_(false)
{
}
@@ -473,12 +658,10 @@ Output_segment::add_output_section(Output_section* os,
elfcpp::Elf_Word seg_flags)
{
assert((os->flags() & elfcpp::SHF_ALLOC) != 0);
+ assert(!this->is_align_known_);
- // Update the segment flags and alignment.
+ // Update the segment flags.
this->flags_ |= seg_flags;
- uint64_t addralign = os->addralign();
- if (addralign > this->align_)
- this->align_ = addralign;
Output_segment::Output_data_list* pdl;
if (os->type() == elfcpp::SHT_NOBITS)
@@ -524,12 +707,28 @@ Output_segment::add_output_section(Output_section* os,
{
pdl = &this->output_data_;
bool nobits = os->type() == elfcpp::SHT_NOBITS;
+ bool sawtls = false;
Layout::Data_list::iterator p = pdl->end();
do
{
--p;
- if ((*p)->is_section_flag_set(elfcpp::SHF_TLS)
- && (nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS)))
+ bool insert;
+ if ((*p)->is_section_flag_set(elfcpp::SHF_TLS))
+ {
+ sawtls = true;
+ // Put a NOBITS section after the first TLS section.
+ // But a PROGBITS section after the first TLS/PROGBITS
+ // section.
+ insert = nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS);
+ }
+ else
+ {
+ // If we've gone past the TLS sections, but we've seen a
+ // TLS section, then we need to insert this section now.
+ insert = sawtls;
+ }
+
+ if (insert)
{
++p;
pdl->insert(p, os);
@@ -537,6 +736,9 @@ Output_segment::add_output_section(Output_section* os,
}
}
while (p != pdl->begin());
+
+ // There are no TLS sections yet; put this one at the end of the
+ // section list.
}
pdl->push_back(os);
@@ -548,28 +750,59 @@ Output_segment::add_output_section(Output_section* os,
void
Output_segment::add_initial_output_data(Output_data* od)
{
- uint64_t addralign = od->addralign();
- if (addralign > this->align_)
- this->align_ = addralign;
-
+ assert(!this->is_align_known_);
this->output_data_.push_front(od);
}
// Return the maximum alignment of the Output_data in Output_segment.
-// We keep this up to date as we add Output_sections and Output_data.
+// Once we compute this, we prohibit new sections from being added.
uint64_t
-Output_segment::max_data_align() const
+Output_segment::addralign()
{
+ if (!this->is_align_known_)
+ {
+ uint64_t addralign;
+
+ addralign = Output_segment::maximum_alignment(&this->output_data_);
+ if (addralign > this->align_)
+ this->align_ = addralign;
+
+ addralign = Output_segment::maximum_alignment(&this->output_bss_);
+ if (addralign > this->align_)
+ this->align_ = addralign;
+
+ this->is_align_known_ = true;
+ }
+
return this->align_;
}
+// Return the maximum alignment of a list of Output_data.
+
+uint64_t
+Output_segment::maximum_alignment(const Output_data_list* pdl)
+{
+ uint64_t ret = 0;
+ for (Output_data_list::const_iterator p = pdl->begin();
+ p != pdl->end();
+ ++p)
+ {
+ uint64_t addralign = (*p)->addralign();
+ if (addralign > ret)
+ ret = addralign;
+ }
+ return ret;
+}
+
// Set the section addresses for an Output_segment. ADDR is the
-// address and *POFF is the file offset. Return the address of the
-// immediately following segment. Update *POFF.
+// address and *POFF is the file offset. Set the section indexes
+// starting with *PSHNDX. Return the address of the immediately
+// following segment. Update *POFF and *PSHNDX.
uint64_t
-Output_segment::set_section_addresses(uint64_t addr, off_t* poff)
+Output_segment::set_section_addresses(uint64_t addr, off_t* poff,
+ unsigned int* pshndx)
{
assert(this->type_ == elfcpp::PT_LOAD);
@@ -579,13 +812,16 @@ Output_segment::set_section_addresses(uint64_t addr, off_t* poff)
off_t orig_off = *poff;
this->offset_ = orig_off;
- addr = this->set_section_list_addresses(&this->output_data_, addr, poff);
+ *poff = align_address(*poff, this->addralign());
+
+ addr = this->set_section_list_addresses(&this->output_data_, addr, poff,
+ pshndx);
this->filesz_ = *poff - orig_off;
off_t off = *poff;
uint64_t ret = this->set_section_list_addresses(&this->output_bss_, addr,
- poff);
+ poff, pshndx);
this->memsz_ = *poff - orig_off;
// Ignore the file offset adjustments made by the BSS Output_data
@@ -599,26 +835,36 @@ Output_segment::set_section_addresses(uint64_t addr, off_t* poff)
uint64_t
Output_segment::set_section_list_addresses(Output_data_list* pdl,
- uint64_t addr, off_t* poff)
+ uint64_t addr, off_t* poff,
+ unsigned int* pshndx)
{
- off_t off = *poff;
+ off_t startoff = *poff;
+ off_t off = startoff;
for (Output_data_list::iterator p = pdl->begin();
p != pdl->end();
++p)
{
- uint64_t addralign = (*p)->addralign();
- addr = (addr + addralign - 1) & ~ (addralign - 1);
- off = (off + addralign - 1) & ~ (addralign - 1);
- (*p)->set_address(addr, off);
+ off = align_address(off, (*p)->addralign());
+ (*p)->set_address(addr + (off - startoff), off);
+
+ // Unless this is a PT_TLS segment, we want to ignore the size
+ // of a SHF_TLS/SHT_NOBITS section. Such a section does not
+ // affect the size of a PT_LOAD segment.
+ if (this->type_ == elfcpp::PT_TLS
+ || !(*p)->is_section_flag_set(elfcpp::SHF_TLS)
+ || !(*p)->is_section_type(elfcpp::SHT_NOBITS))
+ off += (*p)->data_size();
- uint64_t size = (*p)->data_size();
- addr += size;
- off += size;
+ if ((*p)->is_section())
+ {
+ (*p)->set_out_shndx(*pshndx);
+ ++*pshndx;
+ }
}
*poff = off;
- return addr;
+ return addr + (off - startoff);
}
// For a non-PT_LOAD segment, set the offset from the sections, if
@@ -667,8 +913,6 @@ Output_segment::set_offset()
this->memsz_ = (last->address()
+ last->data_size()
- this->vaddr_);
-
- // this->align_ was set as we added items.
}
// Return the number of Output_sections in an Output_segment.
@@ -700,7 +944,7 @@ Output_segment::output_section_count_list(const Output_data_list* pdl) const
template<int size, bool big_endian>
void
-Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr) const
+Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr)
{
ophdr->put_p_type(this->type_);
ophdr->put_p_offset(this->offset_);
@@ -709,7 +953,7 @@ Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr) const
ophdr->put_p_filesz(this->filesz_);
ophdr->put_p_memsz(this->memsz_);
ophdr->put_p_flags(this->flags_);
- ophdr->put_p_align(this->align_);
+ ophdr->put_p_align(this->addralign());
}
// Write the section headers into V.
@@ -717,13 +961,22 @@ Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr) const
template<int size, bool big_endian>
unsigned char*
Output_segment::write_section_headers(const Stringpool* secnamepool,
- unsigned char* v
+ unsigned char* v,
+ unsigned int *pshndx
ACCEPT_SIZE_ENDIAN) const
{
+ // Every section that is attached to a segment must be attached to a
+ // PT_LOAD segment, so we only write out section headers for PT_LOAD
+ // segments.
+ if (this->type_ != elfcpp::PT_LOAD)
+ return v;
+
v = this->write_section_headers_list SELECT_SIZE_ENDIAN_NAME (
- secnamepool, &this->output_data_, v SELECT_SIZE_ENDIAN(size, big_endian));
+ secnamepool, &this->output_data_, v, pshndx
+ SELECT_SIZE_ENDIAN(size, big_endian));
v = this->write_section_headers_list SELECT_SIZE_ENDIAN_NAME (
- secnamepool, &this->output_bss_, v SELECT_SIZE_ENDIAN(size, big_endian));
+ secnamepool, &this->output_bss_, v, pshndx
+ SELECT_SIZE_ENDIAN(size, big_endian));
return v;
}
@@ -731,7 +984,8 @@ template<int size, bool big_endian>
unsigned char*
Output_segment::write_section_headers_list(const Stringpool* secnamepool,
const Output_data_list* pdl,
- unsigned char* v
+ unsigned char* v,
+ unsigned int* pshndx
ACCEPT_SIZE_ENDIAN) const
{
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
@@ -742,9 +996,11 @@ Output_segment::write_section_headers_list(const Stringpool* secnamepool,
if ((*p)->is_section())
{
const Output_section* ps = static_cast<const Output_section*>(*p);
+ assert(*pshndx == ps->out_shndx());
elfcpp::Shdr_write<size, big_endian> oshdr(v);
ps->write_header(secnamepool, &oshdr);
v += shdr_size;
+ ++*pshndx;
}
}
return v;
@@ -834,6 +1090,7 @@ template
off_t
Output_section::add_input_section<32, false>(
Object* object,
+ unsigned int shndx,
const char* secname,
const elfcpp::Shdr<32, false>& shdr);
@@ -841,6 +1098,7 @@ template
off_t
Output_section::add_input_section<32, true>(
Object* object,
+ unsigned int shndx,
const char* secname,
const elfcpp::Shdr<32, true>& shdr);
@@ -848,6 +1106,7 @@ template
off_t
Output_section::add_input_section<64, false>(
Object* object,
+ unsigned int shndx,
const char* secname,
const elfcpp::Shdr<64, false>& shdr);
@@ -855,7 +1114,24 @@ template
off_t
Output_section::add_input_section<64, true>(
Object* object,
+ unsigned int shndx,
const char* secname,
const elfcpp::Shdr<64, true>& shdr);
+template
+void
+Output_section_got<32, false>::do_write(Output_file* of);
+
+template
+void
+Output_section_got<32, true>::do_write(Output_file* of);
+
+template
+void
+Output_section_got<64, false>::do_write(Output_file* of);
+
+template
+void
+Output_section_got<64, true>::do_write(Output_file* of);
+
} // End namespace gold.
diff --git a/gold/output.h b/gold/output.h
index e036b98..5c72c02 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -5,6 +5,7 @@
#include <cassert>
#include <list>
+#include <vector>
#include "elfcpp.h"
#include "layout.h"
@@ -31,17 +32,21 @@ class Output_data
virtual
~Output_data();
- // Return the address.
+ // Return the address. This is only valid after Layout::finalize is
+ // finished.
uint64_t
address() const
{ return this->address_; }
- // Return the size of the data.
+ // Return the size of the data. This must be valid after
+ // Layout::finalize calls set_address, but need not be valid before
+ // then.
off_t
data_size() const
{ return this->data_size_; }
- // Return the file offset.
+ // Return the file offset. This is only valid after
+ // Layout::finalize is finished.
off_t
offset() const
{ return this->offset_; }
@@ -67,11 +72,23 @@ class Output_data
is_section_flag_set(elfcpp::Elf_Xword shf) const
{ return this->do_is_section_flag_set(shf); }
- // Set the address and file offset of this data.
+ // Return the output section index, if there is an output section.
+ unsigned int
+ out_shndx() const
+ { return this->do_out_shndx(); }
+
+ // Set the output section index, if this is an output section.
+ void
+ set_out_shndx(unsigned int shndx)
+ { this->do_set_out_shndx(shndx); }
+
+ // Set the address and file offset of this data. This is called
+ // during Layout::finalize.
void
set_address(uint64_t addr, off_t off);
- // Write the data to the output file.
+ // Write the data to the output file. This is called after
+ // Layout::finalize is complete.
void
write(Output_file* file)
{ this->do_write(file); }
@@ -104,6 +121,16 @@ class Output_data
do_is_section_flag_set(elfcpp::Elf_Xword) const
{ return false; }
+ // Return the output section index, if there is an output section.
+ virtual unsigned int
+ do_out_shndx() const
+ { abort(); }
+
+ // Set the output section index, if this is an output section.
+ virtual void
+ do_set_out_shndx(unsigned int)
+ { abort(); }
+
// Set the address and file offset of the data. This only needs to
// be implemented if the child needs to know.
virtual void
@@ -270,6 +297,198 @@ class Output_file_header : public Output_data
const Output_section* shstrtab_;
};
+// Output sections are mainly comprised of input sections. However,
+// there are cases where we have data to write out which is not in an
+// input section. Output_section_data is used in such cases. This is
+// an abstract base class.
+
+class Output_section_data : public Output_data
+{
+ public:
+ Output_section_data(off_t data_size, uint64_t addralign)
+ : Output_data(data_size), output_section_(NULL), addralign_(addralign)
+ { }
+
+ Output_section_data(uint64_t addralign)
+ : Output_data(0), output_section_(NULL), addralign_(addralign)
+ { }
+
+ // Record the output section.
+ void
+ set_output_section(Output_section* os)
+ {
+ assert(this->output_section_ == NULL);
+ this->output_section_ = os;
+ }
+
+ protected:
+ // The child class must implement do_write.
+
+ // Return the required alignment.
+ uint64_t
+ do_addralign() const
+ { return this->addralign_; }
+
+ // Return the section index of the output section.
+ unsigned int
+ do_out_shndx() const;
+
+ private:
+ // The output section for this section.
+ const Output_section* output_section_;
+ // The required alignment.
+ uint64_t addralign_;
+};
+
+// Output_section_common is used to handle the common symbols. This
+// is quite simple.
+
+class Output_section_common : public Output_section_data
+{
+ public:
+ Output_section_common(uint64_t addralign)
+ : Output_section_data(addralign)
+ { }
+
+ // Set the size.
+ void
+ set_common_size(off_t common_size)
+ { this->set_data_size(common_size); }
+
+ // Write out the data--there is nothing to do, as common symbols are
+ // always zero and are stored in the BSS.
+ void
+ do_write(Output_file*)
+ { }
+};
+
+// Output_section_got is used to manage a GOT. Each entry in the GOT
+// is for one symbol--either a global symbol or a local symbol in an
+// object. The target specific code adds entries to the GOT as
+// needed. The GOT code is then responsible for writing out the data
+// and for generating relocs as required.
+
+template<int size, bool big_endian>
+class Output_section_got : public Output_section_data
+{
+ public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype;
+
+ Output_section_got()
+ : Output_section_data(Output_data::default_alignment(size)),
+ entries_()
+ { }
+
+ // Add an entry for a global symbol to the GOT. This returns the
+ // offset of the new entry from the start of the GOT.
+ unsigned int
+ add_global(Symbol* gsym)
+ {
+ this->entries_.push_back(Got_entry(gsym));
+ this->set_got_size();
+ return this->last_got_offset();
+ }
+
+ // Add an entry for a local symbol to the GOT. This returns the
+ // offset of the new entry from the start of the GOT.
+ unsigned int
+ add_local(Object* object, unsigned int sym_index)
+ {
+ this->entries_.push_back(Got_entry(object, sym_index));
+ this->set_got_size();
+ return this->last_got_offset();
+ }
+
+ // Add a constant to the GOT. This returns the offset of the new
+ // entry from the start of the GOT.
+ unsigned int
+ add_constant(Valtype constant)
+ {
+ this->entries_.push_back(Got_entry(constant));
+ this->set_got_size();
+ return this->last_got_offset();
+ }
+
+ // Write out the GOT table.
+ void
+ do_write(Output_file*);
+
+ private:
+ // This POD class holds a single GOT entry.
+ class Got_entry
+ {
+ public:
+ // Create a zero entry.
+ Got_entry()
+ : local_sym_index_(CONSTANT_CODE)
+ { this->u_.constant = 0; }
+
+ // Create a global symbol entry.
+ Got_entry(Symbol* gsym)
+ : local_sym_index_(GSYM_CODE)
+ { this->u_.gsym = gsym; }
+
+ // Create a local symbol entry.
+ Got_entry(Object* object, unsigned int local_sym_index)
+ : local_sym_index_(local_sym_index)
+ {
+ assert(local_sym_index != GSYM_CODE
+ && local_sym_index != CONSTANT_CODE);
+ this->u_.object = object;
+ }
+
+ // Create a constant entry. The constant is a host value--it will
+ // be swapped, if necessary, when it is written out.
+ Got_entry(Valtype constant)
+ : local_sym_index_(CONSTANT_CODE)
+ { this->u_.constant = constant; }
+
+ // Write the GOT entry to an output view.
+ void
+ write(unsigned char* pov) const;
+
+ private:
+ enum
+ {
+ GSYM_CODE = -1U,
+ CONSTANT_CODE = -2U
+ };
+
+ union
+ {
+ // For a local symbol, the object.
+ Object* object;
+ // For a global symbol, the symbol.
+ Symbol* gsym;
+ // For a constant, the constant.
+ Valtype constant;
+ } u_;
+ // For a local symbol, the local symbol index. This is -1U for a
+ // global symbol, or -2U for a constant.
+ unsigned int local_sym_index_;
+ };
+
+ typedef std::vector<Got_entry> Got_entries;
+
+ // Return the offset into the GOT of GOT entry I.
+ unsigned int
+ got_offset(unsigned int i) const
+ { return i * (size / 8); }
+
+ // Return the offset into the GOT of the last entry added.
+ unsigned int
+ last_got_offset() const
+ { return this->got_offset(this->entries_.size() - 1); }
+
+ // Set the size of the section.
+ void
+ set_got_size()
+ { this->set_data_size(this->got_offset(this->entries_.size())); }
+
+ // The list of GOT entries.
+ Got_entries entries_;
+};
+
// An output section. We don't expect to have too many output
// sections, so we don't bother to do a template on the size.
@@ -278,16 +497,20 @@ class Output_section : public Output_data
public:
// Create an output section, giving the name, type, and flags.
Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword,
- unsigned int shndx);
+ bool may_add_data);
virtual ~Output_section();
- // Add a new input section named NAME with header SHDR from object
- // OBJECT. Return the offset within the output section.
+ // Add a new input section SHNDX, named NAME, with header SHDR, from
+ // object OBJECT. Return the offset within the output section.
template<int size, bool big_endian>
off_t
- add_input_section(Object* object, const char *name,
+ add_input_section(Object* object, unsigned int shndx, const char *name,
const elfcpp::Shdr<size, big_endian>& shdr);
+ // Add generated data ODATA to this output section.
+ virtual void
+ add_output_section_data(Output_section_data* posd);
+
// Return the section name.
const char*
name() const
@@ -303,15 +526,15 @@ class Output_section : public Output_data
flags() const
{ return this->flags_; }
- // Return the address alignment.
- uint64_t
- addralign() const
- { return this->addralign_; }
-
- // Return the section index.
+ // Return the section index in the output file.
unsigned int
- shndx() const
- { return this->shndx_; }
+ do_out_shndx() const
+ { return this->out_shndx_; }
+
+ // Set the output section index.
+ void
+ do_set_out_shndx(unsigned int shndx)
+ { this->out_shndx_ = shndx; }
// Set the entsize field.
void
@@ -333,12 +556,19 @@ class Output_section : public Output_data
set_addralign(uint64_t v)
{ this->addralign_ = v; }
+ // Set the address of the Output_section. For a typical
+ // Output_section, there is nothing to do, but if there are any
+ // Output_section_data objects we need to set the final addresses
+ // here.
+ void
+ do_set_address(uint64_t, off_t);
+
// Write the data to the file. For a typical Output_section, this
- // does nothing. We write out the data by looping over all the
- // input sections.
+ // does nothing: the data is written out by calling Object::Relocate
+ // on each input object. But if there are any Output_section_data
+ // objects we do need to write them out here.
virtual void
- do_write(Output_file*)
- { }
+ do_write(Output_file*);
// Return the address alignment--function required by parent class.
uint64_t
@@ -366,6 +596,83 @@ class Output_section : public Output_data
write_header(const Stringpool*, elfcpp::Shdr_write<size, big_endian>*) const;
private:
+ // In some cases we need to keep a list of the input sections
+ // associated with this output section. We only need the list if we
+ // might have to change the offsets of the input section within the
+ // output section after we add the input section. The ordinary
+ // input sections will be written out when we process the object
+ // file, and as such we don't need to track them here. We do need
+ // to track Output_section_data objects here. We store instances of
+ // this structure in a std::vector, so it must be a POD. There can
+ // be many instances of this structure, so we use a union to save
+ // some space.
+ class Input_section
+ {
+ public:
+ Input_section()
+ : shndx_(0), p2align_(0), data_size_(0)
+ { this->u_.object = NULL; }
+
+ Input_section(Object* object, unsigned int shndx, off_t data_size,
+ uint64_t addralign)
+ : shndx_(shndx),
+ p2align_(ffsll(static_cast<long long>(addralign))),
+ data_size_(data_size)
+ {
+ assert(shndx != -1U);
+ this->u_.object = object;
+ }
+
+ Input_section(Output_section_data* posd)
+ : shndx_(-1U),
+ p2align_(ffsll(static_cast<long long>(posd->addralign()))),
+ data_size_(0)
+ { this->u_.posd = posd; }
+
+ // The required alignment.
+ uint64_t
+ addralign() const
+ { return static_cast<uint64_t>(1) << this->p2align_; }
+
+ // Return the required size.
+ off_t
+ data_size() const;
+
+ // Set the address and file offset. This is called during
+ // Layout::finalize. SECOFF is the file offset of the enclosing
+ // section.
+ void
+ set_address(uint64_t addr, off_t off, off_t secoff);
+
+ // Write out the data. This does nothing for an input section.
+ void
+ write(Output_file*);
+
+ private:
+ // Whether this is an input section.
+ bool
+ is_input_section() const
+ { return this->shndx_ != -1U; }
+
+ // For an ordinary input section, this is the section index in
+ // the input file. For an Output_section_data, this is -1U.
+ unsigned int shndx_;
+ // The required alignment, stored as a power of 2.
+ unsigned int p2align_;
+ // For an ordinary input section, the section size.
+ off_t data_size_;
+ union
+ {
+ // If shndx_ != -1U, this points to the object which holds the
+ // input section.
+ Object* object;
+ // If shndx_ == -1U, this is the data to write out.
+ Output_section_data* posd;
+ } u_;
+ };
+
+ typedef std::vector<Input_section> Input_section_list;
+
// Most of these fields are only valid after layout.
// The name of the section. This will point into a Stringpool.
@@ -385,16 +692,35 @@ class Output_section : public Output_data
// The section flags.
elfcpp::Elf_Xword flags_;
// The section index.
- unsigned int shndx_;
+ unsigned int out_shndx_;
+ // The input sections. This will be empty in cases where we don't
+ // need to keep track of them.
+ Input_section_list input_sections_;
+ // The offset of the first entry in input_sections_.
+ off_t first_input_offset_;
+ // Whether we permit adding data.
+ bool may_add_data_;
};
// A special Output_section which represents the symbol table
-// (SHT_SYMTAB).
+// (SHT_SYMTAB). The actual data is written out by
+// Symbol_table::write_globals.
class Output_section_symtab : public Output_section
{
public:
- Output_section_symtab(const char* name, off_t size, unsigned int shndx);
+ Output_section_symtab(const char* name, off_t size);
+
+ // The data is written out by Symbol_table::write_globals. We don't
+ // do anything here.
+ void
+ do_write(Output_file*)
+ { }
+
+ // We don't expect to see any input sections or data here.
+ void
+ add_output_section_data(Output_section_data*)
+ { abort(); }
};
// A special Output_section which holds a string table.
@@ -402,13 +728,17 @@ class Output_section_symtab : public Output_section
class Output_section_strtab : public Output_section
{
public:
- Output_section_strtab(const char* name, Stringpool* contents,
- unsigned int shndx);
+ Output_section_strtab(const char* name, Stringpool* contents);
// Write out the data.
void
do_write(Output_file*);
+ // We don't expect to see any input sections or data here.
+ void
+ add_output_section_data(Output_section_data*)
+ { abort(); }
+
private:
Stringpool* contents_;
};
@@ -448,9 +778,14 @@ class Output_segment
memsz() const
{ return this->memsz_; }
+ // Return the file size.
+ off_t
+ filesz() const
+ { return this->filesz_; }
+
// Return the maximum alignment of the Output_data.
uint64_t
- max_data_align() const;
+ addralign();
// Add an Output_section to this segment.
void
@@ -463,11 +798,12 @@ class Output_segment
// Set the address of the segment to ADDR and the offset to *POFF
// (aligned if necessary), and set the addresses and offsets of all
- // contained output sections accordingly. Return the address of the
- // immediately following segment. Update *POFF. This should only
- // be called for a PT_LOAD segment.
+ // contained output sections accordingly. Set the section indexes
+ // of all contained output sections starting with *PSHNDX. Return
+ // the address of the immediately following segment. Update *POFF
+ // and *PSHNDX. This should only be called for a PT_LOAD segment.
uint64_t
- set_section_addresses(uint64_t addr, off_t* poff);
+ set_section_addresses(uint64_t addr, off_t* poff, unsigned int* pshndx);
// Set the offset of this segment based on the section. This should
// only be called for a non-PT_LOAD segment.
@@ -481,13 +817,14 @@ class Output_segment
// Write the segment header into *OPHDR.
template<int size, bool big_endian>
void
- write_header(elfcpp::Phdr_write<size, big_endian>*) const;
+ write_header(elfcpp::Phdr_write<size, big_endian>*);
// Write the section headers of associated sections into V.
template<int size, bool big_endian>
unsigned char*
write_section_headers(const Stringpool*,
- unsigned char* v ACCEPT_SIZE_ENDIAN) const;
+ unsigned char* v,
+ unsigned int* pshndx ACCEPT_SIZE_ENDIAN) const;
private:
Output_segment(const Output_segment&);
@@ -495,9 +832,14 @@ class Output_segment
typedef std::list<Output_data*> Output_data_list;
+ // Find the maximum alignment in an Output_data_list.
+ static uint64_t
+ maximum_alignment(const Output_data_list*);
+
// Set the section addresses in an Output_data_list.
uint64_t
- set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff);
+ set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff,
+ unsigned int* pshndx);
// Return the number of Output_sections in an Output_data_list.
unsigned int
@@ -507,7 +849,8 @@ class Output_segment
template<int size, bool big_endian>
unsigned char*
write_section_headers_list(const Stringpool*, const Output_data_list*,
- unsigned char* v ACCEPT_SIZE_ENDIAN) const;
+ unsigned char* v,
+ unsigned int* pshdx ACCEPT_SIZE_ENDIAN) const;
// The list of output data with contents attached to this segment.
Output_data_list output_data_;
@@ -529,6 +872,8 @@ class Output_segment
elfcpp::Elf_Word type_;
// The segment flags.
elfcpp::Elf_Word flags_;
+ // Whether we have set align_.
+ bool is_align_known_;
};
// This class represents the output file.
diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in
index 76d60f9..5d4a4a0 100644
--- a/gold/po/POTFILES.in
+++ b/gold/po/POTFILES.in
@@ -1,5 +1,9 @@
archive.cc
archive.h
+common.cc
+common.h
+defstd.cc
+defstd.h
dirsearch.cc
dirsearch.h
fileread.cc
diff --git a/gold/po/gold.pot b/gold/po/gold.pot
index 66f9572..a62d946 100644
--- a/gold/po/gold.pot
+++ b/gold/po/gold.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-10-20 13:39-0700\n"
+"POT-Creation-Date: 2006-11-03 10:04-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,42 +16,42 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: archive.cc:69
+#: archive.cc:62
#, c-format
msgid "%s: %s: no archive symbol table (run ranlib)\n"
msgstr ""
-#: archive.cc:98
+#: archive.cc:91
#, c-format
msgid "%s: %s: bad archive symbol table names\n"
msgstr ""
-#: archive.cc:132
+#: archive.cc:129
#, c-format
msgid "%s; %s: malformed archive header at %ld\n"
msgstr ""
-#: archive.cc:153
+#: archive.cc:150
#, c-format
msgid "%s: %s: malformed archive header size at %ld\n"
msgstr ""
-#: archive.cc:165
+#: archive.cc:162
#, c-format
msgid "%s: %s: malformed archive header name at %ld\n"
msgstr ""
-#: archive.cc:191
+#: archive.cc:188
#, c-format
msgid "%s: %s: bad extended name index at %ld\n"
msgstr ""
-#: archive.cc:202
+#: archive.cc:199
#, c-format
msgid "%s: %s: bad extended name entry at header %ld\n"
msgstr ""
-#: archive.cc:284 archive.cc:297
+#: archive.cc:279 archive.cc:292
#, c-format
msgid "%s: %s: member at %ld is not an ELF object"
msgstr ""
@@ -66,32 +66,32 @@ msgstr ""
msgid "%s: warning: close(%s) failed: %s"
msgstr ""
-#: fileread.cc:131
+#: fileread.cc:129
#, c-format
msgid "%s: %s: lseek to %lld failed: %s"
msgstr ""
-#: fileread.cc:141
+#: fileread.cc:139
#, c-format
msgid "%s: %s: read failed: %s\n"
msgstr ""
-#: fileread.cc:151
+#: fileread.cc:149 fileread.cc:232
#, c-format
msgid "%s: %s: file too short: read only %lld of %lld bytes at %lld\n"
msgstr ""
-#: fileread.cc:267
+#: fileread.cc:323
#, c-format
msgid "%s: cannot find %s\n"
msgstr ""
-#: fileread.cc:275
+#: fileread.cc:331
#, c-format
msgid "%s: cannot open %s: %s\n"
msgstr ""
-#: gold.cc:100
+#: gold.cc:102
msgid "no input files"
msgstr ""
@@ -139,319 +139,361 @@ msgstr ""
msgid "pthread_cond_signal failed"
msgstr ""
-#: i386.cc:223 i386.cc:319 i386.cc:466
+#. FIXME: This needs to specify the location somehow.
+#: i386.cc:88
+#, c-format
+msgid "%s: missing expected TLS relocation\n"
+msgstr ""
+
+#: i386.cc:306 i386.cc:434 i386.cc:627
#, c-format
msgid "%s: %s: unexpected reloc %u in object file\n"
msgstr ""
-#: i386.cc:256 i386.cc:277
+#: i386.cc:339 i386.cc:358
#, c-format
msgid "%s: %s: unsupported reloc %u against local symbol\n"
msgstr ""
-#: i386.cc:354 i386.cc:376
+#: i386.cc:415 i386.cc:469 i386.cc:487
#, c-format
msgid "%s: %s: unsupported reloc %u against global symbol %s\n"
msgstr ""
-#: i386.cc:397
+#: i386.cc:509
#, c-format
msgid "%s: %s: unsupported RELA reloc section\n"
msgstr ""
-#: i386.cc:503 i386.cc:571
+#: i386.cc:548
+#, c-format
+msgid "%s: %s: missing expected TLS relocation\n"
+msgstr ""
+
+#: i386.cc:594 i386.cc:659 i386.cc:732 i386.cc:743
#, c-format
msgid "%s: %s: unsupported reloc %u\n"
msgstr ""
-#: i386.cc:528
+#: i386.cc:686
#, c-format
msgid "%s: %s: TLS reloc but no TLS segment\n"
msgstr ""
-#: i386.cc:559
+#: i386.cc:717
#, c-format
msgid "%s: %s: unsupported reloc type %u\n"
msgstr ""
-#: i386.cc:689
+#: i386.cc:926
#, c-format
msgid "%s: %s: TLS relocation out of range\n"
msgstr ""
-#: i386.cc:707
+#: i386.cc:944
#, c-format
msgid "%s: %s: TLS relocation against invalid instruction\n"
msgstr ""
-#: object.cc:60
+#: object.cc:41
#, c-format
msgid "%s: %s: bad e_ehsize field (%d != %d)\n"
msgstr ""
-#: object.cc:67
+#: object.cc:48
#, c-format
msgid "%s: %s: bad e_shentsize field (%d != %d)\n"
msgstr ""
-#: object.cc:108 object.cc:418
+#: object.cc:89 object.cc:329 object.cc:425
#, c-format
msgid "%s: %s: bad section name offset for section %u: %lu\n"
msgstr ""
-#: object.cc:131
+#: object.cc:112
#, c-format
msgid "%s: %s: unsupported ELF machine number %d\n"
msgstr ""
-#: object.cc:226
+#: object.cc:207
#, c-format
msgid "%s: %s: invalid symbol table name index: %u\n"
msgstr ""
-#: object.cc:234
+#: object.cc:215
#, c-format
msgid "%s: %s: symbol table name section has wrong type: %u\n"
msgstr ""
-#: object.cc:286
+#: object.cc:267
#, c-format
msgid "%s: %s: section group %u link %u out of range\n"
msgstr ""
-#: object.cc:296
+#: object.cc:277
#, c-format
msgid "%s: %s: section group %u info %u out of range\n"
msgstr ""
-#: object.cc:307
+#: object.cc:288
#, c-format
msgid "%s; %s: symtab section %u link %u out of range\n"
msgstr ""
-#: object.cc:323
+#: object.cc:304
#, c-format
msgid "%s: %s: symbol %u name offset %u out of range\n"
msgstr ""
-#: object.cc:345
+#: object.cc:352
#, c-format
msgid "%s: %s: section %u in section group %u out of range"
msgstr ""
-#: object.cc:479
+#: object.cc:486
#, c-format
msgid "%s: %s: size of symbols is not multiple of symbol size\n"
msgstr ""
-#: object.cc:566
+#: object.cc:573
#, c-format
msgid "%s: %s: unknown section index %u for local symbol %u\n"
msgstr ""
-#: object.cc:577
+#: object.cc:584
#, c-format
msgid "%s: %s: local symbol %u section index %u out of range\n"
msgstr ""
#. elfcpp::ET_DYN
-#: object.cc:755
+#: object.cc:763
#, c-format
msgid "%s: %s: dynamic objects are not yet supported\n"
msgstr ""
-#: object.cc:779 object.cc:832 object.cc:853
+#: object.cc:787 object.cc:840 object.cc:861
#, c-format
msgid "%s: %s: ELF file too short\n"
msgstr ""
-#: object.cc:788
+#: object.cc:796
#, c-format
msgid "%s: %s: invalid ELF version 0\n"
msgstr ""
-#: object.cc:791
+#: object.cc:799
#, c-format
msgid "%s: %s: unsupported ELF version %d\n"
msgstr ""
-#: object.cc:799
+#: object.cc:807
#, c-format
msgid "%s: %s: invalid ELF class 0\n"
msgstr ""
-#: object.cc:806
+#: object.cc:814
#, c-format
msgid "%s: %s: unsupported ELF class %d\n"
msgstr ""
-#: object.cc:814
+#: object.cc:822
#, c-format
msgid "%s: %s: invalid ELF data encoding\n"
msgstr ""
-#: object.cc:821
+#: object.cc:829
#, c-format
msgid "%s: %s: unsupported ELF data encoding %d\n"
msgstr ""
-#: options.cc:97
+#: options.cc:115
#, c-format
msgid ""
"Usage: %s [options] file...\n"
"Options:\n"
msgstr ""
-#: options.cc:209
+#: options.cc:227
msgid "Search for library LIBNAME"
msgstr ""
-#: options.cc:210
+#: options.cc:228
msgid "-lLIBNAME --library LIBNAME"
msgstr ""
-#: options.cc:212
+#: options.cc:230
+msgid "Start a library search group"
+msgstr ""
+
+#: options.cc:232
+msgid "End a library search group"
+msgstr ""
+
+#: options.cc:234
msgid "Add directory to search path"
msgstr ""
-#: options.cc:213
+#: options.cc:235
msgid "-L DIR, --library-path DIR"
msgstr ""
-#: options.cc:215
+#: options.cc:237
msgid "Set output file name"
msgstr ""
-#: options.cc:216
+#: options.cc:238
msgid "-o FILE, --output FILE"
msgstr ""
-#: options.cc:218
+#: options.cc:240
msgid "Generate relocatable output"
msgstr ""
-#: options.cc:220
+#: options.cc:242
msgid "Generate shared library"
msgstr ""
-#: options.cc:222
+#: options.cc:244
msgid "Do not link against shared libraries"
msgstr ""
-#: options.cc:224
+#: options.cc:246
msgid "Report usage information"
msgstr ""
-#: options.cc:322 options.cc:373 options.cc:437
+#: options.cc:344 options.cc:395 options.cc:481
msgid "missing argument"
msgstr ""
-#: options.cc:335 options.cc:382
+#: options.cc:357 options.cc:404
msgid "unknown option"
msgstr ""
-#: options.cc:451
+#: options.cc:412
+#, c-format
+msgid "%s: missing group end"
+msgstr ""
+
+#: options.cc:494
+msgid "may not nest groups"
+msgstr ""
+
+#: options.cc:509
+msgid "group end without group start"
+msgstr ""
+
+#: options.cc:519
#, c-format
msgid "%s: use the --help option for usage information\n"
msgstr ""
-#: options.cc:460
+#: options.cc:528
#, c-format
msgid "%s: %s: %s\n"
msgstr ""
-#: options.cc:469
+#: options.cc:537
#, c-format
msgid "%s: -%c: %s\n"
msgstr ""
-#: output.cc:385
+#: output.cc:521
#, c-format
msgid "%s: %s: invalid alignment %lu for section \"%s\"\n"
msgstr ""
-#: output.cc:775
+#: output.cc:1031
#, c-format
msgid "%s: %s: open: %s\n"
msgstr ""
-#: output.cc:784
+#: output.cc:1040
#, c-format
msgid "%s: %s: lseek: %s\n"
msgstr ""
-#: output.cc:791
+#: output.cc:1047
#, c-format
msgid "%s: %s: write: %s\n"
msgstr ""
-#: output.cc:801
+#: output.cc:1057
#, c-format
msgid "%s: %s: mmap: %s\n"
msgstr ""
-#: output.cc:815
+#: output.cc:1071
#, c-format
msgid "%s: %s: munmap: %s\n"
msgstr ""
-#: output.cc:823
+#: output.cc:1079
#, c-format
msgid "%s: %s: close: %s\n"
msgstr ""
+#: readsyms.cc:84
+#, c-format
+msgid "%s: %s: ordinary object found in input group\n"
+msgstr ""
+
#. Here we have to handle any other input file types we need.
-#: readsyms.cc:109
+#: readsyms.cc:126
#, c-format
msgid "%s: %s: not an object or archive\n"
msgstr ""
-#: reloc.cc:165 reloc.cc:392
+#: reloc.cc:168 reloc.cc:408
#, c-format
msgid "%s: %s: relocation section %u has bad info %u\n"
msgstr ""
-#: reloc.cc:176 reloc.cc:409
+#: reloc.cc:187 reloc.cc:425
#, c-format
msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n"
msgstr ""
-#: reloc.cc:192 reloc.cc:428
+#: reloc.cc:203 reloc.cc:444
#, c-format
msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u"
msgstr ""
-#: reloc.cc:203 reloc.cc:439
+#: reloc.cc:214 reloc.cc:455
#, c-format
msgid "%s: %s: reloc section %u size %lu uneven"
msgstr ""
-#: resolve.cc:138
+#: resolve.cc:140
#, c-format
msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n"
msgstr ""
-#: resolve.cc:144
+#: resolve.cc:146
#, c-format
msgid "%s: %s: unsupported symbol binding %d for symbol %s\n"
msgstr ""
-#: symtab.cc:303
+#: symtab.cc:428
#, c-format
msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n"
msgstr ""
-#: symtab.cc:320
+#: symtab.cc:445
#, c-format
msgid "%s: %s: bad global symbol name offset %u at %lu\n"
msgstr ""
-#: target-reloc.h:145
+#: symtab.cc:840 symtab.cc:975
+#, c-format
+msgid "%s: %s: unsupported symbol section 0x%x\n"
+msgstr ""
+
+#: target-reloc.h:181
#, c-format
msgid "%s: %s: reloc has bad offset %zu\n"
msgstr ""
-#: target-reloc.h:176
+#: target-reloc.h:191
#, c-format
msgid "%s: %s: undefined reference to '%s'\n"
msgstr ""
diff --git a/gold/readsyms.cc b/gold/readsyms.cc
index 3a5650a..9ff7ab7 100644
--- a/gold/readsyms.cc
+++ b/gold/readsyms.cc
@@ -22,15 +22,16 @@ Read_symbols::~Read_symbols()
// Add_symbols task.
}
-// Return whether a Read_symbols task is runnable. We need write
-// access to the symbol table. We can read an ordinary input file
-// immediately. For an archive specified using -l, we have to wait
-// until the search path is complete.
+// Return whether a Read_symbols task is runnable. We can read an
+// ordinary input file immediately. For an archive specified using
+// -l, we have to wait until the search path is complete.
Task::Is_runnable_type
Read_symbols::is_runnable(Workqueue*)
{
- if (this->input_.is_lib() && this->dirpath_.token().is_blocked())
+ if (this->input_.is_file()
+ && this->input_.file().is_lib()
+ && this->dirpath_.token().is_blocked())
return IS_BLOCKED;
return IS_RUNNABLE;
@@ -51,7 +52,14 @@ Read_symbols::locks(Workqueue*)
void
Read_symbols::run(Workqueue* workqueue)
{
- Input_file* input_file = new Input_file(this->input_);
+ if (this->input_.is_group())
+ {
+ assert(this->input_group_ == NULL);
+ this->do_group(workqueue);
+ return;
+ }
+
+ Input_file* input_file = new Input_file(this->input_.file());
input_file->open(this->options_, this->dirpath_);
// Read enough of the file to pick up the entire ELF header.
@@ -69,14 +77,22 @@ Read_symbols::run(Workqueue* workqueue)
if (memcmp(p, elfmagic, 4) == 0)
{
// This is an ELF object.
- Object* obj = make_elf_object(this->input_.name(), input_file, 0,
- p, bytes);
- this->input_objects_->add_object(obj);
+ if (this->input_group_ != NULL)
+ {
+ fprintf(stderr,
+ _("%s: %s: ordinary object found in input group\n"),
+ program_name, input_file->name());
+ gold_exit(false);
+ }
+
+ Object* obj = make_elf_object(this->input_.file().name(),
+ input_file, 0, p, bytes);
Read_symbols_data* sd = new Read_symbols_data;
obj->read_symbols(sd);
- workqueue->queue_front(new Add_symbols(this->symtab_, this->layout_,
+ workqueue->queue_front(new Add_symbols(this->input_objects_,
+ this->symtab_, this->layout_,
obj, sd,
this->this_blocker_,
this->next_blocker_));
@@ -93,12 +109,13 @@ Read_symbols::run(Workqueue* workqueue)
if (memcmp(p, Archive::armag, Archive::sarmag) == 0)
{
// This is an archive.
- Archive* arch = new Archive(this->input_.name(), input_file);
+ Archive* arch = new Archive(this->input_.file().name(), input_file);
arch->setup();
workqueue->queue(new Add_archive_symbols(this->symtab_,
this->layout_,
this->input_objects_,
arch,
+ this->input_group_,
this->this_blocker_,
this->next_blocker_));
return;
@@ -111,6 +128,46 @@ Read_symbols::run(Workqueue* workqueue)
gold_exit(false);
}
+// Handle a group. We need to walk through the arguments over and
+// over until we don't see any new undefined symbols. We do this by
+// setting off Read_symbols Tasks as usual, but recording the archive
+// entries instead of deleting them. We also start a Finish_group
+// Task which runs after we've read all the symbols. In that task we
+// process the archives in a loop until we are done.
+
+void
+Read_symbols::do_group(Workqueue* workqueue)
+{
+ Input_group* input_group = new Input_group();
+
+ const Input_file_group* group = this->input_.group();
+ Task_token* this_blocker = this->this_blocker_;
+ for (Input_file_group::const_iterator p = group->begin();
+ p != group->end();
+ ++p)
+ {
+ const Input_argument& arg(*p);
+ assert(arg.is_file());
+
+ Task_token* next_blocker = new Task_token();
+ next_blocker->add_blocker();
+ workqueue->queue(new Read_symbols(this->options_, this->input_objects_,
+ this->symtab_, this->layout_,
+ this->dirpath_, arg, input_group,
+ this_blocker, next_blocker));
+ this_blocker = next_blocker;
+ }
+
+ const int saw_undefined = this->symtab_->saw_undefined();
+ workqueue->queue(new Finish_group(this->input_objects_,
+ this->symtab_,
+ this->layout_,
+ input_group,
+ saw_undefined,
+ this_blocker,
+ this->next_blocker_));
+}
+
// Class Add_symbols.
Add_symbols::~Add_symbols()
@@ -154,13 +211,71 @@ Add_symbols::locks(Workqueue* workqueue)
this->object_);
}
+// Add the symbols in the object to the symbol table.
+
void
Add_symbols::run(Workqueue*)
{
+ this->input_objects_->add_object(this->object_);
this->object_->layout(this->layout_, this->sd_);
this->object_->add_symbols(this->symtab_, this->sd_);
delete this->sd_;
this->sd_ = NULL;
}
+// Class Finish_group.
+
+Finish_group::~Finish_group()
+{
+ if (this->this_blocker_ != NULL)
+ delete this->this_blocker_;
+ // next_blocker_ is deleted by the task associated with the next
+ // input file following the group.
+}
+
+// We need to wait for THIS_BLOCKER_ and unblock NEXT_BLOCKER_.
+
+Task::Is_runnable_type
+Finish_group::is_runnable(Workqueue*)
+{
+ if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
+ return IS_BLOCKED;
+ return IS_RUNNABLE;
+}
+
+Task_locker*
+Finish_group::locks(Workqueue* workqueue)
+{
+ return new Task_locker_block(*this->next_blocker_, workqueue);
+}
+
+// Loop over the archives until there are no new undefined symbols.
+
+void
+Finish_group::run(Workqueue*)
+{
+ int saw_undefined = this->saw_undefined_;
+ while (saw_undefined != this->symtab_->saw_undefined())
+ {
+ saw_undefined = this->symtab_->saw_undefined();
+
+ for (Input_group::const_iterator p = this->input_group_->begin();
+ p != this->input_group_->end();
+ ++p)
+ {
+ Task_lock_obj<Archive> tl(**p);
+
+ (*p)->add_symbols(this->symtab_, this->layout_,
+ this->input_objects_);
+ }
+ }
+
+ // Delete all the archives now that we no longer need them.
+ for (Input_group::const_iterator p = this->input_group_->begin();
+ p != this->input_group_->end();
+ ++p)
+ delete *p;
+ delete this->input_group_;
+}
+
} // End namespace gold.
diff --git a/gold/readsyms.h b/gold/readsyms.h
index 2077d47..2348c32 100644
--- a/gold/readsyms.h
+++ b/gold/readsyms.h
@@ -3,6 +3,8 @@
#ifndef GOLD_READSYMS_H
#define GOLD_READSYMS_H
+#include <vector>
+
#include "workqueue.h"
#include "object.h"
@@ -11,6 +13,8 @@ namespace gold
class Input_objects;
class Symbol_table;
+class Input_group;
+class Archive;
// This Task is responsible for reading the symbols from an input
// file. This also includes reading the relocations so that we can
@@ -24,17 +28,20 @@ class Read_symbols : public Task
{
public:
// DIRPATH is the list of directories to search for libraries.
- // INPUT is the file to read. THIS_BLOCKER is used to prevent the
- // associated Add_symbols task from running before the previous one
- // has completed; it will be NULL for the first task. NEXT_BLOCKER
- // is used to block the next input file from adding symbols.
+ // INPUT is the file to read. INPUT_GROUP is not NULL if we are in
+ // the middle of an input group. THIS_BLOCKER is used to prevent
+ // the associated Add_symbols task from running before the previous
+ // one has completed; it will be NULL for the first task.
+ // NEXT_BLOCKER is used to block the next input file from adding
+ // symbols.
Read_symbols(const General_options& options, Input_objects* input_objects,
Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath,
- const Input_argument& input,
+ const Input_argument& input, Input_group* input_group,
Task_token* this_blocker, Task_token* next_blocker)
: options_(options), input_objects_(input_objects), symtab_(symtab),
layout_(layout), dirpath_(dirpath), input_(input),
- this_blocker_(this_blocker), next_blocker_(next_blocker)
+ input_group_(input_group), this_blocker_(this_blocker),
+ next_blocker_(next_blocker)
{ }
~Read_symbols();
@@ -51,12 +58,17 @@ class Read_symbols : public Task
run(Workqueue*);
private:
+ // Handle an archive group.
+ void
+ do_group(Workqueue*);
+
const General_options& options_;
Input_objects* input_objects_;
Symbol_table* symtab_;
Layout* layout_;
const Dirsearch& dirpath_;
const Input_argument& input_;
+ Input_group* input_group_;
Task_token* this_blocker_;
Task_token* next_blocker_;
};
@@ -71,11 +83,12 @@ class Add_symbols : public Task
// THIS_BLOCKER is used to prevent this task from running before the
// one for the previous input file. NEXT_BLOCKER is used to prevent
// the next task from running.
- Add_symbols(Symbol_table* symtab, Layout* layout, Object* object,
- Read_symbols_data* sd, Task_token* this_blocker,
- Task_token* next_blocker)
- : symtab_(symtab), layout_(layout), object_(object), sd_(sd),
- this_blocker_(this_blocker), next_blocker_(next_blocker)
+ Add_symbols(Input_objects* input_objects, Symbol_table* symtab,
+ Layout* layout, Object* object, Read_symbols_data* sd,
+ Task_token* this_blocker, Task_token* next_blocker)
+ : input_objects_(input_objects), symtab_(symtab), layout_(layout),
+ object_(object), sd_(sd), this_blocker_(this_blocker),
+ next_blocker_(next_blocker)
{ }
~Add_symbols();
@@ -94,6 +107,7 @@ class Add_symbols : public Task
private:
class Add_symbols_locker;
+ Input_objects* input_objects_;
Symbol_table* symtab_;
Layout* layout_;
Object* object_;
@@ -102,6 +116,75 @@ private:
Task_token* next_blocker_;
};
+// This class is used to track the archives in a group.
+
+class Input_group
+{
+ public:
+ typedef std::vector<Archive*> Archives;
+ typedef Archives::const_iterator const_iterator;
+
+ Input_group()
+ : archives_()
+ { }
+
+ // Add an archive to the group.
+ void
+ add_archive(Archive* arch)
+ { this->archives_.push_back(arch); }
+
+ // Loop over the archives in the group.
+
+ const_iterator
+ begin() const
+ { return this->archives_.begin(); }
+
+ const_iterator
+ end() const
+ { return this->archives_.end(); }
+
+ private:
+ Archives archives_;
+};
+
+// This class is used to finish up handling a group. It is just a
+// closure.
+
+class Finish_group : public Task
+{
+ public:
+ Finish_group(Input_objects* input_objects, Symbol_table* symtab,
+ Layout* layout, Input_group* input_group,
+ int saw_undefined, Task_token* this_blocker,
+ Task_token* next_blocker)
+ : input_objects_(input_objects), symtab_(symtab), layout_(layout),
+ input_group_(input_group), saw_undefined_(saw_undefined),
+ this_blocker_(this_blocker), next_blocker_(next_blocker)
+ { }
+
+ ~Finish_group();
+
+ // The standard Task methods.
+
+ Is_runnable_type
+ is_runnable(Workqueue*);
+
+ Task_locker*
+ locks(Workqueue*);
+
+ void
+ run(Workqueue*);
+
+ private:
+ Input_objects* input_objects_;
+ Symbol_table* symtab_;
+ Layout* layout_;
+ Input_group* input_group_;
+ int saw_undefined_;
+ Task_token* this_blocker_;
+ Task_token* next_blocker_;
+};
+
} // end namespace gold
#endif // !defined(GOLD_READSYMS_H)
diff --git a/gold/reloc.cc b/gold/reloc.cc
index bb672e4..ca5e380 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -38,8 +38,8 @@ Read_relocs::run(Workqueue* workqueue)
Read_relocs_data *rd = new Read_relocs_data;
this->object_->read_relocs(rd);
workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_,
- this->object_, rd, this->symtab_lock_,
- this->blocker_));
+ this->layout_, this->object_, rd,
+ this->symtab_lock_, this->blocker_));
}
// Scan_relocs methods.
@@ -52,7 +52,9 @@ Read_relocs::run(Workqueue* workqueue)
Task::Is_runnable_type
Scan_relocs::is_runnable(Workqueue*)
{
- return this->symtab_lock_->is_writable() ? IS_RUNNABLE : IS_LOCKED;
+ if (!this->symtab_lock_->is_writable() || this->object_->is_locked())
+ return IS_LOCKED;
+ return IS_RUNNABLE;
}
// Return the locks we hold: one on the file, one on the symbol table
@@ -85,7 +87,8 @@ Scan_relocs::locks(Workqueue* workqueue)
void
Scan_relocs::run(Workqueue*)
{
- this->object_->scan_relocs(this->options_, this->symtab_, this->rd_);
+ this->object_->scan_relocs(this->options_, this->symtab_, this->layout_,
+ this->rd_);
delete this->rd_;
this->rd_ = NULL;
}
@@ -170,6 +173,14 @@ Sized_object<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
if (!this->is_section_included(shndx))
continue;
+ // We are scanning relocations in order to fill out the GOT and
+ // PLT sections. Relocations for sections which are not
+ // allocated (typically debugging sections) should not add new
+ // GOT and PLT entries. So we skip them.
+ typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size);
+ if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0)
+ continue;
+
if (shdr.get_sh_link() != this->symtab_shnum_)
{
fprintf(stderr,
@@ -239,6 +250,7 @@ template<int size, bool big_endian>
void
Sized_object<size, big_endian>::do_scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Read_relocs_data* rd)
{
Sized_target<size, big_endian>* target = this->sized_target();
@@ -253,7 +265,7 @@ Sized_object<size, big_endian>::do_scan_relocs(const General_options& options,
p != rd->relocs.end();
++p)
{
- target->scan_relocs(options, symtab, this, p->sh_type,
+ target->scan_relocs(options, symtab, layout, this, p->sh_type,
p->contents->data(), p->reloc_count,
this->local_symbol_count_,
local_symbols,
@@ -338,11 +350,15 @@ Sized_object<size, big_endian>::write_sections(const unsigned char* pshdrs,
if (shdr.get_sh_type() == elfcpp::SHT_NOBITS)
continue;
- assert(map_sections[i].offset >= 0
- && map_sections[i].offset < os->data_size());
off_t start = os->offset() + map_sections[i].offset;
off_t sh_size = shdr.get_sh_size();
+ if (sh_size == 0)
+ continue;
+
+ assert(map_sections[i].offset >= 0
+ && map_sections[i].offset + sh_size <= os->data_size());
+
unsigned char* view = of->get_output_view(start, sh_size);
this->read(shdr.get_sh_offset(), sh_size, view);
@@ -477,24 +493,28 @@ template
void
Sized_object<32, false>::do_scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Read_relocs_data* rd);
template
void
Sized_object<32, true>::do_scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Read_relocs_data* rd);
template
void
Sized_object<64, false>::do_scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Read_relocs_data* rd);
template
void
Sized_object<64, true>::do_scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Read_relocs_data* rd);
template
diff --git a/gold/reloc.h b/gold/reloc.h
index a2e9d54..f8f07b8 100644
--- a/gold/reloc.h
+++ b/gold/reloc.h
@@ -13,6 +13,7 @@ namespace gold
class Object;
class Read_relocs_data;
class Stringpool;
+class Layout;
// A class to read the relocations for an object file, and then queue
// up a task to see if they require any GOT/PLT/COPY relocations in
@@ -24,9 +25,9 @@ class Read_relocs : public Task
// SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be
// unblocked when the Scan_relocs task completes.
Read_relocs(const General_options& options, Symbol_table* symtab,
- Object* object, Task_token* symtab_lock,
+ Layout* layout, Object* object, Task_token* symtab_lock,
Task_token* blocker)
- : options_(options), symtab_(symtab), object_(object),
+ : options_(options), symtab_(symtab), layout_(layout), object_(object),
symtab_lock_(symtab_lock), blocker_(blocker)
{ }
@@ -44,6 +45,7 @@ class Read_relocs : public Task
private:
const General_options& options_;
Symbol_table* symtab_;
+ Layout* layout_;
Object* object_;
Task_token* symtab_lock_;
Task_token* blocker_;
@@ -58,10 +60,10 @@ class Scan_relocs : public Task
// SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be
// unblocked when the task completes.
Scan_relocs(const General_options& options, Symbol_table* symtab,
- Object* object, Read_relocs_data* rd, Task_token* symtab_lock,
- Task_token* blocker)
- : options_(options), symtab_(symtab), object_(object), rd_(rd),
- symtab_lock_(symtab_lock), blocker_(blocker)
+ Layout* layout, Object* object, Read_relocs_data* rd,
+ Task_token* symtab_lock, Task_token* blocker)
+ : options_(options), symtab_(symtab), layout_(layout), object_(object),
+ rd_(rd), symtab_lock_(symtab_lock), blocker_(blocker)
{ }
// The standard Task methods.
@@ -80,6 +82,7 @@ class Scan_relocs : public Task
const General_options& options_;
Symbol_table* symtab_;
+ Layout* layout_;
Object* object_;
Read_relocs_data* rd_;
Task_token* symtab_lock_;
diff --git a/gold/resolve.cc b/gold/resolve.cc
index 6b8199c..83e616c 100644
--- a/gold/resolve.cc
+++ b/gold/resolve.cc
@@ -19,12 +19,14 @@ void
Symbol::override_base(const elfcpp::Sym<size, big_endian>& sym,
Object* object)
{
- this->object_ = object;
- this->shnum_ = sym.get_st_shndx(); // FIXME: Handle SHN_XINDEX.
+ assert(this->source_ == FROM_OBJECT);
+ this->u_.from_object.object = object;
+ // FIXME: Handle SHN_XINDEX.
+ this->u_.from_object.shnum = sym.get_st_shndx();
this->type_ = sym.get_st_type();
this->binding_ = sym.get_st_bind();
this->visibility_ = sym.get_st_visibility();
- this->other_ = sym.get_st_nonvis();
+ this->nonvis_ = sym.get_st_nonvis();
}
// Override the fields in Sized_symbol.
@@ -37,7 +39,7 @@ Sized_symbol<size>::override(const elfcpp::Sym<size, big_endian>& sym,
{
this->override_base(sym, object);
this->value_ = sym.get_st_value();
- this->size_ = sym.get_st_size();
+ this->symsize_ = sym.get_st_size();
}
// Resolve a symbol. This is called the second and subsequent times
@@ -315,9 +317,16 @@ Symbol_table::resolve(Sized_symbol<size>* to,
case DYN_DEF * 16 + UNDEF:
case DYN_WEAK_DEF * 16 + UNDEF:
case UNDEF * 16 + UNDEF:
+ // A new undefined reference tells us nothing.
+ return;
+
case WEAK_UNDEF * 16 + UNDEF:
case DYN_UNDEF * 16 + UNDEF:
case DYN_WEAK_UNDEF * 16 + UNDEF:
+ // A strong undef overrides a dynamic or weak undef.
+ to->override(sym, object);
+ return;
+
case COMMON * 16 + UNDEF:
case WEAK_COMMON * 16 + UNDEF:
case DYN_COMMON * 16 + UNDEF:
@@ -391,50 +400,100 @@ Symbol_table::resolve(Sized_symbol<size>* to,
return;
case COMMON * 16 + COMMON:
+ // Set the size to the maximum.
+ if (sym.get_st_size() > to->symsize())
+ to->set_symsize(sym.get_st_size());
+ return;
+
case WEAK_COMMON * 16 + COMMON:
+ // I'm not sure just what a weak common symbol means, but
+ // presumably it can be overridden by a regular common symbol.
+ to->override(sym, object);
+ return;
+
case DYN_COMMON * 16 + COMMON:
case DYN_WEAK_COMMON * 16 + COMMON:
+ {
+ // Use the real common symbol, but adjust the size if necessary.
+ typename Sized_symbol<size>::Size_type symsize = to->symsize();
+ to->override(sym, object);
+ if (to->symsize() < symsize)
+ to->set_symsize(symsize);
+ }
+ return;
case DEF * 16 + WEAK_COMMON:
case WEAK_DEF * 16 + WEAK_COMMON:
case DYN_DEF * 16 + WEAK_COMMON:
case DYN_WEAK_DEF * 16 + WEAK_COMMON:
+ // Whatever a weak common symbol is, it won't override a
+ // definition.
+ return;
+
case UNDEF * 16 + WEAK_COMMON:
case WEAK_UNDEF * 16 + WEAK_COMMON:
case DYN_UNDEF * 16 + WEAK_COMMON:
case DYN_WEAK_UNDEF * 16 + WEAK_COMMON:
+ // A weak common symbol is better than an undefined symbol.
+ to->override(sym, object);
+ return;
+
case COMMON * 16 + WEAK_COMMON:
case WEAK_COMMON * 16 + WEAK_COMMON:
case DYN_COMMON * 16 + WEAK_COMMON:
case DYN_WEAK_COMMON * 16 + WEAK_COMMON:
+ // Ignore a weak common symbol in the presence of a real common
+ // symbol.
+ return;
case DEF * 16 + DYN_COMMON:
case WEAK_DEF * 16 + DYN_COMMON:
case DYN_DEF * 16 + DYN_COMMON:
case DYN_WEAK_DEF * 16 + DYN_COMMON:
+ // Ignore a dynamic common symbol in the presence of a
+ // definition.
+ return;
+
case UNDEF * 16 + DYN_COMMON:
case WEAK_UNDEF * 16 + DYN_COMMON:
case DYN_UNDEF * 16 + DYN_COMMON:
case DYN_WEAK_UNDEF * 16 + DYN_COMMON:
+ // A dynamic common symbol is a definition of sorts.
+ to->override(sym, object);
+ return;
+
case COMMON * 16 + DYN_COMMON:
case WEAK_COMMON * 16 + DYN_COMMON:
case DYN_COMMON * 16 + DYN_COMMON:
case DYN_WEAK_COMMON * 16 + DYN_COMMON:
+ // Set the size to the maximum.
+ if (sym.get_st_size() > to->symsize())
+ to->set_symsize(sym.get_st_size());
+ return;
case DEF * 16 + DYN_WEAK_COMMON:
case WEAK_DEF * 16 + DYN_WEAK_COMMON:
case DYN_DEF * 16 + DYN_WEAK_COMMON:
case DYN_WEAK_DEF * 16 + DYN_WEAK_COMMON:
+ // A common symbol is ignored in the face of a definition.
+ return;
+
case UNDEF * 16 + DYN_WEAK_COMMON:
case WEAK_UNDEF * 16 + DYN_WEAK_COMMON:
case DYN_UNDEF * 16 + DYN_WEAK_COMMON:
case DYN_WEAK_UNDEF * 16 + DYN_WEAK_COMMON:
+ // I guess a weak common symbol is better than a definition.
+ to->override(sym, object);
+ return;
+
case COMMON * 16 + DYN_WEAK_COMMON:
case WEAK_COMMON * 16 + DYN_WEAK_COMMON:
case DYN_COMMON * 16 + DYN_WEAK_COMMON:
case DYN_WEAK_COMMON * 16 + DYN_WEAK_COMMON:
- abort();
- break;
+ // Set the size to the maximum.
+ if (sym.get_st_size() > to->symsize())
+ to->set_symsize(sym.get_st_size());
+ return;
default:
abort();
diff --git a/gold/symtab.cc b/gold/symtab.cc
index 748218d..0f43faa 100644
--- a/gold/symtab.cc
+++ b/gold/symtab.cc
@@ -17,28 +17,85 @@ namespace gold
// Class Symbol.
-// Initialize the fields in the base class Symbol.
+// Initialize fields in Symbol. This initializes everything except u_
+// and source_.
-template<int size, bool big_endian>
void
-Symbol::init_base(const char* name, const char* version, Object* object,
- const elfcpp::Sym<size, big_endian>& sym)
+Symbol::init_fields(const char* name, const char* version,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis)
{
this->name_ = name;
this->version_ = version;
- this->object_ = object;
- this->shnum_ = sym.get_st_shndx(); // FIXME: Handle SHN_XINDEX.
- this->type_ = sym.get_st_type();
- this->binding_ = sym.get_st_bind();
- this->visibility_ = sym.get_st_visibility();
- this->other_ = sym.get_st_nonvis();
- this->is_special_ = false;
+ this->got_offset_ = 0;
+ this->type_ = type;
+ this->binding_ = binding;
+ this->visibility_ = visibility;
+ this->nonvis_ = nonvis;
+ this->is_target_special_ = false;
this->is_def_ = false;
this->is_forwarder_ = false;
+ this->in_dyn_ = false;
+ this->has_got_offset_ = false;
+}
+
+// Initialize the fields in the base class Symbol for SYM in OBJECT.
+
+template<int size, bool big_endian>
+void
+Symbol::init_base(const char* name, const char* version, Object* object,
+ const elfcpp::Sym<size, big_endian>& sym)
+{
+ this->init_fields(name, version, sym.get_st_type(), sym.get_st_bind(),
+ sym.get_st_visibility(), sym.get_st_nonvis());
+ this->u_.from_object.object = object;
+ // FIXME: Handle SHN_XINDEX.
+ this->u_.from_object.shnum = sym.get_st_shndx();
+ this->source_ = FROM_OBJECT;
this->in_dyn_ = object->is_dynamic();
}
-// Initialize the fields in Sized_symbol.
+// Initialize the fields in the base class Symbol for a symbol defined
+// in an Output_data.
+
+void
+Symbol::init_base(const char* name, Output_data* od, elfcpp::STT type,
+ elfcpp::STB binding, elfcpp::STV visibility,
+ unsigned char nonvis, bool offset_is_from_end)
+{
+ this->init_fields(name, NULL, type, binding, visibility, nonvis);
+ this->u_.in_output_data.output_data = od;
+ this->u_.in_output_data.offset_is_from_end = offset_is_from_end;
+ this->source_ = IN_OUTPUT_DATA;
+}
+
+// Initialize the fields in the base class Symbol for a symbol defined
+// in an Output_segment.
+
+void
+Symbol::init_base(const char* name, Output_segment* os, elfcpp::STT type,
+ elfcpp::STB binding, elfcpp::STV visibility,
+ unsigned char nonvis, Segment_offset_base offset_base)
+{
+ this->init_fields(name, NULL, type, binding, visibility, nonvis);
+ this->u_.in_output_segment.output_segment = os;
+ this->u_.in_output_segment.offset_base = offset_base;
+ this->source_ = IN_OUTPUT_SEGMENT;
+}
+
+// Initialize the fields in the base class Symbol for a symbol defined
+// as a constant.
+
+void
+Symbol::init_base(const char* name, elfcpp::STT type,
+ elfcpp::STB binding, elfcpp::STV visibility,
+ unsigned char nonvis)
+{
+ this->init_fields(name, NULL, type, binding, visibility, nonvis);
+ this->source_ = CONSTANT;
+}
+
+// Initialize the fields in Sized_symbol for SYM in OBJECT.
template<int size>
template<bool big_endian>
@@ -48,13 +105,61 @@ Sized_symbol<size>::init(const char* name, const char* version, Object* object,
{
this->init_base(name, version, object, sym);
this->value_ = sym.get_st_value();
- this->size_ = sym.get_st_size();
+ this->symsize_ = sym.get_st_size();
+}
+
+// Initialize the fields in Sized_symbol for a symbol defined in an
+// Output_data.
+
+template<int size>
+void
+Sized_symbol<size>::init(const char* name, Output_data* od,
+ Value_type value, Size_type symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ bool offset_is_from_end)
+{
+ this->init_base(name, od, type, binding, visibility, nonvis,
+ offset_is_from_end);
+ this->value_ = value;
+ this->symsize_ = symsize;
+}
+
+// Initialize the fields in Sized_symbol for a symbol defined in an
+// Output_segment.
+
+template<int size>
+void
+Sized_symbol<size>::init(const char* name, Output_segment* os,
+ Value_type value, Size_type symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ Segment_offset_base offset_base)
+{
+ this->init_base(name, os, type, binding, visibility, nonvis, offset_base);
+ this->value_ = value;
+ this->symsize_ = symsize;
+}
+
+// Initialize the fields in Sized_symbol for a symbol defined as a
+// constant.
+
+template<int size>
+void
+Sized_symbol<size>::init(const char* name, Value_type value, Size_type symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis)
+{
+ this->init_base(name, type, binding, visibility, nonvis);
+ this->value_ = value;
+ this->symsize_ = symsize;
}
// Class Symbol_table.
Symbol_table::Symbol_table()
- : size_(0), offset_(0), table_(), namepool_(), forwarders_()
+ : size_(0), saw_undefined_(0), offset_(0), table_(), namepool_(),
+ forwarders_(), commons_()
{
}
@@ -144,7 +249,7 @@ Symbol_table::resolve(Sized_symbol<size>* to, const Sized_symbol<size>* from
esym.put_st_value(from->value());
esym.put_st_size(from->symsize());
esym.put_st_info(from->binding(), from->type());
- esym.put_st_other(from->visibility(), from->other());
+ esym.put_st_other(from->visibility(), from->nonvis());
esym.put_st_shndx(from->shnum());
Symbol_table::resolve(to, esym.sym(), from->object());
}
@@ -198,12 +303,18 @@ Symbol_table::add_from_object(Sized_object<size, big_endian>* object,
// ins.second: true if new entry was inserted, false if not.
Sized_symbol<size>* ret;
+ bool was_undefined;
+ bool was_common;
if (!ins.second)
{
// We already have an entry for NAME/VERSION.
ret = this->get_sized_symbol SELECT_SIZE_NAME (ins.first->second
SELECT_SIZE(size));
assert(ret != NULL);
+
+ was_undefined = ret->is_undefined();
+ was_common = ret->is_common();
+
Symbol_table::resolve(ret, sym, object);
if (def)
@@ -233,6 +344,10 @@ Symbol_table::add_from_object(Sized_object<size, big_endian>* object,
{
// This is the first time we have seen NAME/VERSION.
assert(ins.first->second == NULL);
+
+ was_undefined = false;
+ was_common = false;
+
if (def && !insdef.second)
{
// We already have an entry for NAME/NULL. Make
@@ -279,6 +394,16 @@ Symbol_table::add_from_object(Sized_object<size, big_endian>* object,
}
}
+ // Record every time we see a new undefined symbol, to speed up
+ // archive groups.
+ if (!was_undefined && ret->is_undefined())
+ ++this->saw_undefined_;
+
+ // Keep track of common symbols, to speed up common symbol
+ // allocation.
+ if (!was_common && ret->is_common())
+ this->commons_.push_back(ret);
+
return ret;
}
@@ -369,6 +494,302 @@ Symbol_table::add_from_object(
}
}
+// Create and return a specially defined symbol. If ONLY_IF_REF is
+// true, then only create the symbol if there is a reference to it.
+
+template<int size, bool big_endian>
+Sized_symbol<size>*
+Symbol_table::define_special_symbol(Target* target, const char* name,
+ bool only_if_ref)
+{
+ assert(this->size_ == size);
+
+ Symbol* oldsym;
+ Sized_symbol<size>* sym;
+
+ if (only_if_ref)
+ {
+ oldsym = this->lookup(name, NULL);
+ if (oldsym == NULL)
+ return NULL;
+ sym = NULL;
+
+ // Canonicalize NAME.
+ name = oldsym->name();
+ }
+ else
+ {
+ // Canonicalize NAME.
+ name = this->namepool_.add(name);
+
+ Symbol* const snull = NULL;
+ const char* const vnull = NULL;
+ std::pair<typename Symbol_table_type::iterator, bool> ins =
+ this->table_.insert(std::make_pair(std::make_pair(name, vnull),
+ snull));
+
+ if (!ins.second)
+ {
+ // We already have a symbol table entry for NAME.
+ oldsym = ins.first->second;
+ assert(oldsym != NULL);
+ sym = NULL;
+ }
+ else
+ {
+ // We haven't seen this symbol before.
+ assert(ins.first->second == NULL);
+
+ if (!target->has_make_symbol())
+ sym = new Sized_symbol<size>();
+ else
+ {
+ assert(target->get_size() == size);
+ assert(target->is_big_endian() ? big_endian : !big_endian);
+ typedef Sized_target<size, big_endian> My_target;
+ My_target* sized_target = static_cast<My_target*>(target);
+ sym = sized_target->make_symbol();
+ if (sym == NULL)
+ return NULL;
+ }
+
+ ins.first->second = sym;
+ oldsym = NULL;
+ }
+ }
+
+ if (oldsym != NULL)
+ {
+ assert(sym == NULL);
+
+ sym = this->get_sized_symbol SELECT_SIZE_NAME (oldsym
+ SELECT_SIZE(size));
+ assert(sym->source() == Symbol::FROM_OBJECT);
+ const int old_shnum = sym->shnum();
+ if (old_shnum != elfcpp::SHN_UNDEF
+ && old_shnum != elfcpp::SHN_COMMON
+ && !sym->object()->is_dynamic())
+ {
+ fprintf(stderr, "%s: linker defined: multiple definition of %s\n",
+ program_name, name);
+ // FIXME: Report old location. Record that we have seen an
+ // error.
+ return NULL;
+ }
+
+ // Our new definition is going to override the old reference.
+ }
+
+ return sym;
+}
+
+// Define a symbol based on an Output_data.
+
+void
+Symbol_table::define_in_output_data(Target* target, const char* name,
+ Output_data* od,
+ uint64_t value, uint64_t symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility,
+ unsigned char nonvis,
+ bool offset_is_from_end,
+ bool only_if_ref)
+{
+ assert(target->get_size() == this->size_);
+ if (this->size_ == 32)
+ this->do_define_in_output_data<32>(target, name, od, value, symsize,
+ type, binding, visibility, nonvis,
+ offset_is_from_end, only_if_ref);
+ else if (this->size_ == 64)
+ this->do_define_in_output_data<64>(target, name, od, value, symsize,
+ type, binding, visibility, nonvis,
+ offset_is_from_end, only_if_ref);
+ else
+ abort();
+}
+
+// Define a symbol in an Output_data, sized version.
+
+template<int size>
+void
+Symbol_table::do_define_in_output_data(
+ Target* target,
+ const char* name,
+ Output_data* od,
+ typename elfcpp::Elf_types<size>::Elf_Addr value,
+ typename elfcpp::Elf_types<size>::Elf_WXword symsize,
+ elfcpp::STT type,
+ elfcpp::STB binding,
+ elfcpp::STV visibility,
+ unsigned char nonvis,
+ bool offset_is_from_end,
+ bool only_if_ref)
+{
+ Sized_symbol<size>* sym;
+
+ if (target->is_big_endian())
+ sym = this->define_special_symbol<size, true>(target, name, only_if_ref);
+ else
+ sym = this->define_special_symbol<size, false>(target, name, only_if_ref);
+
+ if (sym == NULL)
+ return;
+
+ sym->init(name, od, value, symsize, type, binding, visibility, nonvis,
+ offset_is_from_end);
+}
+
+// Define a symbol based on an Output_segment.
+
+void
+Symbol_table::define_in_output_segment(Target* target, const char* name,
+ Output_segment* os,
+ uint64_t value, uint64_t symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility,
+ unsigned char nonvis,
+ Symbol::Segment_offset_base offset_base,
+ bool only_if_ref)
+{
+ assert(target->get_size() == this->size_);
+ if (this->size_ == 32)
+ this->do_define_in_output_segment<32>(target, name, os, value, symsize,
+ type, binding, visibility, nonvis,
+ offset_base, only_if_ref);
+ else if (this->size_ == 64)
+ this->do_define_in_output_segment<64>(target, name, os, value, symsize,
+ type, binding, visibility, nonvis,
+ offset_base, only_if_ref);
+ else
+ abort();
+}
+
+// Define a symbol in an Output_segment, sized version.
+
+template<int size>
+void
+Symbol_table::do_define_in_output_segment(
+ Target* target,
+ const char* name,
+ Output_segment* os,
+ typename elfcpp::Elf_types<size>::Elf_Addr value,
+ typename elfcpp::Elf_types<size>::Elf_WXword symsize,
+ elfcpp::STT type,
+ elfcpp::STB binding,
+ elfcpp::STV visibility,
+ unsigned char nonvis,
+ Symbol::Segment_offset_base offset_base,
+ bool only_if_ref)
+{
+ Sized_symbol<size>* sym;
+
+ if (target->is_big_endian())
+ sym = this->define_special_symbol<size, true>(target, name, only_if_ref);
+ else
+ sym = this->define_special_symbol<size, false>(target, name, only_if_ref);
+
+ if (sym == NULL)
+ return;
+
+ sym->init(name, os, value, symsize, type, binding, visibility, nonvis,
+ offset_base);
+}
+
+// Define a special symbol with a constant value. It is a multiple
+// definition error if this symbol is already defined.
+
+void
+Symbol_table::define_as_constant(Target* target, const char* name,
+ uint64_t value, uint64_t symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ bool only_if_ref)
+{
+ assert(target->get_size() == this->size_);
+ if (this->size_ == 32)
+ this->do_define_as_constant<32>(target, name, value, symsize,
+ type, binding, visibility, nonvis,
+ only_if_ref);
+ else if (this->size_ == 64)
+ this->do_define_as_constant<64>(target, name, value, symsize,
+ type, binding, visibility, nonvis,
+ only_if_ref);
+ else
+ abort();
+}
+
+// Define a symbol as a constant, sized version.
+
+template<int size>
+void
+Symbol_table::do_define_as_constant(
+ Target* target,
+ const char* name,
+ typename elfcpp::Elf_types<size>::Elf_Addr value,
+ typename elfcpp::Elf_types<size>::Elf_WXword symsize,
+ elfcpp::STT type,
+ elfcpp::STB binding,
+ elfcpp::STV visibility,
+ unsigned char nonvis,
+ bool only_if_ref)
+{
+ Sized_symbol<size>* sym;
+
+ if (target->is_big_endian())
+ sym = this->define_special_symbol<size, true>(target, name, only_if_ref);
+ else
+ sym = this->define_special_symbol<size, false>(target, name, only_if_ref);
+
+ if (sym == NULL)
+ return;
+
+ sym->init(name, value, symsize, type, binding, visibility, nonvis);
+}
+
+// Define a set of symbols in output sections.
+
+void
+Symbol_table::define_symbols(const Layout* layout, Target* target, int count,
+ const Define_symbol_in_section* p)
+{
+ for (int i = 0; i < count; ++i, ++p)
+ {
+ Output_section* os = layout->find_output_section(p->output_section);
+ if (os != NULL)
+ this->define_in_output_data(target, p->name, os, p->value, p->size,
+ p->type, p->binding, p->visibility,
+ p->nonvis, p->offset_is_from_end,
+ p->only_if_ref);
+ else
+ this->define_as_constant(target, p->name, 0, p->size, p->type,
+ p->binding, p->visibility, p->nonvis,
+ p->only_if_ref);
+ }
+}
+
+// Define a set of symbols in output segments.
+
+void
+Symbol_table::define_symbols(const Layout* layout, Target* target, int count,
+ const Define_symbol_in_segment* p)
+{
+ for (int i = 0; i < count; ++i, ++p)
+ {
+ Output_segment* os = layout->find_output_segment(p->segment_type,
+ p->segment_flags_set,
+ p->segment_flags_clear);
+ if (os != NULL)
+ this->define_in_output_segment(target, p->name, os, p->value, p->size,
+ p->type, p->binding, p->visibility,
+ p->nonvis, p->offset_base,
+ p->only_if_ref);
+ else
+ this->define_as_constant(target, p->name, 0, p->size, p->type,
+ p->binding, p->visibility, p->nonvis,
+ p->only_if_ref);
+ }
+}
+
// Set the final values for all the symbols. Record the file offset
// OFF. Add their names to POOL. Return the new file offset.
@@ -383,13 +804,15 @@ Symbol_table::finalize(off_t off, Stringpool* pool)
abort();
}
-// Set the final value for all the symbols.
+// Set the final value for all the symbols. This is called after
+// Layout::finalize, so all the output sections have their final
+// address.
template<int size>
off_t
Symbol_table::sized_finalize(off_t off, Stringpool* pool)
{
- off = (off + (size >> 3) - 1) & ~ ((size >> 3) - 1);
+ off = align_address(off, size >> 3);
this->offset_ = off;
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
@@ -402,34 +825,91 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool)
// FIXME: Here we need to decide which symbols should go into
// the output file.
- // FIXME: This is wrong.
- if (sym->shnum() >= elfcpp::SHN_LORESERVE)
- {
- ++p;
- continue;
- }
-
- off_t secoff;
- Output_section* os = sym->object()->output_section(sym->shnum(),
- &secoff);
+ typename Sized_symbol<size>::Value_type value;
- if (os == NULL)
- {
- // We should be able to erase this symbol from the symbol
- // table, but at least with gcc 4.0.2
- // std::unordered_map::erase doesn't appear to return the
- // new iterator.
- // p = this->table_.erase(p);
- ++p;
- }
- else
+ switch (sym->source())
{
- sym->set_value(sym->value() + os->address() + secoff);
- pool->add(sym->name());
- ++p;
- ++count;
- off += sym_size;
+ case Symbol::FROM_OBJECT:
+ {
+ unsigned int shnum = sym->shnum();
+
+ // FIXME: We need some target specific support here.
+ if (shnum >= elfcpp::SHN_LORESERVE
+ && shnum != elfcpp::SHN_ABS)
+ {
+ fprintf(stderr, _("%s: %s: unsupported symbol section 0x%x\n"),
+ program_name, sym->name(), shnum);
+ gold_exit(false);
+ }
+
+ if (shnum == elfcpp::SHN_UNDEF)
+ value = 0;
+ else if (shnum == elfcpp::SHN_ABS)
+ value = sym->value();
+ else
+ {
+ off_t secoff;
+ Output_section* os = sym->object()->output_section(shnum,
+ &secoff);
+
+ if (os == NULL)
+ {
+ // We should be able to erase this symbol from the
+ // symbol table, but at least with gcc 4.0.2
+ // std::unordered_map::erase doesn't appear to return
+ // the new iterator.
+ // p = this->table_.erase(p);
+ ++p;
+ continue;
+ }
+
+ value = sym->value() + os->address() + secoff;
+ }
+ }
+ break;
+
+ case Symbol::IN_OUTPUT_DATA:
+ {
+ Output_data* od = sym->output_data();
+ value = sym->value() + od->address();
+ if (sym->offset_is_from_end())
+ value += od->data_size();
+ }
+ break;
+
+ case Symbol::IN_OUTPUT_SEGMENT:
+ {
+ Output_segment* os = sym->output_segment();
+ value = sym->value() + os->vaddr();
+ switch (sym->offset_base())
+ {
+ case Symbol::SEGMENT_START:
+ break;
+ case Symbol::SEGMENT_END:
+ value += os->memsz();
+ break;
+ case Symbol::SEGMENT_BSS:
+ value += os->filesz();
+ break;
+ default:
+ abort();
+ }
+ }
+ break;
+
+ case Symbol::CONSTANT:
+ value = sym->value();
+ break;
+
+ default:
+ abort();
}
+
+ sym->set_value(value);
+ pool->add(sym->name());
+ ++count;
+ off += sym_size;
+ ++p;
}
this->output_count_ = count;
@@ -481,23 +961,61 @@ Symbol_table::sized_write_globals(const Target*,
// FIXME: This repeats sized_finalize().
- // FIXME: This is wrong.
- if (sym->shnum() >= elfcpp::SHN_LORESERVE)
- continue;
-
- off_t secoff;
- Output_section* os = sym->object()->output_section(sym->shnum(),
- &secoff);
- if (os == NULL)
- continue;
+ unsigned int shndx;
+ switch (sym->source())
+ {
+ case Symbol::FROM_OBJECT:
+ {
+ unsigned int shnum = sym->shnum();
+
+ // FIXME: We need some target specific support here.
+ if (shnum >= elfcpp::SHN_LORESERVE
+ && shnum != elfcpp::SHN_ABS)
+ {
+ fprintf(stderr, _("%s: %s: unsupported symbol section 0x%x\n"),
+ program_name, sym->name(), sym->shnum());
+ gold_exit(false);
+ }
+
+ if (shnum == elfcpp::SHN_UNDEF || shnum == elfcpp::SHN_ABS)
+ shndx = shnum;
+ else
+ {
+ off_t secoff;
+ Output_section* os = sym->object()->output_section(shnum,
+ &secoff);
+ if (os == NULL)
+ continue;
+
+ shndx = os->out_shndx();
+ }
+ }
+ break;
+
+ case Symbol::IN_OUTPUT_DATA:
+ shndx = sym->output_data()->out_shndx();
+ break;
+
+ case Symbol::IN_OUTPUT_SEGMENT:
+ shndx = elfcpp::SHN_ABS;
+ break;
+
+ case Symbol::CONSTANT:
+ shndx = elfcpp::SHN_ABS;
+ break;
+
+ default:
+ abort();
+ }
elfcpp::Sym_write<size, big_endian> osym(ps);
osym.put_st_name(sympool->get_offset(sym->name()));
osym.put_st_value(sym->value());
osym.put_st_size(sym->symsize());
osym.put_st_info(elfcpp::elf_st_info(sym->binding(), sym->type()));
- osym.put_st_other(elfcpp::elf_st_other(sym->visibility(), sym->other()));
- osym.put_st_shndx(os->shndx());
+ osym.put_st_other(elfcpp::elf_st_other(sym->visibility(),
+ sym->nonvis()));
+ osym.put_st_shndx(shndx);
ps += sym_size;
}
diff --git a/gold/symtab.h b/gold/symtab.h
index 2c5fbc9..40a9e57 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -5,6 +5,7 @@
#include <string>
#include <utility>
+#include <vector>
#include <cassert>
#include "elfcpp.h"
@@ -17,6 +18,8 @@ namespace gold
{
class Object;
+class Output_data;
+class Output_segment;
class Output_file;
class Target;
@@ -31,6 +34,37 @@ class Sized_object;
class Symbol
{
public:
+ // Because we want the class to be small, we don't use any virtual
+ // functions. But because symbols can be defined in different
+ // places, we need to classify them. This enum is the different
+ // sources of symbols we support.
+ enum Source
+ {
+ // Symbol defined in an input file--this is the most common case.
+ FROM_OBJECT,
+ // Symbol defined in an Output_data, a special section created by
+ // the target.
+ IN_OUTPUT_DATA,
+ // Symbol defined in an Output_segment, with no associated
+ // section.
+ IN_OUTPUT_SEGMENT,
+ // Symbol value is constant.
+ CONSTANT
+ };
+
+ // When the source is IN_OUTPUT_SEGMENT, we need to describe what
+ // the offset means.
+ enum Segment_offset_base
+ {
+ // From the start of the segment.
+ SEGMENT_START,
+ // From the end of the segment.
+ SEGMENT_END,
+ // From the filesz of the segment--i.e., after the loaded bytes
+ // but before the bytes which are allocated but zeroed.
+ SEGMENT_BSS
+ };
+
// Return the symbol name.
const char*
name() const
@@ -42,10 +76,64 @@ class Symbol
version() const
{ return this->version_; }
+ // Return the symbol source.
+ Source
+ source() const
+ { return this->source_; }
+
// Return the object with which this symbol is associated.
Object*
object() const
- { return this->object_; }
+ {
+ assert(this->source_ == FROM_OBJECT);
+ return this->u_.from_object.object;
+ }
+
+ // Return the index of the section in the input object file.
+ unsigned int
+ shnum() const
+ {
+ assert(this->source_ == FROM_OBJECT);
+ return this->u_.from_object.shnum;
+ }
+
+ // Return the output data section with which this symbol is
+ // associated, if the symbol was specially defined with respect to
+ // an output data section.
+ Output_data*
+ output_data() const
+ {
+ assert(this->source_ == IN_OUTPUT_DATA);
+ return this->u_.in_output_data.output_data;
+ }
+
+ // If this symbol was defined with respect to an output data
+ // section, return whether the value is an offset from end.
+ bool
+ offset_is_from_end() const
+ {
+ assert(this->source_ == IN_OUTPUT_DATA);
+ return this->u_.in_output_data.offset_is_from_end;
+ }
+
+ // Return the output segment with which this symbol is associated,
+ // if the symbol was specially defined with respect to an output
+ // segment.
+ Output_segment*
+ output_segment() const
+ {
+ assert(this->source_ == IN_OUTPUT_SEGMENT);
+ return this->u_.in_output_segment.output_segment;
+ }
+
+ // If this symbol was defined with respect to an output segment,
+ // return the offset base.
+ Segment_offset_base
+ offset_base() const
+ {
+ assert(this->source_ == IN_OUTPUT_SEGMENT);
+ return this->u_.in_output_segment.offset_base;
+ }
// Return the symbol binding.
elfcpp::STB
@@ -64,13 +152,8 @@ class Symbol
// Return the non-visibility part of the st_other field.
unsigned char
- other() const
- { return this->other_; }
-
- // Return the section index.
- unsigned int
- shnum() const
- { return this->shnum_; }
+ nonvis() const
+ { return this->nonvis_; }
// Return whether this symbol is a forwarder. This will never be
// true of a symbol found in the hash table, but may be true of
@@ -94,11 +177,49 @@ class Symbol
set_in_dyn()
{ this->in_dyn_ = true; }
- // Return whether this symbol needs an entry in the dynamic symbol
- // table. FIXME: Needs to be fleshed out.
+ // Return whether this symbol has an entry in the GOT section.
bool
- in_dynsym() const
- { return this->in_dyn_; }
+ has_got_offset() const
+ { return this->has_got_offset_; }
+
+ // Return the offset into the GOT section of this symbol.
+ unsigned int
+ got_offset() const
+ {
+ assert(this->has_got_offset());
+ return this->got_offset_;
+ }
+
+ // Set the GOT offset of this symbol.
+ void
+ set_got_offset(unsigned int got_offset)
+ {
+ this->has_got_offset_ = true;
+ this->got_offset_ = got_offset;
+ }
+
+ // Return whether this symbol is resolved locally. This is always
+ // true when linking statically. It is true for a symbol defined in
+ // this object when using -Bsymbolic. It is true for a symbol
+ // marked local in a version file. FIXME: This needs to be
+ // completed.
+ bool
+ is_resolved_locally() const
+ { return !this->in_dyn_; }
+
+ // Return whether this is an undefined symbol.
+ bool
+ is_undefined() const
+ {
+ return this->source_ == FROM_OBJECT && this->shnum() == elfcpp::SHN_UNDEF;
+ }
+
+ // Return whether this is a common symbol.
+ bool
+ is_common() const
+ {
+ return this->source_ == FROM_OBJECT && this->shnum() == elfcpp::SHN_COMMON;
+ }
protected:
// Instances of this class should always be created at a specific
@@ -106,12 +227,34 @@ class Symbol
Symbol()
{ }
+ // Initialize the general fields.
+ void
+ init_fields(const char* name, const char* version,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis);
+
// Initialize fields from an ELF symbol in OBJECT.
template<int size, bool big_endian>
void
init_base(const char *name, const char* version, Object* object,
const elfcpp::Sym<size, big_endian>&);
+ // Initialize fields for an Output_data.
+ void
+ init_base(const char* name, Output_data*, elfcpp::STT, elfcpp::STB,
+ elfcpp::STV, unsigned char nonvis, bool offset_is_from_end);
+
+ // Initialize fields for an Output_segment.
+ void
+ init_base(const char* name, Output_segment* os, elfcpp::STT type,
+ elfcpp::STB binding, elfcpp::STV visibility,
+ unsigned char nonvis, Segment_offset_base offset_base);
+
+ // Initialize fields for a constant.
+ void
+ init_base(const char* name, elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis);
+
// Override existing symbol.
template<int size, bool big_endian>
void
@@ -126,10 +269,45 @@ class Symbol
// Symbol version (expected to point into a Stringpool). This may
// be NULL.
const char* version_;
- // Object in which symbol is defined, or in which it was first seen.
- Object* object_;
- // Section number in object_ in which symbol is defined.
- unsigned int shnum_;
+
+ union
+ {
+ // This struct is used if SOURCE_ == FROM_OBJECT.
+ struct
+ {
+ // Object in which symbol is defined, or in which it was first
+ // seen.
+ Object* object;
+ // Section number in object_ in which symbol is defined.
+ unsigned int shnum;
+ } from_object;
+
+ // This struct is used if SOURCE_ == IN_OUTPUT_DATA.
+ struct
+ {
+ // Output_data in which symbol is defined. Before
+ // Layout::finalize the symbol's value is an offset within the
+ // Output_data.
+ Output_data* output_data;
+ // True if the offset is from the end, false if the offset is
+ // from the beginning.
+ bool offset_is_from_end;
+ } in_output_data;
+
+ // This struct is used if SOURCE_ == IN_OUTPUT_SEGMENT.
+ struct
+ {
+ // Output_segment in which the symbol is defined. Before
+ // Layout::finalize the symbol's value is an offset.
+ Output_segment* output_segment;
+ // The base to use for the offset before Layout::finalize.
+ Segment_offset_base offset_base;
+ } in_output_segment;
+ } u_;
+
+ // If this symbol has an entry in the GOT section (has_got_offset_
+ // is true), this is the offset.
+ unsigned int got_offset_;
// Symbol type.
elfcpp::STT type_ : 4;
// Symbol binding.
@@ -137,10 +315,12 @@ class Symbol
// Symbol visibility.
elfcpp::STV visibility_ : 2;
// Rest of symbol st_other field.
- unsigned int other_ : 6;
+ unsigned int nonvis_ : 6;
+ // The type of symbol.
+ Source source_ : 2;
// True if this symbol always requires special target-specific
// handling.
- bool is_special_ : 1;
+ bool is_target_special_ : 1;
// True if this is the default version of the symbol.
bool is_def_ : 1;
// True if this symbol really forwards to another symbol. This is
@@ -153,6 +333,8 @@ class Symbol
bool is_forwarder_ : 1;
// True if we've seen this symbol in a dynamic object.
bool in_dyn_ : 1;
+ // True if the symbol has an entry in the GOT section.
+ bool has_got_offset_ : 1;
};
// The parts of a symbol which are size specific. Using a template
@@ -174,6 +356,23 @@ class Sized_symbol : public Symbol
init(const char *name, const char* version, Object* object,
const elfcpp::Sym<size, big_endian>&);
+ // Initialize fields for an Output_data.
+ void
+ init(const char* name, Output_data*, Value_type value, Size_type symsize,
+ elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis,
+ bool offset_is_from_end);
+
+ // Initialize fields for an Output_segment.
+ void
+ init(const char* name, Output_segment*, Value_type value, Size_type symsize,
+ elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis,
+ Segment_offset_base offset_base);
+
+ // Initialize fields for a constant.
+ void
+ init(const char* name, Value_type value, Size_type symsize,
+ elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis);
+
// Override existing symbol.
template<bool big_endian>
void
@@ -188,7 +387,12 @@ class Sized_symbol : public Symbol
// is a template parameter).
Size_type
symsize() const
- { return this->size_; }
+ { return this->symsize_; }
+
+ // Set the symbol size. This is used when resolving common symbols.
+ void
+ set_symsize(Size_type symsize)
+ { this->symsize_ = symsize; }
// Set the symbol value. This is called when we store the final
// values of the symbols into the symbol table.
@@ -200,10 +404,84 @@ class Sized_symbol : public Symbol
Sized_symbol(const Sized_symbol&);
Sized_symbol& operator=(const Sized_symbol&);
- // Symbol value.
+ // Symbol value. Before Layout::finalize this is the offset in the
+ // input section. This is set to the final value during
+ // Layout::finalize.
Value_type value_;
// Symbol size.
- Size_type size_;
+ Size_type symsize_;
+};
+
+// A struct describing a symbol defined by the linker, where the value
+// of the symbol is defined based on an output section. This is used
+// for symbols defined by the linker, like "_init_array_start".
+
+struct Define_symbol_in_section
+{
+ // The symbol name.
+ const char* name;
+ // The name of the output section with which this symbol should be
+ // associated. If there is no output section with that name, the
+ // symbol will be defined as zero.
+ const char* output_section;
+ // The offset of the symbol within the output section. This is an
+ // offset from the start of the output section, unless start_at_end
+ // is true, in which case this is an offset from the end of the
+ // output section.
+ uint64_t value;
+ // The size of the symbol.
+ uint64_t size;
+ // The symbol type.
+ elfcpp::STT type;
+ // The symbol binding.
+ elfcpp::STB binding;
+ // The symbol visibility.
+ elfcpp::STV visibility;
+ // The rest of the st_other field.
+ unsigned char nonvis;
+ // If true, the value field is an offset from the end of the output
+ // section.
+ bool offset_is_from_end;
+ // If true, this symbol is defined only if we see a reference to it.
+ bool only_if_ref;
+};
+
+// A struct describing a symbol defined by the linker, where the value
+// of the symbol is defined based on a segment. This is used for
+// symbols defined by the linker, like "_end". We describe the
+// segment with which the symbol should be associated by its
+// characteristics. If no segment meets these characteristics, the
+// symbol will be defined as zero. If there is more than one segment
+// which meets these characteristics, we will use the first one.
+
+struct Define_symbol_in_segment
+{
+ // The symbol name.
+ const char* name;
+ // The segment type where the symbol should be defined, typically
+ // PT_LOAD.
+ elfcpp::PT segment_type;
+ // Bitmask of segment flags which must be set.
+ elfcpp::PF segment_flags_set;
+ // Bitmask of segment flags which must be clear.
+ elfcpp::PF segment_flags_clear;
+ // The offset of the symbol within the segment. The offset is
+ // calculated from the position set by offset_base.
+ uint64_t value;
+ // The size of the symbol.
+ uint64_t size;
+ // The symbol type.
+ elfcpp::STT type;
+ // The symbol binding.
+ elfcpp::STB binding;
+ // The symbol visibility.
+ elfcpp::STV visibility;
+ // The rest of the st_other field.
+ unsigned char nonvis;
+ // The base from which we compute the offset.
+ Symbol::Segment_offset_base offset_base;
+ // If true, this symbol is defined only if we see a reference to it.
+ bool only_if_ref;
};
// The main linker symbol table.
@@ -226,6 +504,47 @@ class Symbol_table
size_t count, const char* sym_names, size_t sym_name_size,
Symbol** sympointers);
+ // Define a special symbol.
+ template<int size, bool big_endian>
+ Sized_symbol<size>*
+ define_special_symbol(Target* target, const char* name, bool only_if_ref);
+
+ // Define a special symbol based on an Output_data. It is a
+ // multiple definition error if this symbol is already defined.
+ void
+ define_in_output_data(Target*, const char* name, Output_data*,
+ uint64_t value, uint64_t symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ bool offset_is_from_end, bool only_if_ref);
+
+ // Define a special symbol based on an Output_segment. It is a
+ // multiple definition error if this symbol is already defined.
+ void
+ define_in_output_segment(Target*, const char* name, Output_segment*,
+ uint64_t value, uint64_t symsize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ Symbol::Segment_offset_base, bool only_if_ref);
+
+ // Define a special symbol with a constant value. It is a multiple
+ // definition error if this symbol is already defined.
+ void
+ define_as_constant(Target*, const char* name, uint64_t value,
+ uint64_t symsize, elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ bool only_if_ref);
+
+ // Define a set of symbols in output sections.
+ void
+ define_symbols(const Layout*, Target*, int count,
+ const Define_symbol_in_section*);
+
+ // Define a set of symbols in output segments.
+ void
+ define_symbols(const Layout*, Target*, int count,
+ const Define_symbol_in_segment*);
+
// Look up a symbol.
Symbol*
lookup(const char*, const char* version = NULL) const;
@@ -248,6 +567,15 @@ class Symbol_table
const Sized_symbol<size>*
get_sized_symbol(const Symbol* ACCEPT_SIZE) const;
+ // Return the count of undefined symbols seen.
+ int
+ saw_undefined() const
+ { return this->saw_undefined_; }
+
+ // Allocate the common symbols
+ void
+ allocate_commons(const General_options&, Layout*);
+
// Finalize the symbol table after we have set the final addresses
// of all the input sections. This sets the final symbol values and
// adds the names to *POOL. It records the file offset OFF, and
@@ -291,6 +619,43 @@ class Symbol_table
resolve(Sized_symbol<size>* to, const Sized_symbol<size>* from
ACCEPT_SIZE_ENDIAN);
+ // Define a symbol in an Output_data, sized version.
+ template<int size>
+ void
+ do_define_in_output_data(Target*, const char* name, Output_data*,
+ typename elfcpp::Elf_types<size>::Elf_Addr value,
+ typename elfcpp::Elf_types<size>::Elf_WXword ssize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ bool offset_is_from_end, bool only_if_ref);
+
+ // Define a symbol in an Output_segment, sized version.
+ template<int size>
+ void
+ do_define_in_output_segment(
+ Target*, const char* name, Output_segment* os,
+ typename elfcpp::Elf_types<size>::Elf_Addr value,
+ typename elfcpp::Elf_types<size>::Elf_WXword ssize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ Symbol::Segment_offset_base offset_base, bool only_if_ref);
+
+ // Define a symbol as a constant, sized version.
+ template<int size>
+ void
+ do_define_as_constant(
+ Target*, const char* name,
+ typename elfcpp::Elf_types<size>::Elf_Addr value,
+ typename elfcpp::Elf_types<size>::Elf_WXword ssize,
+ elfcpp::STT type, elfcpp::STB binding,
+ elfcpp::STV visibility, unsigned char nonvis,
+ bool only_if_ref);
+
+ // Allocate the common symbols, sized version.
+ template<int size>
+ void
+ do_allocate_commons(const General_options&, Layout*);
+
// Finalize symbols specialized for size.
template<int size>
off_t
@@ -320,9 +685,17 @@ class Symbol_table
typedef Unordered_map<Symbol_table_key, Symbol*, Symbol_table_hash,
Symbol_table_eq> Symbol_table_type;
+ // The type of the list of common symbols.
+
+ typedef std::vector<Symbol*> Commons_type;
+
// The size of the symbols in the symbol table (32 or 64).
int size_;
+ // We increment this every time we see a new undefined symbol, for
+ // use in archive groups.
+ int saw_undefined_;
+
// The file offset within the output symtab section where we should
// write the table.
off_t offset_;
@@ -339,6 +712,13 @@ class Symbol_table
// Forwarding symbols.
Unordered_map<Symbol*, Symbol*> forwarders_;
+
+ // We don't expect there to be very many common symbols, so we keep
+ // a list of them. When we find a common symbol we add it to this
+ // list. It is possible that by the time we process the list the
+ // symbol is no longer a common symbol. It may also have become a
+ // forwarder.
+ Commons_type commons_;
};
// We inline get_sized_symbol for efficiency.
diff --git a/gold/target-reloc.h b/gold/target-reloc.h
index 2d1fe30..ebaa827 100644
--- a/gold/target-reloc.h
+++ b/gold/target-reloc.h
@@ -36,11 +36,14 @@ struct Reloc_types<elfcpp::SHT_RELA, size, big_endian>
// way to avoidmaking a function call for each relocation, and to
// avoid repeating the generic code for each target.
-template<int size, bool big_endian, int sh_type, typename Scan>
+template<int size, bool big_endian, typename Target_type, int sh_type,
+ typename Scan>
inline void
scan_relocs(
const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
+ Target_type* target,
Sized_object<size, big_endian>* object,
const unsigned char* prelocs,
size_t reloc_count,
@@ -68,6 +71,7 @@ scan_relocs(
+ r_sym * sym_size);
const unsigned int shndx = lsym.get_st_shndx();
if (shndx < elfcpp::SHN_LORESERVE
+ && shndx != elfcpp::SHN_UNDEF
&& !object->is_section_included(lsym.get_st_shndx()))
{
// RELOC is a relocation against a local symbol in a
@@ -88,7 +92,8 @@ scan_relocs(
continue;
}
- scan.local(options, object, reloc, r_type, lsym);
+ scan.local(options, symtab, layout, target, object, reloc, r_type,
+ lsym);
}
else
{
@@ -97,7 +102,8 @@ scan_relocs(
if (gsym->is_forwarder())
gsym = symtab->resolve_forwards(gsym);
- scan.global(options, object, reloc, r_type, gsym);
+ scan.global(options, symtab, layout, target, object, reloc, r_type,
+ gsym);
}
}
}
@@ -117,10 +123,12 @@ scan_relocs(
// of relocs. VIEW is the section data, VIEW_ADDRESS is its memory
// address, and VIEW_SIZE is the size.
-template<int size, bool big_endian, int sh_type, typename Relocate>
+template<int size, bool big_endian, typename Target_type, int sh_type,
+ typename Relocate>
inline void
relocate_section(
const Relocate_info<size, big_endian>* relinfo,
+ Target_type* target,
const unsigned char* prelocs,
size_t reloc_count,
unsigned char* view,
@@ -140,13 +148,6 @@ relocate_section(
Reltype reloc(prelocs);
off_t offset = reloc.get_r_offset();
- if (offset < 0 || offset >= view_size)
- {
- fprintf(stderr, _("%s: %s: reloc has bad offset %zu\n"),
- program_name, relinfo->location(i, offset).c_str(),
- static_cast<size_t>(offset));
- gold_exit(false);
- }
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
@@ -169,19 +170,29 @@ relocate_section(
sym = static_cast<Sized_symbol<size>*>(gsym);
value = sym->value();
+ }
- if (sym->shnum() == elfcpp::SHN_UNDEF
- && sym->binding() != elfcpp::STB_WEAK)
- {
- fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"),
- program_name, relinfo->location(i, offset).c_str(),
- sym->name());
- // gold_exit(false);
- }
+ if (!relocate.relocate(relinfo, target, i, reloc, r_type, sym, value,
+ view + offset, view_address + offset, view_size))
+ continue;
+
+ if (offset < 0 || offset >= view_size)
+ {
+ fprintf(stderr, _("%s: %s: reloc has bad offset %zu\n"),
+ program_name, relinfo->location(i, offset).c_str(),
+ static_cast<size_t>(offset));
+ gold_exit(false);
}
- relocate.relocate(relinfo, i, reloc, r_type, sym, value, view + offset,
- view_address + offset, view_size);
+ if (sym != NULL
+ && sym->is_undefined()
+ && sym->binding() != elfcpp::STB_WEAK)
+ {
+ fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"),
+ program_name, relinfo->location(i, offset).c_str(),
+ sym->name());
+ // gold_exit(false);
+ }
}
}
diff --git a/gold/target-select.cc b/gold/target-select.cc
index 28b7527..d15d0e3 100644
--- a/gold/target-select.cc
+++ b/gold/target-select.cc
@@ -34,7 +34,7 @@ extern Target*
select_target(int machine, int size, bool big_endian, int osabi,
int abiversion)
{
- for (const Target_selector* p = target_selectors; p != NULL; p = p->next())
+ for (Target_selector* p = target_selectors; p != NULL; p = p->next())
{
int pmach = p->machine();
if ((pmach == machine || pmach == elfcpp::EM_NONE)
diff --git a/gold/target-select.h b/gold/target-select.h
index 0762d58..8ccd75f 100644
--- a/gold/target-select.h
+++ b/gold/target-select.h
@@ -29,7 +29,7 @@ class Target_selector
// If we can handle this target, return a pointer to a target
// structure. The size and endianness are known.
- virtual Target* recognize(int machine, int osabi, int abiversion) const = 0;
+ virtual Target* recognize(int machine, int osabi, int abiversion) = 0;
// Return the next Target_selector in the linked list.
Target_selector*
diff --git a/gold/target.h b/gold/target.h
index 75f149e..b72998d 100644
--- a/gold/target.h
+++ b/gold/target.h
@@ -149,6 +149,7 @@ class Sized_target : public Target
virtual void
scan_relocs(const General_options& options,
Symbol_table* symtab,
+ Layout* layout,
Sized_object<size, big_endian>* object,
unsigned int sh_type,
const unsigned char* prelocs,
diff --git a/gold/workqueue.h b/gold/workqueue.h
index 5cce2d5..ed7a5b0 100644
--- a/gold/workqueue.h
+++ b/gold/workqueue.h
@@ -17,12 +17,12 @@
#define GOLD_WORKQUEUE_H
#include "gold-threads.h"
-#include "options.h"
#include "fileread.h"
namespace gold
{
+class General_options;
class Task;
class Workqueue;
@@ -286,6 +286,10 @@ class Task
// Run the task.
virtual void
run(Workqueue*) = 0;
+
+ private:
+ Task(const Task&);
+ Task& operator=(const Task&);
};
// A simple task which waits for a blocker and then runs a function.