forked from Imagelibrary/binutils-gdb
* gold.cc (queue_middle_tasks): Move detect_odr_violations..
* layout.cc (Layout_task_runner::run): ..to here. * symtab.h (struct Symbol_location): Extract from.. (class Symbol_table): ..here. * symtab.cc (Symbol_table::linenos_from_loc): Invoke function_location. * target.h (class Target): Add function_location and do_function_location functions. (class Sized_target): Add do_function_location. * object.h (class Sized_relobj_file): Move find_shdr.. (class Object): ..to here. * object.cc: Likewise. Update to suit. Instantiate. (Sized_relobj_file::find_eh_frame): Update find_shdr call. * powerpc.cc (class Powerpc_dynobj): New. (Target_powerpc::do_function_location): New function. (Powerpc_relobj::do_find_special_sections): Update find_shdr call. (Powerpc_dynobj::do_read_symbols): New function. (Target_powerpc::do_make_elf_object): Make a Powerpc_dynobj.
This commit is contained in:
251
gold/powerpc.cc
251
gold/powerpc.cc
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "gold.h"
|
||||
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "elfcpp.h"
|
||||
#include "dwarf.h"
|
||||
@@ -319,6 +320,109 @@ private:
|
||||
std::vector<Stub_table<size, big_endian>*> stub_table_;
|
||||
};
|
||||
|
||||
template<int size, bool big_endian>
|
||||
class Powerpc_dynobj : public Sized_dynobj<size, big_endian>
|
||||
{
|
||||
public:
|
||||
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
|
||||
|
||||
Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
|
||||
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
|
||||
: Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr),
|
||||
opd_shndx_(0), opd_ent_()
|
||||
{ }
|
||||
|
||||
~Powerpc_dynobj()
|
||||
{ }
|
||||
|
||||
// Call Sized_dynobj::do_read_symbols to read the symbols then
|
||||
// read .opd from a dynamic object, filling in opd_ent_ vector,
|
||||
void
|
||||
do_read_symbols(Read_symbols_data*);
|
||||
|
||||
// The .opd section shndx.
|
||||
unsigned int
|
||||
opd_shndx() const
|
||||
{
|
||||
return this->opd_shndx_;
|
||||
}
|
||||
|
||||
// The .opd section address.
|
||||
Address
|
||||
opd_address() const
|
||||
{
|
||||
return this->opd_address_;
|
||||
}
|
||||
|
||||
// Init OPD entry arrays.
|
||||
void
|
||||
init_opd(size_t opd_size)
|
||||
{
|
||||
size_t count = this->opd_ent_ndx(opd_size);
|
||||
this->opd_ent_.resize(count);
|
||||
}
|
||||
|
||||
// Return section and offset of function entry for .opd + R_OFF.
|
||||
unsigned int
|
||||
get_opd_ent(Address r_off, Address* value = NULL) const
|
||||
{
|
||||
size_t ndx = this->opd_ent_ndx(r_off);
|
||||
gold_assert(ndx < this->opd_ent_.size());
|
||||
gold_assert(this->opd_ent_[ndx].shndx != 0);
|
||||
if (value != NULL)
|
||||
*value = this->opd_ent_[ndx].off;
|
||||
return this->opd_ent_[ndx].shndx;
|
||||
}
|
||||
|
||||
// Set section and offset of function entry for .opd + R_OFF.
|
||||
void
|
||||
set_opd_ent(Address r_off, unsigned int shndx, Address value)
|
||||
{
|
||||
size_t ndx = this->opd_ent_ndx(r_off);
|
||||
gold_assert(ndx < this->opd_ent_.size());
|
||||
this->opd_ent_[ndx].shndx = shndx;
|
||||
this->opd_ent_[ndx].off = value;
|
||||
}
|
||||
|
||||
private:
|
||||
// Used to specify extent of executable sections.
|
||||
struct Sec_info
|
||||
{
|
||||
Sec_info(Address start_, Address len_, unsigned int shndx_)
|
||||
: start(start_), len(len_), shndx(shndx_)
|
||||
{ }
|
||||
|
||||
bool
|
||||
operator<(const Sec_info& that) const
|
||||
{ return this->start < that.start; }
|
||||
|
||||
Address start;
|
||||
Address len;
|
||||
unsigned int shndx;
|
||||
};
|
||||
|
||||
struct Opd_ent
|
||||
{
|
||||
unsigned int shndx;
|
||||
Address off;
|
||||
};
|
||||
|
||||
// Return index into opd_ent_ array for .opd entry at OFF.
|
||||
size_t
|
||||
opd_ent_ndx(size_t off) const
|
||||
{ return off >> 4;}
|
||||
|
||||
// For 64-bit the .opd section shndx and address.
|
||||
unsigned int opd_shndx_;
|
||||
Address opd_address_;
|
||||
|
||||
// The first 8-byte word of an OPD entry gives the address of the
|
||||
// entry point of the function. Records the section and offset
|
||||
// corresponding to the address. Note that in dynamic objects,
|
||||
// offset is *not* relative to the section.
|
||||
std::vector<Opd_ent> opd_ent_;
|
||||
};
|
||||
|
||||
template<int size, bool big_endian>
|
||||
class Target_powerpc : public Sized_target<size, big_endian>
|
||||
{
|
||||
@@ -448,6 +552,9 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
||||
int64_t
|
||||
do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
|
||||
|
||||
void
|
||||
do_function_location(Symbol_location*) const;
|
||||
|
||||
// Relocate a section.
|
||||
void
|
||||
relocate_section(const Relocate_info<size, big_endian>*,
|
||||
@@ -1518,8 +1625,9 @@ Powerpc_relobj<size, big_endian>::do_find_special_sections(
|
||||
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);
|
||||
s = this->template find_shdr<size, big_endian>(pshdrs,
|
||||
size == 32 ? ".got2" : ".opd",
|
||||
names, names_size, NULL);
|
||||
if (s != NULL)
|
||||
{
|
||||
unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
|
||||
@@ -1634,6 +1742,105 @@ Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
|
||||
}
|
||||
}
|
||||
|
||||
// Call Sized_dynobj::do_read_symbols to read the symbols then
|
||||
// read .opd from a dynamic object, filling in opd_ent_ vector,
|
||||
|
||||
template<int size, bool big_endian>
|
||||
void
|
||||
Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
|
||||
{
|
||||
Sized_dynobj<size, big_endian>::do_read_symbols(sd);
|
||||
if (size == 64)
|
||||
{
|
||||
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
||||
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);
|
||||
const unsigned char* s = NULL;
|
||||
const unsigned char* opd;
|
||||
section_size_type opd_size;
|
||||
|
||||
// Find and read .opd section.
|
||||
while (1)
|
||||
{
|
||||
s = this->template find_shdr<size, big_endian>(pshdrs, ".opd", names,
|
||||
sd->section_names_size,
|
||||
s);
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
typename elfcpp::Shdr<size, big_endian> shdr(s);
|
||||
if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
|
||||
&& (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0)
|
||||
{
|
||||
this->opd_shndx_ = (s - pshdrs) / shdr_size;
|
||||
this->opd_address_ = shdr.get_sh_addr();
|
||||
opd_size = convert_to_section_size_type(shdr.get_sh_size());
|
||||
opd = this->get_view(shdr.get_sh_offset(), opd_size,
|
||||
true, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build set of executable sections.
|
||||
// Using a set is probably overkill. There is likely to be only
|
||||
// a few executable sections, typically .init, .text and .fini,
|
||||
// and they are generally grouped together.
|
||||
typedef std::set<Sec_info> Exec_sections;
|
||||
Exec_sections exec_sections;
|
||||
s = pshdrs;
|
||||
for (unsigned int i = 1; i < this->shnum(); ++i, s += shdr_size)
|
||||
{
|
||||
typename elfcpp::Shdr<size, big_endian> shdr(s);
|
||||
if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
|
||||
&& ((shdr.get_sh_flags()
|
||||
& (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR))
|
||||
== (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR))
|
||||
&& shdr.get_sh_size() != 0)
|
||||
{
|
||||
exec_sections.insert(Sec_info(shdr.get_sh_addr(),
|
||||
shdr.get_sh_size(), i));
|
||||
}
|
||||
}
|
||||
if (exec_sections.empty())
|
||||
return;
|
||||
|
||||
// Look over the OPD entries. This is complicated by the fact
|
||||
// that some binaries will use two-word entries while others
|
||||
// will use the standard three-word entries. In most cases
|
||||
// the third word (the environment pointer for languages like
|
||||
// Pascal) is unused and will be zero. If the third word is
|
||||
// used it should not be pointing into executable sections,
|
||||
// I think.
|
||||
this->init_opd(opd_size);
|
||||
for (const unsigned char* p = opd; p < opd + opd_size; p += 8)
|
||||
{
|
||||
typedef typename elfcpp::Swap<64, big_endian>::Valtype Valtype;
|
||||
const Valtype* valp = reinterpret_cast<const Valtype*>(p);
|
||||
Valtype val = elfcpp::Swap<64, big_endian>::readval(valp);
|
||||
if (val == 0)
|
||||
// Chances are that this is the third word of an OPD entry.
|
||||
continue;
|
||||
typename Exec_sections::const_iterator e
|
||||
= exec_sections.upper_bound(Sec_info(val, 0, 0));
|
||||
if (e != exec_sections.begin())
|
||||
{
|
||||
--e;
|
||||
if (e->start <= val && val < e->start + e->len)
|
||||
{
|
||||
// We have an address in an executable section.
|
||||
// VAL ought to be the function entry, set it up.
|
||||
this->set_opd_ent(p - opd, e->shndx, val);
|
||||
// Skip second word of OPD entry, the TOC pointer.
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
// If we didn't match any executable sections, we likely
|
||||
// have a non-zero third word in the OPD entry.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set up some symbols.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
@@ -1706,8 +1913,8 @@ Target_powerpc<size, big_endian>::do_make_elf_object(
|
||||
}
|
||||
else if (et == elfcpp::ET_DYN)
|
||||
{
|
||||
Sized_dynobj<size, big_endian>* obj =
|
||||
new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
|
||||
Powerpc_dynobj<size, big_endian>* obj =
|
||||
new Powerpc_dynobj<size, big_endian>(name, input_file, offset, ehdr);
|
||||
obj->setup();
|
||||
return obj;
|
||||
}
|
||||
@@ -5581,6 +5788,42 @@ Target_powerpc<size, big_endian>::do_gc_mark_symbol(
|
||||
}
|
||||
}
|
||||
|
||||
// For a symbol location in .opd, set LOC to the location of the
|
||||
// function entry.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
void
|
||||
Target_powerpc<size, big_endian>::do_function_location(
|
||||
Symbol_location* loc) const
|
||||
{
|
||||
if (size == 64)
|
||||
{
|
||||
if (loc->object->is_dynamic())
|
||||
{
|
||||
Powerpc_dynobj<size, big_endian>* ppc_object
|
||||
= static_cast<Powerpc_dynobj<size, big_endian>*>(loc->object);
|
||||
if (loc->shndx == ppc_object->opd_shndx())
|
||||
{
|
||||
Address dest_off;
|
||||
Address off = loc->offset - ppc_object->opd_address();
|
||||
loc->shndx = ppc_object->get_opd_ent(off, &dest_off);
|
||||
loc->offset = dest_off;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const Powerpc_relobj<size, big_endian>* ppc_object
|
||||
= static_cast<const Powerpc_relobj<size, big_endian>*>(loc->object);
|
||||
if (loc->shndx == ppc_object->opd_shndx())
|
||||
{
|
||||
Address dest_off;
|
||||
loc->shndx = ppc_object->get_opd_ent(loc->offset, &dest_off);
|
||||
loc->offset = dest_off;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan relocations for a section.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
|
||||
Reference in New Issue
Block a user