diff options
Diffstat (limited to 'gold/output.h')
-rw-r--r-- | gold/output.h | 415 |
1 files changed, 380 insertions, 35 deletions
diff --git a/gold/output.h b/gold/output.h index e036b98..5c72c02 100644 --- a/gold/output.h +++ b/gold/output.h @@ -5,6 +5,7 @@ #include <cassert> #include <list> +#include <vector> #include "elfcpp.h" #include "layout.h" @@ -31,17 +32,21 @@ class Output_data virtual ~Output_data(); - // Return the address. + // Return the address. This is only valid after Layout::finalize is + // finished. uint64_t address() const { return this->address_; } - // Return the size of the data. + // Return the size of the data. This must be valid after + // Layout::finalize calls set_address, but need not be valid before + // then. off_t data_size() const { return this->data_size_; } - // Return the file offset. + // Return the file offset. This is only valid after + // Layout::finalize is finished. off_t offset() const { return this->offset_; } @@ -67,11 +72,23 @@ class Output_data is_section_flag_set(elfcpp::Elf_Xword shf) const { return this->do_is_section_flag_set(shf); } - // Set the address and file offset of this data. + // Return the output section index, if there is an output section. + unsigned int + out_shndx() const + { return this->do_out_shndx(); } + + // Set the output section index, if this is an output section. + void + set_out_shndx(unsigned int shndx) + { this->do_set_out_shndx(shndx); } + + // Set the address and file offset of this data. This is called + // during Layout::finalize. void set_address(uint64_t addr, off_t off); - // Write the data to the output file. + // Write the data to the output file. This is called after + // Layout::finalize is complete. void write(Output_file* file) { this->do_write(file); } @@ -104,6 +121,16 @@ class Output_data do_is_section_flag_set(elfcpp::Elf_Xword) const { return false; } + // Return the output section index, if there is an output section. + virtual unsigned int + do_out_shndx() const + { abort(); } + + // Set the output section index, if this is an output section. + virtual void + do_set_out_shndx(unsigned int) + { abort(); } + // Set the address and file offset of the data. This only needs to // be implemented if the child needs to know. virtual void @@ -270,6 +297,198 @@ class Output_file_header : public Output_data const Output_section* shstrtab_; }; +// Output sections are mainly comprised of input sections. However, +// there are cases where we have data to write out which is not in an +// input section. Output_section_data is used in such cases. This is +// an abstract base class. + +class Output_section_data : public Output_data +{ + public: + Output_section_data(off_t data_size, uint64_t addralign) + : Output_data(data_size), output_section_(NULL), addralign_(addralign) + { } + + Output_section_data(uint64_t addralign) + : Output_data(0), output_section_(NULL), addralign_(addralign) + { } + + // Record the output section. + void + set_output_section(Output_section* os) + { + assert(this->output_section_ == NULL); + this->output_section_ = os; + } + + protected: + // The child class must implement do_write. + + // Return the required alignment. + uint64_t + do_addralign() const + { return this->addralign_; } + + // Return the section index of the output section. + unsigned int + do_out_shndx() const; + + private: + // The output section for this section. + const Output_section* output_section_; + // The required alignment. + uint64_t addralign_; +}; + +// Output_section_common is used to handle the common symbols. This +// is quite simple. + +class Output_section_common : public Output_section_data +{ + public: + Output_section_common(uint64_t addralign) + : Output_section_data(addralign) + { } + + // Set the size. + void + set_common_size(off_t common_size) + { this->set_data_size(common_size); } + + // Write out the data--there is nothing to do, as common symbols are + // always zero and are stored in the BSS. + void + do_write(Output_file*) + { } +}; + +// Output_section_got is used to manage a GOT. Each entry in the GOT +// is for one symbol--either a global symbol or a local symbol in an +// object. The target specific code adds entries to the GOT as +// needed. The GOT code is then responsible for writing out the data +// and for generating relocs as required. + +template<int size, bool big_endian> +class Output_section_got : public Output_section_data +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype; + + Output_section_got() + : Output_section_data(Output_data::default_alignment(size)), + entries_() + { } + + // Add an entry for a global symbol to the GOT. This returns the + // offset of the new entry from the start of the GOT. + unsigned int + add_global(Symbol* gsym) + { + this->entries_.push_back(Got_entry(gsym)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Add an entry for a local symbol to the GOT. This returns the + // offset of the new entry from the start of the GOT. + unsigned int + add_local(Object* object, unsigned int sym_index) + { + this->entries_.push_back(Got_entry(object, sym_index)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Add a constant to the GOT. This returns the offset of the new + // entry from the start of the GOT. + unsigned int + add_constant(Valtype constant) + { + this->entries_.push_back(Got_entry(constant)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Write out the GOT table. + void + do_write(Output_file*); + + private: + // This POD class holds a single GOT entry. + class Got_entry + { + public: + // Create a zero entry. + Got_entry() + : local_sym_index_(CONSTANT_CODE) + { this->u_.constant = 0; } + + // Create a global symbol entry. + Got_entry(Symbol* gsym) + : local_sym_index_(GSYM_CODE) + { this->u_.gsym = gsym; } + + // Create a local symbol entry. + Got_entry(Object* object, unsigned int local_sym_index) + : local_sym_index_(local_sym_index) + { + assert(local_sym_index != GSYM_CODE + && local_sym_index != CONSTANT_CODE); + this->u_.object = object; + } + + // Create a constant entry. The constant is a host value--it will + // be swapped, if necessary, when it is written out. + Got_entry(Valtype constant) + : local_sym_index_(CONSTANT_CODE) + { this->u_.constant = constant; } + + // Write the GOT entry to an output view. + void + write(unsigned char* pov) const; + + private: + enum + { + GSYM_CODE = -1U, + CONSTANT_CODE = -2U + }; + + union + { + // For a local symbol, the object. + Object* object; + // For a global symbol, the symbol. + Symbol* gsym; + // For a constant, the constant. + Valtype constant; + } u_; + // For a local symbol, the local symbol index. This is -1U for a + // global symbol, or -2U for a constant. + unsigned int local_sym_index_; + }; + + typedef std::vector<Got_entry> Got_entries; + + // Return the offset into the GOT of GOT entry I. + unsigned int + got_offset(unsigned int i) const + { return i * (size / 8); } + + // Return the offset into the GOT of the last entry added. + unsigned int + last_got_offset() const + { return this->got_offset(this->entries_.size() - 1); } + + // Set the size of the section. + void + set_got_size() + { this->set_data_size(this->got_offset(this->entries_.size())); } + + // The list of GOT entries. + Got_entries entries_; +}; + // An output section. We don't expect to have too many output // sections, so we don't bother to do a template on the size. @@ -278,16 +497,20 @@ class Output_section : public Output_data public: // Create an output section, giving the name, type, and flags. Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword, - unsigned int shndx); + bool may_add_data); virtual ~Output_section(); - // Add a new input section named NAME with header SHDR from object - // OBJECT. Return the offset within the output section. + // Add a new input section SHNDX, named NAME, with header SHDR, from + // object OBJECT. Return the offset within the output section. template<int size, bool big_endian> off_t - add_input_section(Object* object, const char *name, + add_input_section(Object* object, unsigned int shndx, const char *name, const elfcpp::Shdr<size, big_endian>& shdr); + // Add generated data ODATA to this output section. + virtual void + add_output_section_data(Output_section_data* posd); + // Return the section name. const char* name() const @@ -303,15 +526,15 @@ class Output_section : public Output_data flags() const { return this->flags_; } - // Return the address alignment. - uint64_t - addralign() const - { return this->addralign_; } - - // Return the section index. + // Return the section index in the output file. unsigned int - shndx() const - { return this->shndx_; } + do_out_shndx() const + { return this->out_shndx_; } + + // Set the output section index. + void + do_set_out_shndx(unsigned int shndx) + { this->out_shndx_ = shndx; } // Set the entsize field. void @@ -333,12 +556,19 @@ class Output_section : public Output_data set_addralign(uint64_t v) { this->addralign_ = v; } + // Set the address of the Output_section. For a typical + // Output_section, there is nothing to do, but if there are any + // Output_section_data objects we need to set the final addresses + // here. + void + do_set_address(uint64_t, off_t); + // Write the data to the file. For a typical Output_section, this - // does nothing. We write out the data by looping over all the - // input sections. + // does nothing: the data is written out by calling Object::Relocate + // on each input object. But if there are any Output_section_data + // objects we do need to write them out here. virtual void - do_write(Output_file*) - { } + do_write(Output_file*); // Return the address alignment--function required by parent class. uint64_t @@ -366,6 +596,83 @@ class Output_section : public Output_data write_header(const Stringpool*, elfcpp::Shdr_write<size, big_endian>*) const; private: + // In some cases we need to keep a list of the input sections + // associated with this output section. We only need the list if we + // might have to change the offsets of the input section within the + // output section after we add the input section. The ordinary + // input sections will be written out when we process the object + // file, and as such we don't need to track them here. We do need + // to track Output_section_data objects here. We store instances of + // this structure in a std::vector, so it must be a POD. There can + // be many instances of this structure, so we use a union to save + // some space. + class Input_section + { + public: + Input_section() + : shndx_(0), p2align_(0), data_size_(0) + { this->u_.object = NULL; } + + Input_section(Object* object, unsigned int shndx, off_t data_size, + uint64_t addralign) + : shndx_(shndx), + p2align_(ffsll(static_cast<long long>(addralign))), + data_size_(data_size) + { + assert(shndx != -1U); + this->u_.object = object; + } + + Input_section(Output_section_data* posd) + : shndx_(-1U), + p2align_(ffsll(static_cast<long long>(posd->addralign()))), + data_size_(0) + { this->u_.posd = posd; } + + // The required alignment. + uint64_t + addralign() const + { return static_cast<uint64_t>(1) << this->p2align_; } + + // Return the required size. + off_t + data_size() const; + + // Set the address and file offset. This is called during + // Layout::finalize. SECOFF is the file offset of the enclosing + // section. + void + set_address(uint64_t addr, off_t off, off_t secoff); + + // Write out the data. This does nothing for an input section. + void + write(Output_file*); + + private: + // Whether this is an input section. + bool + is_input_section() const + { return this->shndx_ != -1U; } + + // For an ordinary input section, this is the section index in + // the input file. For an Output_section_data, this is -1U. + unsigned int shndx_; + // The required alignment, stored as a power of 2. + unsigned int p2align_; + // For an ordinary input section, the section size. + off_t data_size_; + union + { + // If shndx_ != -1U, this points to the object which holds the + // input section. + Object* object; + // If shndx_ == -1U, this is the data to write out. + Output_section_data* posd; + } u_; + }; + + typedef std::vector<Input_section> Input_section_list; + // Most of these fields are only valid after layout. // The name of the section. This will point into a Stringpool. @@ -385,16 +692,35 @@ class Output_section : public Output_data // The section flags. elfcpp::Elf_Xword flags_; // The section index. - unsigned int shndx_; + unsigned int out_shndx_; + // The input sections. This will be empty in cases where we don't + // need to keep track of them. + Input_section_list input_sections_; + // The offset of the first entry in input_sections_. + off_t first_input_offset_; + // Whether we permit adding data. + bool may_add_data_; }; // A special Output_section which represents the symbol table -// (SHT_SYMTAB). +// (SHT_SYMTAB). The actual data is written out by +// Symbol_table::write_globals. class Output_section_symtab : public Output_section { public: - Output_section_symtab(const char* name, off_t size, unsigned int shndx); + Output_section_symtab(const char* name, off_t size); + + // The data is written out by Symbol_table::write_globals. We don't + // do anything here. + void + do_write(Output_file*) + { } + + // We don't expect to see any input sections or data here. + void + add_output_section_data(Output_section_data*) + { abort(); } }; // A special Output_section which holds a string table. @@ -402,13 +728,17 @@ class Output_section_symtab : public Output_section class Output_section_strtab : public Output_section { public: - Output_section_strtab(const char* name, Stringpool* contents, - unsigned int shndx); + Output_section_strtab(const char* name, Stringpool* contents); // Write out the data. void do_write(Output_file*); + // We don't expect to see any input sections or data here. + void + add_output_section_data(Output_section_data*) + { abort(); } + private: Stringpool* contents_; }; @@ -448,9 +778,14 @@ class Output_segment memsz() const { return this->memsz_; } + // Return the file size. + off_t + filesz() const + { return this->filesz_; } + // Return the maximum alignment of the Output_data. uint64_t - max_data_align() const; + addralign(); // Add an Output_section to this segment. void @@ -463,11 +798,12 @@ class Output_segment // Set the address of the segment to ADDR and the offset to *POFF // (aligned if necessary), and set the addresses and offsets of all - // contained output sections accordingly. Return the address of the - // immediately following segment. Update *POFF. This should only - // be called for a PT_LOAD segment. + // contained output sections accordingly. Set the section indexes + // of all contained output sections starting with *PSHNDX. Return + // the address of the immediately following segment. Update *POFF + // and *PSHNDX. This should only be called for a PT_LOAD segment. uint64_t - set_section_addresses(uint64_t addr, off_t* poff); + set_section_addresses(uint64_t addr, off_t* poff, unsigned int* pshndx); // Set the offset of this segment based on the section. This should // only be called for a non-PT_LOAD segment. @@ -481,13 +817,14 @@ class Output_segment // Write the segment header into *OPHDR. template<int size, bool big_endian> void - write_header(elfcpp::Phdr_write<size, big_endian>*) const; + write_header(elfcpp::Phdr_write<size, big_endian>*); // Write the section headers of associated sections into V. template<int size, bool big_endian> unsigned char* write_section_headers(const Stringpool*, - unsigned char* v ACCEPT_SIZE_ENDIAN) const; + unsigned char* v, + unsigned int* pshndx ACCEPT_SIZE_ENDIAN) const; private: Output_segment(const Output_segment&); @@ -495,9 +832,14 @@ class Output_segment typedef std::list<Output_data*> Output_data_list; + // Find the maximum alignment in an Output_data_list. + static uint64_t + maximum_alignment(const Output_data_list*); + // Set the section addresses in an Output_data_list. uint64_t - set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff); + set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff, + unsigned int* pshndx); // Return the number of Output_sections in an Output_data_list. unsigned int @@ -507,7 +849,8 @@ class Output_segment template<int size, bool big_endian> unsigned char* write_section_headers_list(const Stringpool*, const Output_data_list*, - unsigned char* v ACCEPT_SIZE_ENDIAN) const; + unsigned char* v, + unsigned int* pshdx ACCEPT_SIZE_ENDIAN) const; // The list of output data with contents attached to this segment. Output_data_list output_data_; @@ -529,6 +872,8 @@ class Output_segment elfcpp::Elf_Word type_; // The segment flags. elfcpp::Elf_Word flags_; + // Whether we have set align_. + bool is_align_known_; }; // This class represents the output file. |