This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[GOLD] powerpc64 --gc-sections RFC
- From: Alan Modra <amodra at gmail dot com>
- To: binutils at sourceware dot org
- Date: Wed, 5 Sep 2012 23:31:05 +0930
- Subject: [GOLD] powerpc64 --gc-sections RFC
This is my first bash at making --gc-sections work for powerpc64 gold.
The problem: powerpc64 gcc generates a single .opd section (containing
function descriptors) for --function-sections code rather than
emitting function descriptors in per-group sections. This has the
unfortunate effect of tying all the code together. Taking the address
of a function, or making a function call, results in a reloc that
references .opd, and .opd itself references all function code
sections in the object. So either ld splits up .opd and .rela.opd
itself, or something special needs to happen when garbage collection
references .opd.
I took the second approach. A reloc resolving to .opd adds a
reference from the source section to .opd, *and* to the code section
given by the appropriate .opd reloc. The process is complicated by
.opd relocs not being available if the reference is to another object
that hasn't yet had its relocs read. In that case I queue up the
reference in a container on the destination object, for processing
along with that object's gc_process_relocs. I'm not sure whether this
is a good design. It appears to me that gold currently serializes
gc_process_relocs, so we only have one thread fiddling with
symtab->gc_ and hence we should also only have one thread poking at
ppc_object->access_from_map_. Correct? Another issue is whether
do_read_relocs can be running for one object in parallel with
gc_process_relocs for a different object. If so, I need to make
have_opd() thread safe..
Another tweak for powerpc64 is that _start, undefs on the command
line, and export dynamic syms, need to mark both .opd and their code
section if they happen to be functions.
* target.h (Target::gc_mark_symbol, do_gc_mark_symbol): New functions.
(Sized_target::dest_shndx): New function.
* gc.h (gc_process_relocs): Call dest_shndx.
* gold.cc (queue_middle_tasks): Use gc_mark_symbol on start sym.
* symtab.h (default_gc_mark_symbol): New function.
(gc_mark_symbol): Call above and target gc_mark_symbol.
* symtab.c (Symbol_table::gc_mark_undef_symbols): Use gc_mark_symbol.
(gc_mark_symbol): Rename to default_gc_mark_symbol. Remove
unnecessary cast.
* powerpc.cc (Powerpc_relobj::access_from_map_): New var.
(Powerpc_relobj::have_opd, access_from_map, add_reference): New
functions.
(Target_powerpc::dest_shndx): Translate function descriptor refs
to code refs, or stash away for later processing.
(Target_powerpc::gc_process_relocs): Call gc()->add_reference on
stashed refs.
(Target_powerpc::do_gc_mark_symbol): New function.
Index: gold/target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.68
diff -u -p -r1.68 target.h
--- gold/target.h 5 Sep 2012 00:34:20 -0000 1.68
+++ gold/target.h 5 Sep 2012 11:03:32 -0000
@@ -421,6 +421,11 @@ class Target
size_t* plen) const
{ return this->do_output_section_name(relobj, name, plen); }
+ // Add any extra references for this symbol to the gc work list.
+ void
+ gc_mark_symbol(Symbol_table* symtab, Symbol* sym) const
+ { this->do_gc_mark_symbol(symtab, sym); }
+
protected:
// This struct holds the constant information for a child class. We
// use a struct to avoid the overhead of virtual function calls for
@@ -669,6 +674,11 @@ class Target
do_output_section_name(const Relobj*, const char*, size_t*) const
{ return NULL; }
+ // This may be overridden by the child class.
+ virtual void
+ do_gc_mark_symbol(Symbol_table*, Symbol*) const
+ { }
+
private:
// The implementations of the four do_make_elf_object virtual functions are
// almost identical except for their sizes and endianness. We use a template.
@@ -935,6 +945,18 @@ class Sized_target : public Target
section_size_type /* view_size */)
{ gold_unreachable(); }
+ // Given a reference from SRC_OBJ, SRC_INDX to a location specified
+ // by DST_OBJ, DST_INDX and DST_OFF, return the true destination
+ // section that should be marked during --gc-sections processing.
+
+ virtual unsigned int
+ dest_shndx(Object* /* src_obj */,
+ unsigned int /* src_indx */,
+ Object* /* dst_obj */,
+ unsigned int dst_indx,
+ typename elfcpp::Elf_types<size>::Elf_Addr /* dst_off */)
+ { return dst_indx; }
+
protected:
Sized_target(const Target::Target_info* pti)
: Target(pti)
Index: gold/gc.h
===================================================================
RCS file: /cvs/src/src/gold/gc.h,v
retrieving revision 1.16
diff -u -p -r1.16 gc.h
--- gold/gc.h 26 Apr 2012 00:07:17 -0000 1.16
+++ gold/gc.h 5 Sep 2012 11:03:31 -0000
@@ -187,8 +187,6 @@ gc_process_relocs(
size_t local_count,
const unsigned char* plocal_syms)
{
- Object* dst_obj;
- unsigned int dst_indx;
Scan scan;
typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
@@ -235,6 +233,9 @@ gc_process_relocs(
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
typename elfcpp::Elf_types<size>::Elf_Swxword addend =
Reloc_types<sh_type, size, big_endian>::get_reloc_addend_noerror(&reloc);
+ Object* dst_obj;
+ unsigned int dst_indx;
+ typename elfcpp::Elf_types<size>::Elf_Addr dst_off;
if (r_sym < local_count)
{
@@ -246,6 +247,8 @@ gc_process_relocs(
shndx = src_obj->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
dst_obj = src_obj;
dst_indx = shndx;
+ dst_off = lsym.get_st_value();
+
if (is_icf_tracked)
{
if (is_ordinary)
@@ -288,11 +291,13 @@ gc_process_relocs(
dst_obj = NULL;
dst_indx = 0;
+ dst_off = 0;
bool is_ordinary = false;
if (gsym->source() == Symbol::FROM_OBJECT)
{
dst_obj = gsym->object();
dst_indx = gsym->shndx(&is_ordinary);
+ dst_off = static_cast<const Sized_symbol<size>*>(gsym)->value();
}
// When doing safe folding, check to see if this relocation is that
@@ -347,7 +352,14 @@ gc_process_relocs(
}
if (parameters->options().gc_sections())
{
- symtab->gc()->add_reference(src_obj, src_indx, dst_obj, dst_indx);
+ dst_off += Reloc_types<sh_type, size, big_endian>::
+ get_reloc_addend_noerror(&reloc);
+ unsigned int code_indx = parameters->sized_target<size, big_endian>()
+ ->dest_shndx(src_obj, src_indx, dst_obj, dst_indx, dst_off);
+
+ symtab->gc()->add_reference(src_obj, src_indx, dst_obj, code_indx);
+ if (code_indx != dst_indx)
+ symtab->gc()->add_reference(src_obj, src_indx, dst_obj, dst_indx);
if (cident_section_name != NULL)
{
Garbage_collection::Cident_section_map::iterator ele =
Index: gold/gold.cc
===================================================================
RCS file: /cvs/src/src/gold/gold.cc,v
retrieving revision 1.101
diff -u -p -r1.101 gold.cc
--- gold/gold.cc 24 Aug 2012 18:35:34 -0000 1.101
+++ gold/gold.cc 5 Sep 2012 11:03:31 -0000
@@ -504,15 +504,7 @@ queue_middle_tasks(const General_options
// Find the start symbol if any.
Symbol* start_sym = symtab->lookup(parameters->entry());
if (start_sym != NULL)
- {
- bool is_ordinary;
- unsigned int shndx = start_sym->shndx(&is_ordinary);
- if (is_ordinary)
- {
- symtab->gc()->worklist().push(
- Section_id(start_sym->object(), shndx));
- }
- }
+ symtab->gc_mark_symbol(start_sym);
// Symbols named with -u should not be considered garbage.
symtab->gc_mark_undef_symbols(layout);
gold_assert(symtab->gc() != NULL);
Index: gold/symtab.h
===================================================================
RCS file: /cvs/src/src/gold/symtab.h,v
retrieving revision 1.128
diff -u -p -r1.128 symtab.h
--- gold/symtab.h 18 Aug 2012 11:12:50 -0000 1.128
+++ gold/symtab.h 5 Sep 2012 11:03:32 -0000
@@ -1315,7 +1315,14 @@ class Symbol_table
// This tells garbage collection that this symbol is referenced.
void
- gc_mark_symbol(Symbol* sym);
+ gc_mark_symbol(Symbol* sym)
+ {
+ this->default_gc_mark_symbol(sym);
+ parameters->target().gc_mark_symbol(this, sym);
+ }
+
+ void
+ default_gc_mark_symbol(Symbol* sym);
// During garbage collection, this keeps sections that correspond to
// symbols seen in dynamic objects.
Index: gold/symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.167
diff -u -p -r1.167 symtab.cc
--- gold/symtab.cc 21 Aug 2012 21:41:04 -0000 1.167
+++ gold/symtab.cc 5 Sep 2012 11:03:32 -0000
@@ -577,14 +577,7 @@ Symbol_table::gc_mark_undef_symbols(Layo
if (sym->source() == Symbol::FROM_OBJECT
&& !sym->object()->is_dynamic())
{
- Relobj* obj = static_cast<Relobj*>(sym->object());
- bool is_ordinary;
- unsigned int shndx = sym->shndx(&is_ordinary);
- if (is_ordinary)
- {
- gold_assert(this->gc_ != NULL);
- this->gc_->worklist().push(Section_id(obj, shndx));
- }
+ this->gc_mark_symbol(sym);
}
}
@@ -601,14 +594,7 @@ Symbol_table::gc_mark_undef_symbols(Layo
&& sym->source() == Symbol::FROM_OBJECT
&& !sym->object()->is_dynamic())
{
- Relobj* obj = static_cast<Relobj*>(sym->object());
- bool is_ordinary;
- unsigned int shndx = sym->shndx(&is_ordinary);
- if (is_ordinary)
- {
- gold_assert(this->gc_ != NULL);
- this->gc_->worklist().push(Section_id(obj, shndx));
- }
+ this->gc_mark_symbol(sym);
}
}
@@ -622,29 +608,21 @@ Symbol_table::gc_mark_undef_symbols(Layo
if (sym->source() == Symbol::FROM_OBJECT
&& !sym->object()->is_dynamic())
{
- Relobj* obj = static_cast<Relobj*>(sym->object());
- bool is_ordinary;
- unsigned int shndx = sym->shndx(&is_ordinary);
- if (is_ordinary)
- {
- gold_assert(this->gc_ != NULL);
- this->gc_->worklist().push(Section_id(obj, shndx));
- }
+ this->gc_mark_symbol(sym);
}
}
}
void
-Symbol_table::gc_mark_symbol(Symbol* sym)
+Symbol_table::default_gc_mark_symbol(Symbol* sym)
{
// Add the object and section to the work list.
- Relobj* obj = static_cast<Relobj*>(sym->object());
bool is_ordinary;
unsigned int shndx = sym->shndx(&is_ordinary);
if (is_ordinary && shndx != elfcpp::SHN_UNDEF)
{
gold_assert(this->gc_!= NULL);
- this->gc_->worklist().push(Section_id(obj, shndx));
+ this->gc_->worklist().push(Section_id(sym->object(), shndx));
}
}
Index: gold/powerpc.cc
===================================================================
RCS file: /cvs/src/src/gold/powerpc.cc,v
retrieving revision 1.55
diff -u -p -r1.55 powerpc.cc
--- gold/powerpc.cc 5 Sep 2012 11:27:14 -0000 1.55
+++ gold/powerpc.cc 5 Sep 2012 13:55:39 -0000
@@ -59,6 +59,8 @@ class Powerpc_relobj : public Sized_relo
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef typename elfcpp::Elf_types<size>::Elf_Off Offset;
+ typedef Unordered_set<Section_id, Section_id_hash> Section_refs;
+ typedef Unordered_map<Address, Section_refs> Access_from;
Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
@@ -98,6 +100,14 @@ public:
this->opd_ent_off_.reserve(count);
}
+ bool
+ have_opd() const
+ {
+ // FIXME: Thread safety? We can't call get_opd_ent until
+ // scan_opd_relocs has finished.
+ return this->opd_ent_shndx_.size() != 0;
+ }
+
// Return section and offset of function entry for .opd + R_OFF.
void
get_opd_ent(Address r_off, unsigned int* shndx, Address* value) const
@@ -119,6 +129,27 @@ public:
this->opd_ent_off_[ndx] = value;
}
+ Access_from*
+ access_from_map()
+ { return &this->access_from_map_; }
+
+ // Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
+ // section at DST_OFF.
+ void
+ add_reference(Object* src_obj,
+ unsigned int src_indx,
+ typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
+ {
+ // FIXME: Thread safety? Can multiple threads attempt to update
+ // access_from_map_?
+ Section_id src_id(src_obj, src_indx);
+ typename Access_from::iterator p = this->access_from_map_.find(dst_off);
+ if (p == this->access_from_map_.end())
+ this->access_from_map_[dst_off].insert(src_id);
+ else
+ p->second.insert(src_id);
+ }
+
// Examine .rela.opd to build info about function entry points.
void
scan_opd_relocs(size_t reloc_count,
@@ -160,6 +191,9 @@ private:
// section and offset specified by these relocations.
std::vector<unsigned int> opd_ent_shndx_;
std::vector<Offset> opd_ent_off_;
+ // References made to this object's .opd section during garbage
+ // collection before we have processed .opd relocs
+ Access_from access_from_map_;
};
template<int size, bool big_endian>
@@ -224,6 +258,11 @@ class Target_powerpc : public Sized_targ
return NULL;
}
+ // If SYM is defined in .opd, add the object and section specified
+ // by the function descriptor to the gc work list.
+ void
+ do_gc_mark_symbol(Symbol_table* symtab, Symbol* sym) const;
+
// Finalize the sections.
void
do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
@@ -340,6 +379,26 @@ class Target_powerpc : public Sized_targ
unsigned int
plt_entry_size() const;
+ unsigned int
+ dest_shndx(Object* src_obj,
+ unsigned int src_indx,
+ Object* dst_obj,
+ unsigned int dst_indx,
+ typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
+ {
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(dst_obj);
+ if (size == 64 && dst_indx == ppc_object->opd_shndx())
+ {
+ // FIXME: Thread safety?
+ if (ppc_object->have_opd())
+ ppc_object->get_opd_ent(dst_off, &dst_indx, &dst_off);
+ else
+ ppc_object->add_reference(src_obj, src_indx, dst_off);
+ }
+ return dst_indx;
+ }
+
private:
// The class which scans relocations.
@@ -2913,6 +2972,32 @@ Target_powerpc<size, big_endian>::gc_pro
{
typedef Target_powerpc<size, big_endian> Powerpc;
typedef typename Target_powerpc<size, big_endian>::Scan Scan;
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+ if (size == 64 && data_shndx == ppc_object->opd_shndx())
+ {
+ typename Powerpc_relobj<size, big_endian>::Access_from::iterator p;
+ for (p = ppc_object->access_from_map()->begin();
+ p != ppc_object->access_from_map()->end();
+ ++p)
+ {
+ Address dst_off = p->first;
+ unsigned int dst_indx;
+ ppc_object->get_opd_ent(dst_off, &dst_indx, &dst_off);
+ typename Powerpc_relobj<size, big_endian>::Section_refs::iterator s;
+ for (s = p->second.begin(); s != p->second.end(); ++s)
+ {
+ Object* src_obj = s->first;
+ unsigned int src_indx = s->second;
+ symtab->gc()->add_reference(src_obj, src_indx,
+ ppc_object, dst_indx);
+ }
+ p->second.clear();
+ }
+ ppc_object->access_from_map()->clear();
+ // Don't look at .opd relocs as .opd will reference everything.
+ return;
+ }
gold::gc_process_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan,
typename Target_powerpc::Relocatable_size_for_reloc>(
@@ -2929,6 +3014,32 @@ Target_powerpc<size, big_endian>::gc_pro
plocal_symbols);
}
+// If SYM is defined in .opd, add the object and section specified
+// by the function descriptor to the gc work list.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_gc_mark_symbol(
+ Symbol_table* symtab,
+ Symbol* sym) const
+{
+ if (size == 64)
+ {
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(sym->object());
+ bool is_ordinary;
+ unsigned int shndx = sym->shndx(&is_ordinary);
+ if (is_ordinary && shndx == ppc_object->opd_shndx())
+ {
+ Sized_symbol<size>* gsym = symtab->get_sized_symbol<size>(sym);
+ Address dst_off = gsym->value();
+ unsigned int dst_indx = shndx;
+ ppc_object->get_opd_ent(dst_off, &dst_indx, &dst_off);
+ symtab->gc()->worklist().push(Section_id(ppc_object, dst_indx));
+ }
+ }
+}
+
// Scan relocations for a section.
template<int size, bool big_endian>
--
Alan Modra
Australia Development Lab, IBM