This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
STT_SPARC_REGISTER and gold...
- From: David Miller <davem at davemloft dot net>
- To: iant at google dot com
- Cc: binutils at sourceware dot org
- Date: Wed, 23 Apr 2008 18:50:46 -0700 (PDT)
- Subject: STT_SPARC_REGISTER and gold...
Ian, I just wanted to get a small discussion going again
about this. I've done some work towards adding proper
STT_SPARC_REGISTER support to gold, but I can't get past
a certain area about how to design this. Perhaps you
can help.
BTW, another document for referencing this area of the Sparc
ABI is:
http://www.sparc.org/standards/SCD.4.2.ps.Z
I will add that URL to the elfcpp/sparc.h file when I get a chance.
I've included my work-in-progress patch below. It's just for
reference and nothing to be considered seriously.
So the issue is what to do about whether to keep these things in the
gold symbol table. I think we should but this means:
1) For #scratch register uses, we need to modify the name
internally to gold. All #scratch register symbols have
st_info==0, and thus have the null string. The only
difference between them is the st_value field, which will
indicate the register number.
Non-#scratch STT_SPARC_REGISTER symbols have a normal symbol
name, just like any other symbol, which we should retain.
So my idea was to change the name string for new #scratch
register symbols, to the textual register name, such as
"%g2", "%g3", "%g6", "%g7".
But this means at symbol output time we have to undo this
change somehow, so that the st_info field will end up being
the correct value, zero, for #scratch entries.
2) If we do #1, this means that a hook is need in the symbol
resolver. There is one there, but like the current
->make_symbol() it's not very useful in it's current form
as it would require a target to duplicate all of the
current resolver logic inside of the target :-)
if (object->target()->has_resolve())
{
Sized_target<size, big_endian>* sized_target;
sized_target = object->sized_target<size, big_endian>();
sized_target->resolve(to, sym, object, version);
return;
}
To be optimally useful, it seems as if this interface should
return a trinary state. One of: REJECT, ACCEPT_UNCONDITIONALLY,
and ACCEPT_BUT_CONTINUE_RESOLVE_PROECSSING.
This hook is where I would determine if a STT_SPARC_REGISTER
symbol is compatible with a previously one seen for the same
register. With the #scratch renaming idea in #1 this would
work out perfectly, I think, as the gold symtab entry would
be passed into the target resolver hook.
Next, there is the issue of how to output these things. Based
upon my understanding of how this stuff works, we roughly have
to:
1) Not output STT_SPARC_REGISTER symbols that came from
dynamic objects, they should only be used for validation
and comparison against other STT_SPARC_REGISTER symbols
seen. It is the dynamic linker's job to look at such
entries and take appropriate action.
2) Output (STB_LOCAL && visibility != HIDDEN) STT_SPARC_REGISTER
symbols to the dynamic symbol table.
3) Output DT_SPARC_REGISTER entries into the .dynamic section,
which point to the entries output in #2.
4) Because if the #scratch renaming suggested above, we have
to undo that, or take some action to make the st_info
field get output as zero for such symbol entries.
There are probably some other obscure details I've forgotten.
Anyways, Ian, any suggestions of ideas, given the above?
Thanks!
Index: sparc.cc
===================================================================
RCS file: /cvs/src/src/gold/sparc.cc,v
retrieving revision 1.5
diff -u -p -r1.5 sparc.cc
--- sparc.cc 19 Apr 2008 18:30:58 -0000 1.5
+++ sparc.cc 24 Apr 2008 00:46:44 -0000
@@ -63,6 +63,11 @@ class Target_sparc : public Sized_target
{
}
+ // Make a new symbol table entry for the target.
+ Sized_symbol<size>*
+ make_symbol(Object*, const elfcpp::Sym<size, big_endian>&,
+ const char*, const char*, unsigned int);
+
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(const General_options& options,
@@ -318,6 +323,246 @@ class Target_sparc : public Sized_target
GOT_TYPE_TLS_PAIR = 2, // GOT entry for TLS module/offset pair
};
+ // Class for handling 64-bit ABI STT_SPARC_REGISTER symbol table
+ // entries.
+ //
+ // Such entries are used to record the usage of global registers
+ // by an object file. In assembler the register usage is denoted
+ // using the syntax:
+ //
+ // .register REGISTER, USAGE
+ //
+ // Where REGISTER is one of %g2, %g3, %g6, %g7 and usage is
+ // one of #scratch, #ignore, or a global symbol name. If the
+ // usage is #ignore, the assembler does not record any
+ // information about the global register usage into the object
+ // file. If the usage is #scratch, the symbol name is an empty
+ // string, using st_index 0, and must have binding STB_GLOBAL.
+ //
+ // If the register represents a global symbol, and is initialized
+ // by the current object, st_shndx will be SHN_ABS and there will
+ // be an R_SPARC_REGISTER relocation. Otherwise the st_shndx
+ // will be SHN_UNDEF.
+ //
+ // For the cases where usage information is recorded, the link
+ // editor is expected to collect all of the uses and make sure
+ // they are compatible.
+ class Global_register
+ {
+ public:
+ Global_register()
+ {
+ for (int i = 0; i < 4; i++)
+ this->reg_info_[i].name = NULL;
+ }
+
+ enum Global_register_map
+ {
+ GLOBAL_REG_G2 = 0,
+ GLOBAL_REG_G3 = 1,
+ GLOBAL_REG_G6 = 2,
+ GLOBAL_REG_G7 = 3,
+ };
+
+ int
+ global_reg_to_map(int reg)
+ {
+ switch (reg)
+ {
+ case 2:
+ return GLOBAL_REG_G2;
+ case 3:
+ return GLOBAL_REG_G3;
+ case 6:
+ return GLOBAL_REG_G6;
+ case 7:
+ return GLOBAL_REG_G7;
+ default:
+ gold_unreachable();
+ }
+ }
+
+ int
+ map_to_global_reg(int map)
+ {
+ switch (map)
+ {
+ case GLOBAL_REG_G2:
+ return 2;
+ case GLOBAL_REG_G3:
+ return 3;
+ case GLOBAL_REG_G6:
+ return 6;
+ case GLOBAL_REG_G7:
+ return 7;
+ default:
+ gold_unreachable();
+ }
+ }
+
+ // Check, for the given non-register symbol, that we don't have a
+ // matching STT_SPARC_REGISTER symbol.
+ void
+ check_nonregister_symbol(Object *object, enum elfcpp::STT st_type,
+ const char* name)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ struct Global_reg_info *gp = &this->reg_info_[i];
+
+ if (gp->name == NULL || !*gp->name)
+ continue;
+ if (! strcmp (gp->name, name))
+ {
+ gold_error(_("%s: Symbol `%s' changed type to %d, "
+ "previous STT_SPARC_REGISTER %%g%d in %s.\n"),
+ object->name().c_str(), name,
+ st_type, map_to_global_reg(i),
+ gp->object->name().c_str());
+ }
+ }
+ }
+
+ // Check, for a given STT_SPARC_REGISTER symbol, that we don't
+ // have a conflicting STT_SPARC_REGISTER entry.
+ bool
+ check_register_symbol(Object *object, int reg, const char* name,
+ unsigned int shndx,
+ const elfcpp::Sym<size, big_endian>& sym)
+ {
+ int map = global_reg_to_map(reg);
+ struct Global_reg_info *gp = &this->reg_info_[map];
+
+ // We don't have an existing entry, no conflict.
+ if (gp->name == NULL)
+ return true;
+
+ // Both entries are #scratch, no conflict.
+ if (!*gp->name && !*name)
+ return true;
+
+
+ }
+
+ // Validate a STT_SPARC_REGISTER symbol. Make sure it does not
+ // conflict with any existing entry. Signal an error and return
+ // false on conflict.
+ bool
+ validate(Object *object, int reg, const char* name,
+ unsigned int st_shndx,
+ const elfcpp::Sym<size, big_endian>& sym)
+ {
+ if (size == 32)
+ {
+ gold_error(_("%s: Unexpected STT_SPARC_REGISTER in "
+ "32-bit object.\n"),
+ object->name().c_str());
+ return false;
+ }
+
+ switch (reg)
+ {
+ case 2:
+ case 3:
+ case 6:
+ case 7:
+ break;
+
+ default:
+ gold_error(_("%s: Unexpected STT_SPARC_REGISTER number %d.\n"),
+ object->name().c_str(), reg);
+ return false;
+ }
+
+ if (sym.get_st_size() != 0)
+ {
+ gold_error(_("%s: Unexpected STT_SPARC_REGISTER st_size %lu.\n"),
+ object->name().c_str(),
+ static_cast<unsigned long>(sym.get_st_size()));
+ return false;
+ }
+
+ if (sym.get_st_other() != 0)
+ {
+ gold_error(_("%s: Unexpected STT_SPARC_REGISTER st_other %lu.\n"),
+ object->name().c_str(),
+ static_cast<unsigned long>(sym.get_st_other()));
+ return false;
+ }
+
+ if (sym.get_st_name() == 0)
+ {
+ if (sym.get_st_bind() != elfcpp::STB_GLOBAL)
+ {
+ gold_error(_("%s: STT_SPARC_REGISTER scratch uses incorrect "
+ "binding %lu.\n"),
+ object->name().c_str(),
+ static_cast<unsigned long>(sym.get_st_bind()));
+ return false;
+ }
+ if (st_shndx != elfcpp::SHN_UNDEF)
+ {
+ gold_error(_("%s: STT_SPARC_REGISTER scratch uses incorrect "
+ "st_shndx %u.\n"),
+ object->name().c_str(), st_shndx);
+ return false;
+ }
+ }
+ else
+ {
+ if (st_shndx != elfcpp::SHN_UNDEF && st_shndx != elfcpp::SHN_ABS)
+ {
+ gold_error(_("%s: STT_SPARC_REGISTER global uses incorrect "
+ "st_shndx %u.\n"),
+ object->name().c_str(), st_shndx);
+ return false;
+ }
+ }
+
+ if (!check_register_symbol(object, reg, name, st_shndx, sym))
+ return false;
+
+ return true;
+ }
+
+ // Record information for a STT_SPARC_REGISTER symbol.
+ void
+ record(Object *object, int reg, const char* name,
+ unsigned int shndx, enum elfcpp::STB bind)
+ {
+ int map = global_reg_to_map(reg);
+ struct Global_reg_info *gp = &this->reg_info_[map];
+
+ if (gp->name != NULL)
+ {
+ if (strcmp (gp->name, name))
+ {
+ gold_error(_("%s: Register %%g%d used incompatibly: "
+ "%s, previously was %s in object %s.\n"),
+ object->name().c_str(), reg,
+ (*name ? name : "#scratch"),
+ (*gp->name ? gp->name : "#scratch"),
+ gp->object->name().c_str());
+ }
+ return;
+ }
+ gp->object = object;
+ gp->name = name;
+ gp->shndx = shndx;
+ gp->bind = bind;
+ }
+
+ private:
+ struct Global_reg_info {
+ const char* name;
+ Object* object;
+ unsigned int shndx;
+ enum elfcpp::STB bind;
+ } reg_info_[4];
+ };
+
+ // STT_SPARC_REGISTER symbols seen.
+ class Global_register global_regs_;
// The GOT section.
Output_data_got<size, big_endian>* got_;
// The PLT section.
@@ -340,7 +585,7 @@ Target::Target_info Target_sparc<32, tru
32, // size
true, // is_big_endian
elfcpp::EM_SPARC, // machine_code
- false, // has_make_symbol
+ true, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
@@ -357,7 +602,7 @@ Target::Target_info Target_sparc<64, tru
64, // size
true, // is_big_endian
elfcpp::EM_SPARCV9, // machine_code
- false, // has_make_symbol
+ true, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
@@ -368,6 +613,36 @@ Target::Target_info Target_sparc<64, tru
8 * 1024 // common_pagesize (overridable by -z common-page-size)
};
+template<int size, bool big_endian>
+Sized_symbol<size>*
+Target_sparc<size, big_endian>::make_symbol(Object *object,
+ const elfcpp::Sym<size, big_endian>& sym,
+ const char *name, const char *,
+ unsigned int st_shndx)
+{
+ if (sym.get_st_type() != elfcpp::STT_SPARC_REGISTER)
+ {
+ if (size == 64)
+ this->global_regs_.check_nonregister_symbol(object,
+ sym.get_st_type(),
+ name);
+
+ return new Sized_symbol<size>();
+ }
+
+ int reg = static_cast<int>(sym.get_st_value());
+ if (!this->global_regs_.validate(object, reg, name, st_shndx, sym))
+ return NULL;
+
+ // If this is coming from a dynamic object, do not record it.
+ // Such work is done by the dynamic linker at run time.
+ if (!object->is_dynamic())
+ this->global_regs_.record(object, reg, name,
+ st_shndx, sym.get_st_bind());
+
+ return NULL;
+}
+
// We have to take care here, even when operating in little-endian
// mode, sparc instructions are still big endian.
template<int size, bool big_endian>
Index: symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.94
diff -u -p -r1.94 symtab.cc
--- symtab.cc 19 Apr 2008 18:30:58 -0000 1.94
+++ symtab.cc 24 Apr 2008 00:46:45 -0000
@@ -696,7 +696,7 @@ Symbol_table::add_from_object(Object* ob
ret = new Sized_symbol<size>();
else
{
- ret = target->make_symbol();
+ ret = target->make_symbol(object, sym, name, version, st_shndx);
if (ret == NULL)
{
// This means that we don't want a symbol table
@@ -1207,21 +1207,7 @@ Symbol_table::define_special_symbol(cons
}
}
- const Target& target = parameters->target();
- if (!target.has_make_symbol())
- sym = new Sized_symbol<size>();
- else
- {
- gold_assert(target.get_size() == size);
- gold_assert(target.is_big_endian() ? big_endian : !big_endian);
- typedef Sized_target<size, big_endian> My_target;
- const My_target* sized_target =
- static_cast<const My_target*>(&target);
- sym = sized_target->make_symbol();
- if (sym == NULL)
- return NULL;
- }
-
+ sym = new Sized_symbol<size>();
if (add_to_table)
add_loc->second = sym;
else
Index: target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.29
diff -u -p -r1.29 target.h
--- target.h 9 Apr 2008 00:48:13 -0000 1.29
+++ target.h 24 Apr 2008 00:46:45 -0000
@@ -246,7 +246,8 @@ class Sized_target : public Target
// symbol table. This will only be called if has_make_symbol()
// returns true.
virtual Sized_symbol<size>*
- make_symbol() const
+ make_symbol(Object *, const elfcpp::Sym<size, big_endian>&,
+ const char*, const char*, unsigned int)
{ gold_unreachable(); }
// Resolve a symbol for the target. This should be overridden by a