[PATCH v4] gold: Add Mips64 support.

Cary Coutant ccoutant@gmail.com
Tue Mar 8 23:09:00 GMT 2016


>         * mips.cc: Add Mips64 support.

The ChangeLog should include entries for each new class or function,
and for each changed function. A blanket note like this is used only
when the entire source file is brand new.

>         * output.h (Output_reloc<SHT_REL>::get_address): Change from private to public.
>         (Output_reloc<SHT_REL>::get_symbol_index): Likewise.
>         (Output_data_reloc_base::Sort_relocs_comparison): Change from private to protected.
>         (Output_data_reloc_base::relocs_): Likewise.


+  if (section_type == elfcpp::SHT_NOBITS)
+    return false;
+
+  if (section_flags & elfcpp::SHF_WRITE)
+    return false;

Just out of curiosity... Why would a NOBITS section ever not have
SHF_WRITE set? Is there any reason to test for NOBITS?


+  // Whether this is a STT_SECTION symbol.
+  bool is_section_symbol_;

I suggest moving is_section_symbol_ above shndx_ (or moving shndx_
above tls_type_) to avoid an extra 8 bytes of padding.


  protected:
+  // The name of the options section.
+  const char* mips_elf_options_section_name()
+  { return this->is_newabi() ? ".MIPS.options" : ".options"; }

This should probably be a private method.


+       if (!this->target_->is_output_n64())
+         p->write(pov);
+       else
+         {
+           elfcpp::Mips64_rel_write<big_endian> orel(pov);
+           orel.put_r_offset(p->get_address());
+           orel.put_r_sym(p->get_symbol_index());
+           orel.put_r_ssym(0);
+           orel.put_r_type(p->type());
+           if (p->type() == elfcpp::R_MIPS_REL32)
+             orel.put_r_type2(elfcpp::R_MIPS_64);
+           else
+             orel.put_r_type2(elfcpp::R_MIPS_NONE);
+           orel.put_r_type3(elfcpp::R_MIPS_NONE);
+         }

As I understand it, N64 is the *only* supported 64-bit ABI. A much
less expensive test would be "if (size != 64)", which can be evaluated
at compile time when the template is instantiated. With that change,
you wouldn't need to pass "target" to the Mips_output_data_reloc
constructor.

I wasn't happy with the fact that you had to copy the implementation
of Output_data_reloc_base::do_write(), so I tried a few different
approaches, and finally settled on factoring out the implementation of
that function into a template called
do_write_generic<Output_reloc_writer>(Output_file*). The standard
implementation of do_write() uses a simple Output_reloc_writer class
that simply calls Output_reloc::write() to do the work. (This is all
inlined, so the extra abstraction costs nothing.) With that change,
the Mips_output_data_reloc class can become this:

++// MIPS-specific relocation writer.
++
++template<int sh_type, bool dynamic, int size, bool big_endian>
++struct Mips_output_reloc_writer;
++
++template<int sh_type, bool dynamic, bool big_endian>
++struct Mips_output_reloc_writer<sh_type, dynamic, 32, big_endian>
++{
++  typedef Output_reloc<sh_type, dynamic, 32, big_endian> Output_reloc_type;
++  typedef std::vector<Output_reloc_type> Relocs;
++
++  static void
++  write(typename Relocs::const_iterator p, unsigned char* pov)
++  { p->write(pov); }
++};
++
++template<int sh_type, bool dynamic, bool big_endian>
++struct Mips_output_reloc_writer<sh_type, dynamic, 64, big_endian>
++{
++  typedef Output_reloc<sh_type, dynamic, 64, big_endian> Output_reloc_type;
++  typedef std::vector<Output_reloc_type> Relocs;
++
++  static void
++  write(typename Relocs::const_iterator p, unsigned char* pov)
++  {
++    elfcpp::Mips64_rel_write<big_endian> orel(pov);
++    orel.put_r_offset(p->get_address());
++    orel.put_r_sym(p->get_symbol_index());
++    orel.put_r_ssym(0);
++    orel.put_r_type(p->type());
++    if (p->type() == elfcpp::R_MIPS_REL32)
++      orel.put_r_type2(elfcpp::R_MIPS_64);
++    else
++      orel.put_r_type2(elfcpp::R_MIPS_NONE);
++    orel.put_r_type3(elfcpp::R_MIPS_NONE);
++  }
++};
++
++template<int sh_type, bool dynamic, int size, bool big_endian>
++class Mips_output_data_reloc : public Output_data_reloc<sh_type, dynamic,
++                                                        size, big_endian>
++{
++ public:
++  Mips_output_data_reloc(bool sort_relocs)
++    : Output_data_reloc<sh_type, dynamic, size, big_endian>(sort_relocs)
++  { }
++
++ protected:
++  // Write out the data.
++  void
++  do_write(Output_file* of)
++  {
++    typedef Mips_output_reloc_writer<sh_type, dynamic, size,
big_endian> Writer;
++    this->template do_write_generic<Writer>(of);
++  }
++};

I've committed that change along with your patch to output.h that
moves Output_reloc::get_address() and Output_reloc::get_symbol_index()
to the public interface (see separate email).


   put_r_info(Reltype_write* new_reloc, Reltype* reloc, unsigned int r_sym)
   {
     unsigned int r_type = elfcpp::elf_r_type<32>(reloc->get_r_info());
-    new_reloc->put_r_info(elfcpp::elf_r_info<64>(r_sym, r_type));
+    new_reloc->put_r_info(elfcpp::elf_r_info<32>(r_sym, r_type));

Is this a bug fix to the 32-bit support? If so, it should probably go
in separately. (That patch is preapproved.)


+    ei_class_ = size == 64 ? elfcpp::ELFCLASS64 : elfcpp::ELFCLASS32;

I don't see the point of this data member -- it will always track the
"size" template parameter for the target. Since Target_mips<32,...>
and Target_mips<64,...> are distinct targets, you will never be called
upon to merge them, and merge_processor_specific_flags() will never
see an inconsistency (since a target mismatch will have already been
detected in make_elf_sized_object).


   rel16(unsigned char* view, const Mips_relobj<size, big_endian>* object,
         const Symbol_value<size>* psymval, Mips_address addend_a,
+        bool extract_addend, unsigned int r_type, bool calculate_only,
+        Valtype &calculated_value)

In gold, we try to adhere to a convention where reference parameters
are always const. If you're going to modify a reference parameter, it
should be a pointer. (Also, space goes *after* the '&'.)


+    if (calculate_only)
+      calculated_value = x;
+    else
+      elfcpp::Swap<16, big_endian>::writeval(wv, val);
+
     mips_reloc_shuffle(view, r_type, false);

Do you really want to call mips_reloc_shuffle() if calculate_only is
true? If so, a comment would help, but it seems to me that since you
haven't actually written anything to the view yet, it's not really
doing anything useful.


-    return (Bits<16>::has_overflow32(x)
-            ? This::STATUS_OVERFLOW
-            : This::STATUS_OKAY);
+    return check_overflow<16>(x);

I observe that the returned status is never used if calculate_only is
true (which makes sense). Perhaps you just want to return immediately
after calculating the intermediate result rather than waste time
checking for overflow.


-  0x8df90000,           // l[wd] $25, %lo(.got.plt entry)($15)
+  0x01f90000,           // l[wd] $25, %lo(.got.plt entry)($15)

As above, if this is a bug fix for 32-bit, it should go in separately
(also preapproved).


I see the following pattern in several places:

+  if (sh_type == elfcpp::SHT_REL)
+    {
+      ...
+    }
+  else if (sh_type == elfcpp::SHT_RELA)
+    {
+      ...
+    }

I suggest adding:

++  else
++    gold_unreachable();


+  // The first operation in a record which references a symbol uses the symbol
+  // implied by r_sym. The next operation in a record which references a symbol
+  // uses the special symbol value given by the r_ssym field. A third operation
+  // in a record which references a symbol will assume a NULL symbol,
+  // i.e. value zero.

This comment (which agrees with what I read in the psABI) does not
describe what the code below it does. It looks to me like the first
relocation (r_types[0]) always uses r_sym, the second (r_types[1])
always uses r_ssym, and the third (r_types[2]) always uses NULL.
Perhaps a TODO comment is needed here?

-cary



More information about the Binutils mailing list