This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [GOLD] advice on powerpc64 .opd handling
On Wed, Aug 15, 2012 at 03:39:20PM -0700, Ian Lance Taylor wrote:
> I don't really know the answer here. It sounds like what you need are
> not the contents of the .opd section itself, but the contents of the
> .rela.opd section. gold will read all the relocs before it does
> anything with them. Perhaps you can define a do_read_relocs function
> in your Powerpc_relobj class that calls the Sized_relobj_file
> do_read_relocs routine, then looks through the Section_relocs to find
> the .opd relocs, then builds whatever data structure you need out of
> that.
Yes, that's what I've done after a few false starts. (My first
attempt built up opd entry info in Scan::local and Scan::global. That
was a bad idea since I'll need opd entry info available for garbage
collection when I get around to tackling that problem.)
Anyway, I'm reasonably happy with the following as now gold generates
a dynamically linked ppc64 "Hello world" that actually runs.. I would
appreciate you reviewing though.
* powerpc.cc: Formatting and white space.
(Powerpc_relobj): Rename got2_section_ to special_.
Add opd_ent_count_, opd_ent_shndx_, opd_ent_off_.
(Powerpc_relobj::opd_shndx, init_opd, get_opd_ent, set_opd_ent,
do_read_relocs, opd_ent_ndx): New functions.
(Target_powerpc::is_branch_reloc): New function.
(Powerpc_relobj::do_find_special_sections): Find .opd for 64-bit.
(ld_2_1, cror_15_15_15, cror_31_31_31): New insn constants.
(Output_data_glink): Rename pltresolve_size to pltresolve_size_.
(Output_data_glink::pltresolve_size): New function.
(Output_data_glink::do_write): Correct toc base. Don't try to use
uint16_t for 24-bit offset.
(Target_powerpc::Scan::local): Handle more relocs.
(Target_powerpc::do_finalize_sections): Set up DT_PPC64_GLINK.
(Target_powerpc::Relocate::relocate): Correct toc base calculation.
Plug in toc restoring insn after plt calls. Translate branches
to function descriptor symbols to corresponding entry point.
* symtab.h: Comment typo.
Index: gold/powerpc.cc
===================================================================
RCS file: /cvs/src/src/gold/powerpc.cc,v
retrieving revision 1.49
diff -u -p -r1.49 powerpc.cc
--- gold/powerpc.cc 14 Aug 2012 03:39:03 -0000 1.49
+++ gold/powerpc.cc 16 Aug 2012 12:11:52 -0000
@@ -60,26 +60,93 @@ public:
Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
- got2_section_(0)
+ special_(0)
{ }
~Powerpc_relobj()
{ }
+ // The .got2 section shndx.
unsigned int
got2_shndx() const
{
if (size == 32)
- return this->got2_section_;
+ return this->special_;
else
return 0;
}
+ // The .opd section shndx.
+ unsigned int
+ opd_shndx() const
+ {
+ if (size == 32)
+ return 0;
+ else
+ return this->special_;
+ }
+
+ // Init OPD entry arrays.
+ void
+ init_opd(size_t opd_size)
+ {
+ size_t count = this->opd_ent_ndx(opd_size);
+ this->opd_ent_count_ = count;
+ this->opd_ent_shndx_ = new unsigned int[count];
+ this->opd_ent_off_ = new typename elfcpp::Elf_types<size>::Elf_Off[count];
+ memset(this->opd_ent_shndx_, 0, sizeof(this->opd_ent_shndx_[0]) * count);
+ }
+
+ // Return section and offset of function entry for .opd + R_OFF.
+ void
+ get_opd_ent(typename elfcpp::Elf_types<size>::Elf_Addr r_off,
+ unsigned int* shndx,
+ typename elfcpp::Elf_types<size>::Elf_Addr* value)
+ {
+ size_t ndx = this->opd_ent_ndx(r_off);
+ gold_assert(ndx < this->opd_ent_count_);
+ *shndx = this->opd_ent_shndx_[ndx];
+ *value = this->opd_ent_off_[ndx];
+ }
+
+ // Set section and offset of function entry for .opd + R_OFF.
+ void
+ set_opd_ent(typename elfcpp::Elf_types<size>::Elf_Addr r_off,
+ unsigned int shndx,
+ typename elfcpp::Elf_types<size>::Elf_Addr value)
+ {
+ size_t ndx = this->opd_ent_ndx(r_off);
+ gold_assert(ndx < this->opd_ent_count_);
+ this->opd_ent_shndx_[ndx] = shndx;
+ this->opd_ent_off_[ndx] = value;
+ }
+
+ // Examine .rela.opd to build info about function entry points.
+ void
+ scan_opd_relocs(size_t reloc_count,
+ const unsigned char* prelocs,
+ const unsigned char* plocal_syms);
+
+ void
+ do_read_relocs(Read_relocs_data*);
+
bool
do_find_special_sections(Read_symbols_data* sd);
private:
- unsigned int got2_section_;
+ // Return index into opd_ent_shndx or opd_ent_off array for .opd entry
+ // at OFF. .opd entries are 24 bytes long, but they can be spaced
+ // 16 bytes apart when the language doesn't use the environment pointer
+ // word. Thus dividing the entry section offset by 16 will give a
+ // unique index.
+ size_t
+ opd_ent_ndx(size_t off) const
+ { return off >> 4;}
+
+ unsigned int special_;
+ size_t opd_ent_count_;
+ unsigned int* opd_ent_shndx_;
+ typename elfcpp::Elf_types<size>::Elf_Off* opd_ent_off_;
};
template<int size, bool big_endian>
@@ -342,6 +409,19 @@ class Target_powerpc : public Sized_targ
typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
+ inline bool
+ is_branch_reloc(unsigned int r_type) const
+ {
+ return (r_type == elfcpp::R_POWERPC_REL24
+ || r_type == elfcpp::R_POWERPC_REL14
+ || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
+ || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN
+ || r_type == elfcpp::R_POWERPC_ADDR24
+ || r_type == elfcpp::R_POWERPC_ADDR14
+ || r_type == elfcpp::R_POWERPC_ADDR14_BRTAKEN
+ || r_type == elfcpp::R_POWERPC_ADDR14_BRNTAKEN);
+ }
+
private:
// Do a TLS relocation.
inline void
@@ -460,8 +540,8 @@ Target::Target_info Target_powerpc<32, t
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
+ false, // isolate_execinstr
+ 0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
@@ -486,8 +566,8 @@ Target::Target_info Target_powerpc<32, f
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
+ false, // isolate_execinstr
+ 0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
@@ -512,8 +592,8 @@ Target::Target_info Target_powerpc<64, t
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
+ false, // isolate_execinstr
+ 0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
@@ -538,8 +618,8 @@ Target::Target_info Target_powerpc<64, f
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
+ false, // isolate_execinstr
+ 0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
@@ -656,9 +736,7 @@ public:
This::template rela<16>(view, 16, 0xffff, value, addend);
}
- // R_POWERPC_ADDR16_HA: Same as R_POWERPC_ADDR16_HI except that if the
- // final value of the low 16 bits of the
- // relocation is negative, add one.
+ // R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff
static inline void
addr16_ha(unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr value,
@@ -693,9 +771,7 @@ public:
This::template rela<16>(view, 16, 0xffff, value - address, addend);
}
- // R_POWERPC_REL16_HA: Same as R_POWERPC_REL16_HI except that if the
- // final value of the low 16 bits of the
- // relocation is negative, add one.
+ // R_POWERPC_REL16_HA: ((Symbol + Addend + 0x8000 - Address) >> 16) & 0xffff
static inline void
rel16_ha(unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr value,
@@ -706,30 +782,95 @@ public:
}
};
-// Stash away the index of .got2 in a relocatable object, if such
-// a section exists.
+// Stash away the index of .got2 or .opd in a relocatable object, if
+// such a section exists.
template<int size, bool big_endian>
bool
Powerpc_relobj<size, big_endian>::do_find_special_sections(
Read_symbols_data* sd)
{
- if (size == 32)
+ const unsigned char* const pshdrs = sd->section_headers->data();
+ const unsigned char* namesu = sd->section_names->data();
+ const char* names = reinterpret_cast<const char*>(namesu);
+ section_size_type names_size = sd->section_names_size;
+ const unsigned char* s;
+
+ s = this->find_shdr(pshdrs, size == 32 ? ".got2" : ".opd",
+ names, names_size, NULL);
+ if (s != NULL)
{
- const unsigned char* const pshdrs = sd->section_headers->data();
- const unsigned char* namesu = sd->section_names->data();
- const char* names = reinterpret_cast<const char*>(namesu);
- section_size_type names_size = sd->section_names_size;
- const unsigned char* s;
+ unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
+ this->special_ = ndx;
+ }
+ return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd);
+}
- s = this->find_shdr(pshdrs, ".got2", names, names_size, NULL);
- if (s != NULL)
- {
- unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
- this->got2_section_ = ndx;
+// Examine .rela.opd to build info about function entry points.
+
+template<int size, bool big_endian>
+void
+Powerpc_relobj<size, big_endian>::scan_opd_relocs(
+ size_t reloc_count,
+ const unsigned char* prelocs,
+ const unsigned char* plocal_syms)
+{
+ if (size == 64)
+ {
+ typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
+ Reltype;
+ const int reloc_size
+ = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+
+ for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
+ {
+ Reltype reloc(prelocs);
+ typename elfcpp::Elf_types<size>::Elf_WXword r_info
+ = reloc.get_r_info();
+ unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+ if (r_type == elfcpp::R_PPC64_ADDR64)
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+ typename elfcpp::Elf_types<size>::Elf_Addr value;
+ bool is_ordinary;
+ unsigned int shndx;
+ if (r_sym < this->local_symbol_count())
+ {
+ typename elfcpp::Sym<size, big_endian>
+ lsym(plocal_syms + r_sym * sym_size);
+ shndx = lsym.get_st_shndx();
+ shndx = this->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+ value = lsym.get_st_value();
+ }
+ else
+ shndx = this->symbol_section_and_value(r_sym, &value,
+ &is_ordinary);
+ this->set_opd_ent(reloc.get_r_offset(), shndx,
+ value + reloc.get_r_addend());
+ }
}
}
- return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd);
+}
+
+template<int size, bool big_endian>
+void
+Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
+{
+ Sized_relobj_file<size, big_endian>::do_read_relocs(rd);
+ if (size == 64)
+ {
+ for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin();
+ p != rd->relocs.end();
+ ++p)
+ if (p->data_shndx == this->opd_shndx())
+ {
+ this->init_opd(this->section_size(this->opd_shndx()));
+ this->scan_opd_relocs(p->reloc_count, p->contents->data(),
+ rd->local_symbols->data());
+ break;
+ }
+ }
}
// Set up PowerPC target specific relobj.
@@ -745,21 +886,20 @@ Target_powerpc<size, big_endian>::do_mak
if (et == elfcpp::ET_REL)
{
Powerpc_relobj<size, big_endian>* obj =
- new Powerpc_relobj<size, big_endian>(name, input_file, offset, ehdr);
+ new Powerpc_relobj<size, big_endian>(name, input_file, offset, ehdr);
obj->setup();
return obj;
}
else if (et == elfcpp::ET_DYN)
{
Sized_dynobj<size, big_endian>* obj =
- new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
+ new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
obj->setup();
return obj;
}
else
{
- gold_error(_("%s: unsupported ELF file type %d"),
- name.c_str(), et);
+ gold_error(_("%s: unsupported ELF file type %d"), name.c_str(), et);
return NULL;
}
}
@@ -1013,45 +1153,48 @@ Output_data_plt_powerpc<size, big_endian
}
}
-static const uint32_t addis_11_11 = 0x3d6b0000;
-static const uint32_t addis_11_30 = 0x3d7e0000;
-static const uint32_t addis_12_12 = 0x3d8c0000;
-static const uint32_t addi_11_11 = 0x396b0000;
-static const uint32_t add_0_11_11 = 0x7c0b5a14;
-static const uint32_t add_11_0_11 = 0x7d605a14;
-static const uint32_t b = 0x48000000;
-static const uint32_t bcl_20_31 = 0x429f0005;
-static const uint32_t bctr = 0x4e800420;
-static const uint32_t blrl = 0x4e800021;
-static const uint32_t lis_11 = 0x3d600000;
-static const uint32_t lis_12 = 0x3d800000;
-static const uint32_t lwzu_0_12 = 0x840c0000;
-static const uint32_t lwz_0_12 = 0x800c0000;
-static const uint32_t lwz_11_11 = 0x816b0000;
-static const uint32_t lwz_11_30 = 0x817e0000;
-static const uint32_t lwz_12_12 = 0x818c0000;
-static const uint32_t mflr_0 = 0x7c0802a6;
-static const uint32_t mflr_12 = 0x7d8802a6;
-static const uint32_t mtctr_0 = 0x7c0903a6;
-static const uint32_t mtctr_11 = 0x7d6903a6;
-static const uint32_t mtlr_0 = 0x7c0803a6;
-static const uint32_t nop = 0x60000000;
-static const uint32_t sub_11_11_12 = 0x7d6c5850;
-static const uint32_t addis_12_2 = 0x3d820000;
-static const uint32_t std_2_1 = 0xf8410000;
-static const uint32_t ld_11_12 = 0xe96c0000;
-static const uint32_t ld_2_12 = 0xe84c0000;
-static const uint32_t addi_12_12 = 0x398c0000;
-static const uint32_t ld_11_2 = 0xe9620000;
-static const uint32_t addi_2_2 = 0x38420000;
-static const uint32_t ld_2_2 = 0xe8420000;
-static const uint32_t mflr_11 = 0x7d6802a6;
-static const uint32_t ld_2_11 = 0xe84b0000;
-static const uint32_t mtlr_12 = 0x7d8803a6;
-static const uint32_t add_12_2_11 = 0x7d825a14;
-static const uint32_t li_0_0 = 0x38000000;
-static const uint32_t lis_0_0 = 0x3c000000;
-static const uint32_t ori_0_0_0 = 0x60000000;
+static const uint32_t addis_11_11 = 0x3d6b0000;
+static const uint32_t addis_11_30 = 0x3d7e0000;
+static const uint32_t addis_12_12 = 0x3d8c0000;
+static const uint32_t addi_11_11 = 0x396b0000;
+static const uint32_t add_0_11_11 = 0x7c0b5a14;
+static const uint32_t add_11_0_11 = 0x7d605a14;
+static const uint32_t b = 0x48000000;
+static const uint32_t bcl_20_31 = 0x429f0005;
+static const uint32_t bctr = 0x4e800420;
+static const uint32_t blrl = 0x4e800021;
+static const uint32_t lis_11 = 0x3d600000;
+static const uint32_t lis_12 = 0x3d800000;
+static const uint32_t lwzu_0_12 = 0x840c0000;
+static const uint32_t lwz_0_12 = 0x800c0000;
+static const uint32_t lwz_11_11 = 0x816b0000;
+static const uint32_t lwz_11_30 = 0x817e0000;
+static const uint32_t lwz_12_12 = 0x818c0000;
+static const uint32_t mflr_0 = 0x7c0802a6;
+static const uint32_t mflr_12 = 0x7d8802a6;
+static const uint32_t mtctr_0 = 0x7c0903a6;
+static const uint32_t mtctr_11 = 0x7d6903a6;
+static const uint32_t mtlr_0 = 0x7c0803a6;
+static const uint32_t nop = 0x60000000;
+static const uint32_t sub_11_11_12 = 0x7d6c5850;
+static const uint32_t addis_12_2 = 0x3d820000;
+static const uint32_t std_2_1 = 0xf8410000;
+static const uint32_t ld_2_1 = 0xe8410000;
+static const uint32_t ld_11_12 = 0xe96c0000;
+static const uint32_t ld_2_12 = 0xe84c0000;
+static const uint32_t addi_12_12 = 0x398c0000;
+static const uint32_t ld_11_2 = 0xe9620000;
+static const uint32_t addi_2_2 = 0x38420000;
+static const uint32_t ld_2_2 = 0xe8420000;
+static const uint32_t mflr_11 = 0x7d6802a6;
+static const uint32_t ld_2_11 = 0xe84b0000;
+static const uint32_t mtlr_12 = 0x7d8803a6;
+static const uint32_t add_12_2_11 = 0x7d825a14;
+static const uint32_t li_0_0 = 0x38000000;
+static const uint32_t lis_0_0 = 0x3c000000;
+static const uint32_t ori_0_0_0 = 0x60000000;
+static const uint32_t cror_15_15_15 = 0x4def7b82;
+static const uint32_t cror_31_31_31 = 0x4ffffb82;
// Write out the PLT.
@@ -1149,6 +1292,12 @@ class Output_data_glink : public Output_
return this->pltresolve_;
}
+ int
+ pltresolve_size() const
+ {
+ return this->pltresolve_size_;
+ }
+
protected:
// Write to a map file.
void
@@ -1156,7 +1305,7 @@ class Output_data_glink : public Output_
{ mapfile->print_output_data(this, _("** glink")); }
private:
- static const int pltresolve_size = 16*4;
+ static const int pltresolve_size_ = 16*4;
void
set_final_data_size();
@@ -1171,7 +1320,7 @@ class Output_data_glink : public Output_
Glink_sym_ent(const Symbol* sym,
const elfcpp::Rela<size, big_endian>& reloc,
const Sized_relobj<size, big_endian>* object)
- : sym_(sym), object_(0), addend_(0)
+ : sym_(sym), addend_(0), object_(0)
{
if (size != 32)
this->addend_ = reloc.get_r_addend();
@@ -1185,16 +1334,16 @@ class Output_data_glink : public Output_
}
}
- const Symbol* sym_;
- const Sized_relobj<size, big_endian>* object_;
- unsigned int addend_;
-
bool operator==(const Glink_sym_ent& that) const
{
return (this->sym_ == that.sym_
&& this->object_ == that.object_
&& this->addend_ == that.addend_);
}
+
+ const Symbol* sym_;
+ unsigned int addend_;
+ const Sized_relobj<size, big_endian>* object_;
};
class Glink_sym_ent_hash
@@ -1276,13 +1425,13 @@ Output_data_glink<size, big_endian>::set
total += 4 * (count - 1);
total += -total & 15;
- total += this->pltresolve_size;
+ total += this->pltresolve_size_;
}
else
{
total *= 32;
this->pltresolve_ = total;
- total += this->pltresolve_size;
+ total += this->pltresolve_size_;
// space for branch table
total += 8 * count;
@@ -1314,7 +1463,7 @@ ha(uint32_t a)
template<bool big_endian>
static inline void
-write_insn(unsigned char *p, uint32_t v)
+write_insn(unsigned char* p, uint32_t v)
{
elfcpp::Swap<32, big_endian>::writeval(p, v);
}
@@ -1329,19 +1478,21 @@ Output_data_glink<size, big_endian>::do_
const section_size_type oview_size =
convert_to_section_size_type(this->data_size());
unsigned char* const oview = of->get_output_view(off, oview_size);
- unsigned char *p;
+ unsigned char* p;
// The base address of the .plt section.
uint32_t plt_base = this->targ_->plt_section()->address();
// The address of _GLOBAL_OFFSET_TABLE_.
- const Output_data_got_powerpc<size, big_endian> *got;
+ const Output_data_got_powerpc<size, big_endian>* got;
typename elfcpp::Elf_types<size>::Elf_Addr g_o_t;
got = this->targ_->got_section();
- g_o_t = got->address() + got->g_o_t();
if (size == 64)
{
+ const unsigned int toc_base_offset = 0x8000;
+ g_o_t = got->output_section()->address() + toc_base_offset;
+
// Write out call stubs.
typename Glink_entries::const_iterator g;
for (g = this->glink_entries_.begin();
@@ -1407,7 +1558,7 @@ Output_data_glink<size, big_endian>::do_
write_insn<big_endian>(p, mtctr_11), p += 4;
write_insn<big_endian>(p, ld_11_12 + 16), p += 4;
write_insn<big_endian>(p, bctr), p += 4;
- while (p < oview + this->pltresolve_ + this->pltresolve_size)
+ while (p < oview + this->pltresolve_ + this->pltresolve_size_)
write_insn<big_endian>(p, nop), p += 4;
// Write lazy link call stubs.
@@ -1423,13 +1574,15 @@ Output_data_glink<size, big_endian>::do_
write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4;
write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4;
}
- uint16_t branch_off = this->pltresolve_ + 8 - (p - oview);
+ uint32_t branch_off = this->pltresolve_ + 8 - (p - oview);
write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4;
indx++;
}
}
else
{
+ g_o_t = got->address() + got->g_o_t();
+
// Write out call stubs.
typename Glink_entries::const_iterator g;
for (g = this->glink_entries_.begin();
@@ -1480,8 +1633,8 @@ Output_data_glink<size, big_endian>::do_
// Write out pltresolve branch table.
p = oview + this->pltresolve_;
- unsigned int the_end = oview_size - this->pltresolve_size;
- unsigned char *end_p = oview + the_end;
+ unsigned int the_end = oview_size - this->pltresolve_size_;
+ unsigned char* end_p = oview + the_end;
while (p < end_p - 8 * 4)
write_insn<big_endian>(p, b + end_p - p), p += 4;
while (p < end_p)
@@ -1852,8 +2005,10 @@ Target_powerpc<size, big_endian>::Scan::
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_POWERPC_ADDR32:
- case elfcpp::R_POWERPC_ADDR16_HA:
+ case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_ADDR16_LO:
+ case elfcpp::R_POWERPC_ADDR16_HI:
+ case elfcpp::R_POWERPC_ADDR16_HA:
// If building a shared library (or a position-independent
// executable), we need to create a dynamic relocation for
// this location.
@@ -1893,8 +2048,8 @@ Target_powerpc<size, big_endian>::Scan::
case elfcpp::R_POWERPC_GOT16_HI:
case elfcpp::R_POWERPC_GOT16_HA:
{
- // The symbol requires a GOT entry.
- Output_data_got_powerpc<size, big_endian>* got;
+ // The symbol requires a GOT entry.
+ Output_data_got_powerpc<size, big_endian>* got;
unsigned int r_sym;
got = target->got_section(symtab, layout);
@@ -1985,16 +2140,16 @@ Target_powerpc<size, big_endian>::Scan::
case elfcpp::R_PPC_LOCAL24PC:
break;
+ case elfcpp::R_PPC64_ADDR64:
+ case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_ADDR16_LO:
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_ADDR16_HA:
- case elfcpp::R_POWERPC_ADDR32:
- case elfcpp::R_PPC64_ADDR64:
{
- // Make a PLT entry if necessary.
- if (gsym->needs_plt_entry())
- {
+ // Make a PLT entry if necessary.
+ if (gsym->needs_plt_entry())
+ {
target->make_plt_entry(layout, gsym, reloc, 0);
// Since this is not a PC-relative relocation, we may be
// taking the address of a function. In that case we need to
@@ -2003,16 +2158,16 @@ Target_powerpc<size, big_endian>::Scan::
if (size == 32
&& gsym->is_from_dynobj() && !parameters->options().shared())
gsym->set_needs_dynsym_value();
- }
- // Make a dynamic relocation if necessary.
- if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
- {
- if (gsym->may_need_copy_reloc())
- {
- target->copy_reloc(symtab, layout, object,
- data_shndx, output_section, gsym, reloc);
- }
- else if ((r_type == elfcpp::R_POWERPC_ADDR32
+ }
+ // Make a dynamic relocation if necessary.
+ if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
+ {
+ if (gsym->may_need_copy_reloc())
+ {
+ target->copy_reloc(symtab, layout, object,
+ data_shndx, output_section, gsym, reloc);
+ }
+ else if ((r_type == elfcpp::R_POWERPC_ADDR32
|| r_type == elfcpp::R_PPC64_ADDR64)
&& gsym->can_use_relative_reloc(false))
{
@@ -2086,8 +2241,8 @@ Target_powerpc<size, big_endian>::Scan::
case elfcpp::R_POWERPC_GOT16_HI:
case elfcpp::R_POWERPC_GOT16_HA:
{
- // The symbol requires a GOT entry.
- Output_data_got_powerpc<size, big_endian>* got;
+ // The symbol requires a GOT entry.
+ Output_data_got_powerpc<size, big_endian>* got;
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
@@ -2261,13 +2416,21 @@ Target_powerpc<size, big_endian>::do_fin
layout->add_target_dynamic_tags(false, this->plt_, rel_plt,
this->rela_dyn_, true, size == 32);
+ Output_data_dynamic* odyn = layout->dynamic_data();
if (size == 32)
{
this->got_->finalize_data_size();
- Output_data_dynamic* odyn = layout->dynamic_data();
odyn->add_section_plus_offset(elfcpp::DT_PPC_GOT,
this->got_, this->got_->g_o_t());
}
+ else
+ {
+ this->glink_->finalize_data_size();
+ odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK,
+ this->glink_,
+ (this->glink_->pltresolve()
+ + this->glink_->pltresolve_size() - 32));
+ }
// Emit any relocs we saved in an attempt to avoid generating COPY
// relocs.
@@ -2290,13 +2453,13 @@ Target_powerpc<size, big_endian>::Reloca
const Symbol_value<size>* psymval,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr address,
- section_size_type /* view_size */)
+ section_size_type view_size)
{
const unsigned int toc_base_offset = 0x8000;
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
const Powerpc_relobj<size, big_endian>* const object
= static_cast<const Powerpc_relobj<size, big_endian>*>(relinfo->object);
- elfcpp::Elf_Xword value;
+ typename elfcpp::Elf_types<size>::Elf_Addr value;
if (r_type == elfcpp::R_POWERPC_GOT16
|| r_type == elfcpp::R_POWERPC_GOT16_LO
@@ -2320,7 +2483,8 @@ Target_powerpc<size, big_endian>::Reloca
}
else if (r_type == elfcpp::R_PPC64_TOC)
{
- value = target->got_section()->address() + toc_base_offset;
+ value = (target->got_section()->output_section()->address()
+ + toc_base_offset);
}
else if (gsym != NULL
&& (r_type == elfcpp::R_POWERPC_REL24
@@ -2332,6 +2496,36 @@ Target_powerpc<size, big_endian>::Reloca
glink = target->glink_section();
unsigned int glink_index = glink->find_entry(gsym, rela, object);
value = glink->address() + glink_index * glink->glink_entry_size();
+ if (size == 64)
+ {
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ bool can_plt_call = false;
+ if (rela.get_r_offset() + 8 <= view_size)
+ {
+ Valtype insn2 = elfcpp::Swap<32, big_endian>::readval(wv + 1);
+ if (insn2 == nop
+ || insn2 == cror_15_15_15 || insn2 == cror_31_31_31)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(wv + 1, ld_2_1 + 40);
+ can_plt_call = true;
+ }
+ }
+ if (!can_plt_call)
+ {
+ if (parameters->options().output_is_executable())
+ {
+ Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
+ if ((insn & 1) == 0)
+ can_plt_call = true;
+ }
+ else if (strcmp(gsym->name(), ".__libc_start_main") == 0)
+ can_plt_call = true;
+ }
+ if (!can_plt_call)
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("call lacks nop, can't restore toc"));
+ }
}
else
{
@@ -2339,6 +2533,25 @@ Target_powerpc<size, big_endian>::Reloca
if (r_type != elfcpp::R_PPC_PLTREL24)
addend = rela.get_r_addend();
value = psymval->value(object, addend);
+ if (size == 64 && this->is_branch_reloc(r_type))
+ {
+ Powerpc_relobj<size, big_endian>* symobj = const_cast
+ <Powerpc_relobj<size, big_endian>*>(object);
+ if (gsym != NULL)
+ symobj = static_cast
+ <Powerpc_relobj<size, big_endian>*>(gsym->object());
+ unsigned int shndx = symobj->opd_shndx();
+ typename elfcpp::Elf_types<size>::Elf_Addr opd_addr
+ = (symobj->output_section(shndx)->address()
+ + symobj->output_section_offset(shndx));
+ if (value >= opd_addr
+ && value < opd_addr + symobj->section_size(shndx))
+ {
+ symobj->get_opd_ent(value - opd_addr, &shndx, &value);
+ value += (symobj->output_section(shndx)->address()
+ + symobj->output_section_offset(shndx));
+ }
+ }
}
switch (r_type)
@@ -2350,7 +2563,8 @@ Target_powerpc<size, big_endian>::Reloca
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// Subtract the TOC base address.
- value -= target->got_section()->address() + toc_base_offset;
+ value -= (target->got_section()->output_section()->address()
+ + toc_base_offset);
break;
case elfcpp::R_POWERPC_SECTOFF:
@@ -2716,13 +2930,13 @@ Target_powerpc<size, big_endian>::reloca
new_offset = offset + offset_in_output_section;
else
{
- section_offset_type sot_offset =
- convert_types<section_offset_type, Address>(offset);
+ section_offset_type sot_offset =
+ convert_types<section_offset_type, Address>(offset);
section_offset_type new_sot_offset =
- output_section->output_offset(object, relinfo->data_shndx,
- sot_offset);
+ output_section->output_offset(object, relinfo->data_shndx,
+ sot_offset);
gold_assert(new_sot_offset != -1);
- new_offset = new_sot_offset;
+ new_offset = new_sot_offset;
}
reloc_write.put_r_offset(new_offset);
Index: gold/symtab.h
===================================================================
RCS file: /cvs/src/src/gold/symtab.h,v
retrieving revision 1.127
diff -u -p -r1.127 symtab.h
--- gold/symtab.h 31 Oct 2011 21:36:54 -0000 1.127
+++ gold/symtab.h 16 Aug 2012 12:03:01 -0000
@@ -62,7 +62,7 @@ class Garbage_collection;
class Icf;
// The base class of an entry in the symbol table. The symbol table
-// can have a lot of entries, so we don't want this class to big.
+// can have a lot of entries, so we don't want this class too big.
// Size dependent fields can be found in the template class
// Sized_symbol. Targets may support their own derived classes.
--
Alan Modra
Australia Development Lab, IBM