[PATCH] gold: Native Client target support

Roland McGrath mcgrathr@google.com
Tue Apr 24 17:24:00 GMT 2012


This makes gold support the Native Client targets that BFD ld already
supports (i386, x86_64 of both ELF classes, arm of both bytesexes).
The essential differences from the vanilla targets for each machine
are in PLT generation and in address layout and file layout.  In all
those respects it matches what BFD ld produces for the nacl targets.

This is a diff -b patch for readability in some places. 
The actual commit will have all the indentation correct.

Ok for trunk?


Thanks,
Roland


2012-04-24  Roland McGrath  <mcgrathr@google.com>

	* configure.ac (ENABLE_GOLD): Consider *-*-nacl* targets ELF.
	* configure: Regenerate.

gold/
2012-04-24  Roland McGrath  <mcgrathr@google.com>

	* nacl.cc: New file.
	* nacl.h: New file.
	* Makefile.am (CCFILES, HFILES): Add them.
	* Makefile.in: Regenerate.
	* i386.cc (Output_data_plt_i386_nacl): New class.
	(Output_data_plt_i386_nacl_exec): New class.
	(Output_data_plt_i386_nacl_dyn): New class.
	(Target_i386_nacl): New class.
	(Target_selector_i386_nacl): New class.
	(target_selector_i386): Use it instead of Target_selector_i386.
	* x86_64.cc (Output_data_plt_x86_64_nacl): New class.
	(Target_x86_64_nacl): New class.
	(Target_selector_x86_64_nacl): New class.
	(target_selector_x86_64, target_selector_x32): Use it instead of
	Target_selector_x86_64.
	* arm.cc (Output_data_plt_arm_nacl): New class.
	(Target_arm_nacl): New class.
	(Target_selector_arm_nacl): New class.
	(target_selector_arm, target_selector_armbe): Use it instead of
	Target_selector_arm.

	* target-select.cc (select_target): Take new Input_file*
	argument, pass it on to recognize method of selector.
	* object.cc (make_elf_sized_object): Update caller.
	* parameters.cc (parameters_force_valid_target): Likewise.
	* incremental.cc (make_sized_incremental_binary): Likewise.
	* target-select.h: Update decl.
	(Target_selector::recognize): Take new Input_file* argument,
	pass it on to do_recognize.
	(Target_selector::do_recognize): Take new Input_file* argument.
	* freebsd.h (Target_selector_freebsd::do_recognize): Likewise.
	* powerpc.cc (Target_selector_powerpc::do_recognize): Likewise.
	* sparc.cc (Target_selector_sparc::do_recognize): Likewise.
	* testsuite/testfile.cc (Target_selector::do_recognize): Likewise.

	* target.h (Target::Target_info): New members isolate_execinstr
	and rosegment_gap.
	(Target::isolate_execinstr, Target::rosegment_gap): New methods.
	* arm.cc (Target_arm::arm_info): Update initializer.
	* i386.cc (Target_i386::i386_info): Likewise.
	* powerpc.cc (Target_powerpc::powerpc_info): Likewise.
	* sparc.cc (Target_sparc::sparc_info): Likewise.
	* x86_64.cc (Target_x86_64::x86_64_info): Likewise.
	* testsuite/testfile.cc (Target_test::test_target_info): Likewise.
	* layout.cc (Layout::attach_allocated_section_to_segment):
	Take new const Target* argument.  If target->isolate_execinstr(), act
	like --rosegment.
	(Layout::find_first_load_seg): Take new const Target* argument;
	If target->isolate_execinstr(), reject PF_X segments.
	(Layout::relaxation_loop_body): Update caller.
	(Layout::set_segment_offsets): If target->isolate_execinstr(),
	reset file offset to zero when we hit LOAD_SEG, and then a second
	loop over the segments before LOAD_SEG to reassign offsets after
	addresses have been determined.  Handle target->rosegment_gap().
	(Layout::attach_section_to_segment): Take new const Target* argument;
	pass it to attach_allocated_section_to_segment.
	(Layout::make_output_section): Update caller.
	(Layout::attach_sections_to_segments): Take new const Target* argument;
	pass it to attach_section_to_segment.
	* gold.cc (queue_middle_tasks): Update caller.
	* layout.h (Layout): Update method decls with new arguments.

	* arm.cc (Target_arm::Target_arm): Take optional argument for the
	Target_info pointer to use.
	(Target_arm::make_data_plt): New virtual method.
	(Target_arm::make_plt_entry): Use it.
	(Output_data_plt_arm::Output_data_plt_arm): Take additional argument
	for the section alignment.
	(Output_data_plt_arm::first_plt_entry_offset): Make abstract virtual.
	(Output_data_plt_arm::get_plt_entry_size): Likewise.
	(Output_data_plt_arm::fill_plt_entry): New abstract virtual method.
	(Output_data_plt_arm::fill_first_plt_entry): Likewise.
	(Output_data_plt_arm::set_final_data_size): Use get_plt_entry_size
	method instead of sizeof(plt_entry).
	(Output_data_plt_arm::add_entry): Likewise.
	Use first_plt_entry_offset method instead of sizeof(first_plt_entry).
	(Target_arm::first_plt_entry_offset): Call method on this->plt_ rather
	than static method.
	(Target_arm::plt_entry_size): Likewise.
	(Output_data_plt_arm::first_plt_entry, Output_data_plt_arm::plt_entry):
	Move to ...
	(Output_data_plt_arm_standard): ... here, new class.
	(Output_data_plt_arm::do_write): Move guts of PLT filling to...
	(Output_data_plt_arm_standard::fill_first_plt_entry): ... here ...
	(Output_data_plt_arm_standard::fill_plt_entry): ... and here.

	* x86_64.cc (Output_data_plt_x86_64::Output_data_plt_x86_64):
	Take additional argument for the PLT entry size.
	(Output_data_plt_x86_64::get_tlsdesc_plt_offset):
	Use get_plt_entry_size method rather than plt_entry_size variable.
	(Output_data_plt_x86_64::reserve_slot): Likewise.
	(Output_data_plt_x86_64::do_adjust_output_section): Likewise.
	(Output_data_plt_x86_64::add_entry): Likewise.
	(Output_data_plt_x86_64::add_local_ifunc_entry): Likewise.
	(Output_data_plt_x86_64::address_for_global): Likewise.
	(Output_data_plt_x86_64::address_for_local): Likewise.
	(Output_data_plt_x86_64::set_final_data_size): Likewise.
	(Output_data_plt_x86_64::first_plt_entry_offset): Likewise.
	Make method non-static.
	(Output_data_plt_x86_64::get_plt_entry_size): Make abstract virtual.
	(Output_data_plt_x86_64::add_eh_frame): New abstract virtual method.
	(Output_data_plt_x86_64::fill_first_plt_entry): Likewise.
	(Output_data_plt_x86_64::fill_plt_entry): Likewise.
	(Output_data_plt_x86_64::fill_tlsdesc_entry): Likewise.
	(Output_data_plt_x86_64::plt_entry_size)
	(Output_data_plt_x86_64::first_plt_entry)
	(Output_data_plt_x86_64::plt_entry)
	(Output_data_plt_x86_64::tlsdesc_plt_entry)
	(Output_data_plt_x86_64::plt_eh_frame_fde_size)
	(Output_data_plt_x86_64::plt_eh_frame_fde): Move to ...
	(Output_data_plt_x86_64_standard): ... here, new class.
	(Target_x86_64::Target_x86_64): Take optional argument for the
	Target_info pointer to use.
	(Target_x86_64::make_data_plt): New virtual method.
	(Target_x86_64::init_got_plt_for_update): Use it.
	Call this->plt_->add_eh_frame method here.
	(Output_data_plt_x86_64::init):	Don't do add_eh_frame_for_plt here.
	(Target_x86_64::first_plt_entry_offset): Call method on this->plt_
	rather than static method.
	(Target_x86_64::plt_entry_size): Likewise.
	(Output_data_plt_x86_64::do_write): Use get_plt_entry_size method
	rather than plt_entry_size variable.  Move guts of PLT filling to...
	(Output_data_plt_x86_64_standard::fill_first_plt_entry): ... here ...
	(Output_data_plt_x86_64_standard::fill_plt_entry): ... and here ...
	(Output_data_plt_x86_64_standard::fill_tlsdesc_entry): ... and here.

	* i386.cc (Output_data_plt_i386::Output_data_plt_i386): Take
	additional argument for the section alignment.
	Don't do add_eh_frame_for_plt here.
	(Output_data_plt_i386::first_plt_entry_offset): Make the method
	non-static.  Use get_plt_entry_size method rather than plt_entry_size
	variable.
	(Output_data_plt_i386::get_plt_entry_size): Make abstract virtual.
	(Output_data_plt_i386::add_eh_frame): New abstract virtual method.
	(Output_data_plt_i386::fill_first_plt_entry): Likewise.
	(Output_data_plt_i386::fill_plt_entry): Likewise.
	(Output_data_plt_i386::set_final_data_size): Use get_plt_entry_size
	method instead of plt_entry_size.
	(Output_data_plt_i386::plt_entry_size)
	(Output_data_plt_i386::plt_eh_frame_fde_size)
	(Output_data_plt_i386::plt_eh_frame_fde): Move to ...
	(Output_data_plt_i386_standard): ... here, new class.
	(Output_data_plt_i386::exec_first_plt_entry)
	(Output_data_plt_i386::exec_plt_entry): Move to ...
	(Output_data_plt_i386_exec): ... here, new class.
	(Output_data_plt_i386::dyn_first_plt_entry): Move to ...
	(Output_data_plt_i386::dyn_plt_entry)
	(Output_data_plt_i386_dyn): ... here, new class.
	(Target_i386::Target_i386): Take optional argument for the Target_info
	pointer to use.
	(Target_i386::make_data_plt): New virtual method.
	(Target_i386::make_plt_section): Use it.
	Call this->plt_->add_eh_frame method here.
	(Output_data_plt_i386::add_entry): Use get_plt_entry_size method
	rather than plt_entry_size variable.
	(Output_data_plt_i386::add_local_ifunc_entry): Likewise.
	(Output_data_plt_i386::address_for_local): Likewise.
	(Output_data_plt_i386::do_write): Likewise.
	Move guts of PLT filling to...
	(Output_data_plt_i386_exec::fill_first_plt_entry): ... here ...
	(Output_data_plt_i386_exec::fill_plt_entry): ... and here ...
	(Output_data_plt_i386_dyn::fill_first_plt_entry): ... and here ...
	(Output_data_plt_i386_dyn::fill_plt_entry): ... and here.

diff --git a/configure.ac b/configure.ac
index c279587..396c87b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -313,7 +313,7 @@ case "${ENABLE_GOLD}" in
       *-*-elf* | *-*-sysv4* | *-*-unixware* | *-*-eabi* | hppa*64*-*-hpux* \
       | *-*-linux* | frv-*-uclinux* | *-*-irix5* | *-*-irix6* \
       | *-*-netbsd* | *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* \
-      | *-*-solaris2* | *-*-nto*)
+      | *-*-solaris2* | *-*-nto* | *-*-nacl*)
         case "${target}" in
           *-*-linux*aout* | *-*-linux*oldld*)
             ;;
diff --git a/gold/Makefile.am b/gold/Makefile.am
index 7d4b725..72ffdf5 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -64,6 +64,7 @@ CCFILES = \
 	layout.cc \
 	mapfile.cc \
 	merge.cc \
+	nacl.cc \
 	object.cc \
 	options.cc \
 	output.cc \
@@ -111,6 +112,7 @@ HFILES = \
 	layout.h \
 	mapfile.h \
 	merge.h \
+	nacl.h \
 	object.h \
 	options.h \
 	output.h \
diff --git a/gold/arm.cc b/gold/arm.cc
index dc6e64a..f899f54 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -51,6 +51,7 @@
 #include "gc.h"
 #include "attributes.h"
 #include "arm-reloc-property.h"
+#include "nacl.h"
 
 namespace
 {
@@ -61,6 +62,9 @@ template<bool big_endian>
 class Output_data_plt_arm;
 
 template<bool big_endian>
+class Output_data_plt_arm_standard;
+
+template<bool big_endian>
 class Stub_table;
 
 template<bool big_endian>
@@ -2113,8 +2117,8 @@ class Target_arm : public Sized_target<32, big_endian>
   // When were are relocating a stub, we pass this as the relocation number.
   static const size_t fake_relnum_for_stubs = static_cast<size_t>(-1);
 
-  Target_arm()
-    : Sized_target<32, big_endian>(&arm_info),
+  Target_arm(const Target::Target_info* info = &arm_info)
+    : Sized_target<32, big_endian>(info),
       got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
       copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL), 
       got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
@@ -2518,6 +2522,12 @@ class Target_arm : public Sized_target<32, big_endian>
   virtual void
   do_define_standard_symbols(Symbol_table*, Layout*);
 
+  virtual Output_data_plt_arm<big_endian>*
+  make_data_plt(Layout* layout, Output_data_space* got_plt)
+  {
+    return new Output_data_plt_arm_standard<big_endian>(layout, got_plt);
+  }
+
  private:
   // The class which scans relocations.
   class Scan
@@ -2915,6 +2925,8 @@ const Target::Target_info Target_arm<big_endian>::arm_info =
   0x8000,		// default_text_segment_address
   0x1000,		// abi_pagesize (overridable by -z max-page-size)
   0x1000,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -7189,6 +7201,9 @@ Arm_output_data_got<big_endian>::do_write(Output_file* of)
 }
 
 // A class to handle the PLT data.
+// This is an abstract base class that handles most of the linker details
+// but does not know the actual contents of PLT entries.  The derived
+// classes below fill in those details.
 
 template<bool big_endian>
 class Output_data_plt_arm : public Output_section_data
@@ -7197,7 +7212,7 @@ class Output_data_plt_arm : public Output_section_data
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
     Reloc_section;
 
-  Output_data_plt_arm(Layout*, Output_data_space*);
+  Output_data_plt_arm(Layout*, uint64_t addralign, Output_data_space*);
 
   // Add an entry to the PLT.
   void
@@ -7214,14 +7229,12 @@ class Output_data_plt_arm : public Output_section_data
   { return this->count_; }
 
   // Return the offset of the first non-reserved PLT entry.
-  static unsigned int
-  first_plt_entry_offset()
-  { return sizeof(first_plt_entry); }
+  virtual unsigned int
+  first_plt_entry_offset() const = 0;
 
   // Return the size of a PLT entry.
-  static unsigned int
-  get_plt_entry_size()
-  { return sizeof(plt_entry); }
+  virtual unsigned int
+  get_plt_entry_size() const = 0;
 
  protected:
   void
@@ -7232,19 +7245,27 @@ class Output_data_plt_arm : public Output_section_data
   do_print_to_mapfile(Mapfile* mapfile) const
   { mapfile->print_output_data(this, _("** PLT")); }
 
- private:
-  // Template for the first PLT entry.
-  static const uint32_t first_plt_entry[5];
+  // Fill in the first PLT entry.
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       Arm_address got_address,
+		       Arm_address plt_address) = 0;
 
-  // Template for subsequent PLT entries. 
-  static const uint32_t plt_entry[3];
+  // Fill in a normal PLT entry.
+  virtual void
+  fill_plt_entry(unsigned char* pov,
+		 Arm_address got_address,
+		 Arm_address plt_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset) = 0;
 
+ private:
   // Set the final size.
   void
   set_final_data_size()
   {
-    this->set_data_size(sizeof(first_plt_entry)
-			+ this->count_ * sizeof(plt_entry));
+    this->set_data_size(this->first_plt_entry_offset()
+			+ this->count_ * this->get_plt_entry_size());
   }
 
   // Write out the PLT data.
@@ -7259,14 +7280,76 @@ class Output_data_plt_arm : public Output_section_data
   unsigned int count_;
 };
 
+template<bool big_endian>
+class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian>
+{
+ public:
+  Output_data_plt_arm_standard(Layout* layout, Output_data_space* got_plt)
+    : Output_data_plt_arm<big_endian>(layout, 4, got_plt)
+  { }
+
+  // Return the offset of the first non-reserved PLT entry.
+  virtual unsigned int
+  first_plt_entry_offset() const
+  { return sizeof(first_plt_entry); }
+
+  // Return the size of a PLT entry.
+  virtual unsigned int
+  get_plt_entry_size() const
+  { return sizeof(plt_entry); }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       Arm_address got_address,
+		       Arm_address plt_address)
+  {
+    // Write first PLT entry.  All but the last word are constants.
+    const size_t num_first_plt_words = (sizeof(first_plt_entry)
+					/ sizeof(plt_entry[0]));
+    for (size_t i = 0; i < num_first_plt_words - 1; i++)
+      elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
+    // Last word in first PLT entry is &GOT[0] - .
+    elfcpp::Swap<32, big_endian>::writeval(pov + 16,
+					   got_address - (plt_address + 16));
+  }
+
+  virtual void
+  fill_plt_entry(unsigned char* pov,
+		 Arm_address got_address,
+		 Arm_address plt_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset)
+  {
+    int32_t offset = ((got_address + got_offset)
+		      - (plt_address + plt_offset + 8));
+
+    gold_assert(offset >= 0 && offset < 0x0fffffff);
+    uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
+    elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
+    uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
+    elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
+    uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff);
+    elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
+  }
+
+ private:
+  // Template for the first PLT entry.
+  static const uint32_t first_plt_entry[5];
+
+  // Template for subsequent PLT entries.
+  static const uint32_t plt_entry[3];
+};
+
 // Create the PLT section.  The ordinary .got section is an argument,
 // since we need to refer to the start.  We also create our own .got
 // section just for PLT entries.
 
 template<bool big_endian>
 Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout,
+						     uint64_t addralign,
 						     Output_data_space* got_plt)
-  : Output_section_data(4), got_plt_(got_plt), count_(0)
+  : Output_section_data(addralign), got_plt_(got_plt), count_(0)
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
@@ -7291,8 +7374,8 @@ Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
 
   // Note that when setting the PLT offset we skip the initial
   // reserved PLT entry.
-  gsym->set_plt_offset((this->count_) * sizeof(plt_entry)
-		       + sizeof(first_plt_entry));
+  gsym->set_plt_offset((this->count_) * this->get_plt_entry_size()
+		       + this->first_plt_entry_offset());
 
   ++this->count_;
 
@@ -7320,7 +7403,7 @@ Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
 
 // The first entry in the PLT.
 template<bool big_endian>
-const uint32_t Output_data_plt_arm<big_endian>::first_plt_entry[5] =
+const uint32_t Output_data_plt_arm_standard<big_endian>::first_plt_entry[5] =
 {
   0xe52de004,	// str   lr, [sp, #-4]!
   0xe59fe004,   // ldr   lr, [pc, #4]
@@ -7332,7 +7415,7 @@ const uint32_t Output_data_plt_arm<big_endian>::first_plt_entry[5] =
 // Subsequent entries in the PLT.
 
 template<bool big_endian>
-const uint32_t Output_data_plt_arm<big_endian>::plt_entry[3] =
+const uint32_t Output_data_plt_arm_standard<big_endian>::plt_entry[3] =
 {
   0xe28fc600,	// add   ip, pc, #0xNN00000
   0xe28cca00,	// add   ip, ip, #0xNN000
@@ -7362,46 +7445,29 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
   Arm_address plt_address = this->address();
   Arm_address got_address = this->got_plt_->address();
 
-  // Write first PLT entry.  All but the last word are constants.
-  const size_t num_first_plt_words = (sizeof(first_plt_entry)
-				      / sizeof(plt_entry[0]));
-  for (size_t i = 0; i < num_first_plt_words - 1; i++)
-    elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
-  // Last word in first PLT entry is &GOT[0] - .
-  elfcpp::Swap<32, big_endian>::writeval(pov + 16,
-					 got_address - (plt_address + 16));
-  pov += sizeof(first_plt_entry);
+  // Write first PLT entry.
+  this->fill_first_plt_entry(pov, got_address, plt_address);
+  pov += this->first_plt_entry_offset();
 
   unsigned char* got_pov = got_view;
 
   memset(got_pov, 0, 12);
   got_pov += 12;
 
-  const int rel_size = elfcpp::Elf_sizes<32>::rel_size;
-  unsigned int plt_offset = sizeof(first_plt_entry);
-  unsigned int plt_rel_offset = 0;
+  unsigned int plt_offset = this->first_plt_entry_offset();
   unsigned int got_offset = 12;
   const unsigned int count = this->count_;
   for (unsigned int i = 0;
        i < count;
        ++i,
-	 pov += sizeof(plt_entry),
+	 pov += this->get_plt_entry_size(),
 	 got_pov += 4,
-	 plt_offset += sizeof(plt_entry),
-	 plt_rel_offset += rel_size,
+	 plt_offset += this->get_plt_entry_size(),
 	 got_offset += 4)
     {
       // Set and adjust the PLT entry itself.
-      int32_t offset = ((got_address + got_offset)
-			 - (plt_address + plt_offset + 8));
-
-      gold_assert(offset >= 0 && offset < 0x0fffffff);
-      uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
-      elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
-      uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
-      elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
-      uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff);
-      elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
+      this->fill_plt_entry(pov, got_address, plt_address,
+			   got_offset, plt_offset);
 
       // Set the entry in the GOT.
       elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
@@ -7429,7 +7495,8 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
       // Create the GOT sections first.
       this->got_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_arm<big_endian>(layout, this->got_plt_);
+      this->plt_ = this->make_data_plt(layout, this->got_plt_);
+
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
 				       | elfcpp::SHF_EXECINSTR),
@@ -7455,7 +7522,7 @@ template<bool big_endian>
 unsigned int
 Target_arm<big_endian>::first_plt_entry_offset() const
 {
-  return Output_data_plt_arm<big_endian>::first_plt_entry_offset();
+  return this->plt_->first_plt_entry_offset();
 }
 
 // Return the size of each PLT entry.
@@ -7464,7 +7531,7 @@ template<bool big_endian>
 unsigned int
 Target_arm<big_endian>::plt_entry_size() const
 {
-  return Output_data_plt_arm<big_endian>::get_plt_entry_size();
+  return this->plt_->get_plt_entry_size();
 }
 
 // Get the section to use for TLS_DESC relocations.
@@ -11980,7 +12047,201 @@ Target_arm<big_endian>::do_define_standard_symbols(
     }
 }
 
-Target_selector_arm<false> target_selector_arm;
-Target_selector_arm<true> target_selector_armbe;
+// NaCl variant.  It uses different PLT contents.
+
+template<bool big_endian>
+class Output_data_plt_arm_nacl;
+
+template<bool big_endian>
+class Target_arm_nacl : public Target_arm<big_endian>
+{
+ public:
+  Target_arm_nacl()
+    : Target_arm<big_endian>(&arm_nacl_info)
+  { }
+
+ protected:
+  virtual Output_data_plt_arm<big_endian>*
+  make_data_plt(Layout* layout, Output_data_space* got_plt)
+  {
+    return new Output_data_plt_arm_nacl<big_endian>(layout, got_plt);
+  }
+
+ private:
+  static const Target::Target_info arm_nacl_info;
+};
+
+template<bool big_endian>
+const Target::Target_info Target_arm_nacl<big_endian>::arm_nacl_info =
+{
+  32,			// size
+  big_endian,		// is_big_endian
+  elfcpp::EM_ARM,	// machine_code
+  false,		// has_make_symbol
+  false,		// has_resolve
+  false,		// has_code_fill
+  true,			// is_default_stack_executable
+  false,		// can_icf_inline_merge_sections
+  '\0',			// wrap_char
+  "/lib/ld-nacl-arm.so.1", // dynamic_linker
+  0x20000,		// default_text_segment_address
+  0x10000,		// abi_pagesize (overridable by -z max-page-size)
+  0x10000,		// common_pagesize (overridable by -z common-page-size)
+  true,                 // isolate_execinstr
+  0x10000000,           // rosegment_gap
+  elfcpp::SHN_UNDEF,	// small_common_shndx
+  elfcpp::SHN_UNDEF,	// large_common_shndx
+  0,			// small_common_section_flags
+  0,			// large_common_section_flags
+  ".ARM.attributes",	// attributes_section
+  "aeabi"		// attributes_vendor
+};
+
+template<bool big_endian>
+class Output_data_plt_arm_nacl : public Output_data_plt_arm<big_endian>
+{
+ public:
+  Output_data_plt_arm_nacl(Layout* layout, Output_data_space* got_plt)
+    : Output_data_plt_arm<big_endian>(layout, 16, got_plt)
+  { }
+
+  // Return the offset of the first non-reserved PLT entry.
+  virtual unsigned int
+  first_plt_entry_offset() const
+  { return sizeof(first_plt_entry); }
+
+  // Return the size of a PLT entry.
+  virtual unsigned int
+  get_plt_entry_size() const
+  { return sizeof(plt_entry); }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       Arm_address got_address,
+		       Arm_address plt_address)
+  {
+    // Write first PLT entry.  All but first two words are constants.
+    const size_t num_first_plt_words = (sizeof(first_plt_entry)
+					/ sizeof(first_plt_entry[0]));
+
+    int32_t got_displacement = got_address + 8 - (plt_address + 16);
+
+    elfcpp::Swap<32, big_endian>::writeval
+      (pov + 0, first_plt_entry[0] | arm_movw_immediate (got_displacement));
+    elfcpp::Swap<32, big_endian>::writeval
+      (pov + 4, first_plt_entry[1] | arm_movt_immediate (got_displacement));
+
+    for (size_t i = 2; i < num_first_plt_words; ++i)
+      elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
+  }
+
+  virtual void
+  fill_plt_entry(unsigned char* pov,
+		 Arm_address got_address,
+		 Arm_address plt_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset)
+  {
+    // Calculate the displacement between the PLT slot and the
+    // common tail that's part of the special initial PLT slot.
+    int32_t tail_displacement = (plt_address + (11 * sizeof(uint32_t))
+				 - (plt_address + plt_offset
+				    + sizeof(plt_entry) + sizeof(uint32_t)));
+    gold_assert((tail_displacement & 3) == 0);
+    tail_displacement >>= 2;
+
+    gold_assert ((tail_displacement & 0xff000000) == 0
+		 || (-tail_displacement & 0xff000000) == 0);
+
+    // Calculate the displacement between the PLT slot and the entry
+    // in the GOT.  The offset accounts for the value produced by
+    // adding to pc in the penultimate instruction of the PLT stub.
+    const int32_t got_displacement = (got_address + got_offset
+				      - (plt_address + sizeof(plt_entry)));
+
+    elfcpp::Swap<32, big_endian>::writeval
+      (pov + 0, plt_entry[0] | arm_movw_immediate (got_displacement));
+    elfcpp::Swap<32, big_endian>::writeval
+      (pov + 4, plt_entry[1] | arm_movt_immediate (got_displacement));
+    elfcpp::Swap<32, big_endian>::writeval
+      (pov + 8, plt_entry[2]);
+    elfcpp::Swap<32, big_endian>::writeval
+      (pov + 12, plt_entry[3] | (tail_displacement & 0x00ffffff));
+  }
+
+ private:
+  inline uint32_t arm_movw_immediate(uint32_t value)
+  {
+    return (value & 0x00000fff) | ((value & 0x0000f000) << 4);
+  }
+
+  inline uint32_t arm_movt_immediate(uint32_t value)
+  {
+    return ((value & 0x0fff0000) >> 16) | ((value & 0xf0000000) >> 12);
+  }
+
+  // Template for the first PLT entry.
+  static const uint32_t first_plt_entry[16];
+
+  // Template for subsequent PLT entries.
+  static const uint32_t plt_entry[4];
+};
+
+// The first entry in the PLT.
+template<bool big_endian>
+const uint32_t Output_data_plt_arm_nacl<big_endian>::first_plt_entry[16] =
+{
+  // First bundle:
+  0xe300c000,                           // movw	ip, #:lower16:&GOT[2]-.+8
+  0xe340c000,                           // movt	ip, #:upper16:&GOT[2]-.+8
+  0xe08cc00f,                           // add	ip, ip, pc
+  0xe52dc008,                           // str	ip, [sp, #-8]!
+  // Second bundle:
+  0xe7dfcf1f,                           // bfc	ip, #30, #2
+  0xe59cc000,                           // ldr	ip, [ip]
+  0xe3ccc13f,                           // bic	ip, ip, #0xc000000f
+  0xe12fff1c,                           // bx	ip
+  // Third bundle:
+  0xe320f000,                           // nop
+  0xe320f000,                           // nop
+  0xe320f000,                           // nop
+  // .Lplt_tail:
+  0xe50dc004,                           // str	ip, [sp, #-4]
+  // Fourth bundle:
+  0xe7dfcf1f,                           // bfc	ip, #30, #2
+  0xe59cc000,                           // ldr	ip, [ip]
+  0xe3ccc13f,                           // bic	ip, ip, #0xc000000f
+  0xe12fff1c,                           // bx	ip
+};
+
+// Subsequent entries in the PLT.
+
+template<bool big_endian>
+const uint32_t Output_data_plt_arm_nacl<big_endian>::plt_entry[4] =
+{
+  0xe300c000,                           // movw	ip, #:lower16:&GOT[n]-.+8
+  0xe340c000,                           // movt	ip, #:upper16:&GOT[n]-.+8
+  0xe08cc00f,                           // add	ip, ip, pc
+  0xea000000,                           // b	.Lplt_tail
+};
+
+template<bool big_endian>
+class Target_selector_arm_nacl
+  : public Target_selector_nacl<Target_selector_arm<big_endian>,
+				Target_arm_nacl<big_endian> >
+{
+ public:
+  Target_selector_arm_nacl()
+    : Target_selector_nacl<Target_selector_arm<big_endian>,
+			   Target_arm_nacl<big_endian> >(
+	  "arm",
+	  big_endian ? "elf32-bigarm-nacl" : "elf32-littlearm-nacl",
+	  big_endian ? "armelfb_nacl" : "armelf_nacl")
+  { }
+};
+
+Target_selector_arm_nacl<false> target_selector_arm;
+Target_selector_arm_nacl<true> target_selector_armbe;
 
 } // End anonymous namespace.
diff --git a/gold/freebsd.h b/gold/freebsd.h
index 175dd05..da7b85f 100644
--- a/gold/freebsd.h
+++ b/gold/freebsd.h
@@ -1,6 +1,6 @@
 // freebsd.h -- FreeBSD support for gold    -*- C++ -*-
 
-// Copyright 2009, 2011 Free Software Foundation, Inc.
+// Copyright 2009,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -48,7 +48,7 @@ class Target_selector_freebsd : public Target_selector
   // If we see a FreeBSD input file, mark the output file as using
   // FreeBSD.
   virtual Target*
-  do_recognize(int, int osabi, int)
+  do_recognize(Input_file*, int, int osabi, int)
   {
     Target* ret = this->instantiate_target();
     if (osabi == elfcpp::ELFOSABI_FREEBSD)
diff --git a/gold/gold.cc b/gold/gold.cc
index f810bf9..e9453e6 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -1,6 +1,6 @@
 // gold.cc -- main linker functions
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -676,11 +676,12 @@ queue_middle_tasks(const General_options& options,
   // Define symbols from any linker scripts.
   layout->define_script_symbols(symtab);
 
-  // Attach sections to segments.
-  layout->attach_sections_to_segments();
-
   // TODO(csilvers): figure out a more principled way to get the target
   Target* target = const_cast<Target*>(&parameters->target());
+
+  // Attach sections to segments.
+  layout->attach_sections_to_segments(target);
+
   if (!parameters->options().relocatable())
     {
       // Predefine standard symbols.
diff --git a/gold/i386.cc b/gold/i386.cc
index b4174bc..7171492 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -40,6 +40,7 @@
 #include "target-select.h"
 #include "tls.h"
 #include "freebsd.h"
+#include "nacl.h"
 #include "gc.h"
 
 namespace
@@ -48,13 +49,17 @@ namespace
 using namespace gold;
 
 // A class to handle the PLT data.
+// This is an abstract base class that handles most of the linker details
+// but does not know the actual contents of PLT entries.  The derived
+// classes below fill in those details.
 
 class Output_data_plt_i386 : public Output_section_data
 {
  public:
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section;
 
-  Output_data_plt_i386(Layout*, Output_data_space*, Output_data_space*);
+  Output_data_plt_i386(Layout*, uint64_t addralign,
+		       Output_data_space*, Output_data_space*);
 
   // Add an entry to the PLT.
   void
@@ -90,14 +95,13 @@ class Output_data_plt_i386 : public Output_section_data
   { return this->count_ + this->irelative_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
-  static unsigned int
+  unsigned int
   first_plt_entry_offset()
-  { return plt_entry_size; }
+  { return get_plt_entry_size(); }
 
   // Return the size of a PLT entry.
-  static unsigned int
-  get_plt_entry_size()
-  { return plt_entry_size; }
+  virtual unsigned int
+  get_plt_entry_size() const = 0;
 
   // Return the PLT address to use for a global symbol.
   uint64_t
@@ -107,7 +111,30 @@ class Output_data_plt_i386 : public Output_section_data
   uint64_t
   address_for_local(const Relobj*, unsigned int symndx);
 
+  // Add .eh_frame information for the PLT.
+  virtual void
+  add_eh_frame(Layout*) = 0;
+
  protected:
+  // Fill the first PLT entry, given the pointer to the PLT section data
+  // and the runtime address of the GOT.
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       elfcpp::Elf_types<32>::Elf_Addr got_address) = 0;
+
+  // Fill a normal PLT entry, given the pointer to the entry's data in the
+  // section, the runtime address of the GOT, the offset into the GOT of
+  // the corresponding slot, the offset into the relocation section of the
+  // corresponding reloc, and the offset of this entry within the whole
+  // PLT.  Return the offset from this PLT entry's runtime address that
+  // should be used to compute the initial value of the GOT slot.
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 elfcpp::Elf_types<32>::Elf_Addr got_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_rel_offset) = 0;
+
   void
   do_adjust_output_section(Output_section* os);
 
@@ -116,34 +143,18 @@ class Output_data_plt_i386 : public Output_section_data
   do_print_to_mapfile(Mapfile* mapfile) const
   { mapfile->print_output_data(this, _("** PLT")); }
 
- private:
-  // The size of an entry in the PLT.
-  static const int plt_entry_size = 16;
-
-  // The first entry in the PLT for an executable.
-  static const unsigned char exec_first_plt_entry[plt_entry_size];
-
-  // The first entry in the PLT for a shared object.
-  static const unsigned char dyn_first_plt_entry[plt_entry_size];
-
-  // Other entries in the PLT for an executable.
-  static const unsigned char exec_plt_entry[plt_entry_size];
-
-  // Other entries in the PLT for a shared object.
-  static const unsigned char dyn_plt_entry[plt_entry_size];
-
   // The .eh_frame unwind information for the PLT.
+  // The CIE is common across variants of the PLT format.
   static const int plt_eh_frame_cie_size = 16;
-  static const int plt_eh_frame_fde_size = 32;
   static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size];
-  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
 
+ private:
   // Set the final size.
   void
   set_final_data_size()
   {
     this->set_data_size((this->count_ + this->irelative_count_ + 1)
-			* plt_entry_size);
+			* get_plt_entry_size());
   }
 
   // Write out the PLT data.
@@ -193,6 +204,126 @@ class Output_data_plt_i386 : public Output_section_data
   std::vector<Local_ifunc> local_ifuncs_;
 };
 
+// This is an abstract class for the standard PLT layout.
+// The derived classes below handle the actual PLT contents
+// for the executable (non-PIC) and shared-library (PIC) cases.
+// The unwind information is uniform across those two, so it's here.
+
+class Output_data_plt_i386_standard : public Output_data_plt_i386
+{
+ public:
+  Output_data_plt_i386_standard(Layout* layout,
+				Output_data_space* got_plt,
+				Output_data_space* got_irelative)
+    : Output_data_plt_i386(layout, plt_entry_size, got_plt, got_irelative)
+  { }
+
+  virtual unsigned int
+  get_plt_entry_size() const
+  {
+    return plt_entry_size;
+  }
+
+  virtual void
+  add_eh_frame(Layout* layout)
+  {
+    layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
+				 plt_eh_frame_fde, plt_eh_frame_fde_size);
+  }
+
+ protected:
+  // The size of an entry in the PLT.
+  static const int plt_entry_size = 16;
+
+  // The .eh_frame unwind information for the PLT.
+  static const int plt_eh_frame_fde_size = 32;
+  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
+};
+
+// Actually fill the PLT contents for an executable (non-PIC).
+
+class Output_data_plt_i386_exec : public Output_data_plt_i386_standard
+{
+public:
+  Output_data_plt_i386_exec(Layout* layout,
+			    Output_data_space* got_plt,
+			    Output_data_space* got_irelative)
+    : Output_data_plt_i386_standard(layout, got_plt, got_irelative)
+  { }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       elfcpp::Elf_types<32>::Elf_Addr got_address)
+  {
+    memcpy(pov, first_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4);
+    elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8);
+  }
+
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 elfcpp::Elf_types<32>::Elf_Addr got_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_rel_offset)
+  {
+    memcpy(pov, plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						got_address + got_offset);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset);
+    elfcpp::Swap<32, false>::writeval(pov + 12, - (plt_offset + 12 + 4));
+    return 6;
+  }
+
+ private:
+  // The first entry in the PLT for an executable.
+  static const unsigned char first_plt_entry[plt_entry_size];
+
+  // Other entries in the PLT for an executable.
+  static const unsigned char plt_entry[plt_entry_size];
+};
+
+// Actually fill the PLT contents for a shared library (PIC).
+
+class Output_data_plt_i386_dyn : public Output_data_plt_i386_standard
+{
+ public:
+  Output_data_plt_i386_dyn(Layout* layout,
+			   Output_data_space* got_plt,
+			   Output_data_space* got_irelative)
+    : Output_data_plt_i386_standard(layout, got_plt, got_irelative)
+  { }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov, elfcpp::Elf_types<32>::Elf_Addr)
+  {
+    memcpy(pov, first_plt_entry, plt_entry_size);
+  }
+
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 elfcpp::Elf_types<32>::Elf_Addr,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_rel_offset)
+  {
+    memcpy(pov, plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset);
+    elfcpp::Swap<32, false>::writeval(pov + 12, - (plt_offset + 12 + 4));
+    return 6;
+  }
+
+ private:
+  // The first entry in the PLT for a shared object.
+  static const unsigned char first_plt_entry[plt_entry_size];
+
+  // Other entries in the PLT for a shared object.
+  static const unsigned char plt_entry[plt_entry_size];
+};
+
 // The i386 target class.
 // TLS info comes from
 //   http://people.redhat.com/drepper/tls.pdf
@@ -203,8 +334,8 @@ class Target_i386 : public Sized_target<32, false>
  public:
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section;
 
-  Target_i386()
-    : Sized_target<32, false>(&i386_info),
+  Target_i386(const Target::Target_info* info = &i386_info)
+    : Sized_target<32, false>(info),
       got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
       got_tlsdesc_(NULL), global_offset_table_(NULL), rel_dyn_(NULL),
       rel_irelative_(NULL), copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL),
@@ -372,6 +503,21 @@ class Target_i386 : public Sized_target<32, false>
   unsigned int
   plt_entry_size() const;
 
+ protected:
+  // Instantiate the plt_ member.
+  // This chooses the right PLT flavor for an executable or a shared object.
+  virtual Output_data_plt_i386*
+  make_data_plt(Layout* layout,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative,
+		bool dyn)
+  {
+    if (dyn)
+      return new Output_data_plt_i386_dyn(layout, got_plt, got_irelative);
+    else
+      return new Output_data_plt_i386_exec(layout, got_plt, got_irelative);
+  }
+
  private:
   // The class which scans relocations.
   struct Scan
@@ -693,10 +839,12 @@ const Target::Target_info Target_i386::i386_info =
   true,			// is_default_stack_executable
   true,			// can_icf_inline_merge_sections
   '\0',			// wrap_char
-  "/usr/lib/libc.so.1",	// dynamic_linker
+  "/lib/ld-nacl-x86-32.so.1", // dynamic_linker
   0x08048000,		// default_text_segment_address
   0x1000,		// abi_pagesize (overridable by -z max-page-size)
   0x1000,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -824,9 +972,11 @@ Target_i386::rel_irelative_section(Layout* layout)
 // section just for PLT entries.
 
 Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
+					   uint64_t addralign,
 					   Output_data_space* got_plt,
 					   Output_data_space* got_irelative)
-  : Output_section_data(16), layout_(layout), tls_desc_rel_(NULL),
+  : Output_section_data(addralign),
+    layout_(layout), tls_desc_rel_(NULL),
     irelative_rel_(NULL), got_plt_(got_plt), got_irelative_(got_irelative),
     count_(0), irelative_count_(0), global_ifuncs_(), local_ifuncs_()
 {
@@ -834,11 +984,6 @@ Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
 				  elfcpp::SHF_ALLOC, this->rel_,
 				  ORDER_DYNAMIC_PLT_RELOCS, false);
-
-  // Add unwind information if requested.
-  if (parameters->options().ld_generated_unwind_info())
-    layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
-				 plt_eh_frame_fde, plt_eh_frame_fde_size);
 }
 
 void
@@ -861,7 +1006,7 @@ Output_data_plt_i386::add_entry(Symbol_table* symtab, Layout* layout,
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
     {
-      gsym->set_plt_offset(this->irelative_count_ * plt_entry_size);
+      gsym->set_plt_offset(this->irelative_count_ * get_plt_entry_size());
       ++this->irelative_count_;
       section_offset_type got_offset =
 	this->got_irelative_->current_data_size();
@@ -878,7 +1023,7 @@ Output_data_plt_i386::add_entry(Symbol_table* symtab, Layout* layout,
     {
       // When setting the PLT offset we skip the initial reserved PLT
       // entry.
-      gsym->set_plt_offset((this->count_ + 1) * plt_entry_size);
+      gsym->set_plt_offset((this->count_ + 1) * get_plt_entry_size());
 
       ++this->count_;
 
@@ -909,7 +1054,7 @@ Output_data_plt_i386::add_local_ifunc_entry(
     Sized_relobj_file<32, false>* relobj,
     unsigned int local_sym_index)
 {
-  unsigned int plt_offset = this->irelative_count_ * plt_entry_size;
+  unsigned int plt_offset = this->irelative_count_ * get_plt_entry_size();
   ++this->irelative_count_;
 
   section_offset_type got_offset = this->got_irelative_->current_data_size();
@@ -998,7 +1143,7 @@ Output_data_plt_i386::address_for_global(const Symbol* gsym)
   uint64_t offset = 0;
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
-    offset = (this->count_ + 1) * plt_entry_size;
+    offset = (this->count_ + 1) * get_plt_entry_size();
   return this->address() + offset;
 }
 
@@ -1008,12 +1153,12 @@ Output_data_plt_i386::address_for_global(const Symbol* gsym)
 uint64_t
 Output_data_plt_i386::address_for_local(const Relobj*, unsigned int)
 {
-  return this->address() + (this->count_ + 1) * plt_entry_size;
+  return this->address() + (this->count_ + 1) * get_plt_entry_size();
 }
 
 // The first entry in the PLT for an executable.
 
-const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386_exec::first_plt_entry[plt_entry_size] =
 {
   0xff, 0x35,	// pushl contents of memory address
   0, 0, 0, 0,	// replaced with address of .got + 4
@@ -1024,7 +1169,7 @@ const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
 
 // The first entry in the PLT for a shared object.
 
-const unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386_dyn::first_plt_entry[plt_entry_size] =
 {
   0xff, 0xb3, 4, 0, 0, 0,	// pushl 4(%ebx)
   0xff, 0xa3, 8, 0, 0, 0,	// jmp *8(%ebx)
@@ -1033,7 +1178,7 @@ const unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] =
 
 // Subsequent entries in the PLT for an executable.
 
-const unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386_exec::plt_entry[plt_entry_size] =
 {
   0xff, 0x25,	// jmp indirect
   0, 0, 0, 0,	// replaced with address of symbol in .got
@@ -1045,7 +1190,7 @@ const unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] =
 
 // Subsequent entries in the PLT for a shared object.
 
-const unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] =
+const unsigned char Output_data_plt_i386_dyn::plt_entry[plt_entry_size] =
 {
   0xff, 0xa3,	// jmp *offset(%ebx)
   0, 0, 0, 0,	// replaced with offset of symbol in .got
@@ -1077,7 +1222,7 @@ Output_data_plt_i386::plt_eh_frame_cie[plt_eh_frame_cie_size] =
 };
 
 const unsigned char
-Output_data_plt_i386::plt_eh_frame_fde[plt_eh_frame_fde_size] =
+Output_data_plt_i386_standard::plt_eh_frame_fde[plt_eh_frame_fde_size] =
 {
   0, 0, 0, 0,				// Replaced with offset to .plt.
   0, 0, 0, 0,				// Replaced with size of .plt.
@@ -1130,15 +1275,8 @@ Output_data_plt_i386::do_write(Output_file* of)
   elfcpp::Elf_types<32>::Elf_Addr plt_address = this->address();
   elfcpp::Elf_types<32>::Elf_Addr got_address = this->got_plt_->address();
 
-  if (parameters->options().output_is_position_independent())
-    memcpy(pov, dyn_first_plt_entry, plt_entry_size);
-  else
-    {
-      memcpy(pov, exec_first_plt_entry, plt_entry_size);
-      elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4);
-      elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8);
-    }
-  pov += plt_entry_size;
+  this->fill_first_plt_entry(pov, got_address);
+  pov += get_plt_entry_size();
 
   unsigned char* got_pov = got_view;
 
@@ -1155,40 +1293,29 @@ Output_data_plt_i386::do_write(Output_file* of)
 
   const int rel_size = elfcpp::Elf_sizes<32>::rel_size;
 
-  unsigned int plt_offset = plt_entry_size;
+  unsigned int plt_offset = get_plt_entry_size();
   unsigned int plt_rel_offset = 0;
   unsigned int got_offset = 12;
   const unsigned int count = this->count_ + this->irelative_count_;
   for (unsigned int i = 0;
        i < count;
        ++i,
-	 pov += plt_entry_size,
+	 pov += get_plt_entry_size(),
 	 got_pov += 4,
-	 plt_offset += plt_entry_size,
+	 plt_offset += get_plt_entry_size(),
 	 plt_rel_offset += rel_size,
 	 got_offset += 4)
     {
       // Set and adjust the PLT entry itself.
-
-      if (parameters->options().output_is_position_independent())
-	{
-	  memcpy(pov, dyn_plt_entry, plt_entry_size);
-	  elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset);
-	}
-      else
-	{
-	  memcpy(pov, exec_plt_entry, plt_entry_size);
-	  elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
-						      (got_address
-						       + got_offset));
-	}
-
-      elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset);
-      elfcpp::Swap<32, false>::writeval(pov + 12,
-					- (plt_offset + plt_entry_size));
+      unsigned int lazy_offset = this->fill_plt_entry(pov,
+						      got_address,
+						      got_offset,
+						      plt_offset,
+						      plt_rel_offset);
 
       // Set the entry in the GOT.
-      elfcpp::Swap<32, false>::writeval(got_pov, plt_address + plt_offset + 6);
+      elfcpp::Swap<32, false>::writeval(got_pov,
+					plt_address + plt_offset + lazy_offset);
     }
 
   // If any STT_GNU_IFUNC symbols have PLT entries, we need to change
@@ -1235,8 +1362,16 @@ Target_i386::make_plt_section(Symbol_table* symtab, Layout* layout)
       // Create the GOT sections first.
       this->got_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_i386(layout, this->got_plt_,
-					    this->got_irelative_);
+      const bool dyn = parameters->options().output_is_position_independent();
+      this->plt_ = make_data_plt(layout,
+				 this->got_plt_,
+				 this->got_irelative_,
+				 dyn);
+
+      // Add unwind information if requested.
+      if (parameters->options().ld_generated_unwind_info())
+	this->plt_->add_eh_frame(layout);
+
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
 				       | elfcpp::SHF_EXECINSTR),
@@ -1292,7 +1427,7 @@ Target_i386::plt_entry_count() const
 unsigned int
 Target_i386::first_plt_entry_offset() const
 {
-  return Output_data_plt_i386::first_plt_entry_offset();
+  return this->plt_->first_plt_entry_offset();
 }
 
 // Return the size of each PLT entry.
@@ -1300,7 +1435,7 @@ Target_i386::first_plt_entry_offset() const
 unsigned int
 Target_i386::plt_entry_size() const
 {
-  return Output_data_plt_i386::get_plt_entry_size();
+  return this->plt_->get_plt_entry_size();
 }
 
 // Get the section to use for TLS_DESC relocations.
@@ -3631,6 +3766,302 @@ public:
   { return new Target_i386(); }
 };
 
-Target_selector_i386 target_selector_i386;
+// NaCl variant.  It uses different PLT contents.
+
+class Output_data_plt_i386_nacl : public Output_data_plt_i386
+{
+ public:
+  Output_data_plt_i386_nacl(Layout* layout,
+			    Output_data_space* got_plt,
+			    Output_data_space* got_irelative)
+    : Output_data_plt_i386(layout, plt_entry_size, got_plt, got_irelative)
+  { }
+
+  virtual unsigned int
+  get_plt_entry_size() const
+  {
+    return plt_entry_size;
+  }
+
+  virtual void
+  add_eh_frame(Layout* layout)
+  {
+    layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
+				 plt_eh_frame_fde, plt_eh_frame_fde_size);
+  }
+
+ protected:
+  // The size of an entry in the PLT.
+  static const int plt_entry_size = 64;
+
+  // The .eh_frame unwind information for the PLT.
+  static const int plt_eh_frame_fde_size = 32;
+  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
+};
+
+class Output_data_plt_i386_nacl_exec : public Output_data_plt_i386_nacl
+{
+public:
+  Output_data_plt_i386_nacl_exec(Layout* layout,
+				 Output_data_space* got_plt,
+				 Output_data_space* got_irelative)
+    : Output_data_plt_i386_nacl(layout, got_plt, got_irelative)
+  { }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       elfcpp::Elf_types<32>::Elf_Addr got_address)
+  {
+    memcpy(pov, exec_first_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4);
+    elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8);
+  }
+
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 elfcpp::Elf_types<32>::Elf_Addr got_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_rel_offset)
+  {
+    memcpy(pov, exec_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						got_address + got_offset);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 33, plt_rel_offset);
+    elfcpp::Swap<32, false>::writeval(pov + 38, - (plt_offset + 38 + 4));
+    return 32;
+  }
+
+ private:
+  // The first entry in the PLT for an executable.
+  static const unsigned char exec_first_plt_entry[plt_entry_size];
+
+  // Other entries in the PLT for an executable.
+  static const unsigned char exec_plt_entry[plt_entry_size];
+};
+
+class Output_data_plt_i386_nacl_dyn : public Output_data_plt_i386_nacl
+{
+ public:
+  Output_data_plt_i386_nacl_dyn(Layout* layout,
+				Output_data_space* got_plt,
+				Output_data_space* got_irelative)
+    : Output_data_plt_i386_nacl(layout, got_plt, got_irelative)
+  { }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov, elfcpp::Elf_types<32>::Elf_Addr)
+  {
+    memcpy(pov, dyn_first_plt_entry, plt_entry_size);
+  }
+
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 elfcpp::Elf_types<32>::Elf_Addr,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_rel_offset)
+  {
+    memcpy(pov, dyn_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 33, plt_rel_offset);
+    elfcpp::Swap<32, false>::writeval(pov + 38, - (plt_offset + 38 + 4));
+    return 32;
+  }
+
+ private:
+  // The first entry in the PLT for a shared object.
+  static const unsigned char dyn_first_plt_entry[plt_entry_size];
+
+  // Other entries in the PLT for a shared object.
+  static const unsigned char dyn_plt_entry[plt_entry_size];
+};
+
+class Target_i386_nacl : public Target_i386
+{
+ public:
+  Target_i386_nacl()
+    : Target_i386(&i386_nacl_info)
+  { }
+
+  virtual Output_data_plt_i386*
+  make_data_plt(Layout* layout,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative,
+		bool dyn)
+  {
+    if (dyn)
+      return new Output_data_plt_i386_nacl_dyn(layout, got_plt, got_irelative);
+    else
+      return new Output_data_plt_i386_nacl_exec(layout, got_plt, got_irelative);
+  }
+
+ private:
+  static const Target::Target_info i386_nacl_info;
+};
+
+const Target::Target_info Target_i386_nacl::i386_nacl_info =
+{
+  32,			// size
+  false,		// is_big_endian
+  elfcpp::EM_386,	// machine_code
+  false,		// has_make_symbol
+  false,		// has_resolve
+  true,			// has_code_fill
+  true,			// is_default_stack_executable
+  true,			// can_icf_inline_merge_sections
+  '\0',			// wrap_char
+  "/usr/lib/libc.so.1",	// dynamic_linker
+  0x20000,		// default_text_segment_address
+  0x10000,		// abi_pagesize (overridable by -z max-page-size)
+  0x10000,		// common_pagesize (overridable by -z common-page-size)
+  true,                 // isolate_execinstr
+  0x10000000,           // rosegment_gap
+  elfcpp::SHN_UNDEF,	// small_common_shndx
+  elfcpp::SHN_UNDEF,	// large_common_shndx
+  0,			// small_common_section_flags
+  0,			// large_common_section_flags
+  NULL,			// attributes_section
+  NULL			// attributes_vendor
+};
+
+#define	NACLMASK	0xe0            // 32-byte alignment mask
+
+const unsigned char
+Output_data_plt_i386_nacl_exec::exec_first_plt_entry[plt_entry_size] =
+{
+  0xff, 0x35,                          // pushl contents of memory address
+  0, 0, 0, 0,                          // replaced with address of .got + 4
+  0x8b, 0x0d,                          // movl contents of address, %ecx
+  0, 0, 0, 0,                          // replaced with address of .got + 8
+  0x83, 0xe1, NACLMASK,                // andl $NACLMASK, %ecx
+  0xff, 0xe1,                          // jmp *%ecx
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90
+};
+
+// The first entry in the PLT for a shared object.
+
+const unsigned char
+Output_data_plt_i386_nacl_dyn::dyn_first_plt_entry[plt_entry_size] =
+{
+  0xff, 0xb3, 4, 0, 0, 0,	// pushl 4(%ebx)
+  0x8b, 0x4b, 0x08,		// mov 0x8(%ebx), %ecx
+  0x83, 0xe1, NACLMASK,         // andl $NACLMASK, %ecx
+  0xff, 0xe1,                   // jmp *%ecx
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90,  // nops
+  0x90, 0x90, 0x90, 0x90, 0x90   // nops
+};
+
+// Subsequent entries in the PLT for an executable.
+
+const unsigned char
+Output_data_plt_i386_nacl_exec::exec_plt_entry[plt_entry_size] =
+{
+  0x8b, 0x0d,                    // movl contents of address, %ecx */
+  0, 0, 0, 0,                    // replaced with address of symbol in .got
+  0x83, 0xe1, NACLMASK,          // andl $NACLMASK, %ecx
+  0xff, 0xe1,                    // jmp *%ecx
+
+  // Pad to the next 32-byte boundary with nop instructions.
+  0x90,
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+
+  // Lazy GOT entries point here (32-byte aligned).
+  0x68,                       // pushl immediate
+  0, 0, 0, 0,                 // replaced with offset into relocation table
+  0xe9,                       // jmp relative
+  0, 0, 0, 0,                 // replaced with offset to start of .plt
+
+  // Pad to the next 32-byte boundary with nop instructions.
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+  0x90, 0x90
+};
+
+// Subsequent entries in the PLT for a shared object.
+
+const unsigned char
+Output_data_plt_i386_nacl_dyn::dyn_plt_entry[plt_entry_size] =
+{
+  0x8b, 0x8b,          // movl offset(%ebx), %ecx
+  0, 0, 0, 0,          // replaced with offset of symbol in .got
+  0x83, 0xe1, 0xe0,    // andl $NACLMASK, %ecx
+  0xff, 0xe1,          // jmp *%ecx
+
+  // Pad to the next 32-byte boundary with nop instructions.
+  0x90,
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+
+  // Lazy GOT entries point here (32-byte aligned).
+  0x68,                // pushl immediate
+  0, 0, 0, 0,          // replaced with offset into relocation table.
+  0xe9,                // jmp relative
+  0, 0, 0, 0,          // replaced with offset to start of .plt.
+
+  // Pad to the next 32-byte boundary with nop instructions.
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+  0x90, 0x90
+};
+
+const unsigned char
+Output_data_plt_i386_nacl::plt_eh_frame_fde[plt_eh_frame_fde_size] =
+{
+  0, 0, 0, 0,				// Replaced with offset to .plt.
+  0, 0, 0, 0,				// Replaced with size of .plt.
+  0,					// Augmentation size.
+  elfcpp::DW_CFA_def_cfa_offset, 8,	// DW_CFA_def_cfa_offset: 8.
+  elfcpp::DW_CFA_advance_loc + 6,	// Advance 6 to __PLT__ + 6.
+  elfcpp::DW_CFA_def_cfa_offset, 12,	// DW_CFA_def_cfa_offset: 12.
+  elfcpp::DW_CFA_advance_loc + 58,	// Advance 58 to __PLT__ + 64.
+  elfcpp::DW_CFA_def_cfa_expression,	// DW_CFA_def_cfa_expression.
+  13,					// Block length.
+  elfcpp::DW_OP_breg4, 4,		// Push %esp + 4.
+  elfcpp::DW_OP_breg8, 0,		// Push %eip.
+  elfcpp::DW_OP_const1u, 63,            // Push 0x3f.
+  elfcpp::DW_OP_and,			// & (%eip & 0x3f).
+  elfcpp::DW_OP_const1u, 37,            // Push 0x25.
+  elfcpp::DW_OP_ge,			// >= ((%eip & 0x3f) >= 0x25)
+  elfcpp::DW_OP_lit2,			// Push 2.
+  elfcpp::DW_OP_shl,			// << (((%eip & 0x3f) >= 0x25) << 2)
+  elfcpp::DW_OP_plus,			// + ((((%eip&0x3f)>=0x25)<<2)+%esp+4
+  elfcpp::DW_CFA_nop,			// Align to 32 bytes.
+  elfcpp::DW_CFA_nop
+};
+
+// The selector for i386-nacl object files.
+
+class Target_selector_i386_nacl
+  : public Target_selector_nacl<Target_selector_i386, Target_i386_nacl>
+{
+ public:
+  Target_selector_i386_nacl()
+    : Target_selector_nacl<Target_selector_i386,
+			   Target_i386_nacl>("x86-32",
+					     "elf32-i386-nacl",
+					     "elf_i386_nacl")
+  { }
+};
+
+Target_selector_i386_nacl target_selector_i386;
 
 } // End anonymous namespace.
diff --git a/gold/incremental.cc b/gold/incremental.cc
index 60097a8..fbad192 100644
--- a/gold/incremental.cc
+++ b/gold/incremental.cc
@@ -839,7 +839,8 @@ Incremental_binary*
 make_sized_incremental_binary(Output_file* file,
                               const elfcpp::Ehdr<size, big_endian>& ehdr)
 {
-  Target* target = select_target(ehdr.get_e_machine(), size, big_endian,
+  Target* target = select_target(NULL,  // XXX
+                                 ehdr.get_e_machine(), size, big_endian,
                                  ehdr.get_e_ident()[elfcpp::EI_OSABI],
                                  ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
   if (target == NULL)
diff --git a/gold/layout.cc b/gold/layout.cc
index 65d1432..774b779 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -1,6 +1,6 @@
 // layout.cc -- lay out output file sections for gold
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -1508,7 +1508,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
   // need to attach this one now.  This happens for sections created
   // directly by the linker.
   if (this->sections_are_attached_)
-    this->attach_section_to_segment(os);
+    this->attach_section_to_segment(&parameters->target(), os);
 
   return os;
 }
@@ -1585,12 +1585,12 @@ Layout::default_section_order(Output_section* os, bool is_relro_local)
 // seen all the input sections.
 
 void
-Layout::attach_sections_to_segments()
+Layout::attach_sections_to_segments(const Target* target)
 {
   for (Section_list::iterator p = this->section_list_.begin();
        p != this->section_list_.end();
        ++p)
-    this->attach_section_to_segment(*p);
+    this->attach_section_to_segment(target, *p);
 
   this->sections_are_attached_ = true;
 }
@@ -1598,18 +1598,19 @@ Layout::attach_sections_to_segments()
 // Attach an output section to a segment.
 
 void
-Layout::attach_section_to_segment(Output_section* os)
+Layout::attach_section_to_segment(const Target* target, Output_section* os)
 {
   if ((os->flags() & elfcpp::SHF_ALLOC) == 0)
     this->unattached_section_list_.push_back(os);
   else
-    this->attach_allocated_section_to_segment(os);
+    this->attach_allocated_section_to_segment(target, os);
 }
 
 // Attach an allocated output section to a segment.
 
 void
-Layout::attach_allocated_section_to_segment(Output_section* os)
+Layout::attach_allocated_section_to_segment(const Target* target,
+					    Output_section* os)
 {
   elfcpp::Elf_Xword flags = os->flags();
   gold_assert((flags & elfcpp::SHF_ALLOC) != 0);
@@ -1649,7 +1650,7 @@ Layout::attach_allocated_section_to_segment(Output_section* os)
       if (!parameters->options().omagic()
 	  && ((*p)->flags() & elfcpp::PF_W) != (seg_flags & elfcpp::PF_W))
 	continue;
-      if (parameters->options().rosegment()
+      if ((target->isolate_execinstr() || parameters->options().rosegment())
           && ((*p)->flags() & elfcpp::PF_X) != (seg_flags & elfcpp::PF_X))
         continue;
       // If -Tbss was specified, we need to separate the data and BSS
@@ -1951,7 +1952,7 @@ Layout::define_group_signatures(Symbol_table* symtab)
 // necessary.
 
 Output_segment*
-Layout::find_first_load_seg()
+Layout::find_first_load_seg(const Target* target)
 {
   Output_segment* best = NULL;
   for (Segment_list::const_iterator p = this->segment_list_.begin();
@@ -1961,7 +1962,9 @@ Layout::find_first_load_seg()
       if ((*p)->type() == elfcpp::PT_LOAD
 	  && ((*p)->flags() & elfcpp::PF_R) != 0
 	  && (parameters->options().omagic()
-	      || ((*p)->flags() & elfcpp::PF_W) == 0))
+	      || ((*p)->flags() & elfcpp::PF_W) == 0)
+	  && (!target->isolate_execinstr()
+	      || ((*p)->flags() & elfcpp::PF_X) == 0))
         {
           if (best == NULL || this->segment_precedes(*p, best))
             best = *p;
@@ -2148,7 +2151,7 @@ Layout::relaxation_loop_body(
   else if (parameters->options().relocatable())
     load_seg = NULL;
   else
-    load_seg = this->find_first_load_seg();
+    load_seg = this->find_first_load_seg(target);
 
   if (parameters->options().oformat_enum()
       != General_options::OBJECT_FORMAT_ELF)
@@ -3054,13 +3057,15 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg,
 
   // Find the PT_LOAD segments, and set their addresses and offsets
   // and their section's addresses and offsets.
-  uint64_t addr;
+  uint64_t start_addr;
   if (parameters->options().user_set_Ttext())
-    addr = parameters->options().Ttext();
+    start_addr = parameters->options().Ttext();
   else if (parameters->options().output_is_position_independent())
-    addr = 0;
+    start_addr = 0;
   else
-    addr = target->default_text_segment_address();
+    start_addr = target->default_text_segment_address();
+
+  uint64_t addr = start_addr;
   off_t off = 0;
 
   // If LOAD_SEG is NULL, then the file header and segment headers
@@ -3085,15 +3090,39 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg,
   const bool check_sections = parameters->options().check_sections();
   Output_segment* last_load_segment = NULL;
 
+  unsigned int shndx_begin = *pshndx;
+  unsigned int shndx_load_seg = *pshndx;
+
   for (Segment_list::iterator p = this->segment_list_.begin();
        p != this->segment_list_.end();
        ++p)
     {
       if ((*p)->type() == elfcpp::PT_LOAD)
 	{
+	  if (target->isolate_execinstr())
+	    {
+	      // When we hit the segment that should contain the
+	      // file headers, reset the file offset so we place
+	      // it and subsequent segments appropriately.
+	      // We'll fix up the preceding segments below.
+	      if (load_seg == *p)
+		{
+		  if (off == 0)
+		    load_seg = NULL;
+		  else
+		    {
+		      off = 0;
+		      shndx_load_seg = *pshndx;
+		    }
+		}
+	    }
+	  else
+	    {
+	      // Verify that the file headers fall into the first segment.
 	  if (load_seg != NULL && load_seg != *p)
 	    gold_unreachable();
 	  load_seg = NULL;
+	    }
 
 	  bool are_addresses_set = (*p)->are_addresses_set();
 	  if (are_addresses_set)
@@ -3145,16 +3174,37 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg,
 	      addr = align_address(addr, (*p)->maximum_alignment());
 	      aligned_addr = addr;
 
+	      if (load_seg == *p)
+		{
+		  // This is the segment that will contain the file
+		  // headers, so its offset will have to be exactly zero.
+		  gold_assert(orig_off == 0);
+
+		  // If the target wants a fixed minimum distance from the
+		  // text segment to the read-only segment, move up now.
+		  uint64_t min_addr = start_addr + target->rosegment_gap();
+		  if (addr < min_addr)
+		    addr = min_addr;
+
+		  // But this is not the first segment!  To make its
+		  // address congruent with its offset, that address better
+		  // be aligned to the ABI-mandated page size.
+		  addr = align_address(addr, abi_pagesize);
+		  aligned_addr = addr;
+		}
+	      else
+		{
 	      if ((addr & (abi_pagesize - 1)) != 0)
                 addr = addr + abi_pagesize;
 
 	      off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1));
 	    }
+	    }
 
 	  if (!parameters->options().nmagic()
 	      && !parameters->options().omagic())
 	    off = align_file_offset(off, addr, abi_pagesize);
-	  else if (load_seg == NULL)
+	  else
 	    {
 	      // This is -N or -n with a section script which prevents
 	      // us from using a load segment.  We need to ensure that
@@ -3239,6 +3289,38 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg,
 	}
     }
 
+  if (load_seg != NULL && target->isolate_execinstr())
+    {
+      // Process the early segments again, setting their file offsets
+      // so they land after the segments starting at LOAD_SEG.
+      off = align_file_offset(off, 0, target->abi_pagesize());
+
+      for (Segment_list::iterator p = this->segment_list_.begin();
+	   *p != load_seg;
+	   ++p)
+	{
+	  if ((*p)->type() == elfcpp::PT_LOAD)
+	    {
+	      // We repeat the whole job of assigning addresses and
+	      // offsets, but we really only want to change the offsets and
+	      // must ensure that the addresses all come out the same as
+	      // they did the first time through.
+	      bool has_relro = false;
+	      const uint64_t old_addr = (*p)->vaddr();
+	      const uint64_t old_end = old_addr + (*p)->memsz();
+	      uint64_t new_addr = (*p)->set_section_addresses(this, true,
+							      old_addr,
+							      &increase_relro,
+							      &has_relro,
+							      &off,
+							      &shndx_begin);
+	      gold_assert(new_addr == old_end);
+	    }
+	}
+
+      gold_assert(shndx_begin == shndx_load_seg);
+    }
+
   // Handle the non-PT_LOAD segments, setting their offsets from their
   // section's offsets.
   for (Segment_list::iterator p = this->segment_list_.begin();
diff --git a/gold/layout.h b/gold/layout.h
index f81ea3b..ea84548 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -1,6 +1,6 @@
 // layout.h -- lay out output file sections for gold  -*- C++ -*-
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -903,7 +903,7 @@ class Layout
 
   // Attach sections to segments.
   void
-  attach_sections_to_segments();
+  attach_sections_to_segments(const Target*);
 
   // For relaxation clean up, we need to know output section data created
   // from a linker script.
@@ -982,7 +982,7 @@ class Layout
   // Find the first read-only PT_LOAD segment, creating one if
   // necessary.
   Output_segment*
-  find_first_load_seg();
+  find_first_load_seg(const Target*);
 
   // Count the local symbols in the regular symbol table and the dynamic
   // symbol table, and build the respective string pools.
@@ -1079,7 +1079,7 @@ class Layout
 
   // Attach a section to a segment.
   void
-  attach_section_to_segment(Output_section*);
+  attach_section_to_segment(const Target*, Output_section*);
 
   // Get section order.
   Output_section_order
@@ -1087,7 +1087,7 @@ class Layout
 
   // Attach an allocated section to a segment.
   void
-  attach_allocated_section_to_segment(Output_section*);
+  attach_allocated_section_to_segment(const Target*, Output_section*);
 
   // Make the .eh_frame section.
   Output_section*
diff --git a/gold/nacl.cc b/gold/nacl.cc
new file mode 100644
index 0000000..71be4e9
--- /dev/null
+++ b/gold/nacl.cc
@@ -0,0 +1,46 @@
+// nacl.cc -- Native Client support for gold
+
+// Copyright 2012 Free Software Foundation, Inc.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+#include "gold.h"
+
+#include <cstdio>
+
+#include "nacl.h"
+#include "elfcpp.h"
+
+namespace gold
+{
+
+// Copied from object.cc:Object::error.
+void
+Sniff_file::error(const char* format, ...) const
+{
+  va_list args;
+  va_start(args, format);
+  char* buf = NULL;
+  if (vasprintf(&buf, format, args) < 0)
+    gold_nomem();
+  va_end(args);
+  gold_error(_("%s: %s"), this->file_.filename().c_str(), buf);
+  free(buf);
+}
+
+} // end namespace gold
diff --git a/gold/nacl.h b/gold/nacl.h
new file mode 100644
index 0000000..f54867a
--- /dev/null
+++ b/gold/nacl.h
@@ -0,0 +1,235 @@
+// nacl.h -- Native Client support for gold    -*- C++ -*-
+
+// Copyright 2012 Free Software Foundation, Inc.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+#include "elfcpp_file.h"
+#include "fileread.h"
+#include "layout.h"
+#include "target-select.h"
+#include "target.h"
+
+#ifndef GOLD_NACL_H
+#define GOLD_NACL_H
+
+namespace gold
+{
+
+class Sniff_file
+{
+ public:
+  Sniff_file(Input_file* input_file)
+    : file_(input_file->file())
+  { }
+
+  class Location
+  {
+   public:
+    Location(off_t file_offset, off_t data_size)
+      : offset_(file_offset), size_(data_size)
+    { }
+
+    inline off_t offset() const
+    { return offset_; }
+
+    inline section_size_type size() const
+    { return size_; }
+
+   private:
+    off_t offset_;
+    section_size_type size_;
+  };
+
+  class View
+  {
+   public:
+    View(File_read& file, off_t file_offset, off_t data_size)
+      : data_(file.get_view(0, file_offset, data_size, false, false))
+    { }
+
+    const unsigned char* data()
+    { return data_; }
+
+   private:
+    const unsigned char* data_;
+  };
+
+  View view(off_t file_offset, off_t data_size)
+  {
+    return View(file_, file_offset, data_size);
+  }
+
+  View view(Location loc)
+  {
+    return View(file_, loc.offset(), loc.size());
+  }
+
+  // Report an error.
+  void
+  error(const char* format, ...) const ATTRIBUTE_PRINTF_2;
+
+ private:
+  File_read& file_;
+};
+
+
+template<class base_selector, class nacl_target>
+class Target_selector_nacl : public base_selector
+{
+ public:
+  Target_selector_nacl(const char* nacl_abi_name,
+                       const char* bfd_name, const char* emulation)
+    : base_selector(), is_nacl_(false), nacl_abi_name_(nacl_abi_name),
+      bfd_name_(bfd_name), emulation_(emulation)
+  { }
+
+ protected:
+  virtual Target*
+  do_instantiate_target()
+  {
+    if (is_nacl_)
+      return new nacl_target();
+    return base_selector::do_instantiate_target();
+  }
+
+  virtual Target*
+  do_recognize(Input_file* file, int machine, int osabi, int abiversion)
+  {
+    this->is_nacl_ = recognize_nacl_file(file);
+    if (this->is_nacl_)
+      return this->instantiate_target();
+    return base_selector::do_recognize(file, machine, osabi, abiversion);
+  }
+
+  virtual Target*
+  do_recognize_by_bfd_name(const char* name)
+  {
+    this->is_nacl_ = strcmp(name, this->bfd_name_) == 0;
+    return this->instantiate_target();
+  }
+
+  virtual void
+  do_supported_bfd_names(std::vector<const char*>* names)
+  {
+    gold_assert(this->bfd_name_ != NULL);
+    base_selector::do_supported_bfd_names(names);
+    names->push_back(this->bfd_name_);
+  }
+
+  virtual void
+  do_supported_emulations(std::vector<const char*>* emulations)
+  {
+    gold_assert(this->emulation_ != NULL);
+    base_selector::do_supported_emulations(emulations);
+    emulations->push_back(this->emulation_);
+  }
+
+  virtual const char*
+  do_target_bfd_name(const Target* target)
+  {
+    return (!this->is_our_target(target) ? NULL
+            : this->is_nacl_ ? this->bfd_name_
+            : base_selector::do_target_bfd_name(target));
+  }
+
+ private:
+  bool
+  recognize_nacl_file(Input_file* input_file)
+  {
+    if (this->is_big_endian())
+      {
+#if defined(HAVE_TARGET_32_BIG) || defined(HAVE_TARGET_64_BIG)
+# ifdef HAVE_TARGET_32_BIG
+        if (this->get_size() == 32)
+          return do_recognize_nacl_file<32, true>(input_file);
+# endif
+# ifdef HAVE_TARGET_64_BIG
+        if (this->get_size() == 64)
+          return do_recognize_nacl_file<64, true>(input_file);
+# endif
+#endif
+        gold_unreachable();
+      }
+    else
+      {
+#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_64_LITTLE)
+# ifdef HAVE_TARGET_32_LITTLE
+        if (this->get_size() == 32)
+          return do_recognize_nacl_file<32, false>(input_file);
+# endif
+# ifdef HAVE_TARGET_64_LITTLE
+        if (this->get_size() == 64)
+          return do_recognize_nacl_file<64, false>(input_file);
+# endif
+#endif
+        gold_unreachable();
+      }
+  }
+
+  template<int size, bool big_endian>
+  bool
+  do_recognize_nacl_file(Input_file* input_file)
+  {
+    Sniff_file file(input_file);
+    elfcpp::Elf_file<size, big_endian, Sniff_file> elf_file(&file);
+    const unsigned int shnum = elf_file.shnum();
+    for (unsigned int shndx = 1; shndx < shnum; ++shndx)
+      {
+        if (elf_file.section_type(shndx) == elfcpp::SHT_NOTE)
+          {
+            Sniff_file::Location loc = elf_file.section_contents(shndx);
+            if (loc.size() < (3 * 4
+                              + align_address(sizeof "NaCl", 4)
+                              + align_address(nacl_abi_name_.size() + 1, 4)))
+              continue;
+            Sniff_file::View view(file.view(loc));
+            const unsigned char* note_data = view.data();
+            if ((elfcpp::Swap<32, big_endian>::readval(note_data + 0)
+                 == sizeof "NaCl")
+                && (elfcpp::Swap<32, big_endian>::readval(note_data + 4)
+                    == nacl_abi_name_.size() + 1)
+                && (elfcpp::Swap<32, big_endian>::readval(note_data + 8)
+                    == elfcpp::NT_VERSION))
+              {
+                const unsigned char* name = note_data + 12;
+                const unsigned char* desc = (name
+                                             + align_address(sizeof "NaCl", 4));
+                if (!memcmp(name, "NaCl", sizeof "NaCl")
+                    && !memcmp(desc, nacl_abi_name_.c_str(),
+                               nacl_abi_name_.size() + 1))
+                  return true;
+              }
+          }
+      }
+    return false;
+  }
+
+  // Whether we decided this was the NaCl target variant.
+  bool is_nacl_;
+  // The string found in the NaCl ABI note.
+  std::string nacl_abi_name_;
+  // BFD name of NaCl target, for compatibility.
+  const char* const bfd_name_;
+  // GNU linker emulation for this NaCl target, for compatibility.
+  const char* const emulation_;
+};
+
+} // end namespace gold
+
+#endif // !defined(GOLD_NACL_H)
diff --git a/gold/object.cc b/gold/object.cc
index 15e5d05..8c1359c 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -1,6 +1,6 @@
 // object.cc -- support for an object file for linking in gold
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -2934,7 +2934,8 @@ make_elf_sized_object(const std::string& name, Input_file* input_file,
 		      off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr,
 		      bool* punconfigured)
 {
-  Target* target = select_target(ehdr.get_e_machine(), size, big_endian,
+  Target* target = select_target(input_file,
+                                 ehdr.get_e_machine(), size, big_endian,
 				 ehdr.get_e_ident()[elfcpp::EI_OSABI],
 				 ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
   if (target == NULL)
diff --git a/gold/parameters.cc b/gold/parameters.cc
index 7fc5730..a73899e 100644
--- a/gold/parameters.cc
+++ b/gold/parameters.cc
@@ -1,6 +1,6 @@
 // parameters.cc -- general parameters for a link using gold
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -352,7 +352,8 @@ parameters_force_valid_target()
   else
     is_big_endian = GOLD_DEFAULT_BIG_ENDIAN;
 
-  Target* target = select_target(elfcpp::GOLD_DEFAULT_MACHINE,
+  Target* target = select_target(NULL,
+                                 elfcpp::GOLD_DEFAULT_MACHINE,
 				 GOLD_DEFAULT_SIZE,
 				 is_big_endian,
 				 elfcpp::GOLD_DEFAULT_OSABI,
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index b443304..02773be 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -1,6 +1,6 @@
 // powerpc.cc -- powerpc target support for gold.
 
-// Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 // Written by David S. Miller <davem@davemloft.net>
 //        and David Edelsohn <edelsohn@gnu.org>
 
@@ -389,6 +389,8 @@ Target::Target_info Target_powerpc<32, true>::powerpc_info =
   0x10000000,		// default_text_segment_address
   64 * 1024,		// abi_pagesize (overridable by -z max-page-size)
   4 * 1024,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -413,6 +415,8 @@ Target::Target_info Target_powerpc<32, false>::powerpc_info =
   0x10000000,		// default_text_segment_address
   64 * 1024,		// abi_pagesize (overridable by -z max-page-size)
   4 * 1024,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -437,6 +441,8 @@ Target::Target_info Target_powerpc<64, true>::powerpc_info =
   0x10000000,		// default_text_segment_address
   64 * 1024,		// abi_pagesize (overridable by -z max-page-size)
   8 * 1024,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -461,6 +467,8 @@ Target::Target_info Target_powerpc<64, false>::powerpc_info =
   0x10000000,		// default_text_segment_address
   64 * 1024,		// abi_pagesize (overridable by -z max-page-size)
   8 * 1024,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -2132,7 +2140,8 @@ public:
 		       : (big_endian ? "elf32ppc" : "elf32lppc")))
   { }
 
-  Target* do_recognize(int machine, int, int)
+  virtual Target*
+  do_recognize(Input_file*, int machine, int, int)
   {
     switch (size)
       {
@@ -2153,7 +2162,8 @@ public:
     return this->instantiate_target();
   }
 
-  Target* do_instantiate_target()
+  virtual Target*
+  do_instantiate_target()
   { return new Target_powerpc<size, big_endian>(); }
 };
 
diff --git a/gold/sparc.cc b/gold/sparc.cc
index eb238f9..e38e7ca 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -467,6 +467,8 @@ Target::Target_info Target_sparc<32, true>::sparc_info =
   0x00010000,		// default_text_segment_address
   64 * 1024,		// abi_pagesize (overridable by -z max-page-size)
   8 * 1024,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -491,6 +493,8 @@ Target::Target_info Target_sparc<64, true>::sparc_info =
   0x100000,		// default_text_segment_address
   64 * 1024,		// abi_pagesize (overridable by -z max-page-size)
   8 * 1024,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_UNDEF,	// large_common_shndx
   0,			// small_common_section_flags
@@ -4194,7 +4198,8 @@ public:
 		      (size == 64 ? "elf64_sparc" : "elf32_sparc"))
   { }
 
-  Target* do_recognize(int machine, int, int)
+  virtual Target*
+  do_recognize(Input_file*, int machine, int, int)
   {
     switch (size)
       {
@@ -4216,7 +4221,8 @@ public:
     return this->instantiate_target();
   }
 
-  Target* do_instantiate_target()
+  virtual Target*
+  do_instantiate_target()
   { return new Target_sparc<size, big_endian>(); }
 };
 
diff --git a/gold/target-select.cc b/gold/target-select.cc
index 9370a87..90211d8 100644
--- a/gold/target-select.cc
+++ b/gold/target-select.cc
@@ -1,6 +1,6 @@
 // target-select.cc -- select a target for an object file
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -98,8 +98,8 @@ Target_selector::do_target_bfd_name(const Target* target)
 // Find the target for an ELF file.
 
 Target*
-select_target(int machine, int size, bool is_big_endian, int osabi,
-	      int abiversion)
+select_target(Input_file* input_file, int machine, int size, bool is_big_endian,
+              int osabi, int abiversion)
 {
   for (Target_selector* p = target_selectors; p != NULL; p = p->next())
     {
@@ -108,7 +108,7 @@ select_target(int machine, int size, bool is_big_endian, int osabi,
 	  && p->get_size() == size
 	  && (p->is_big_endian() ? is_big_endian : !is_big_endian))
 	{
-	  Target* ret = p->recognize(machine, osabi, abiversion);
+	  Target* ret = p->recognize(input_file, machine, osabi, abiversion);
 	  if (ret != NULL)
 	    return ret;
 	}
diff --git a/gold/target-select.h b/gold/target-select.h
index 310c0b9..7706077 100644
--- a/gold/target-select.h
+++ b/gold/target-select.h
@@ -1,6 +1,6 @@
 // target-select.h -- select a target for an object file  -*- C++ -*-
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -30,6 +30,7 @@
 namespace gold
 {
 
+class Input_file;
 class Target;
 class Target_selector;
 
@@ -76,8 +77,8 @@ class Target_selector
   // If we can handle this target, return a pointer to a target
   // structure.  The size and endianness are known.
   Target*
-  recognize(int machine, int osabi, int abiversion)
-  { return this->do_recognize(machine, osabi, abiversion); }
+  recognize(Input_file* input_file, int machine, int osabi, int abiversion)
+  { return this->do_recognize(input_file, machine, osabi, abiversion); }
 
   // If NAME matches the target, return a pointer to a target
   // structure.
@@ -160,7 +161,7 @@ class Target_selector
   // checks, or to check for multiple machine codes if the machine_
   // field is EM_NONE.
   virtual Target*
-  do_recognize(int, int, int)
+  do_recognize(Input_file*, int, int, int)
   { return this->instantiate_target(); }
 
   // Recognize a target by name.  When this is called we already know
@@ -241,7 +242,7 @@ class Target_selector
 // Select the target for an ELF file.
 
 extern Target*
-select_target(int machine, int size, bool big_endian, int osabi,
+select_target(Input_file*, int machine, int size, bool big_endian, int osabi,
 	      int abiversion);
 
 // Select a target using a BFD name.
diff --git a/gold/target.h b/gold/target.h
index ff97aba..0a50d06 100644
--- a/gold/target.h
+++ b/gold/target.h
@@ -1,6 +1,6 @@
 // target.h -- target support for gold   -*- C++ -*-
 
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2006,2007,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -142,6 +142,16 @@ class Target
 		      this->abi_pagesize());
   }
 
+  // Return whether PF_X segments must contain nothing but the contents of
+  // SHF_EXECINSTR sections (no non-executable data, no headers).
+  bool
+  isolate_execinstr() const
+  { return this->pti_->isolate_execinstr; }
+
+  uint64_t
+  rosegment_gap() const
+  { return this->pti_->rosegment_gap; }
+
   // If we see some object files with .note.GNU-stack sections, and
   // some objects files without them, this returns whether we should
   // consider the object files without them to imply that the stack
@@ -435,6 +445,11 @@ class Target
     uint64_t abi_pagesize;
     // The common page size used by actual implementations.
     uint64_t common_pagesize;
+    // Whether PF_X segments must contain nothing but the contents of
+    // SHF_EXECINSTR sections (no non-executable data, no headers).
+    bool isolate_execinstr;
+    // If nonzero, distance from the text segment to the read-only segment.
+    uint64_t rosegment_gap;
     // The special section index for small common symbols; SHN_UNDEF
     // if none.
     elfcpp::Elf_Half small_common_shndx;
diff --git a/gold/testsuite/testfile.cc b/gold/testsuite/testfile.cc
index 93e716a..4857fb5 100644
--- a/gold/testsuite/testfile.cc
+++ b/gold/testsuite/testfile.cc
@@ -1,6 +1,6 @@
 // testfile.cc -- Dummy ELF objects for testing purposes.
 
-// Copyright 2006, 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009, 2011, 2012 Free Software Foundation, Inc.
 // Written by Ian Lance Taylor <iant@google.com>.
 
 // This file is part of gold.
@@ -100,6 +100,8 @@ const Target::Target_info Target_test<size, big_endian>::test_target_info =
   0x08000000,				// default_text_segment_address
   0x1000,				// abi_pagesize
   0x1000,				// common_pagesize
+  false,                                // isolate_execinstr
+  0,                                    // rosegment_gap
   elfcpp::SHN_UNDEF,			// small_common_shndx
   elfcpp::SHN_UNDEF,			// large_common_shndx
   0,					// small_common_section_flags
@@ -154,15 +156,15 @@ class Target_selector_test : public Target_selector
     : Target_selector(0xffff, size, big_endian, NULL, NULL)
   { }
 
-  Target*
+  virtual Target*
   do_instantiate_target()
   {
     gold_unreachable();
     return NULL;
   }
 
-  Target*
-  do_recognize(int, int, int)
+  virtual Target*
+  do_recognize(Input_file*, int, int, int)
   {
     if (size == 32)
       {
@@ -198,11 +200,11 @@ class Target_selector_test : public Target_selector
     return NULL;
   }
 
-  Target*
+  virtual Target*
   do_recognize_by_name(const char*)
   { return NULL; }
 
-  void
+  virtual void
   do_supported_names(std::vector<const char*>*)
   { }
 };
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index d67924b..f10e531 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -40,6 +40,7 @@
 #include "target-select.h"
 #include "tls.h"
 #include "freebsd.h"
+#include "nacl.h"
 #include "gc.h"
 #include "icf.h"
 
@@ -49,6 +50,9 @@ namespace
 using namespace gold;
 
 // A class to handle the PLT data.
+// This is an abstract base class that handles most of the linker details
+// but does not know the actual contents of PLT entries.  The derived
+// classes below fill in those details.
 
 template<int size>
 class Output_data_plt_x86_64 : public Output_section_data
@@ -56,20 +60,23 @@ class Output_data_plt_x86_64 : public Output_section_data
  public:
   typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, false> Reloc_section;
 
-  Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got,
+  Output_data_plt_x86_64(Layout* layout, uint64_t addralign,
+			 Output_data_got<64, false>* got,
 			 Output_data_space* got_plt,
 			 Output_data_space* got_irelative)
-    : Output_section_data(16), layout_(layout), tlsdesc_rel_(NULL),
+    : Output_section_data(addralign), layout_(layout), tlsdesc_rel_(NULL),
       irelative_rel_(NULL), got_(got), got_plt_(got_plt),
       got_irelative_(got_irelative), count_(0), irelative_count_(0),
       tlsdesc_got_offset_(-1U), free_list_()
   { this->init(layout); }
 
-  Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got,
+  Output_data_plt_x86_64(Layout* layout, uint64_t plt_entry_size,
+			 Output_data_got<64, false>* got,
 			 Output_data_space* got_plt,
 			 Output_data_space* got_irelative,
 			 unsigned int plt_count)
-    : Output_section_data((plt_count + 1) * plt_entry_size, 16, false),
+    : Output_section_data((plt_count + 1) * plt_entry_size,
+			  plt_entry_size, false),
       layout_(layout), tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got),
       got_plt_(got_plt), got_irelative_(got_irelative), count_(plt_count),
       irelative_count_(0), tlsdesc_got_offset_(-1U), free_list_()
@@ -118,7 +125,7 @@ class Output_data_plt_x86_64 : public Output_section_data
   // Return the offset of the reserved TLSDESC_PLT entry.
   unsigned int
   get_tlsdesc_plt_offset() const
-  { return (this->count_ + this->irelative_count_ + 1) * plt_entry_size; }
+  { return (this->count_ + this->irelative_count_ + 1) * get_plt_entry_size(); }
 
   // Return the .rela.plt section data.
   Reloc_section*
@@ -145,21 +152,20 @@ class Output_data_plt_x86_64 : public Output_section_data
   { return this->count_ + this->irelative_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
-  static unsigned int
+  unsigned int
   first_plt_entry_offset()
-  { return plt_entry_size; }
+  { return get_plt_entry_size(); }
 
   // Return the size of a PLT entry.
-  static unsigned int
-  get_plt_entry_size()
-  { return plt_entry_size; }
+  virtual unsigned int
+  get_plt_entry_size() const = 0;
 
   // Reserve a slot in the PLT for an existing symbol in an incremental update.
   void
   reserve_slot(unsigned int plt_index)
   {
-    this->free_list_.remove((plt_index + 1) * plt_entry_size,
-			    (plt_index + 2) * plt_entry_size);
+    this->free_list_.remove((plt_index + 1) * get_plt_entry_size(),
+			    (plt_index + 2) * get_plt_entry_size());
   }
 
   // Return the PLT address to use for a global symbol.
@@ -170,6 +176,10 @@ class Output_data_plt_x86_64 : public Output_section_data
   uint64_t
   address_for_local(const Relobj*, unsigned int symndx);
 
+  // Add .eh_frame information for the PLT.
+  virtual void
+  add_eh_frame(Layout*) = 0;
+
  protected:
   void
   do_adjust_output_section(Output_section* os);
@@ -179,27 +189,37 @@ class Output_data_plt_x86_64 : public Output_section_data
   do_print_to_mapfile(Mapfile* mapfile) const
   { mapfile->print_output_data(this, _("** PLT")); }
 
- private:
-  // The size of an entry in the PLT.
-  static const int plt_entry_size = 16;
-
-  // The first entry in the PLT.
-  // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same
-  // procedure linkage table for both programs and shared objects."
-  static const unsigned char first_plt_entry[plt_entry_size];
-
-  // Other entries in the PLT for an executable.
-  static const unsigned char plt_entry[plt_entry_size];
-
-  // The reserved TLSDESC entry in the PLT for an executable.
-  static const unsigned char tlsdesc_plt_entry[plt_entry_size];
-
-  // The .eh_frame unwind information for the PLT.
+  // Fill in the first PLT entry.
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		       typename elfcpp::Elf_types<size>::Elf_Addr plt_address)
+    = 0;
+
+  // Fill in a normal PLT entry.  Returns the offset into the entry that
+  // should be the initial GOT slot value.
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		 typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_index) = 0;
+
+  // Fill in the reserved TLSDESC PLT entry.
+  virtual void
+  fill_tlsdesc_entry(unsigned char* pov,
+		     typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		     typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+		     typename elfcpp::Elf_types<size>::Elf_Addr got_base,
+		     unsigned int tlsdesc_got_offset,
+		     unsigned int plt_offset) = 0;
+
+  // The CIE of the .eh_frame unwind information for the PLT.
   static const int plt_eh_frame_cie_size = 16;
-  static const int plt_eh_frame_fde_size = 32;
   static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size];
-  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
 
+ private:
   // Set the final size.
   void
   set_final_data_size();
@@ -237,6 +257,120 @@ class Output_data_plt_x86_64 : public Output_section_data
   Free_list free_list_;
 };
 
+template<int size>
+class Output_data_plt_x86_64_standard : public Output_data_plt_x86_64<size>
+{
+ public:
+  Output_data_plt_x86_64_standard(Layout* layout,
+				  Output_data_got<64, false>* got,
+				  Output_data_space* got_plt,
+				  Output_data_space* got_irelative)
+    : Output_data_plt_x86_64<size>(layout, plt_entry_size,
+				   got, got_plt, got_irelative)
+  { }
+
+  Output_data_plt_x86_64_standard(Layout* layout,
+				  Output_data_got<64, false>* got,
+				  Output_data_space* got_plt,
+				  Output_data_space* got_irelative,
+				  unsigned int plt_count)
+    : Output_data_plt_x86_64<size>(layout, plt_entry_size,
+				   got, got_plt, got_irelative,
+				   plt_count)
+  { }
+
+  // Return the size of a PLT entry.
+  virtual unsigned int
+  get_plt_entry_size() const
+  { return plt_entry_size; }
+
+  virtual void
+  add_eh_frame(Layout* layout)
+  {
+    layout->add_eh_frame_for_plt(this,
+				 this->plt_eh_frame_cie,
+				 this->plt_eh_frame_cie_size,
+				 plt_eh_frame_fde,
+				 plt_eh_frame_fde_size);
+  }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		       typename elfcpp::Elf_types<size>::Elf_Addr plt_address)
+  {
+    memcpy(pov, first_plt_entry, plt_entry_size);
+    // We do a jmp relative to the PC at the end of this instruction.
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						(got_address + 8
+						 - (plt_address + 6)));
+    elfcpp::Swap<32, false>::writeval(pov + 8,
+				      (got_address + 16
+				       - (plt_address + 12)));
+  }
+
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		 typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_index)
+  {
+    memcpy(pov, plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						(got_address + got_offset
+						 - (plt_address + plt_offset
+						    + 6)));
+
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_index);
+    elfcpp::Swap<32, false>::writeval(pov + 12,
+				      - (plt_offset + plt_entry_size));
+
+    return 6;
+  }
+
+  virtual void
+  fill_tlsdesc_entry(unsigned char* pov,
+		     typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		     typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+		     typename elfcpp::Elf_types<size>::Elf_Addr got_base,
+		     unsigned int tlsdesc_got_offset,
+		     unsigned int plt_offset)
+  {
+    memcpy(pov, tlsdesc_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						(got_address + 8
+						 - (plt_address + plt_offset
+						    + 6)));
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 8,
+						(got_base
+						 + tlsdesc_got_offset
+						 - (plt_address + plt_offset
+						    + 12)));
+  }
+
+ private:
+  // The size of an entry in the PLT.
+  static const int plt_entry_size = 16;
+
+  // The first entry in the PLT.
+  // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same
+  // procedure linkage table for both programs and shared objects."
+  static const unsigned char first_plt_entry[plt_entry_size];
+
+  // Other entries in the PLT for an executable.
+  static const unsigned char plt_entry[plt_entry_size];
+
+  // The reserved TLSDESC entry in the PLT for an executable.
+  static const unsigned char tlsdesc_plt_entry[plt_entry_size];
+
+  // The .eh_frame unwind information for the PLT.
+  static const int plt_eh_frame_fde_size = 32;
+  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
+};
+
 // The x86_64 target class.
 // See the ABI at
 //   http://www.x86-64.org/documentation/abi.pdf
@@ -252,8 +386,8 @@ class Target_x86_64 : public Sized_target<size, false>
   // uses only Elf64_Rela relocation entries with explicit addends."
   typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, false> Reloc_section;
 
-  Target_x86_64()
-    : Sized_target<size, false>(&x86_64_info),
+  Target_x86_64(const Target::Target_info* info = &x86_64_info)
+    : Sized_target<size, false>(info),
       got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
       got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
       rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY),
@@ -477,6 +611,28 @@ class Target_x86_64 : public Sized_target<size, false>
     return this->tlsdesc_reloc_info_.size() - 1;
   }
 
+  virtual Output_data_plt_x86_64<size>*
+  make_data_plt(Layout* layout,
+		Output_data_got<64, false>* got,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative)
+  {
+    return new Output_data_plt_x86_64_standard<size>(layout, got, got_plt,
+						     got_irelative);
+  }
+
+  virtual Output_data_plt_x86_64<size>*
+  make_data_plt(Layout* layout,
+		Output_data_got<64, false>* got,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative,
+		unsigned int plt_count)
+  {
+    return new Output_data_plt_x86_64_standard<size>(layout, got, got_plt,
+						     got_irelative,
+						     plt_count);
+  }
+
  private:
   // The class which scans relocations.
   class Scan
@@ -818,6 +974,8 @@ const Target::Target_info Target_x86_64<64>::x86_64_info =
   0x400000,		// default_text_segment_address
   0x1000,		// abi_pagesize (overridable by -z max-page-size)
   0x1000,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_X86_64_LCOMMON,	// large_common_shndx
   0,			// small_common_section_flags
@@ -842,6 +1000,8 @@ const Target::Target_info Target_x86_64<32>::x86_64_info =
   0x400000,		// default_text_segment_address
   0x1000,		// abi_pagesize (overridable by -z max-page-size)
   0x1000,		// common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
   elfcpp::SHN_UNDEF,	// small_common_shndx
   elfcpp::SHN_X86_64_LCOMMON,	// large_common_shndx
   0,			// small_common_section_flags
@@ -988,18 +1148,13 @@ Output_data_plt_x86_64<size>::init(Layout* layout)
   layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
 				  elfcpp::SHF_ALLOC, this->rel_,
 				  ORDER_DYNAMIC_PLT_RELOCS, false);
-
-  // Add unwind information if requested.
-  if (parameters->options().ld_generated_unwind_info())
-    layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
-				 plt_eh_frame_fde, plt_eh_frame_fde_size);
 }
 
 template<int size>
 void
 Output_data_plt_x86_64<size>::do_adjust_output_section(Output_section* os)
 {
-  os->set_entsize(plt_entry_size);
+  os->set_entsize(get_plt_entry_size());
 }
 
 // Add an entry to the PLT.
@@ -1040,7 +1195,7 @@ Output_data_plt_x86_64<size>::add_entry(Symbol_table* symtab, Layout* layout,
       // Note that when setting the PLT offset for a non-IRELATIVE
       // entry we skip the initial reserved PLT entry.
       plt_index = *pcount + offset;
-      plt_offset = plt_index * plt_entry_size;
+      plt_offset = plt_index * get_plt_entry_size();
 
       ++*pcount;
 
@@ -1057,7 +1212,8 @@ Output_data_plt_x86_64<size>::add_entry(Symbol_table* symtab, Layout* layout,
       // FIXME: This is probably not correct for IRELATIVE relocs.
 
       // For incremental updates, find an available slot.
-      plt_offset = this->free_list_.allocate(plt_entry_size, plt_entry_size, 0);
+      plt_offset = this->free_list_.allocate(get_plt_entry_size(),
+					     get_plt_entry_size(), 0);
       if (plt_offset == -1)
 	gold_fallback(_("out of patch space (PLT);"
 			" relink with --incremental-full"));
@@ -1065,7 +1221,7 @@ Output_data_plt_x86_64<size>::add_entry(Symbol_table* symtab, Layout* layout,
       // The GOT and PLT entries have a 1-1 correspondance, so the GOT offset
       // can be calculated from the PLT index, adjusting for the three
       // reserved entries at the beginning of the GOT.
-      plt_index = plt_offset / plt_entry_size - 1;
+      plt_index = plt_offset / get_plt_entry_size() - 1;
       got_offset = (plt_index - offset + reserved) * 8;
     }
 
@@ -1090,7 +1246,7 @@ Output_data_plt_x86_64<size>::add_local_ifunc_entry(
     Sized_relobj_file<size, false>* relobj,
     unsigned int local_sym_index)
 {
-  unsigned int plt_offset = this->irelative_count_ * plt_entry_size;
+  unsigned int plt_offset = this->irelative_count_ * get_plt_entry_size();
   ++this->irelative_count_;
 
   section_offset_type got_offset = this->got_irelative_->current_data_size();
@@ -1202,7 +1358,7 @@ Output_data_plt_x86_64<size>::address_for_global(const Symbol* gsym)
   uint64_t offset = 0;
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
-    offset = (this->count_ + 1) * plt_entry_size;
+    offset = (this->count_ + 1) * get_plt_entry_size();
   return this->address() + offset;
 }
 
@@ -1213,7 +1369,7 @@ template<int size>
 uint64_t
 Output_data_plt_x86_64<size>::address_for_local(const Relobj*, unsigned int)
 {
-  return this->address() + (this->count_ + 1) * plt_entry_size;
+  return this->address() + (this->count_ + 1) * get_plt_entry_size();
 }
 
 // Set the final size.
@@ -1224,14 +1380,14 @@ Output_data_plt_x86_64<size>::set_final_data_size()
   unsigned int count = this->count_ + this->irelative_count_;
   if (this->has_tlsdesc_entry())
     ++count;
-  this->set_data_size((count + 1) * plt_entry_size);
+  this->set_data_size((count + 1) * get_plt_entry_size());
 }
 
 // The first entry in the PLT for an executable.
 
 template<int size>
 const unsigned char
-Output_data_plt_x86_64<size>::first_plt_entry[plt_entry_size] =
+Output_data_plt_x86_64_standard<size>::first_plt_entry[plt_entry_size] =
 {
   // From AMD64 ABI Draft 0.98, page 76
   0xff, 0x35,	// pushq contents of memory address
@@ -1245,7 +1401,7 @@ Output_data_plt_x86_64<size>::first_plt_entry[plt_entry_size] =
 
 template<int size>
 const unsigned char
-Output_data_plt_x86_64<size>::plt_entry[plt_entry_size] =
+Output_data_plt_x86_64_standard<size>::plt_entry[plt_entry_size] =
 {
   // From AMD64 ABI Draft 0.98, page 76
   0xff, 0x25,	// jmpq indirect
@@ -1260,7 +1416,7 @@ Output_data_plt_x86_64<size>::plt_entry[plt_entry_size] =
 
 template<int size>
 const unsigned char
-Output_data_plt_x86_64<size>::tlsdesc_plt_entry[plt_entry_size] =
+Output_data_plt_x86_64_standard<size>::tlsdesc_plt_entry[plt_entry_size] =
 {
   // From Alexandre Oliva, "Thread-Local Storage Descriptors for IA32
   // and AMD64/EM64T", Version 0.9.4 (2005-10-10).
@@ -1296,7 +1452,7 @@ Output_data_plt_x86_64<size>::plt_eh_frame_cie[plt_eh_frame_cie_size] =
 
 template<int size>
 const unsigned char
-Output_data_plt_x86_64<size>::plt_eh_frame_fde[plt_eh_frame_fde_size] =
+Output_data_plt_x86_64_standard<size>::plt_eh_frame_fde[plt_eh_frame_fde_size] =
 {
   0, 0, 0, 0,				// Replaced with offset to .plt.
   0, 0, 0, 0,				// Replaced with size of .plt.
@@ -1356,15 +1512,8 @@ Output_data_plt_x86_64<size>::do_write(Output_file* of)
   typename elfcpp::Elf_types<size>::Elf_Addr got_address
     = this->got_plt_->address();
 
-  memcpy(pov, first_plt_entry, plt_entry_size);
-  // We do a jmp relative to the PC at the end of this instruction.
-  elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
-					      (got_address + 8
-					       - (plt_address + 6)));
-  elfcpp::Swap<32, false>::writeval(pov + 8,
-				    (got_address + 16
-				     - (plt_address + 12)));
-  pov += plt_entry_size;
+  this->fill_first_plt_entry(pov, got_address, plt_address);
+  pov += get_plt_entry_size();
 
   unsigned char* got_pov = got_view;
 
@@ -1379,47 +1528,35 @@ Output_data_plt_x86_64<size>::do_write(Output_file* of)
   memset(got_pov, 0, 16);
   got_pov += 16;
 
-  unsigned int plt_offset = plt_entry_size;
+  unsigned int plt_offset = get_plt_entry_size();
   unsigned int got_offset = 24;
   const unsigned int count = this->count_ + this->irelative_count_;
   for (unsigned int plt_index = 0;
        plt_index < count;
        ++plt_index,
-	 pov += plt_entry_size,
+	 pov += get_plt_entry_size(),
 	 got_pov += 8,
-	 plt_offset += plt_entry_size,
+	 plt_offset += get_plt_entry_size(),
 	 got_offset += 8)
     {
       // Set and adjust the PLT entry itself.
-      memcpy(pov, plt_entry, plt_entry_size);
-      elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
-						  (got_address + got_offset
-						   - (plt_address + plt_offset
-						      + 6)));
-
-      elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_index);
-      elfcpp::Swap<32, false>::writeval(pov + 12,
-					- (plt_offset + plt_entry_size));
+      unsigned int lazy_offset = this->fill_plt_entry(pov,
+						      got_address, plt_address,
+						      got_offset, plt_offset,
+						      plt_index);
 
       // Set the entry in the GOT.
-      elfcpp::Swap<64, false>::writeval(got_pov, plt_address + plt_offset + 6);
+      elfcpp::Swap<64, false>::writeval(got_pov,
+					plt_address + plt_offset + lazy_offset);
     }
 
   if (this->has_tlsdesc_entry())
     {
       // Set and adjust the reserved TLSDESC PLT entry.
       unsigned int tlsdesc_got_offset = this->get_tlsdesc_got_offset();
-      memcpy(pov, tlsdesc_plt_entry, plt_entry_size);
-      elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
-						  (got_address + 8
-						   - (plt_address + plt_offset
-						      + 6)));
-      elfcpp::Swap_unaligned<32, false>::writeval(pov + 8,
-						  (got_base
-						   + tlsdesc_got_offset
-						   - (plt_address + plt_offset
-						      + 12)));
-      pov += plt_entry_size;
+      this->fill_tlsdesc_entry(pov, got_address, plt_address, got_base,
+			       tlsdesc_got_offset, plt_offset);
+      pov += get_plt_entry_size();
     }
 
   gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
@@ -1440,9 +1577,13 @@ Target_x86_64<size>::make_plt_section(Symbol_table* symtab, Layout* layout)
       // Create the GOT sections first.
       this->got_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_x86_64<size>(layout, this->got_,
-						    this->got_plt_,
+      this->plt_ = make_data_plt(layout, this->got_, this->got_plt_,
 						    this->got_irelative_);
+
+      // Add unwind information if requested.
+      if (parameters->options().ld_generated_unwind_info())
+	this->plt_->add_eh_frame(layout);
+
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
 				       | elfcpp::SHF_EXECINSTR),
@@ -1515,7 +1656,7 @@ template<int size>
 unsigned int
 Target_x86_64<size>::first_plt_entry_offset() const
 {
-  return Output_data_plt_x86_64<size>::first_plt_entry_offset();
+  return this->plt_->first_plt_entry_offset();
 }
 
 // Return the size of each PLT entry.
@@ -1524,7 +1665,7 @@ template<int size>
 unsigned int
 Target_x86_64<size>::plt_entry_size() const
 {
-  return Output_data_plt_x86_64<size>::get_plt_entry_size();
+  return this->plt_->get_plt_entry_size();
 }
 
 // Create the GOT and PLT sections for an incremental update.
@@ -1581,10 +1722,15 @@ Target_x86_64<size>::init_got_plt_for_update(Symbol_table* symtab,
 				  ORDER_NON_RELRO_FIRST, false);
 
   // Create the PLT section.
-  this->plt_ = new Output_data_plt_x86_64<size>(layout, this->got_,
+  this->plt_ = this->make_data_plt(layout, this->got_,
 						this->got_plt_,
 						this->got_irelative_,
 						plt_count);
+
+  // Add unwind information if requested.
+  if (parameters->options().ld_generated_unwind_info())
+    this->plt_->add_eh_frame(layout);
+
   layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				  elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
 				  this->plt_, ORDER_PLT, false);
@@ -4209,7 +4355,333 @@ public:
 
 };
 
-Target_selector_x86_64<64> target_selector_x86_64;
-Target_selector_x86_64<32> target_selector_x32;
+// NaCl variant.  It uses different PLT contents.
+
+template<int size>
+class Output_data_plt_x86_64_nacl : public Output_data_plt_x86_64<size>
+{
+ public:
+  Output_data_plt_x86_64_nacl(Layout* layout,
+			      Output_data_got<64, false>* got,
+			      Output_data_space* got_plt,
+			      Output_data_space* got_irelative)
+    : Output_data_plt_x86_64<size>(layout, plt_entry_size,
+				   got, got_plt, got_irelative)
+  { }
+
+  Output_data_plt_x86_64_nacl(Layout* layout,
+			      Output_data_got<64, false>* got,
+			      Output_data_space* got_plt,
+			      Output_data_space* got_irelative,
+			      unsigned int plt_count)
+    : Output_data_plt_x86_64<size>(layout, plt_entry_size,
+				   got, got_plt, got_irelative,
+				   plt_count)
+  { }
+
+  // Return the size of a PLT entry.
+  virtual unsigned int
+  get_plt_entry_size() const
+  { return plt_entry_size; }
+
+  virtual void
+  add_eh_frame(Layout* layout)
+  {
+    layout->add_eh_frame_for_plt(this,
+				 this->plt_eh_frame_cie,
+				 this->plt_eh_frame_cie_size,
+				 plt_eh_frame_fde,
+				 plt_eh_frame_fde_size);
+  }
+
+ protected:
+  virtual void
+  fill_first_plt_entry(unsigned char* pov,
+		       typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		       typename elfcpp::Elf_types<size>::Elf_Addr plt_address)
+  {
+    memcpy(pov, first_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						(got_address + 8
+						 - (plt_address + 2 + 4)));
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 9,
+						(got_address + 16
+						 - (plt_address + 9 + 4)));
+  }
+
+  virtual unsigned int
+  fill_plt_entry(unsigned char* pov,
+		 typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		 typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+		 unsigned int got_offset,
+		 unsigned int plt_offset,
+		 unsigned int plt_index)
+  {
+    memcpy(pov, plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 3,
+						(got_address + got_offset
+						 - (plt_address + plt_offset
+						    + 3 + 4)));
+
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 33, plt_index);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 38,
+						- (plt_offset + 38 + 4));
+
+    return 32;
+  }
+
+  virtual void
+  fill_tlsdesc_entry(unsigned char* pov,
+		     typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+		     typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+		     typename elfcpp::Elf_types<size>::Elf_Addr got_base,
+		     unsigned int tlsdesc_got_offset,
+		     unsigned int plt_offset)
+  {
+    memcpy(pov, tlsdesc_plt_entry, plt_entry_size);
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+						(got_address + 8
+						 - (plt_address + plt_offset
+						    + 2 + 4)));
+    elfcpp::Swap_unaligned<32, false>::writeval(pov + 9,
+						(got_base
+						 + tlsdesc_got_offset
+						 - (plt_address + plt_offset
+						    + 9 + 4)));
+  }
+
+ private:
+  // The size of an entry in the PLT.
+  static const int plt_entry_size = 64;
+
+  // The first entry in the PLT.
+  // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same
+  // procedure linkage table for both programs and shared objects."
+  static const unsigned char first_plt_entry[plt_entry_size];
+
+  // Other entries in the PLT for an executable.
+  static const unsigned char plt_entry[plt_entry_size];
+
+  // The reserved TLSDESC entry in the PLT for an executable.
+  static const unsigned char tlsdesc_plt_entry[plt_entry_size];
+
+  // The .eh_frame unwind information for the PLT.
+  static const int plt_eh_frame_fde_size = 32;
+  static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
+};
+
+template<int size>
+class Target_x86_64_nacl : public Target_x86_64<size>
+{
+ public:
+  Target_x86_64_nacl()
+    : Target_x86_64<size>(&x86_64_nacl_info)
+  { }
+
+  virtual Output_data_plt_x86_64<size>*
+  make_data_plt(Layout* layout,
+		Output_data_got<64, false>* got,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative)
+  {
+    return new Output_data_plt_x86_64_nacl<size>(layout, got, got_plt,
+						 got_irelative);
+  }
+
+  virtual Output_data_plt_x86_64<size>*
+  make_data_plt(Layout* layout,
+		Output_data_got<64, false>* got,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative,
+		unsigned int plt_count)
+  {
+    return new Output_data_plt_x86_64_nacl<size>(layout, got, got_plt,
+						 got_irelative,
+						 plt_count);
+  }
+
+ private:
+  static const Target::Target_info x86_64_nacl_info;
+};
+
+template<>
+const Target::Target_info Target_x86_64_nacl<64>::x86_64_nacl_info =
+{
+  64,			// size
+  false,		// is_big_endian
+  elfcpp::EM_X86_64,	// machine_code
+  false,		// has_make_symbol
+  false,		// has_resolve
+  true,			// has_code_fill
+  true,			// is_default_stack_executable
+  true,			// can_icf_inline_merge_sections
+  '\0',			// wrap_char
+  "/lib64/ld-nacl-x86-64.so.1", // dynamic_linker
+  0x20000,		// default_text_segment_address
+  0x10000,		// abi_pagesize (overridable by -z max-page-size)
+  0x10000,		// common_pagesize (overridable by -z common-page-size)
+  true,                 // isolate_execinstr
+  0x10000000,           // rosegment_gap
+  elfcpp::SHN_UNDEF,	// small_common_shndx
+  elfcpp::SHN_X86_64_LCOMMON,	// large_common_shndx
+  0,			// small_common_section_flags
+  elfcpp::SHF_X86_64_LARGE,	// large_common_section_flags
+  NULL,			// attributes_section
+  NULL			// attributes_vendor
+};
+
+template<>
+const Target::Target_info Target_x86_64_nacl<32>::x86_64_nacl_info =
+{
+  32,			// size
+  false,		// is_big_endian
+  elfcpp::EM_X86_64,	// machine_code
+  false,		// has_make_symbol
+  false,		// has_resolve
+  true,			// has_code_fill
+  true,			// is_default_stack_executable
+  true,			// can_icf_inline_merge_sections
+  '\0',			// wrap_char
+  "/lib/ld-nacl-x86-64.so.1", // dynamic_linker
+  0x20000,		// default_text_segment_address
+  0x10000,		// abi_pagesize (overridable by -z max-page-size)
+  0x10000,		// common_pagesize (overridable by -z common-page-size)
+  true,                 // isolate_execinstr
+  0x10000000,           // rosegment_gap
+  elfcpp::SHN_UNDEF,	// small_common_shndx
+  elfcpp::SHN_X86_64_LCOMMON,	// large_common_shndx
+  0,			// small_common_section_flags
+  elfcpp::SHF_X86_64_LARGE,	// large_common_section_flags
+  NULL,			// attributes_section
+  NULL			// attributes_vendor
+};
+
+#define	NACLMASK	0xe0            // 32-byte alignment mask.
+
+// The first entry in the PLT.
+
+template<int size>
+const unsigned char
+Output_data_plt_x86_64_nacl<size>::first_plt_entry[plt_entry_size] =
+{
+  0xff, 0x35,                         // pushq contents of memory address
+  0, 0, 0, 0,                         // replaced with address of .got + 8
+  0x4c, 0x8b, 0x1d,                   // mov GOT+16(%rip), %r11
+  0, 0, 0, 0,                         // replaced with address of .got + 16
+  0x41, 0x83, 0xe3, NACLMASK,         // and $-32, %r11d
+  0x4d, 0x01, 0xfb,                   // add %r15, %r11
+  0x41, 0xff, 0xe3,                   // jmpq *%r11
+
+  // 9-byte nop sequence to pad out to the next 32-byte boundary.
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopl %cs:0x0(%rax,%rax,1)
+
+  // 32 bytes of nop to pad out to the standard size
+  0x66, 0x66, 0x66, 0x66, 0x66, 0x66,    // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+  0x66, 0x66, 0x66, 0x66, 0x66, 0x66,    // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+  0x66,                                  // excess data32 prefix
+  0x90                                   // nop
+};
+
+// Subsequent entries in the PLT.
+
+template<int size>
+const unsigned char
+Output_data_plt_x86_64_nacl<size>::plt_entry[plt_entry_size] =
+{
+  0x4c, 0x8b, 0x1d,              // mov name@GOTPCREL(%rip),%r11
+  0, 0, 0, 0,                    // replaced with address of symbol in .got
+  0x41, 0x83, 0xe3, NACLMASK,    // and $-32, %r11d
+  0x4d, 0x01, 0xfb,              // add %r15, %r11
+  0x41, 0xff, 0xe3,              // jmpq *%r11
+
+  // 15-byte nop sequence to pad out to the next 32-byte boundary.
+  0x66, 0x66, 0x66, 0x66, 0x66, 0x66,    // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+
+  // Lazy GOT entries point here (32-byte aligned).
+  0x68,                       // pushq immediate
+  0, 0, 0, 0,                 // replaced with index into relocation table
+  0xe9,                       // jmp relative
+  0, 0, 0, 0,                 // replaced with offset to start of .plt0
+
+  // 22 bytes of nop to pad out to the standard size.
+  0x66, 0x66, 0x66, 0x66, 0x66, 0x66,    // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+  0x0f, 0x1f, 0x80, 0, 0, 0, 0,          // nopl 0x0(%rax)
+};
+
+// The reserved TLSDESC entry in the PLT.
+
+template<int size>
+const unsigned char
+Output_data_plt_x86_64_nacl<size>::tlsdesc_plt_entry[plt_entry_size] =
+{
+  0xff, 0x35,			// pushq x(%rip)
+  0, 0, 0, 0,	// replaced with address of linkmap GOT entry (at PLTGOT + 8)
+  0x4c, 0x8b, 0x1d,		// mov y(%rip),%r11
+  0, 0, 0, 0,	// replaced with offset of reserved TLSDESC_GOT entry
+  0x41, 0x83, 0xe3, NACLMASK,	// and $-32, %r11d
+  0x4d, 0x01, 0xfb,             // add %r15, %r11
+  0x41, 0xff, 0xe3,             // jmpq *%r11
+
+  // 41 bytes of nop to pad out to the standard size.
+  0x66, 0x66, 0x66, 0x66, 0x66, 0x66,    // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+  0x66, 0x66, 0x66, 0x66, 0x66, 0x66,    // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+  0x66, 0x66,                            // excess data32 prefixes
+  0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw %cs:0x0(%rax,%rax,1)
+};
+
+template<int size>
+const unsigned char
+Output_data_plt_x86_64_nacl<size>::plt_eh_frame_fde[plt_eh_frame_fde_size] =
+{
+  0, 0, 0, 0,				// Replaced with offset to .plt.
+  0, 0, 0, 0,				// Replaced with size of .plt.
+  0,					// Augmentation size.
+  elfcpp::DW_CFA_def_cfa_offset, 16,	// DW_CFA_def_cfa_offset: 16.
+  elfcpp::DW_CFA_advance_loc + 6,	// Advance 6 to __PLT__ + 6.
+  elfcpp::DW_CFA_def_cfa_offset, 24,	// DW_CFA_def_cfa_offset: 24.
+  elfcpp::DW_CFA_advance_loc + 58,	// Advance 58 to __PLT__ + 64.
+  elfcpp::DW_CFA_def_cfa_expression,	// DW_CFA_def_cfa_expression.
+  13,					// Block length.
+  elfcpp::DW_OP_breg7, 8,		// Push %rsp + 8.
+  elfcpp::DW_OP_breg16, 0,		// Push %rip.
+  elfcpp::DW_OP_const1u, 63,		// Push 0x3f.
+  elfcpp::DW_OP_and,			// & (%rip & 0x3f).
+  elfcpp::DW_OP_const1u, 37,            // Push 0x25.
+  elfcpp::DW_OP_ge,			// >= ((%rip & 0x3f) >= 0x25)
+  elfcpp::DW_OP_lit3,			// Push 3.
+  elfcpp::DW_OP_shl,			// << (((%rip & 0x3f) >= 0x25) << 3)
+  elfcpp::DW_OP_plus,			// + ((((%rip&0x3f)>=0x25)<<3)+%rsp+8
+  elfcpp::DW_CFA_nop,			// Align to 32 bytes.
+  elfcpp::DW_CFA_nop
+};
+
+// The selector for x86_64-nacl object files.
+
+template<int size>
+class Target_selector_x86_64_nacl
+  : public Target_selector_nacl<Target_selector_x86_64<size>,
+				Target_x86_64_nacl<size> >
+{
+ public:
+  Target_selector_x86_64_nacl()
+    : Target_selector_nacl<Target_selector_x86_64<size>,
+			   Target_x86_64_nacl<size> >("x86-64",
+						      size == 64
+						      ? "elf64-x86-64-nacl"
+						      : "elf32-x86-64-nacl",
+						      size == 64
+						      ? "elf_x86_64_nacl"
+						      : "elf32_x86_64_nacl")
+  { }
+};
+
+Target_selector_x86_64_nacl<64> target_selector_x86_64;
+Target_selector_x86_64_nacl<32> target_selector_x32;
 
 } // End anonymous namespace.



More information about the Binutils mailing list