diff options
-rw-r--r-- | elfcpp/elfcpp.h | 288 | ||||
-rw-r--r-- | elfcpp/elfcpp_internal.h | 30 | ||||
-rw-r--r-- | elfcpp/i386.h | 63 | ||||
-rw-r--r-- | gold/Makefile.am | 5 | ||||
-rw-r--r-- | gold/Makefile.in | 14 | ||||
-rw-r--r-- | gold/archive.cc | 360 | ||||
-rw-r--r-- | gold/archive.h | 143 | ||||
-rw-r--r-- | gold/fileread.cc | 10 | ||||
-rw-r--r-- | gold/gold.cc | 46 | ||||
-rw-r--r-- | gold/gold.h | 15 | ||||
-rw-r--r-- | gold/i386.cc | 122 | ||||
-rw-r--r-- | gold/layout.cc | 220 | ||||
-rw-r--r-- | gold/layout.h | 104 | ||||
-rw-r--r-- | gold/object.cc | 124 | ||||
-rw-r--r-- | gold/object.h | 64 | ||||
-rw-r--r-- | gold/options.cc | 137 | ||||
-rw-r--r-- | gold/options.h | 29 | ||||
-rw-r--r-- | gold/output.cc | 421 | ||||
-rw-r--r-- | gold/output.h | 138 | ||||
-rw-r--r-- | gold/po/POTFILES.in | 5 | ||||
-rw-r--r-- | gold/po/gold.pot | 213 | ||||
-rw-r--r-- | gold/readsyms.cc | 23 | ||||
-rw-r--r-- | gold/reloc.cc | 260 | ||||
-rw-r--r-- | gold/reloc.h | 45 | ||||
-rw-r--r-- | gold/resolve.cc | 7 | ||||
-rw-r--r-- | gold/stringpool.cc | 143 | ||||
-rw-r--r-- | gold/stringpool.h | 72 | ||||
-rw-r--r-- | gold/symtab.cc | 114 | ||||
-rw-r--r-- | gold/symtab.h | 18 | ||||
-rw-r--r-- | gold/target-reloc.h | 119 | ||||
-rw-r--r-- | gold/target.h | 34 |
31 files changed, 3163 insertions, 223 deletions
diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index 6f8f0dd..3ae005d 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -34,6 +34,7 @@ struct Elf_types<32> typedef uint32_t Elf_Addr; typedef uint32_t Elf_Off; typedef uint32_t Elf_WXword; + typedef int32_t Elf_Swxword; }; template<> @@ -42,6 +43,7 @@ struct Elf_types<64> typedef uint64_t Elf_Addr; typedef uint64_t Elf_Off; typedef uint64_t Elf_WXword; + typedef int64_t Elf_Swxword; }; // Offsets within the Ehdr e_ident field. @@ -471,6 +473,62 @@ elf_st_other(STV vis, unsigned char nonvis) + (static_cast<unsigned char>(vis) & 3)); } +// Reloc information from Rel/Rela r_info field. + +template<int size> +unsigned int +elf_r_sym(typename Elf_types<size>::Elf_WXword); + +template<> +inline unsigned int +elf_r_sym<32>(Elf_Word v) +{ + return v >> 8; +} + +template<> +inline unsigned int +elf_r_sym<64>(Elf_Xword v) +{ + return v >> 32; +} + +template<int size> +unsigned int +elf_r_type(typename Elf_types<size>::Elf_WXword); + +template<> +inline unsigned int +elf_r_type<32>(Elf_Word v) +{ + return v & 0xff; +} + +template<> +inline unsigned int +elf_r_type<64>(Elf_Xword v) +{ + return v & 0xffffffff; +} + +template<int size> +typename Elf_types<size>::Elf_WXword +elf_r_info(unsigned int s, unsigned int t); + +template<> +inline Elf_Word +elf_r_info<32>(unsigned int s, unsigned int t) +{ + return (s << 8) + (t & 0xff); +} + +template<> +inline Elf_Xword +elf_r_info<64>(unsigned int s, unsigned int t) +{ + return (static_cast<Elf_Xword>(s) << 32) + (t & 0xffffffff); +} + } // End namespace elfcpp. // Include internal details after defining the types. @@ -490,10 +548,15 @@ struct Elf_sizes { // Size of ELF file header. static const int ehdr_size = sizeof(internal::Ehdr_data<size>); + // Size of ELF segment header. + static const int phdr_size = sizeof(internal::Phdr_data<size>); // Size of ELF section header. static const int shdr_size = sizeof(internal::Shdr_data<size>); // Size of ELF symbol table entry. static const int sym_size = sizeof(internal::Sym_data<size>); + // Sizes of ELF reloc entries. + static const int rel_size = sizeof(internal::Rel_data<size>); + static const int rela_size = sizeof(internal::Rela_data<size>); }; // Given the address of an Elf_Word, return the value. @@ -505,6 +568,15 @@ read_elf_word(const Elf_Word* p) return internal::convert_word<big_endian>(*p); } +// Store an Elf_Word into an address. + +template<bool big_endian> +inline void +write_elf_word(Elf_Word* p, Elf_Word v) +{ + *p = internal::convert_word<big_endian>(v); +} + // Accessor class for the ELF file header. template<int size, bool big_endian> @@ -575,6 +647,76 @@ class Ehdr const internal::Ehdr_data<size>* p_; }; +// Write class for the ELF file header. + +template<int size, bool big_endian> +class Ehdr_write +{ + public: + Ehdr_write(unsigned char* p) + : p_(reinterpret_cast<internal::Ehdr_data<size>*>(p)) + { } + + void + put_e_ident(const unsigned char v[EI_NIDENT]) const + { memcpy(this->p_->e_ident, v, EI_NIDENT); } + + void + put_e_type(Elf_Half v) + { this->p_->e_type = internal::convert_half<big_endian>(v); } + + void + put_e_machine(Elf_Half v) + { this->p_->e_machine = internal::convert_half<big_endian>(v); } + + void + put_e_version(Elf_Word v) + { this->p_->e_version = internal::convert_word<big_endian>(v); } + + void + put_e_entry(typename Elf_types<size>::Elf_Addr v) + { this->p_->e_entry = internal::convert_addr<size, big_endian>(v); } + + void + put_e_phoff(typename Elf_types<size>::Elf_Off v) + { this->p_->e_phoff = internal::convert_off<size, big_endian>(v); } + + void + put_e_shoff(typename Elf_types<size>::Elf_Off v) + { this->p_->e_shoff = internal::convert_off<size, big_endian>(v); } + + void + put_e_flags(Elf_Word v) + { this->p_->e_flags = internal::convert_word<big_endian>(v); } + + void + put_e_ehsize(Elf_Half v) + { this->p_->e_ehsize = internal::convert_half<big_endian>(v); } + + void + put_e_phentsize(Elf_Half v) + { this->p_->e_phentsize = internal::convert_half<big_endian>(v); } + + void + put_e_phnum(Elf_Half v) + { this->p_->e_phnum = internal::convert_half<big_endian>(v); } + + void + put_e_shentsize(Elf_Half v) + { this->p_->e_shentsize = internal::convert_half<big_endian>(v); } + + void + put_e_shnum(Elf_Half v) + { this->p_->e_shnum = internal::convert_half<big_endian>(v); } + + void + put_e_shstrndx(Elf_Half v) + { this->p_->e_shstrndx = internal::convert_half<big_endian>(v); } + + private: + internal::Ehdr_data<size>* p_; +}; + // Accessor class for an ELF section header. template<int size, bool big_endian> @@ -630,6 +772,60 @@ class Shdr const internal::Shdr_data<size>* p_; }; +// Write class for an ELF section header. + +template<int size, bool big_endian> +class Shdr_write +{ + public: + Shdr_write(unsigned char* p) + : p_(reinterpret_cast<internal::Shdr_data<size>*>(p)) + { } + + void + put_sh_name(Elf_Word v) + { this->p_->sh_name = internal::convert_word<big_endian>(v); } + + void + put_sh_type(Elf_Word v) + { this->p_->sh_type = internal::convert_word<big_endian>(v); } + + void + put_sh_flags(typename Elf_types<size>::Elf_WXword v) + { this->p_->sh_flags = internal::convert_wxword<size, big_endian>(v); } + + void + put_sh_addr(typename Elf_types<size>::Elf_Addr v) + { this->p_->sh_addr = internal::convert_addr<size, big_endian>(v); } + + void + put_sh_offset(typename Elf_types<size>::Elf_Off v) + { this->p_->sh_offset = internal::convert_off<size, big_endian>(v); } + + void + put_sh_size(typename Elf_types<size>::Elf_WXword v) + { this->p_->sh_size = internal::convert_wxword<size, big_endian>(v); } + + void + put_sh_link(Elf_Word v) + { this->p_->sh_link = internal::convert_word<big_endian>(v); } + + void + put_sh_info(Elf_Word v) + { this->p_->sh_info = internal::convert_word<big_endian>(v); } + + void + put_sh_addralign(typename Elf_types<size>::Elf_WXword v) + { this->p_->sh_addralign = internal::convert_wxword<size, big_endian>(v); } + + void + put_sh_entsize(typename Elf_types<size>::Elf_WXword v) + { this->p_->sh_entsize = internal::convert_wxword<size, big_endian>(v); } + + private: + internal::Shdr_data<size>* p_; +}; + // Accessor class for an ELF segment header. template<int size, bool big_endian> @@ -676,6 +872,52 @@ class Phdr const internal::Phdr_data<size>* p_; }; +// Write class for an ELF segment header. + +template<int size, bool big_endian> +class Phdr_write +{ + public: + Phdr_write(unsigned char* p) + : p_(reinterpret_cast<internal::Phdr_data<size>*>(p)) + { } + + void + put_p_type(Elf_Word v) + { this->p_->p_type = internal::convert_word<big_endian>(v); } + + void + put_p_offset(typename Elf_types<size>::Elf_Off v) + { this->p_->p_offset = internal::convert_off<size, big_endian>(v); } + + void + put_p_vaddr(typename Elf_types<size>::Elf_Addr v) + { this->p_->p_vaddr = internal::convert_addr<size, big_endian>(v); } + + void + put_p_paddr(typename Elf_types<size>::Elf_Addr v) + { this->p_->p_paddr = internal::convert_addr<size, big_endian>(v); } + + void + put_p_filesz(typename Elf_types<size>::Elf_WXword v) + { this->p_->p_filesz = internal::convert_wxword<size, big_endian>(v); } + + void + put_p_memsz(typename Elf_types<size>::Elf_WXword v) + { this->p_->p_memsz = internal::convert_wxword<size, big_endian>(v); } + + void + put_p_flags(Elf_Word v) + { this->p_->p_flags = internal::convert_word<big_endian>(v); } + + void + put_p_align(typename Elf_types<size>::Elf_WXword v) + { this->p_->p_align = internal::convert_wxword<size, big_endian>(v); } + + private: + internal::Phdr_data<size>* p_; +}; + // Accessor class for an ELF symbol table entry. template<int size, bool big_endian> @@ -780,6 +1022,52 @@ class Sym_write internal::Sym_data<size>* p_; }; +// Accessor classes for Elf relocation table entries. + +template<int size, bool big_endian> +class Rel +{ + public: + Rel(const unsigned char* p) + : p_(reinterpret_cast<const internal::Rel_data<size>*>(p)) + { } + + typename Elf_types<size>::Elf_Addr + get_r_offset() const + { return internal::convert_addr<size, big_endian>(this->p_->r_offset); } + + typename Elf_types<size>::Elf_WXword + get_r_info() const + { return internal::convert_wxword<size, big_endian>(this->p_->r_info); } + + private: + const internal::Rel_data<size>* p_; +}; + +template<int size, bool big_endian> +class Rela +{ + public: + Rela(const unsigned char* p) + : p_(reinterpret_cast<const internal::Rela_data<size>*>(p)) + { } + + typename Elf_types<size>::Elf_Addr + get_r_offset() const + { return internal::convert_addr<size, big_endian>(this->p_->r_offset); } + + typename Elf_types<size>::Elf_WXword + get_r_info() const + { return internal::convert_wxword<size, big_endian>(this->p_->r_info); } + + typename Elf_types<size>::Elf_Swxword + get_r_addend() const + { return internal::convert_swxword<size, big_endian>(this->p_->r_addend); } + + private: + const internal::Rela_data<size>* p_; +}; + } // End namespace elfcpp. #endif // !defined(ELFPCP_H) diff --git a/elfcpp/elfcpp_internal.h b/elfcpp/elfcpp_internal.h index 696343a..0d69bae 100644 --- a/elfcpp/elfcpp_internal.h +++ b/elfcpp/elfcpp_internal.h @@ -158,8 +158,17 @@ convert_off(typename Elf_types<size>::Elf_Off v) // Convert Elf_WXword. template<int size, bool big_endian> -inline typename Elf_types<size>::Elf_Off -convert_wxword(typename Elf_types<size>::Elf_Off v) +inline typename Elf_types<size>::Elf_WXword +convert_wxword(typename Elf_types<size>::Elf_WXword v) +{ + return convert_addr_size<size, big_endian == host_big_endian>(v); +} + +// Convert ELF_Swxword. + +template<int size, bool big_endian> +inline typename Elf_types<size>::Elf_Swxword +convert_swxword(typename Elf_types<size>::Elf_Swxword v) { return convert_addr_size<size, big_endian == host_big_endian>(v); } @@ -264,6 +273,23 @@ struct Sym_data<64> Elf_Xword st_size; }; +// Elf relocation table entries. + +template<int size> +struct Rel_data +{ + typename Elf_types<size>::Elf_Addr r_offset; + typename Elf_types<size>::Elf_WXword r_info; +}; + +template<int size> +struct Rela_data +{ + typename Elf_types<size>::Elf_Addr r_offset; + typename Elf_types<size>::Elf_WXword r_info; + typename Elf_types<size>::Elf_Swxword r_addend; +}; + } // End namespace internal. } // End namespace elfcpp. diff --git a/elfcpp/i386.h b/elfcpp/i386.h new file mode 100644 index 0000000..01efd87 --- /dev/null +++ b/elfcpp/i386.h @@ -0,0 +1,63 @@ +// i386.h -- ELF definitions specific to EM_386 -*- C++ -*- + +#ifndef ELFCPP_I386_H +#define ELFCPP_I386_H + +namespace elfcpp +{ + +enum +{ + R_386_NONE = 0, + R_386_32 = 1, + R_386_PC32 = 2, + R_386_GOT32 = 3, + R_386_PLT32 = 4, + R_386_COPY = 5, + R_386_GLOB_DAT = 6, + R_386_JUMP_SLOT = 7, + R_386_RELATIVE = 8, + R_386_GOTOFF = 9, + R_386_GOTPC = 10, + // Used by Sun. + R_386_32PLT = 11, + // TLS extensions. + R_386_TLS_TPOFF = 14, + R_386_TLS_IE = 15, + R_386_TLS_GOTIE = 16, + R_386_TLS_LE = 17, + R_386_TLS_GD = 18, + R_386_TLS_LDM = 19, + // GNU extensions. + R_386_16 = 20, + R_386_PC16 = 21, + R_386_8 = 22, + R_386_PC8 = 23, + // More TLS relocs. + R_386_TLS_GD_32 = 24, + R_386_TLS_GD_PUSH = 25, + R_386_TLS_GD_CALL = 26, + R_386_TLS_GD_POP = 27, + R_386_TLS_LDM_32 = 28, + R_386_TLS_LDM_PUSH = 29, + R_386_TLS_LDM_CALL = 30, + R_386_TLS_LDM_POP = 31, + R_386_TLS_LDO_32 = 32, + R_386_TLS_IE_32 = 33, + R_386_TLS_LE_32 = 34, + R_386_TLS_DTPMOD32 = 35, + R_386_TLS_DTPOFF32 = 36, + R_386_TLS_TPOFF32 = 37, + R_386_TLS_GOTDESC = 39, + R_386_TLS_DESC_CALL = 40, + R_386_TLS_DESC = 41, + // Used by Intel. + R_386_USED_BY_INTEL_200 = 200, + // GNU vtable garbage collection extensions. + R_386_GNU_VTINHERIT = 250, + R_386_GNU_VTENTRY = 251 +}; + +} // End namespace elfcpp. + +#endif // !defined(ELFCPP_I386_H) diff --git a/gold/Makefile.am b/gold/Makefile.am index ed26af9..8545a77 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -18,6 +18,7 @@ INCLUDES = -D_GNU_SOURCE \ noinst_PROGRAMS = ld-new CCFILES = \ + archive.cc \ dirsearch.cc \ fileread.cc \ gold.cc \ @@ -27,6 +28,7 @@ CCFILES = \ options.cc \ output.cc \ readsyms.cc \ + reloc.cc \ resolve.cc \ symtab.cc \ stringpool.cc \ @@ -34,6 +36,7 @@ CCFILES = \ workqueue.cc HFILES = \ + archive.h \ dirsearch.h \ fileread.h \ gold.h \ @@ -43,9 +46,11 @@ HFILES = \ options.h \ output.h \ readsyms.h \ + reloc.h \ stringpool.h \ symtab.h \ target.h \ + target-reloc.h \ target-select.h \ workqueue.h diff --git a/gold/Makefile.in b/gold/Makefile.in index cb9caa1..a9d6073 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -65,9 +65,10 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = po/Makefile.in PROGRAMS = $(noinst_PROGRAMS) -am__objects_1 = dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \ - gold-threads.$(OBJEXT) layout.$(OBJEXT) object.$(OBJEXT) \ - options.$(OBJEXT) output.$(OBJEXT) readsyms.$(OBJEXT) \ +am__objects_1 = archive.$(OBJEXT) dirsearch.$(OBJEXT) \ + fileread.$(OBJEXT) gold.$(OBJEXT) gold-threads.$(OBJEXT) \ + layout.$(OBJEXT) object.$(OBJEXT) options.$(OBJEXT) \ + output.$(OBJEXT) readsyms.$(OBJEXT) reloc.$(OBJEXT) \ resolve.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \ target-select.$(OBJEXT) workqueue.$(OBJEXT) am__objects_2 = @@ -231,6 +232,7 @@ INCLUDES = -D_GNU_SOURCE \ @INCINTL@ CCFILES = \ + archive.cc \ dirsearch.cc \ fileread.cc \ gold.cc \ @@ -240,6 +242,7 @@ CCFILES = \ options.cc \ output.cc \ readsyms.cc \ + reloc.cc \ resolve.cc \ symtab.cc \ stringpool.cc \ @@ -247,6 +250,7 @@ CCFILES = \ workqueue.cc HFILES = \ + archive.h \ dirsearch.h \ fileread.h \ gold.h \ @@ -256,9 +260,11 @@ HFILES = \ options.h \ output.h \ readsyms.h \ + reloc.h \ stringpool.h \ symtab.h \ target.h \ + target-reloc.h \ target-select.h \ workqueue.h @@ -340,6 +346,7 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirsearch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@ @@ -350,6 +357,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readsyms.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reloc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringpool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@ diff --git a/gold/archive.cc b/gold/archive.cc new file mode 100644 index 0000000..6019325 --- /dev/null +++ b/gold/archive.cc @@ -0,0 +1,360 @@ +// archive.cc -- archive support for gold + +#include "gold.h" + +#include <cerrno> +#include <cstring> +#include <climits> +#include <vector> + +#include "elfcpp.h" +#include "fileread.h" +#include "symtab.h" +#include "object.h" +#include "archive.h" + +namespace gold +{ + +// The header of an entry in the archive. This is all readable text, +// padded with spaces where necesary. If the contents of an archive +// are all text file, the entire archive is readable. + +struct Archive::Archive_header +{ + // The entry name. + char ar_name[16]; + // The file modification time. + char ar_date[12]; + // The user's UID in decimal. + char ar_uid[6]; + // The user's GID in decimal. + char ar_gid[6]; + // The file mode in octal. + char ar_mode[8]; + // The file size in decimal. + char ar_size[10]; + // The final magic code. + char ar_fmag[2]; +}; + +// Archive methods. + +const char Archive::armag[sarmag] = +{ + '!', '<', 'a', 'r', 'c', 'h', '>', '\n' +}; + +const char Archive::arfmag[2] = { '`', '\n' }; + +// Get a view into the underlying file. + +const unsigned char* +Archive::get_view(off_t start, off_t size) +{ + return this->input_file_->file().get_view(start, size); +} + +// Set up the archive: read the symbol map and the extended name +// table. + +void +Archive::setup() +{ + // The first member of the archive should be the symbol table. + std::string armap_name; + off_t armap_size = this->read_header(sarmag, &armap_name); + if (!armap_name.empty()) + { + fprintf(stderr, _("%s: %s: no archive symbol table (run ranlib)\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + // Read in the entire armap. + const unsigned char* p = this->get_view(sarmag + sizeof(Archive_header), + armap_size); + + // Numbers in the armap are always big-endian. + const elfcpp::Elf_Word* pword = reinterpret_cast<const elfcpp::Elf_Word*>(p); + unsigned int nsyms = elfcpp::read_elf_word<true>(pword); + ++pword; + + // Note that the addition is in units of sizeof(elfcpp::Elf_Word). + const char* pnames = reinterpret_cast<const char*>(pword + nsyms); + + this->armap_.resize(nsyms); + + for (unsigned int i = 0; i < nsyms; ++i) + { + this->armap_[i].name = pnames; + this->armap_[i].offset = elfcpp::read_elf_word<true>(pword); + pnames += strlen(pnames) + 1; + ++pword; + } + + if (reinterpret_cast<const unsigned char*>(pnames) - p > armap_size) + { + fprintf(stderr, _("%s: %s: bad archive symbol table names\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + // See if there is an extended name table. + off_t off = sarmag + sizeof(Archive_header) + armap_size; + if ((off & 1) != 0) + ++off; + std::string xname; + off_t extended_size = this->read_header(off, &xname); + if (xname == "/") + { + p = this->get_view(off + sizeof(Archive_header), extended_size); + const char* px = reinterpret_cast<const char*>(p); + this->extended_names_.assign(px, extended_size); + } + + // Opening the file locked it. Unlock it now. + this->input_file_->file().unlock(); +} + +// Read the header of an archive member at OFF. Fail if something +// goes wrong. Return the size of the member. Set *PNAME to the name +// of the member. + +off_t +Archive::read_header(off_t off, std::string* pname) +{ + const unsigned char* p = this->get_view(off, sizeof(Archive_header)); + const Archive_header* hdr = reinterpret_cast<const Archive_header*>(p); + + if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) + { + fprintf(stderr, _("%s; %s: malformed archive header at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + const int size_string_size = sizeof hdr->ar_size; + char size_string[size_string_size + 1]; + memcpy(size_string, hdr->ar_size, size_string_size); + char* ps = size_string + size_string_size; + while (ps[-1] == ' ') + --ps; + *ps = '\0'; + + errno = 0; + char* end; + off_t member_size = strtol(size_string, &end, 10); + if (*end != '\0' + || member_size < 0 + || (member_size == LONG_MAX && errno == ERANGE)) + { + fprintf(stderr, _("%s: %s: malformed archive header size at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + if (hdr->ar_name[0] != '/') + { + const char* name_end = strchr(hdr->ar_name, '/'); + if (name_end == NULL + || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) + { + fprintf(stderr, _("%s: %s: malformed archive header name at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + pname->assign(hdr->ar_name, name_end - hdr->ar_name); + } + else if (hdr->ar_name[1] == ' ') + { + // This is the symbol table. + pname->clear(); + } + else if (hdr->ar_name[1] == '/') + { + // This is the extended name table. + pname->assign(1, '/'); + } + else + { + errno = 0; + long x = strtol(hdr->ar_name + 1, &end, 10); + if (*end != ' ' + || x < 0 + || (x == LONG_MAX && errno == ERANGE) + || static_cast<size_t>(x) >= this->extended_names_.size()) + { + fprintf(stderr, _("%s: %s: bad extended name index at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + const char* name = this->extended_names_.data() + x; + const char* name_end = strchr(name, '/'); + if (static_cast<size_t>(name_end - name) > this->extended_names_.size() + || name_end[1] != '\n') + { + fprintf(stderr, _("%s: %s: bad extended name entry at header %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + pname->assign(name, name_end - name); + } + + return member_size; +} + +// Select members from the archive and add them to the link. We walk +// through the elements in the archive map, and look each one up in +// the symbol table. If it exists as a strong undefined symbol, we +// pull in the corresponding element. We have to do this in a loop, +// since pulling in one element may create new undefined symbols which +// may be satisfied by other objects in the archive. + +void +Archive::add_symbols(Symbol_table* symtab, Input_objects* input_objects) +{ + size_t armap_size = this->armap_.size(); + std::vector<bool> seen; + seen.resize(this->armap_.size()); + seen.clear(); + + bool added_new_object; + do + { + added_new_object = false; + off_t last = -1; + for (size_t i = 0; i < armap_size; ++i) + { + if (seen[i]) + continue; + if (this->armap_[i].offset == last) + { + seen[i] = true; + continue; + } + + Symbol* sym = symtab->lookup(this->armap_[i].name); + if (sym == NULL) + continue; + else if (sym->shnum() != elfcpp::SHN_UNDEF) + { + seen[i] = true; + continue; + } + else if (sym->binding() == elfcpp::STB_WEAK) + continue; + + // We want to include this object in the link. + last = this->armap_[i].offset; + this->include_member(symtab, input_objects, last); + added_new_object = true; + } + } + while (added_new_object); +} + +// Include an archive member in the link. OFF is the file offset of +// the member header. + +void +Archive::include_member(Symbol_table* symtab, Input_objects* input_objects, + off_t off) +{ + std::string n; + this->read_header(off, &n); + + size_t memoff = off + sizeof(Archive_header); + + // Read enough of the file to pick up the entire ELF header. + int ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size; + off_t bytes; + const unsigned char* p = this->input_file_->file().get_view(memoff, + ehdr_size, + &bytes); + if (bytes < 4) + { + fprintf(stderr, _("%s: %s: member at %ld is not an ELF object"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + static unsigned char elfmagic[4] = + { + elfcpp::ELFMAG0, elfcpp::ELFMAG1, + elfcpp::ELFMAG2, elfcpp::ELFMAG3 + }; + if (memcmp(p, elfmagic, 4) != 0) + { + fprintf(stderr, _("%s: %s: member at %ld is not an ELF object"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + Object* obj = make_elf_object((std::string(this->input_file_->name()) + + "(" + n + ")"), + this->input_file_, memoff, p, bytes); + + input_objects->add_object(obj); + + Read_symbols_data sd = obj->read_symbols(); + obj->add_symbols(symtab, sd); +} + +// Add_archive_symbols methods. + +Add_archive_symbols::~Add_archive_symbols() +{ + if (this->this_blocker_ != NULL) + delete this->this_blocker_; + // next_blocker_ is deleted by the task associated with the next + // input file. +} + +// Return whether we can add the archive symbols. We are blocked by +// this_blocker_. We block next_blocker_. We also lock the file. + +Task::Is_runnable_type +Add_archive_symbols::is_runnable(Workqueue*) +{ + if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; +} + +class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker +{ + public: + Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue, + Archive* archive) + : blocker_(token, workqueue), archlock_(*archive) + { } + + private: + Task_locker_block blocker_; + Task_locker_obj<Archive> archlock_; +}; + +Task_locker* +Add_archive_symbols::locks(Workqueue* workqueue) +{ + return new Add_archive_symbols_locker(*this->next_blocker_, + workqueue, + this->archive_); +} + +void +Add_archive_symbols::run(Workqueue*) +{ + this->archive_->add_symbols(this->symtab_, this->input_objects_); +} + +} // End namespace gold. diff --git a/gold/archive.h b/gold/archive.h new file mode 100644 index 0000000..14d1c3b --- /dev/null +++ b/gold/archive.h @@ -0,0 +1,143 @@ +// archive.h -- archive support for gold -*- C++ -*- + +#ifndef GOLD_ARCHIVE_H +#define GOLD_ARCHIVE_H + +#include <string> +#include <vector> + +#include "workqueue.h" + +namespace gold +{ + +class Input_file; +class Input_objects; +class Symbol_table; + +// This class represents an archive--generally a libNAME.a file. +// Archives have a symbol table and a list of objects. + +class Archive +{ + public: + Archive(const std::string& name, Input_file* input_file) + : name_(name), input_file_(input_file), armap_(), extended_names_() + { } + + // The length of the magic string at the start of an archive. + static const int sarmag = 8; + + // The magic string at the start of an archive. + static const char armag[sarmag]; + + // The string expected at the end of an archive member header. + static const char arfmag[2]; + + // The name of the object. + const std::string& + name() const + { return this->name_; } + + // Set up the archive: read the symbol map. + void + setup(); + + // Lock the underlying file. + void + lock() + { this->input_file_->file().lock(); } + + // Unlock the underlying file. + void + unlock() + { this->input_file_->file().unlock(); } + + // Return whether the underlying file is locked. + bool + is_locked() const + { return this->input_file_->file().is_locked(); } + + // Select members from the archive as needed and add them to the + // link. + void + add_symbols(Symbol_table*, Input_objects*); + + private: + Archive(const Archive&); + Archive& operator=(const Archive&); + + struct Archive_header; + class Add_archive_symbols_locker; + + // Get a view into the underlying file. + const unsigned char* + get_view(off_t start, off_t size); + + // Read an archive member header at OFF. Return the size of the + // member, and set *PNAME to the name. + off_t + read_header(off_t off, std::string* pname); + + // Include an archive member in the link. + void + include_member(Symbol_table*, Input_objects*, off_t off); + + // An entry in the archive map of symbols to object files. + struct Armap_entry + { + // The symbol name. + const char* name; + // The offset to the file. + off_t offset; + }; + + // Name of object as printed to user. + std::string name_; + // For reading the file. + Input_file* input_file_; + // The archive map. + std::vector<Armap_entry> armap_; + // The extended name table. + std::string extended_names_; +}; + +// This class is used to read an archive and pick out the desired +// elements and add them to the link. + +class Add_archive_symbols : public Task +{ + public: + Add_archive_symbols(Symbol_table* symtab, Input_objects* input_objects, + Archive* archive, Task_token* this_blocker, + Task_token* next_blocker) + : symtab_(symtab), input_objects_(input_objects), archive_(archive), + this_blocker_(this_blocker), next_blocker_(next_blocker) + { } + + ~Add_archive_symbols(); + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Add_archive_symbols_locker; + + Symbol_table* symtab_; + Input_objects* input_objects_; + Archive* archive_; + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_ARCHIVE_H) diff --git a/gold/fileread.cc b/gold/fileread.cc index a92da9d..987408e 100644 --- a/gold/fileread.cc +++ b/gold/fileread.cc @@ -256,15 +256,15 @@ Input_file::open(const General_options& options, const Dirsearch& dirpath) else { std::string n1("lib"); - n1 += this->input_argument_.lib_basename(); + n1 += this->input_argument_.name(); std::string n2; - if (options.is_static()) + if (!options.is_static()) n2 = n1 + ".so"; n1 += ".a"; name = dirpath.find(n1, n2); if (name.empty()) { - fprintf(stderr, _("%s: cannot find %s"), program_name, + fprintf(stderr, _("%s: cannot find %s\n"), program_name, this->input_argument_.name()); gold_exit(false); } @@ -272,8 +272,8 @@ Input_file::open(const General_options& options, const Dirsearch& dirpath) if (!this->file_.open(name)) { - fprintf(stderr, _("%s: cannot open %s: %s"), program_name, name.c_str(), - strerror(errno)); + fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name, + name.c_str(), strerror(errno)); gold_exit(false); } } diff --git a/gold/gold.cc b/gold/gold.cc index 9576e4a..f403910 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -14,6 +14,7 @@ #include "symtab.h" #include "object.h" #include "layout.h" +#include "reloc.h" namespace gold { @@ -97,6 +98,51 @@ queue_initial_tasks(const General_options& options, } // end anonymous namespace. +namespace gold +{ + +// Queue up the final set of tasks. This is called at the end of +// Layout_task. + +void +queue_final_tasks(const General_options& options, + const Input_objects* input_objects, + const Symbol_table* symtab, + const Layout* layout, + Workqueue* workqueue, + Output_file* of) +{ + // Use a blocker to block the final cleanup task. + Task_token* final_blocker = new Task_token(); + + // Queue a task for each input object to relocate the sections and + // write out the local symbols. + for (Input_objects::Object_list::const_iterator p = input_objects->begin(); + p != input_objects->end(); + ++p) + { + final_blocker->add_blocker(); + workqueue->queue(new Relocate_task(options, symtab, layout->sympool(), + *p, of, final_blocker)); + } + + // Queue a task to write out the symbol table. + final_blocker->add_blocker(); + workqueue->queue(new Write_symbols_task(symtab, input_objects->target(), + layout->sympool(), of, + final_blocker)); + + // Queue a task to write out everything else. + final_blocker->add_blocker(); + workqueue->queue(new Write_data_task(layout, of, final_blocker)); + + // Queue a task to close the output file. This will be blocked by + // FINAL_BLOCKER. + workqueue->queue(new Close_task(of, final_blocker)); +} + +} // End namespace gold. + int main(int argc, char** argv) { diff --git a/gold/gold.h b/gold/gold.h index 03737a2..cb17ae7 100644 --- a/gold/gold.h +++ b/gold/gold.h @@ -80,6 +80,13 @@ struct hash<T*> namespace gold { +class General_options; +class Input_objects; +class Symbol_table; +class Layout; +class Workqueue; +class Output_file; + // The name of the program as used in error messages. extern const char* program_name; @@ -103,6 +110,14 @@ gold_nomem() ATTRIBUTE_NORETURN; extern void gold_unreachable() ATTRIBUTE_NORETURN; +extern void +queue_final_tasks(const General_options&, + const Input_objects*, + const Symbol_table*, + const Layout*, + Workqueue*, + Output_file* of); + } // End namespace gold. #endif // !defined(GOLD_GOLD_H) diff --git a/gold/i386.cc b/gold/i386.cc index 21dd57a..32ee881 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -2,7 +2,10 @@ #include "gold.h" #include "elfcpp.h" +#include "i386.h" +#include "object.h" #include "target.h" +#include "target-reloc.h" #include "target-select.h" namespace @@ -19,21 +22,124 @@ class Target_i386 : public Sized_target<32, false> : Sized_target<32, false>(&i386_info) { } + void + relocate_section(const Symbol_table* symtab, + Sized_object<32, false>*, + unsigned int, + const unsigned char*, + size_t, + unsigned int, + const elfcpp::Elf_types<32>::Elf_Addr*, + Symbol**, + unsigned char*, + elfcpp::Elf_types<32>::Elf_Addr, + off_t); + + // The class which implements relocation. + struct Relocate + { + inline void + operator()(Sized_object<32, false>*, const elfcpp::Rel<32, false>&, + unsigned int r_type, Sized_symbol<32>*, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned char*, elfcpp::Elf_types<32>::Elf_Addr); + + }; + private: static const Target::Target_info i386_info; }; const Target::Target_info Target_i386::i386_info = { - 32, // size - false, // is_big_endian - false, // has_make_symbol - false, // has_resolve, - 0x08048000, // text_segment_address, - 0x1000, // abi_pagesize - 0x1000 // common_pagesize + 32, // size + false, // is_big_endian + elfcpp::EM_386, // machine_code + false, // has_make_symbol + false, // has_resolve, + 0x08048000, // text_segment_address, + 0x1000, // abi_pagesize + 0x1000 // common_pagesize }; +// Perform a relocation. + +inline void +Target_i386::Relocate::operator()(Sized_object<32, false>* object, + const elfcpp::Rel<32, false>&, + unsigned int r_type, + Sized_symbol<32>*, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address) +{ + switch (r_type) + { + case elfcpp::R_386_NONE: + break; + + case elfcpp::R_386_32: + { + elfcpp::Elf_Word* wv = reinterpret_cast<elfcpp::Elf_Word*>(view); + unsigned int x = elfcpp::read_elf_word<false>(wv); + elfcpp::write_elf_word<false>(wv, x + value); + } + break; + + case elfcpp::R_386_PC32: + { + elfcpp::Elf_Word* wv = reinterpret_cast<elfcpp::Elf_Word*>(view); + unsigned int x = elfcpp::read_elf_word<false>(wv); + elfcpp::write_elf_word<false>(wv, x + value - address); + } + break; + + default: + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, object->name().c_str(), r_type); + // gold_exit(false); + } +} + +// Relocate section data. + +void +Target_i386::relocate_section(const Symbol_table* symtab, + Sized_object<32, false>* object, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned int local_count, + const elfcpp::Elf_types<32>::Elf_Addr* values, + Symbol** global_syms, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address, + off_t view_size) +{ + if (sh_type == elfcpp::SHT_RELA) + { + fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"), + program_name, object->name().c_str()); + gold_exit(false); + } + + gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>( + symtab, + object, + prelocs, + reloc_count, + local_count, + values, + global_syms, + view, + address, + view_size); +} + +// The i386 target. + +Target_i386 target_i386; + // The selector for i386 object files. class Target_selector_i386 : public Target_selector @@ -53,7 +159,7 @@ public: Target* Target_selector_i386::recognize(int, int, int) const { - return new Target_i386(); + return &target_i386; } Target_selector_i386 target_selector_i386; diff --git a/gold/layout.cc b/gold/layout.cc index d91f731..2448bf8 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -43,23 +43,34 @@ Layout_task::locks(Workqueue*) // have been read. void -Layout_task::run(Workqueue*) +Layout_task::run(Workqueue* workqueue) { - Layout layout(this->options_); - layout.init(); + // Nothing ever frees this. + Layout* layout = new Layout(this->options_); + layout->init(); for (Input_objects::Object_list::const_iterator p = this->input_objects_->begin(); p != this->input_objects_->end(); ++p) - (*p)->layout(&layout); - layout.finalize(this->input_objects_, this->symtab_); + (*p)->layout(layout); + off_t file_size = layout->finalize(this->input_objects_, this->symtab_); + + // Now we know the final size of the output file and we know where + // each piece of information goes. + Output_file* of = new Output_file(this->options_); + of->open(file_size); + + // Queue up the final set of tasks. + gold::queue_final_tasks(this->options_, this->input_objects_, + this->symtab_, layout, workqueue, of); } // Layout methods. Layout::Layout(const General_options& options) - : options_(options), namepool_(), sympool_(), signatures_(), - section_name_map_(), segment_list_(), section_list_() + : options_(options), last_shndx_(0), namepool_(), sympool_(), signatures_(), + section_name_map_(), segment_list_(), section_list_(), + special_output_list_() { } @@ -121,6 +132,10 @@ Output_section* Layout::layout(Object* object, const char* name, const elfcpp::Shdr<size, big_endian>& shdr, off_t* off) { + // We discard empty input sections. + if (shdr.get_sh_size() == 0) + return NULL; + if (!this->include_section(object, name, shdr)) return NULL; @@ -188,7 +203,9 @@ Output_section* Layout::make_output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags) { - Output_section* os = new Output_section(name, type, flags); + ++this->last_shndx_; + Output_section* os = new Output_section(name, type, flags, + this->last_shndx_); if ((flags & elfcpp::SHF_ALLOC) == 0) this->section_list_.push_back(os); @@ -354,19 +371,24 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) // Lay out the segment headers. int size = input_objects->target()->get_size(); + bool big_endian = input_objects->target()->is_big_endian(); Output_segment_headers* segment_headers; - segment_headers = new Output_segment_headers(size, this->segment_list_); + segment_headers = new Output_segment_headers(size, big_endian, + this->segment_list_); load_seg->add_initial_output_data(segment_headers); + this->special_output_list_.push_back(segment_headers); // FIXME: Attach them to PT_PHDRS if necessary. // Lay out the file header. Output_file_header* file_header; file_header = new Output_file_header(size, + big_endian, this->options_, input_objects->target(), symtab, segment_headers); load_seg->add_initial_output_data(file_header); + this->special_output_list_.push_back(file_header); // Set the file offsets of all the segments. off_t off = this->set_segment_offsets(input_objects->target(), load_seg); @@ -375,7 +397,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) // FIXME: We don't need to do this if we are stripping symbols. Output_section* osymtab; Output_section* ostrtab; - this->create_symtab_sections(input_objects, symtab, &osymtab, &ostrtab); + this->create_symtab_sections(size, input_objects, symtab, &off, + &osymtab, &ostrtab); // Create the .shstrtab section. Output_section* shstrtab_section = this->create_shstrtab(); @@ -385,8 +408,7 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) off = this->set_section_offsets(off); // Create the section table header. - Output_section_headers* oshdrs = this->create_shdrs(size, off); - off += oshdrs->data_size(); + Output_section_headers* oshdrs = this->create_shdrs(size, big_endian, &off); file_header->set_section_info(oshdrs, shstrtab_section); @@ -577,8 +599,11 @@ Layout::set_section_offsets(off_t off) p != this->section_list_.end(); ++p) { + if ((*p)->offset() != -1) + continue; uint64_t addralign = (*p)->addralign(); - off = (off + addralign - 1) & ~ (addralign - 1); + if (addralign != 0) + off = (off + addralign - 1) & ~ (addralign - 1); (*p)->set_address(0, off); off += (*p)->data_size(); } @@ -588,12 +613,35 @@ Layout::set_section_offsets(off_t off) // Create the symbol table sections. void -Layout::create_symtab_sections(const Input_objects* input_objects, +Layout::create_symtab_sections(int size, const Input_objects* input_objects, Symbol_table* symtab, + off_t* poff, Output_section** posymtab, Output_section** postrtab) { - off_t off = 0; + int symsize; + unsigned int align; + if (size == 32) + { + symsize = elfcpp::Elf_sizes<32>::sym_size; + align = 4; + } + else if (size == 64) + { + symsize = elfcpp::Elf_sizes<64>::sym_size; + align = 8; + } + else + abort(); + + off_t off = *poff; + off = (off + align - 1) & ~ (align - 1); + off_t startoff = off; + + // Save space for the dummy symbol at the start of the section. We + // never bother to write this out--it will just be left as zero. + off += symsize; + for (Input_objects::Object_list::const_iterator p = input_objects->begin(); p != input_objects->end(); ++p) @@ -602,11 +650,37 @@ Layout::create_symtab_sections(const Input_objects* input_objects, off = (*p)->finalize_local_symbols(off, &this->sympool_); } + unsigned int local_symcount = (off - startoff) / symsize; + assert(local_symcount * symsize == off - startoff); + off = symtab->finalize(off, &this->sympool_); - *posymtab = new Output_section_symtab(this->namepool_.add(".symtab"), off); - *postrtab = new Output_section_strtab(this->namepool_.add(".strtab"), - &this->sympool_); + this->sympool_.set_string_offsets(); + + ++this->last_shndx_; + const char* symtab_name = this->namepool_.add(".symtab"); + Output_section* osymtab = new Output_section_symtab(symtab_name, + off - startoff, + this->last_shndx_); + this->section_list_.push_back(osymtab); + + ++this->last_shndx_; + const char* strtab_name = this->namepool_.add(".strtab"); + Output_section *ostrtab = new Output_section_strtab(strtab_name, + &this->sympool_, + this->last_shndx_); + this->section_list_.push_back(ostrtab); + this->special_output_list_.push_back(ostrtab); + + osymtab->set_address(0, startoff); + osymtab->set_link(ostrtab->shndx()); + osymtab->set_info(local_symcount); + osymtab->set_entsize(symsize); + osymtab->set_addralign(align); + + *poff = off; + *posymtab = osymtab; + *postrtab = ostrtab; } // Create the .shstrtab section, which holds the names of the @@ -621,10 +695,15 @@ Layout::create_shstrtab() const char* name = this->namepool_.add(".shstrtab"); + this->namepool_.set_string_offsets(); + + ++this->last_shndx_; Output_section* os = new Output_section_strtab(name, - &this->namepool_); + &this->namepool_, + this->last_shndx_); this->section_list_.push_back(os); + this->special_output_list_.push_back(os); return os; } @@ -633,14 +712,18 @@ Layout::create_shstrtab() // offset. Output_section_headers* -Layout::create_shdrs(int size, off_t off) +Layout::create_shdrs(int size, bool big_endian, off_t* poff) { Output_section_headers* oshdrs; - oshdrs = new Output_section_headers(size, this->segment_list_, - this->section_list_); + oshdrs = new Output_section_headers(size, big_endian, this->segment_list_, + this->section_list_, + &this->namepool_); uint64_t addralign = oshdrs->addralign(); - off = (off + addralign - 1) & ~ (addralign - 1); + off_t off = (*poff + addralign - 1) & ~ (addralign - 1); oshdrs->set_address(0, off); + off += oshdrs->data_size(); + *poff = off; + this->special_output_list_.push_back(oshdrs); return oshdrs; } @@ -733,6 +816,97 @@ Layout::add_comdat(const char* signature, bool group) } } +// Write out data not associated with a section or the symbol table. + +void +Layout::write_data(Output_file* of) const +{ + for (Data_list::const_iterator p = this->special_output_list_.begin(); + p != this->special_output_list_.end(); + ++p) + (*p)->write(of); +} + +// Write_data_task methods. + +// We can always run this task. + +Task::Is_runnable_type +Write_data_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We need to unlock FINAL_BLOCKER when finished. + +Task_locker* +Write_data_task::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->final_blocker_, workqueue); +} + +// Run the task--write out the data. + +void +Write_data_task::run(Workqueue*) +{ + this->layout_->write_data(this->of_); +} + +// Write_symbols_task methods. + +// We can always run this task. + +Task::Is_runnable_type +Write_symbols_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We need to unlock FINAL_BLOCKER when finished. + +Task_locker* +Write_symbols_task::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->final_blocker_, workqueue); +} + +// Run the task--write out the symbols. + +void +Write_symbols_task::run(Workqueue*) +{ + this->symtab_->write_globals(this->target_, this->sympool_, this->of_); +} + +// Close_task methods. + +// We can't run until FINAL_BLOCKER is unblocked. + +Task::Is_runnable_type +Close_task::is_runnable(Workqueue*) +{ + if (this->final_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; +} + +// We don't lock anything. + +Task_locker* +Close_task::locks(Workqueue*) +{ + return NULL; +} + +// Run the task--close the file. + +void +Close_task::run(Workqueue*) +{ + this->of_->close(); +} + // Instantiate the templates we need. We could use the configure // script to restrict this to only the ones for implemented targets. diff --git a/gold/layout.h b/gold/layout.h index 75b2151..930d2b9 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -23,6 +23,7 @@ class Output_section_symtab; class Output_section_headers; class Output_segment; class Output_data; +class Target; // This Task handles mapping the input sections to output sections and // laying them out in memory. @@ -84,6 +85,11 @@ class Layout layout(Object *object, const char* name, const elfcpp::Shdr<size, big_endian>& shdr, off_t* offset); + // Return the Stringpool used for symbol names. + const Stringpool* + sympool() const + { return &this->sympool_; } + // Return whether a section is a .gnu.linkonce section, given the // section name. static inline bool @@ -101,6 +107,11 @@ class Layout off_t finalize(const Input_objects*, Symbol_table*); + // Write out data not associated with an input file or the symbol + // table. + void + write_data(Output_file*) const; + // The list of segments. typedef std::vector<Output_segment*> Segment_list; @@ -143,7 +154,7 @@ class Layout // Create the output sections for the symbol table. void - create_symtab_sections(const Input_objects*, Symbol_table*, + create_symtab_sections(int size, const Input_objects*, Symbol_table*, off_t*, Output_section** osymtab, Output_section** ostrtab); @@ -153,7 +164,7 @@ class Layout // Create the section header table. Output_section_headers* - create_shdrs(int size, off_t); + create_shdrs(int size, bool big_endian, off_t*); // Return whether to include this section in the link. template<int size, bool big_endian> @@ -207,6 +218,8 @@ class Layout // A reference to the options on the command line. const General_options& options_; + // The index of the last output section. + unsigned int last_shndx_; // The output section names. Stringpool namepool_; // The output symbol names. @@ -220,6 +233,93 @@ class Layout // The list of output sections which are not attached to any output // segment. Section_list section_list_; + // The list of sections which require special output because they + // are not comprised of input sections. + Data_list special_output_list_; +}; + +// This task handles writing out data which is not part of a section +// or segment. + +class Write_data_task : public Task +{ + public: + Write_data_task(const Layout* layout, Output_file* of, + Task_token* final_blocker) + : layout_(layout), of_(of), final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const Layout* layout_; + Output_file* of_; + Task_token* final_blocker_; +}; + +// This task handles writing out the global symbols. + +class Write_symbols_task : public Task +{ + public: + Write_symbols_task(const Symbol_table* symtab, const Target* target, + const Stringpool* sympool, Output_file* of, + Task_token* final_blocker) + : symtab_(symtab), target_(target), sympool_(sympool), of_(of), + final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const Symbol_table* symtab_; + const Target* target_; + const Stringpool* sympool_; + Output_file* of_; + Task_token* final_blocker_; +}; + +// This task handles closing the file. + +class Close_task : public Task +{ + public: + Close_task(Output_file* of, Task_token* final_blocker) + : of_(of), final_blocker_(final_blocker) + { } + + // The standard task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + Output_file* of_; + Task_token* final_blocker_; }; } // End namespace gold. diff --git a/gold/object.cc b/gold/object.cc index 85019fb..6186f65 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -9,6 +9,7 @@ #include "object.h" #include "target-select.h" #include "layout.h" +#include "output.h" namespace gold { @@ -47,8 +48,11 @@ Sized_object<size, big_endian>::Sized_object( shoff_(ehdr.get_e_shoff()), shstrndx_(0), symtab_shnum_(0), + local_symbol_count_(0), + output_local_symbol_count_(0), symbols_(NULL), - local_symbol_offset_(0) + local_symbol_offset_(0), + values_(NULL) { if (ehdr.get_e_ehsize() != This::ehdr_size) { @@ -77,6 +81,7 @@ template<int size, bool big_endian> const unsigned char* Sized_object<size, big_endian>::section_header(unsigned int shnum) { + assert(shnum < this->shnum()); off_t symtabshdroff = this->shoff_ + shnum * This::shdr_size; return this->get_view(symtabshdroff, This::shdr_size); } @@ -393,7 +398,7 @@ Sized_object<size, big_endian>::do_layout(Layout* layout) const char* pnames = reinterpret_cast<const char*>(pnamesu); std::vector<Map_to_output>& map_sections(this->map_to_output()); - map_sections.reserve(shnum); + map_sections.resize(shnum); // Keep track of which sections to omit. std::vector<bool> omit(shnum, false); @@ -446,16 +451,24 @@ Sized_object<size, big_endian>::do_layout(Layout* layout) } // Finalize the local symbols. Here we record the file offset at -// which they should be output and we add their names to *POOL. -// Return the new file offset. This function is always called from -// the main thread. The actual output of the local symbols will occur -// in a separate task. +// which they should be output, we add their names to *POOL, and we +// add their values to THIS->VALUES_. Return the new file offset. +// This function is always called from the main thread. The actual +// output of the local symbols will occur in a separate task. template<int size, bool big_endian> off_t Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off, Stringpool* pool) { + if (this->symtab_shnum_ == 0) + { + // This object has no symbols. Weird but legal. + return off; + } + + off = (off + (size >> 3) - 1) & ~ ((off_t) (size >> 3) - 1); + this->local_symbol_offset_ = off; // Read the symbol table section header. @@ -469,6 +482,10 @@ Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off, const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), locsize); + this->local_symbol_count_ = loccount; + + this->values_ = new typename elfcpp::Elf_types<size>::Elf_Addr[loccount]; + // Read the section header for the symbol names. typename This::Shdr strtabshdr( this->section_header(symtabshdr.get_sh_link())); @@ -483,9 +500,10 @@ Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off, std::vector<Map_to_output>& mo(this->map_to_output()); unsigned int shnum = this->shnum(); + unsigned int count = 0; // Skip the first, dummy, symbol. psyms += sym_size; - for (unsigned int i = 1; i < loccount; ++i) + for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size) { elfcpp::Sym<size, big_endian> sym(psyms); @@ -493,15 +511,17 @@ Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off, if (shndx >= elfcpp::SHN_LORESERVE) { - if (shndx != elfcpp::SHN_ABS) + if (shndx == elfcpp::SHN_ABS) + this->values_[i] = sym.get_st_value(); + else { + // FIXME: Handle SHN_XINDEX. fprintf(stderr, _("%s: %s: unknown section index %u " "for local symbol %u\n"), program_name, this->name().c_str(), shndx, i); gold_exit(false); } - // FIXME: Handle SHN_XINDEX. } else { @@ -515,18 +535,98 @@ Sized_object<size, big_endian>::do_finalize_local_symbols(off_t off, } if (mo[shndx].output_section == NULL) - continue; + { + this->values_[i] = 0; + continue; + } + + this->values_[i] = (mo[shndx].output_section->address() + + sym.get_st_value()); } pool->add(pnames + sym.get_st_name()); off += sym_size; - - psyms += sym_size; + ++count; } + this->output_local_symbol_count_ = count; + return off; } +// Write out the local symbols. + +template<int size, bool big_endian> +void +Sized_object<size, big_endian>::write_local_symbols(Output_file* of, + const Stringpool* sympool) +{ + if (this->symtab_shnum_ == 0) + { + // This object has no symbols. Weird but legal. + return; + } + + // Read the symbol table section header. + typename This::Shdr symtabshdr(this->section_header(this->symtab_shnum_)); + assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + unsigned int local_symbol_count = this->local_symbol_count_; + assert(local_symbol_count == symtabshdr.get_sh_info()); + + // Read the local symbols. + const int sym_size = This::sym_size; + off_t locsize = local_symbol_count * sym_size; + const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), + locsize); + + // Read the section header for the symbol names. + typename This::Shdr strtabshdr( + this->section_header(symtabshdr.get_sh_link())); + assert(strtabshdr.get_sh_type() == elfcpp::SHT_STRTAB); + + // Read the symbol names. + const unsigned char* pnamesu = this->get_view(strtabshdr.get_sh_offset(), + strtabshdr.get_sh_size()); + const char* pnames = reinterpret_cast<const char*>(pnamesu); + + // Get a view into the output file. + off_t output_size = this->output_local_symbol_count_ * sym_size; + unsigned char* oview = of->get_output_view(this->local_symbol_offset_, + output_size); + + std::vector<Map_to_output>& mo(this->map_to_output()); + + psyms += sym_size; + unsigned char* ov = oview; + for (unsigned int i = 1; i < local_symbol_count; ++i, psyms += sym_size) + { + elfcpp::Sym<size, big_endian> isym(psyms); + elfcpp::Sym_write<size, big_endian> osym(ov); + + unsigned int st_shndx = isym.get_st_shndx(); + if (st_shndx < elfcpp::SHN_LORESERVE) + { + assert(st_shndx < mo.size()); + if (mo[st_shndx].output_section == NULL) + continue; + st_shndx = mo[st_shndx].output_section->shndx(); + } + + osym.put_st_name(sympool->get_offset(pnames + isym.get_st_name())); + osym.put_st_value(this->values_[i]); + osym.put_st_size(isym.get_st_size()); + osym.put_st_info(isym.get_st_info()); + osym.put_st_other(isym.get_st_other()); + osym.put_st_shndx(st_shndx); + + ov += sym_size; + } + + assert(ov - oview == output_size); + + of->write_output_view(this->local_symbol_offset_, output_size, oview); +} + // Input_objects methods. void diff --git a/gold/object.h b/gold/object.h index e3df36d..198e015 100644 --- a/gold/object.h +++ b/gold/object.h @@ -15,8 +15,9 @@ namespace gold { class Stringpool; -class Output_section; class Layout; +class Output_section; +class Output_file; // Data to pass from read_symbols() to add_symbols(). @@ -116,6 +117,12 @@ class Object finalize_local_symbols(off_t off, Stringpool* pool) { return this->do_finalize_local_symbols(off, pool); } + // Relocate the input sections and write out the local symbols. + void + relocate(const General_options& options, const Symbol_table* symtab, + const Stringpool* sympool, Output_file* of) + { return this->do_relocate(options, symtab, sympool, of); } + // What we need to know to map an input section to an output // section. We keep an array of these, one for each input section, // indexed by the input section number. @@ -132,7 +139,10 @@ class Object // information. const Map_to_output* section_output_info(unsigned int shnum) const - { return &this->map_to_output_[shnum]; } + { + assert(shnum < this->map_to_output_.size()); + return &this->map_to_output_[shnum]; + } protected: // Read the symbols--implemented by child class. @@ -152,6 +162,12 @@ class Object virtual off_t do_finalize_local_symbols(off_t, Stringpool*) = 0; + // Relocate the input sections and write out the local + // symbols--implemented by child class. + virtual void + do_relocate(const General_options& options, const Symbol_table* symtab, + const Stringpool*, Output_file* of) = 0; + // Get the file. Input_file* input_file() const @@ -199,7 +215,7 @@ class Object Object(const Object&); Object& operator=(const Object&); - // Name of object as printed to use. + // Name of object as printed to user. std::string name_; // For reading the file. Input_file* input_file_; @@ -263,6 +279,11 @@ class Sized_object : public Object off_t do_finalize_local_symbols(off_t, Stringpool*); + // Relocate the input sections and write out the local symbols. + void + do_relocate(const General_options& options, const Symbol_table* symtab, + const Stringpool*, Output_file* of); + // Return the appropriate Sized_target structure. Sized_target<size, big_endian>* sized_target() @@ -301,6 +322,30 @@ class Sized_object : public Object include_linkonce_section(Layout*, const char*, const elfcpp::Shdr<size, big_endian>&); + // Views and sizes when relocating. + struct View_size + { + unsigned char* view; + typename elfcpp::Elf_types<size>::Elf_Addr address; + off_t offset; + off_t view_size; + }; + + typedef std::vector<View_size> Views; + + // Write section data to the output file. Record the views and + // sizes in VIEWS for use when relocating. + void + write_sections(const unsigned char* pshdrs, Output_file*, Views*); + + // Relocate the sections in the output file. + void + relocate_sections(const Symbol_table*, const unsigned char* pshdrs, Views*); + + // Write out the local symbols. + void + write_local_symbols(Output_file*, const Stringpool*); + // ELF file header e_flags field. unsigned int flags_; // File offset of section header table. @@ -309,10 +354,16 @@ class Sized_object : public Object unsigned int shstrndx_; // Index of SHT_SYMTAB section. unsigned int symtab_shnum_; + // The number of local symbols. + unsigned int local_symbol_count_; + // The number of local symbols which go into the output file. + unsigned int output_local_symbol_count_; // The entries in the symbol table for the external symbols. Symbol** symbols_; // File offset for local symbols. off_t local_symbol_offset_; + // Values of local symbols. + typename elfcpp::Elf_types<size>::Elf_Addr *values_; }; // A class to manage the list of all objects. @@ -362,9 +413,10 @@ class Input_objects // Return an Object appropriate for the input file. P is BYTES long, // and holds the ELF header. -extern Object* make_elf_object(const std::string& name, Input_file*, - off_t offset, const unsigned char* p, - off_t bytes); +extern Object* +make_elf_object(const std::string& name, Input_file*, + off_t offset, const unsigned char* p, + off_t bytes); } // end namespace gold diff --git a/gold/options.cc b/gold/options.cc index ad0ac83..e5a16f1 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -5,9 +5,12 @@ #include "gold.h" #include "options.h" +namespace gold +{ + // The information we keep for a single command line option. -struct gold::options::One_option +struct options::One_option { // The single character option name, or '\0' if this is only a long // option. @@ -42,23 +45,23 @@ struct gold::options::One_option // be 0 if this function changes *argv. ARG points to the location // in *ARGV where the option starts, which may be helpful for a // short option. - int (*special)(int argc, char** argv, char *arg, gold::Command_line*); + int (*special)(int argc, char** argv, char *arg, Command_line*); // If this is a position independent option which does not take an // argument, this is the member function to call to record it. - void (gold::General_options::*general_noarg)(); + void (General_options::*general_noarg)(); // If this is a position independent function which takes an // argument, this is the member function to call to record it. - void (gold::General_options::*general_arg)(const char*); + void (General_options::*general_arg)(const char*); // If this is a position dependent option which does not take an // argument, this is the member function to call to record it. - void (gold::Position_dependent_options::*dependent_noarg)(); + void (Position_dependent_options::*dependent_noarg)(); // If this is a position dependent option which takes an argument, // this is the member function to record it. - void (gold::Position_dependent_options::*dependent_arg)(const char*); + void (Position_dependent_options::*dependent_arg)(const char*); // Return whether this option takes an argument. bool @@ -66,16 +69,26 @@ struct gold::options::One_option { return this->general_arg != NULL || this->dependent_arg != NULL; } }; -class gold::options::Command_line_options +class options::Command_line_options { public: static const One_option options[]; static const int options_size; }; +} // End namespace gold. + namespace { +// Handle the special -l option, which adds an input file. + +int +library(int argc, char** argv, char* arg, gold::Command_line* cmdline) +{ + return cmdline->process_l_option(argc, argv, arg); +} + // Report usage information for ld --help, and exit. int @@ -162,7 +175,10 @@ help(int, char**, char*, gold::Command_line*) return 0; } -} // End empty namespace. +} // End anonymous namespace. + +namespace gold +{ // Helper macros used to specify the options. We could also do this // using constructors, but then g++ would generate code to initialize @@ -170,75 +186,84 @@ help(int, char**, char*, gold::Command_line*) // we get better startup time. #define GENERAL_NOARG(short_option, long_option, doc, help, dash, func) \ - { short_option, long_option, doc, help, gold::options::One_option::dash, \ + { short_option, long_option, doc, help, options::One_option::dash, \ NULL, func, NULL, NULL, NULL } #define GENERAL_ARG(short_option, long_option, doc, help, dash, func) \ - { short_option, long_option, doc, help, gold::options::One_option::dash, \ + { short_option, long_option, doc, help, options::One_option::dash, \ NULL, NULL, func, NULL, NULL } #define POSDEP_NOARG(short_option, long_option, doc, help, dash, func) \ - { short_option, long_option, doc, help, gold::options::One_option::dash, \ + { short_option, long_option, doc, help, options::One_option::dash, \ NULL, NULL, NULL, func, NULL } #define POSDEP_ARG(short_option, long_option, doc, help, dash, func) \ - { short_option, long_option, doc, help, gold::options::One_option::dash, \ + { short_option, long_option, doc, help, options::One_option::dash, \ NULL, NULL, NULL, NULL, func } #define SPECIAL(short_option, long_option, doc, help, dash, func) \ - { short_option, long_option, doc, help, gold::options::One_option::dash, \ + { short_option, long_option, doc, help, options::One_option::dash, \ func, NULL, NULL, NULL, NULL } // Here is the actual list of options which we accept. -const gold::options::One_option -gold::options::Command_line_options::options[] = +const options::One_option +options::Command_line_options::options[] = { + SPECIAL('l', "library", N_("Search for library LIBNAME"), + N_("-lLIBNAME --library LIBNAME"), TWO_DASHES, + &library), GENERAL_ARG('L', "library-path", N_("Add directory to search path"), N_("-L DIR, --library-path DIR"), TWO_DASHES, - &gold::General_options::add_to_search_path), + &General_options::add_to_search_path), + GENERAL_ARG('o', "output", N_("Set output file name"), + N_("-o FILE, --output FILE"), TWO_DASHES, + &General_options::set_output_file_name), GENERAL_NOARG('r', NULL, N_("Generate relocatable output"), NULL, - ONE_DASH, &gold::General_options::set_relocatable), + ONE_DASH, &General_options::set_relocatable), GENERAL_NOARG('\0', "static", N_("Do not link against shared libraries"), - NULL, ONE_DASH, &gold::General_options::set_static), + NULL, ONE_DASH, &General_options::set_static), SPECIAL('\0', "help", N_("Report usage information"), NULL, TWO_DASHES, &help) }; -const int gold::options::Command_line_options::options_size = +const int options::Command_line_options::options_size = sizeof (options) / sizeof (options[0]); // The default values for the general options. -gold::General_options::General_options() - : is_relocatable_(false) +General_options::General_options() + : search_path_(), + output_file_name_("a.out"), + is_relocatable_(false), + is_static_(false) { } // The default values for the position dependent options. -gold::Position_dependent_options::Position_dependent_options() +Position_dependent_options::Position_dependent_options() : do_static_search_(false) { } // Construct a Command_line. -gold::Command_line::Command_line() +Command_line::Command_line() { } // Process the command line options. void -gold::Command_line::process(int argc, char** argv) +Command_line::process(int argc, char** argv) { - const int options_size = gold::options::Command_line_options::options_size; - const gold::options::One_option* options = - gold::options::Command_line_options::options; + const int options_size = options::Command_line_options::options_size; + const options::One_option* options = + options::Command_line_options::options; bool no_more_options = false; int i = 0; while (i < argc) { if (argv[i][0] != '-' || no_more_options) { - this->inputs_.push_back(Input_argument(argv[i], + this->inputs_.push_back(Input_argument(argv[i], false, this->position_options_)); ++i; continue; @@ -275,7 +300,7 @@ gold::Command_line::process(int argc, char** argv) if (options[j].long_option != NULL && (dashes == 2 || (options[j].dash - != gold::options::One_option::EXACTLY_TWO_DASHES)) + != options::One_option::EXACTLY_TWO_DASHES)) && first == options[j].long_option[0] && strcmp(opt, options[j].long_option) == 0) { @@ -356,13 +381,17 @@ gold::Command_line::process(int argc, char** argv) ++s; } } + + // FIXME: We should only do this when configured in native mode. + this->options_.add_to_search_path("/lib"); + this->options_.add_to_search_path("/usr/lib"); } // Apply a command line option. void -gold::Command_line::apply_option(const gold::options::One_option& opt, - const char* arg) +Command_line::apply_option(const options::One_option& opt, + const char* arg) { if (arg == NULL) { @@ -371,7 +400,7 @@ gold::Command_line::apply_option(const gold::options::One_option& opt, else if (opt.dependent_noarg) (this->position_options_.*(opt.dependent_noarg))(); else - gold::gold_unreachable(); + gold_unreachable(); } else { @@ -380,35 +409,63 @@ gold::Command_line::apply_option(const gold::options::One_option& opt, else if (opt.dependent_arg) (this->position_options_.*(opt.dependent_arg))(arg); else - gold::gold_unreachable(); + gold_unreachable(); } } +// Handle the -l option, which requires special treatment. + +int +Command_line::process_l_option(int argc, char** argv, char* arg) +{ + int ret; + const char* libname; + if (arg[1] != '\0') + { + ret = 1; + libname = arg + 1; + } + else if (argc > 1) + { + ret = 2; + libname = argv[argc + 1]; + } + else + this->usage(_("missing argument"), arg); + + this->inputs_.push_back(Input_argument(libname, true, + this->position_options_)); + + return ret; +} + // Report a usage error. */ void -gold::Command_line::usage() +Command_line::usage() { fprintf(stderr, _("%s: use the --help option for usage information\n"), - gold::program_name); - gold::gold_exit(false); + program_name); + gold_exit(false); } void -gold::Command_line::usage(const char* msg, const char *opt) +Command_line::usage(const char* msg, const char *opt) { fprintf(stderr, _("%s: %s: %s\n"), - gold::program_name, opt, msg); + program_name, opt, msg); this->usage(); } void -gold::Command_line::usage(const char* msg, char opt) +Command_line::usage(const char* msg, char opt) { fprintf(stderr, _("%s: -%c: %s\n"), - gold::program_name, opt, msg); + program_name, opt, msg); this->usage(); } + +} // End namespace gold. diff --git a/gold/options.h b/gold/options.h index 6ac1ab9..ba32ef5 100644 --- a/gold/options.h +++ b/gold/options.h @@ -13,6 +13,7 @@ #define GOLD_OPTIONS_H #include <list> +#include <string> namespace gold { @@ -41,6 +42,11 @@ class General_options search_path() const { return this->search_path_; } + // -o: Output file name. + const char* + output_file_name() const + { return this->output_file_name_; } + // -r: Whether we are doing a relocatable link. bool is_relocatable() const @@ -60,6 +66,10 @@ class General_options { this->search_path_.push_back(arg); } void + set_output_file_name(const char* arg) + { this->output_file_name_ = arg; } + + void set_relocatable() { this->is_relocatable_ = true; } @@ -68,6 +78,7 @@ class General_options { this->is_static_ = true; } Dir_list search_path_; + const char* output_file_name_; bool is_relocatable_; bool is_static_; @@ -109,8 +120,9 @@ class Position_dependent_options class Input_argument { public: - Input_argument(const char* name, const Position_dependent_options& options) - : name_(name), options_(options) + Input_argument(const char* name, bool is_lib, + const Position_dependent_options& options) + : name_(name), is_lib_(is_lib), options_(options) { } const char* @@ -123,14 +135,11 @@ class Input_argument bool is_lib() const - { return this->name_[0] == '-' && this->name_[1] == 'l'; } - - const char* - lib_basename() const - { return this->name_ + 2; } + { return this->is_lib_; } private: const char* name_; + bool is_lib_; Position_dependent_options options_; }; @@ -146,12 +155,18 @@ class Command_line void process(int argc, char** argv); + // Handle a -l option. + int + process_l_option(int, char**, char*); + + // Get the general options. const General_options& options() const { return this->options_; } typedef std::list<Input_argument> Input_argument_list; + // Get the list of input files. const Input_argument_list& inputs() const { return this->inputs_; } diff --git a/gold/output.cc b/gold/output.cc index 3940f82..ababd8c 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -3,6 +3,10 @@ #include "gold.h" #include <cstdlib> +#include <cerrno> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> #include <algorithm> #include "object.h" @@ -55,14 +59,18 @@ Output_data_const::do_write(Output_file* output) Output_section_headers::Output_section_headers( int size, + bool big_endian, const Layout::Segment_list& segment_list, - const Layout::Section_list& section_list) + const Layout::Section_list& section_list, + const Stringpool* secnamepool) : size_(size), + big_endian_(big_endian), segment_list_(segment_list), - section_list_(section_list) + section_list_(section_list), + secnamepool_(secnamepool) { - // Count all the sections. - off_t count = 0; + // Count all the sections. Start with 1 for the null section. + off_t count = 1; for (Layout::Segment_list::const_iterator p = segment_list.begin(); p != segment_list.end(); ++p) @@ -80,37 +88,158 @@ Output_section_headers::Output_section_headers( this->set_data_size(count * shdr_size); } +// Write out the section headers. + void -Output_section_headers::do_write(Output_file*) +Output_section_headers::do_write(Output_file* of) { - // FIXME: Unimplemented. - abort(); + if (this->size_ == 32) + { + if (this->big_endian_) + this->do_sized_write<32, true>(of); + else + this->do_sized_write<32, false>(of); + } + else if (this->size_ == 64) + { + if (this->big_endian_) + this->do_sized_write<64, true>(of); + else + this->do_sized_write<64, false>(of); + } + else + abort(); +} + +template<int size, bool big_endian> +void +Output_section_headers::do_sized_write(Output_file* of) +{ + off_t all_shdrs_size = this->data_size(); + unsigned char* view = of->get_output_view(this->offset(), all_shdrs_size); + + const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + unsigned char* v = view; + + { + typename elfcpp::Shdr_write<size, big_endian> oshdr(v); + oshdr.put_sh_name(0); + oshdr.put_sh_type(elfcpp::SHT_NULL); + oshdr.put_sh_flags(0); + oshdr.put_sh_addr(0); + oshdr.put_sh_offset(0); + oshdr.put_sh_size(0); + oshdr.put_sh_link(0); + oshdr.put_sh_info(0); + oshdr.put_sh_addralign(0); + oshdr.put_sh_entsize(0); + } + + v += shdr_size; + + for (Layout::Segment_list::const_iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + v = (*p)->write_section_headers<size, big_endian>(this->secnamepool_, v); + for (Layout::Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + elfcpp::Shdr_write<size, big_endian> oshdr(v); + (*p)->write_header(this->secnamepool_, &oshdr); + v += shdr_size; + } + + of->write_output_view(this->offset(), all_shdrs_size, view); } // Output_segment_header methods. +Output_segment_headers::Output_segment_headers( + int size, + bool big_endian, + const Layout::Segment_list& segment_list) + : size_(size), big_endian_(big_endian), segment_list_(segment_list) +{ + int phdr_size; + if (size == 32) + phdr_size = elfcpp::Elf_sizes<32>::phdr_size; + else if (size == 64) + phdr_size = elfcpp::Elf_sizes<64>::phdr_size; + else + abort(); + + this->set_data_size(segment_list.size() * phdr_size); +} + void -Output_segment_headers::do_write(Output_file*) +Output_segment_headers::do_write(Output_file* of) { - // FIXME: Unimplemented. - abort(); + if (this->size_ == 32) + { + if (this->big_endian_) + this->do_sized_write<32, true>(of); + else + this->do_sized_write<32, false>(of); + } + else if (this->size_ == 64) + { + if (this->big_endian_) + this->do_sized_write<64, true>(of); + else + this->do_sized_write<64, false>(of); + } + else + abort(); +} + +template<int size, bool big_endian> +void +Output_segment_headers::do_sized_write(Output_file* of) +{ + const int phdr_size = elfcpp::Elf_sizes<size>::phdr_size; + off_t all_phdrs_size = this->segment_list_.size() * phdr_size; + unsigned char* view = of->get_output_view(this->offset(), + all_phdrs_size); + unsigned char* v = view; + for (Layout::Segment_list::const_iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + { + elfcpp::Phdr_write<size, big_endian> ophdr(v); + (*p)->write_header(&ophdr); + v += phdr_size; + } + + of->write_output_view(this->offset(), all_phdrs_size, view); } // Output_file_header methods. Output_file_header::Output_file_header(int size, + bool big_endian, const General_options& options, const Target* target, const Symbol_table* symtab, const Output_segment_headers* osh) : size_(size), + big_endian_(big_endian), options_(options), target_(target), symtab_(symtab), - program_header_(osh), + segment_header_(osh), section_header_(NULL), shstrtab_(NULL) { + int ehdr_size; + if (size == 32) + ehdr_size = elfcpp::Elf_sizes<32>::ehdr_size; + else if (size == 64) + ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size; + else + abort(); + + this->set_data_size(ehdr_size); } // Set the section table information for a file header. @@ -126,10 +255,96 @@ Output_file_header::set_section_info(const Output_section_headers* shdrs, // Write out the file header. void -Output_file_header::do_write(Output_file*) +Output_file_header::do_write(Output_file* of) { - // FIXME: Unimplemented. - abort(); + if (this->size_ == 32) + { + if (this->big_endian_) + this->do_sized_write<32, true>(of); + else + this->do_sized_write<32, false>(of); + } + else if (this->size_ == 64) + { + if (this->big_endian_) + this->do_sized_write<64, true>(of); + else + this->do_sized_write<64, false>(of); + } + else + abort(); +} + +// Write out the file header with appropriate size and endianess. + +template<int size, bool big_endian> +void +Output_file_header::do_sized_write(Output_file* of) +{ + assert(this->offset() == 0); + + int ehdr_size = elfcpp::Elf_sizes<size>::ehdr_size; + unsigned char* view = of->get_output_view(0, ehdr_size); + elfcpp::Ehdr_write<size, big_endian> oehdr(view); + + unsigned char e_ident[elfcpp::EI_NIDENT]; + memset(e_ident, 0, elfcpp::EI_NIDENT); + e_ident[elfcpp::EI_MAG0] = elfcpp::ELFMAG0; + e_ident[elfcpp::EI_MAG1] = elfcpp::ELFMAG1; + e_ident[elfcpp::EI_MAG2] = elfcpp::ELFMAG2; + e_ident[elfcpp::EI_MAG3] = elfcpp::ELFMAG3; + if (size == 32) + e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS32; + else if (size == 64) + e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS64; + else + abort(); + e_ident[elfcpp::EI_DATA] = (big_endian + ? elfcpp::ELFDATA2MSB + : elfcpp::ELFDATA2LSB); + e_ident[elfcpp::EI_VERSION] = elfcpp::EV_CURRENT; + // FIXME: Some targets may need to set EI_OSABI and EI_ABIVERSION. + oehdr.put_e_ident(e_ident); + + elfcpp::ET e_type; + // FIXME: ET_DYN. + if (this->options_.is_relocatable()) + e_type = elfcpp::ET_REL; + else + e_type = elfcpp::ET_EXEC; + oehdr.put_e_type(e_type); + + oehdr.put_e_machine(this->target_->machine_code()); + oehdr.put_e_version(elfcpp::EV_CURRENT); + + Symbol* sym = this->symtab_->lookup("_start"); + typename Sized_symbol<size>::Value_type v; + if (sym == NULL) + v = 0; + else + { + Sized_symbol<size>* ssym; + ssym = this->symtab_->get_sized_symbol<size>(sym); + v = ssym->value(); + } + oehdr.put_e_entry(v); + + oehdr.put_e_phoff(this->segment_header_->offset()); + oehdr.put_e_shoff(this->section_header_->offset()); + + // FIXME: The target needs to set the flags. + oehdr.put_e_flags(0); + + oehdr.put_e_ehsize(elfcpp::Elf_sizes<size>::ehdr_size); + oehdr.put_e_phentsize(elfcpp::Elf_sizes<size>::phdr_size); + oehdr.put_e_phnum(this->segment_header_->data_size() + / elfcpp::Elf_sizes<size>::phdr_size); + oehdr.put_e_shentsize(elfcpp::Elf_sizes<size>::shdr_size); + oehdr.put_e_shnum(this->section_header_->data_size() + / elfcpp::Elf_sizes<size>::shdr_size); + oehdr.put_e_shstrndx(this->shstrtab_->shndx()); + + of->write_output_view(0, ehdr_size, view); } // Output_section methods. @@ -137,14 +352,15 @@ Output_file_header::do_write(Output_file*) // Construct an Output_section. NAME will point into a Stringpool. Output_section::Output_section(const char* name, elfcpp::Elf_Word type, - elfcpp::Elf_Xword flags) + elfcpp::Elf_Xword flags, unsigned int shndx) : name_(name), addralign_(0), entsize_(0), link_(0), info_(0), type_(type), - flags_(flags) + flags_(flags), + shndx_(shndx) { } @@ -184,13 +400,33 @@ Output_section::add_input_section(Object* object, const char* secname, || this->type_ != elfcpp::SHT_NOBITS) this->set_data_size(ssize + shdr.get_sh_size()); - return size; + return ssize; +} + +// Write the section header to *OSHDR. + +template<int size, bool big_endian> +void +Output_section::write_header(const Stringpool* secnamepool, + elfcpp::Shdr_write<size, big_endian>* oshdr) const +{ + oshdr->put_sh_name(secnamepool->get_offset(this->name_)); + oshdr->put_sh_type(this->type_); + oshdr->put_sh_flags(this->flags_); + oshdr->put_sh_addr(this->address()); + oshdr->put_sh_offset(this->offset()); + oshdr->put_sh_size(this->data_size()); + oshdr->put_sh_link(this->link_); + oshdr->put_sh_info(this->info_); + oshdr->put_sh_addralign(this->addralign_); + oshdr->put_sh_entsize(this->entsize_); } // Output_section_symtab methods. -Output_section_symtab::Output_section_symtab(const char* name, off_t size) - : Output_section(name, elfcpp::SHT_SYMTAB, 0) +Output_section_symtab::Output_section_symtab(const char* name, off_t size, + unsigned int shndx) + : Output_section(name, elfcpp::SHT_SYMTAB, 0, shndx) { this->set_data_size(size); } @@ -198,17 +434,18 @@ Output_section_symtab::Output_section_symtab(const char* name, off_t size) // Output_section_strtab methods. Output_section_strtab::Output_section_strtab(const char* name, - Stringpool* contents) - : Output_section(name, elfcpp::SHT_STRTAB, 0), + Stringpool* contents, + unsigned int shndx) + : Output_section(name, elfcpp::SHT_STRTAB, 0, shndx), contents_(contents) { + this->set_data_size(contents->get_strtab_size()); } void -Output_section_strtab::do_write(Output_file*) +Output_section_strtab::do_write(Output_file* of) { - // FIXME: Unimplemented. - abort(); + this->contents_->write(of, this->offset()); } // Output segment methods. @@ -260,7 +497,7 @@ Output_segment::add_output_section(Output_section* os, // section, there are normally only a few output sections in an // output segment. This loop is expected to be fast. - if (os->type() == elfcpp::SHT_NOTE) + if (os->type() == elfcpp::SHT_NOTE && !pdl->empty()) { Layout::Data_list::iterator p = pdl->end(); do @@ -281,7 +518,7 @@ Output_segment::add_output_section(Output_section* os, // case: we group the SHF_TLS/SHT_NOBITS sections right after the // SHF_TLS/SHT_PROGBITS sections. This lets us set up PT_TLS // correctly. - if ((os->flags() & elfcpp::SHF_TLS) != 0) + if ((os->flags() & elfcpp::SHF_TLS) != 0 && !this->output_data_.empty()) { pdl = &this->output_data_; bool nobits = os->type() == elfcpp::SHT_NOBITS; @@ -345,12 +582,15 @@ Output_segment::set_section_addresses(uint64_t addr, off_t* poff) off_t off = *poff; - return this->set_section_list_addresses(&this->output_bss_, addr, poff); + uint64_t ret = this->set_section_list_addresses(&this->output_bss_, addr, + poff); this->memsz_ = *poff - orig_off; // Ignore the file offset adjustments made by the BSS Output_data // objects. *poff = off; + + return ret; } // Set the addresses in a list of Output_data structures. @@ -454,12 +694,135 @@ Output_segment::output_section_count_list(const Output_data_list* pdl) const return count; } +// Write the segment data into *OPHDR. + +template<int size, bool big_endian> +void +Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr) const +{ + ophdr->put_p_type(this->type_); + ophdr->put_p_offset(this->offset_); + ophdr->put_p_vaddr(this->vaddr_); + ophdr->put_p_paddr(this->paddr_); + ophdr->put_p_filesz(this->filesz_); + ophdr->put_p_memsz(this->memsz_); + ophdr->put_p_flags(this->flags_); + ophdr->put_p_align(this->align_); +} + +// Write the section headers into V. + +template<int size, bool big_endian> +unsigned char* +Output_segment::write_section_headers(const Stringpool* secnamepool, + unsigned char* v) const +{ + v = this->write_section_headers_list<size, big_endian>(secnamepool, + &this->output_data_, + v); + v = this->write_section_headers_list<size, big_endian>(secnamepool, + &this->output_bss_, + v); + return v; +} + +template<int size, bool big_endian> +unsigned char* +Output_segment::write_section_headers_list(const Stringpool* secnamepool, + const Output_data_list* pdl, + unsigned char* v) const +{ + const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + for (Output_data_list::const_iterator p = pdl->begin(); + p != pdl->end(); + ++p) + { + if ((*p)->is_section()) + { + Output_section* ps = static_cast<const Output_section*>(*p); + elfcpp::Shdr_write<size, big_endian> oshdr(v); + ps->write_header(secnamepool, &oshdr); + v += shdr_size; + } + } + return v; +} + // Output_file methods. +Output_file::Output_file(const General_options& options) + : options_(options), + name_(options.output_file_name()), + o_(-1), + file_size_(0), + base_(NULL) +{ +} + +// Open the output file. + void -Output_file::write(off_t, const void*, off_t) +Output_file::open(off_t file_size) { - abort(); + this->file_size_ = file_size; + + int mode = this->options_.is_relocatable() ? 0666 : 0777; + int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode); + if (o < 0) + { + fprintf(stderr, _("%s: %s: open: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->o_ = o; + + // Write out one byte to make the file the right size. + if (::lseek(o, file_size - 1, SEEK_SET) < 0) + { + fprintf(stderr, _("%s: %s: lseek: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + char b = 0; + if (::write(o, &b, 1) != 1) + { + fprintf(stderr, _("%s: %s: write: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + + // Map the file into memory. + void* base = ::mmap(NULL, file_size, PROT_READ | PROT_WRITE, + MAP_SHARED, o, 0); + if (base == MAP_FAILED) + { + fprintf(stderr, _("%s: %s: mmap: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->base_ = static_cast<unsigned char*>(base); +} + +// Close the output file. + +void +Output_file::close() +{ + if (::munmap(this->base_, this->file_size_) < 0) + { + fprintf(stderr, _("%s: %s: munmap: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->base_ = NULL; + + if (::close(this->o_) < 0) + { + fprintf(stderr, _("%s: %s: close: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->o_ = -1; } // Instantiate the templates we need. We could use the configure diff --git a/gold/output.h b/gold/output.h index 25c5b2a..c6f2c78 100644 --- a/gold/output.h +++ b/gold/output.h @@ -12,6 +12,7 @@ namespace gold { +class General_options; class Object; class Output_file; @@ -24,7 +25,7 @@ class Output_data { public: explicit Output_data(off_t data_size = 0) - : address_(0), data_size_(data_size), offset_(0) + : address_(0), data_size_(data_size), offset_(-1) { } virtual @@ -166,8 +167,10 @@ class Output_section_headers : public Output_data { public: Output_section_headers(int size, + bool big_endian, const Layout::Segment_list&, - const Layout::Section_list&); + const Layout::Section_list&, + const Stringpool*); // Write the data to the file. void @@ -179,9 +182,16 @@ class Output_section_headers : public Output_data { return Output_data::default_alignment(this->size_); } private: + // Write the data to the file with the right size and endianness. + template<int size, bool big_endian> + void + do_sized_write(Output_file*); + int size_; + bool big_endian_; const Layout::Segment_list& segment_list_; const Layout::Section_list& section_list_; + const Stringpool* secnamepool_; }; // Output the segment headers. @@ -189,9 +199,8 @@ class Output_section_headers : public Output_data class Output_segment_headers : public Output_data { public: - Output_segment_headers(int size, const Layout::Segment_list& segment_list) - : size_(size), segment_list_(segment_list) - { } + Output_segment_headers(int size, bool big_endian, + const Layout::Segment_list& segment_list); // Write the data to the file. void @@ -203,7 +212,13 @@ class Output_segment_headers : public Output_data { return Output_data::default_alignment(this->size_); } private: + // Write the data to the file with the right size and endianness. + template<int size, bool big_endian> + void + do_sized_write(Output_file*); + int size_; + bool big_endian_; const Layout::Segment_list& segment_list_; }; @@ -213,6 +228,7 @@ class Output_file_header : public Output_data { public: Output_file_header(int size, + bool big_endian, const General_options&, const Target*, const Symbol_table*, @@ -239,11 +255,17 @@ class Output_file_header : public Output_data { assert(off == 0); } private: + // Write the data to the file with the right size and endianness. + template<int size, bool big_endian> + void + do_sized_write(Output_file*); + int size_; + bool big_endian_; const General_options& options_; const Target* target_; const Symbol_table* symtab_; - const Output_segment_headers* program_header_; + const Output_segment_headers* segment_header_; const Output_section_headers* section_header_; const Output_section* shstrtab_; }; @@ -255,7 +277,8 @@ 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); + Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword, + unsigned int shndx); virtual ~Output_section(); // Add a new input section named NAME with header SHDR from object @@ -285,6 +308,31 @@ class Output_section : public Output_data addralign() const { return this->addralign_; } + // Return the section index. + unsigned int + shndx() const + { return this->shndx_; } + + // Set the entsize field. + void + set_entsize(uint64_t v) + { this->entsize_ = v; } + + // Set the link field. + void + set_link(unsigned int v) + { this->link_ = v; } + + // Set the info field. + void + set_info(unsigned int v) + { this->info_ = v; } + + // Set the addralign field. + void + set_addralign(uint64_t v) + { this->addralign_ = v; } + // 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. @@ -312,6 +360,11 @@ class Output_section : public Output_data do_is_section_flag_set(elfcpp::Elf_Xword flag) const { return (this->flags_ & flag) != 0; } + // Write the section header into *OPHDR. + template<int size, bool big_endian> + void + write_header(const Stringpool*, elfcpp::Shdr_write<size, big_endian>*) const; + private: // Most of these fields are only valid after layout. @@ -331,6 +384,8 @@ class Output_section : public Output_data elfcpp::Elf_Word type_; // The section flags. elfcpp::Elf_Xword flags_; + // The section index. + unsigned int shndx_; }; // A special Output_section which represents the symbol table @@ -339,7 +394,7 @@ class Output_section : public Output_data class Output_section_symtab : public Output_section { public: - Output_section_symtab(const char* name, off_t size); + Output_section_symtab(const char* name, off_t size, unsigned int shndx); }; // A special Output_section which holds a string table. @@ -347,7 +402,8 @@ class Output_section_symtab : public Output_section class Output_section_strtab : public Output_section { public: - Output_section_strtab(const char* name, Stringpool* contents); + Output_section_strtab(const char* name, Stringpool* contents, + unsigned int shndx); // Write out the data. void @@ -417,6 +473,16 @@ class Output_segment unsigned int output_section_count() const; + // Write the segment header into *OPHDR. + template<int size, bool big_endian> + void + write_header(elfcpp::Phdr_write<size, big_endian>*) const; + + // 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) const; + private: Output_segment(const Output_segment&); Output_segment& operator=(const Output_segment&); @@ -431,6 +497,12 @@ class Output_segment unsigned int output_section_count_list(const Output_data_list*) const; + // Write the section headers in the list into V. + template<int size, bool big_endian> + unsigned char* + write_section_headers_list(const Stringpool*, const Output_data_list*, + unsigned char* v) const; + // The list of output data with contents attached to this segment. Output_data_list output_data_; // The list of output data without contents attached to this segment. @@ -453,19 +525,55 @@ class Output_segment elfcpp::Elf_Word flags_; }; -// This class represents the output file. The output file is a -// collection of output segments and a collection of output sections -// which are not associated with segments. +// This class represents the output file. class Output_file { public: - Output_file(); - ~Output_file(); + Output_file(const General_options& options); + + // Open the output file. FILE_SIZE is the final size of the file. + void + open(off_t file_size); + + // Close the output file and make sure there are no error. + void + close(); + + // We currently always use mmap which makes the view handling quite + // simple. In the future we may support other approaches. // Write data to the output file. void - write(off_t off, const void* data, off_t len); + write(off_t offset, const void* data, off_t len) + { memcpy(this->base_ + offset, data, len); } + + // Get a buffer to use to write to the file, given the offset into + // the file and the size. + unsigned char* + get_output_view(off_t start, off_t size) + { + assert(start >= 0 && size >= 0 && start + size <= this->file_size_); + return this->base_ + start; + } + + // VIEW must have been returned by get_output_view. Write the + // buffer to the file, passing in the offset and the size. + void + write_output_view(off_t, off_t, unsigned char*) + { } + + private: + // General options. + const General_options& options_; + // File name. + const char* name_; + // File descriptor. + int o_; + // File size. + off_t file_size_; + // Base of file mapped into memory. + unsigned char* base_; }; } // End namespace gold. diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in index 4bb4aad..76d60f9 100644 --- a/gold/po/POTFILES.in +++ b/gold/po/POTFILES.in @@ -1,3 +1,5 @@ +archive.cc +archive.h dirsearch.cc dirsearch.h fileread.cc @@ -17,12 +19,15 @@ output.cc output.h readsyms.cc readsyms.h +reloc.cc +reloc.h resolve.cc stringpool.cc stringpool.h symtab.cc symtab.h target.h +target-reloc.h target-select.cc target-select.h workqueue.cc diff --git a/gold/po/gold.pot b/gold/po/gold.pot index b0852a3..83ad026 100644 --- a/gold/po/gold.pot +++ b/gold/po/gold.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2006-09-27 15:38-0700\n" +"POT-Creation-Date: 2006-09-29 12:54-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -16,6 +16,46 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" +#: archive.cc:69 +#, c-format +msgid "%s: %s: no archive symbol table (run ranlib)\n" +msgstr "" + +#: archive.cc:98 +#, c-format +msgid "%s: %s: bad archive symbol table names\n" +msgstr "" + +#: archive.cc:132 +#, c-format +msgid "%s; %s: malformed archive header at %ld\n" +msgstr "" + +#: archive.cc:153 +#, c-format +msgid "%s: %s: malformed archive header size at %ld\n" +msgstr "" + +#: archive.cc:165 +#, c-format +msgid "%s: %s: malformed archive header name at %ld\n" +msgstr "" + +#: archive.cc:191 +#, c-format +msgid "%s: %s: bad extended name index at %ld\n" +msgstr "" + +#: archive.cc:202 +#, c-format +msgid "%s: %s: bad extended name entry at header %ld\n" +msgstr "" + +#: archive.cc:283 archive.cc:296 +#, c-format +msgid "%s: %s: member at %ld is not an ELF object" +msgstr "" + #: dirsearch.cc:51 #, c-format msgid "can not read directory %s" @@ -43,15 +83,15 @@ msgstr "" #: fileread.cc:267 #, c-format -msgid "%s: cannot find %s" +msgid "%s: cannot find %s\n" msgstr "" #: fileread.cc:275 #, c-format -msgid "%s: cannot open %s: %s" +msgid "%s: cannot open %s: %s\n" msgstr "" -#: gold.cc:75 +#: gold.cc:76 msgid "no input files" msgstr "" @@ -99,172 +139,255 @@ msgstr "" msgid "pthread_cond_signal failed" msgstr "" -#: object.cc:55 +#: i386.cc:98 +#, c-format +msgid "%s: %s: unsupported reloc %u\n" +msgstr "" + +#: i386.cc:121 +#, c-format +msgid "%s: %s: unsupported RELA reloc section\n" +msgstr "" + +#: object.cc:59 #, c-format msgid "%s: %s: bad e_ehsize field (%d != %d)\n" msgstr "" -#: object.cc:62 +#: object.cc:66 #, c-format msgid "%s: %s: bad e_shentsize field (%d != %d)\n" msgstr "" -#: object.cc:98 +#: object.cc:103 #, c-format msgid "%s: %s: unsupported ELF machine number %d\n" msgstr "" -#: object.cc:171 +#: object.cc:176 #, c-format msgid "%s: %s: invalid symbol table name index: %u\n" msgstr "" -#: object.cc:179 +#: object.cc:184 #, c-format msgid "%s: %s: symbol table name section has wrong type: %u\n" msgstr "" -#: object.cc:216 +#: object.cc:221 #, c-format msgid "%s: %s: size of symbols is not multiple of symbol size\n" msgstr "" -#: object.cc:270 +#: object.cc:275 #, c-format msgid "%s: %s: section group %u link %u out of range\n" msgstr "" -#: object.cc:280 +#: object.cc:285 #, c-format msgid "%s: %s: section group %u info %u out of range\n" msgstr "" -#: object.cc:291 +#: object.cc:296 #, c-format msgid "%s; %s: symtab section %u link %u out of range\n" msgstr "" -#: object.cc:307 +#: object.cc:312 #, c-format msgid "%s: %s: symbol %u name offset %u out of range\n" msgstr "" -#: object.cc:329 +#: object.cc:334 #, c-format msgid "%s: %s: section %u in section group %u out of range" msgstr "" -#: object.cc:408 +#: object.cc:413 #, c-format msgid "%s: %s: bad section name offset for section %u: %lu\n" msgstr "" -#: object.cc:499 +#: object.cc:520 #, c-format msgid "%s: %s: unknown section index %u for local symbol %u\n" msgstr "" -#: object.cc:511 +#: object.cc:531 #, c-format msgid "%s: %s: local symbol %u section index %u out of range\n" msgstr "" #. elfcpp::ET_DYN -#: object.cc:584 +#: object.cc:684 #, c-format msgid "%s: %s: dynamic objects are not yet supported\n" msgstr "" -#: object.cc:608 object.cc:661 object.cc:682 +#: object.cc:708 object.cc:761 object.cc:782 #, c-format msgid "%s: %s: ELF file too short\n" msgstr "" -#: object.cc:617 +#: object.cc:717 #, c-format msgid "%s: %s: invalid ELF version 0\n" msgstr "" -#: object.cc:620 +#: object.cc:720 #, c-format msgid "%s: %s: unsupported ELF version %d\n" msgstr "" -#: object.cc:628 +#: object.cc:728 #, c-format msgid "%s: %s: invalid ELF class 0\n" msgstr "" -#: object.cc:635 +#: object.cc:735 #, c-format msgid "%s: %s: unsupported ELF class %d\n" msgstr "" -#: object.cc:643 +#: object.cc:743 #, c-format msgid "%s: %s: invalid ELF data encoding\n" msgstr "" -#: object.cc:650 +#: object.cc:750 #, c-format msgid "%s: %s: unsupported ELF data encoding %d\n" msgstr "" -#: options.cc:84 +#: options.cc:97 #, c-format msgid "" "Usage: %s [options] file...\n" "Options:\n" msgstr "" -#: options.cc:193 +#: options.cc:209 +msgid "Search for library LIBNAME" +msgstr "" + +#: options.cc:210 +msgid "-lLIBNAME --library LIBNAME" +msgstr "" + +#: options.cc:212 msgid "Add directory to search path" msgstr "" -#: options.cc:194 +#: options.cc:213 msgid "-L DIR, --library-path DIR" msgstr "" -#: options.cc:196 +#: options.cc:215 +msgid "Set output file name" +msgstr "" + +#: options.cc:216 +msgid "-o FILE, --output FILE" +msgstr "" + +#: options.cc:218 msgid "Generate relocatable output" msgstr "" -#: options.cc:198 +#: options.cc:220 msgid "Do not link against shared libraries" msgstr "" -#: options.cc:200 +#: options.cc:222 msgid "Report usage information" msgstr "" -#: options.cc:294 options.cc:345 +#: options.cc:319 options.cc:370 options.cc:434 msgid "missing argument" msgstr "" -#: options.cc:307 options.cc:354 +#: options.cc:332 options.cc:379 msgid "unknown option" msgstr "" -#: options.cc:393 +#: options.cc:448 #, c-format msgid "%s: use the --help option for usage information\n" msgstr "" -#: options.cc:402 +#: options.cc:457 #, c-format msgid "%s: %s: %s\n" msgstr "" -#: options.cc:411 +#: options.cc:466 #, c-format msgid "%s: -%c: %s\n" msgstr "" -#: output.cc:167 +#: output.cc:383 #, c-format msgid "%s: %s: invalid alignment %lu for section \"%s\"\n" msgstr "" +#: output.cc:773 +#, c-format +msgid "%s: %s: open: %s\n" +msgstr "" + +#: output.cc:782 +#, c-format +msgid "%s: %s: lseek: %s\n" +msgstr "" + +#: output.cc:789 +#, c-format +msgid "%s: %s: write: %s\n" +msgstr "" + +#: output.cc:799 +#, c-format +msgid "%s: %s: mmap: %s\n" +msgstr "" + +#: output.cc:813 +#, c-format +msgid "%s: %s: munmap: %s\n" +msgstr "" + +#: output.cc:821 +#, c-format +msgid "%s: %s: close: %s\n" +msgstr "" + +#. Here we have to handle archives and any other input file +#. types we need. +#: readsyms.cc:107 +#, c-format +msgid "%s: %s: not an object or archive\n" +msgstr "" + +#: reloc.cc:165 +#, c-format +msgid "%s: %s: relocation section %u has bad info %u\n" +msgstr "" + +#: reloc.cc:182 +#, c-format +msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n" +msgstr "" + +#: reloc.cc:201 +#, c-format +msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u" +msgstr "" + +#: reloc.cc:212 +#, c-format +msgid "%s: %s: reloc section %u size %lu uneven" +msgstr "" + #: resolve.cc:144 #, c-format msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n" @@ -275,12 +398,22 @@ msgstr "" msgid "%s: %s: unsupported symbol binding %d for symbol %s\n" msgstr "" -#: symtab.cc:322 +#: symtab.cc:347 #, c-format msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n" msgstr "" -#: symtab.cc:336 +#: symtab.cc:361 #, c-format msgid "%s: %s: bad global symbol name offset %u at %lu\n" msgstr "" + +#: target-reloc.h:76 +#, c-format +msgid "%s: %s: reloc %zu has bad offset %lu\n" +msgstr "" + +#: target-reloc.h:106 +#, c-format +msgid "%s: %s: undefined reference to '%s'\n" +msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 99fa1b1..ee0e063 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -7,8 +7,9 @@ #include "elfcpp.h" #include "options.h" #include "dirsearch.h" -#include "readsyms.h" #include "object.h" +#include "archive.h" +#include "readsyms.h" namespace gold { @@ -85,9 +86,27 @@ Read_symbols::run(Workqueue* workqueue) } } + if (bytes >= Archive::sarmag) + { + if (memcmp(p, Archive::armag, Archive::sarmag) == 0) + { + // This is an archive. + Archive* arch = new Archive(this->input_.name(), input_file); + arch->setup(); + workqueue->queue(new Add_archive_symbols(this->symtab_, + this->input_objects_, + arch, + this->this_blocker_, + this->next_blocker_)); + return; + } + } + // Here we have to handle archives and any other input file // types we need. - gold_fatal("only objects are currently supported", false); + fprintf(stderr, _("%s: %s: not an object or archive\n"), + program_name, input_file->file().filename().c_str()); + gold_exit(false); } // Class Add_symbols. diff --git a/gold/reloc.cc b/gold/reloc.cc new file mode 100644 index 0000000..905eeae --- /dev/null +++ b/gold/reloc.cc @@ -0,0 +1,260 @@ +// reloc.cc -- relocate input files for gold. + +#include "gold.h" + +#include "workqueue.h" +#include "object.h" +#include "output.h" +#include "reloc.h" + +namespace gold +{ + +// Relocate_task methods. + +// These tasks are always runnable. + +Task::Is_runnable_type +Relocate_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We want to lock the file while we run. We want to unblock +// FINAL_BLOCKER when we are done. + +class Relocate_task::Relocate_locker : public Task_locker +{ + public: + Relocate_locker(Task_token& token, Workqueue* workqueue, + Object* object) + : blocker_(token, workqueue), objlock_(*object) + { } + + private: + Task_locker_block blocker_; + Task_locker_obj<Object> objlock_; +}; + +Task_locker* +Relocate_task::locks(Workqueue* workqueue) +{ + return new Relocate_locker(*this->final_blocker_, workqueue, + this->object_); +} + +// Run the task. + +void +Relocate_task::run(Workqueue*) +{ + this->object_->relocate(this->options_, this->symtab_, this->sympool_, + this->of_); +} + +// Relocate the input sections and write out the local symbols. + +template<int size, bool big_endian> +void +Sized_object<size, big_endian>::do_relocate(const General_options&, + const Symbol_table* symtab, + const Stringpool* sympool, + Output_file* of) +{ + unsigned int shnum = this->shnum(); + + // Read the section headers. + const unsigned char* pshdrs = this->get_view(this->shoff_, + shnum * This::shdr_size); + + Views views; + views.resize(shnum); + + // Make two passes over the sections. The first one copies the + // section data to the output file. The second one applies + // relocations. + + this->write_sections(pshdrs, of, &views); + + // Apply relocations. + + this->relocate_sections(symtab, pshdrs, &views); + + // Write out the accumulated views. + for (unsigned int i = 1; i < shnum; ++i) + { + if (views[i].view != NULL) + of->write_output_view(views[i].offset, views[i].view_size, + views[i].view); + } + + // Write out the local symbols. + this->write_local_symbols(of, sympool); +} + +// Write section data to the output file. PSHDRS points to the +// section headers. Record the views in *PVIEWS for use when +// relocating. + +template<int size, bool big_endian> +void +Sized_object<size, big_endian>::write_sections(const unsigned char* pshdrs, + Output_file* of, + Views* pviews) +{ + unsigned int shnum = this->shnum(); + std::vector<Map_to_output>& map_sections(this->map_to_output()); + + const unsigned char* p = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) + { + View_size* pvs = &(*pviews)[i]; + + pvs->view = NULL; + + const Output_section* os = map_sections[i].output_section; + if (os == NULL) + continue; + + typename This::Shdr shdr(p); + + if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) + continue; + + assert(map_sections[i].offset >= 0 + && map_sections[i].offset < os->data_size()); + off_t start = os->offset() + map_sections[i].offset; + off_t sh_size = shdr.get_sh_size(); + + unsigned char* view = of->get_output_view(start, sh_size); + this->input_file()->file().read(shdr.get_sh_offset(), + sh_size, + view); + pvs->view = view; + pvs->address = os->address() + map_sections[i].offset; + pvs->offset = start; + pvs->view_size = sh_size; + } +} + +// Relocate section data. VIEWS points to the section data as views +// in the output file. + +template<int size, bool big_endian> +void +Sized_object<size, big_endian>::relocate_sections(const Symbol_table* symtab, + const unsigned char* pshdrs, + Views* pviews) +{ + unsigned int shnum = this->shnum(); + std::vector<Map_to_output>& map_sections(this->map_to_output()); + Sized_target<size, big_endian>* target = this->sized_target(); + + const unsigned char* p = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) + { + typename This::Shdr shdr(p); + + unsigned int sh_type = shdr.get_sh_type(); + if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) + continue; + + unsigned int index = shdr.get_sh_info(); + if (index >= this->shnum()) + { + fprintf(stderr, _("%s: %s: relocation section %u has bad info %u\n"), + program_name, this->name().c_str(), i, index); + gold_exit(false); + } + + if (map_sections[index].output_section == NULL) + { + // This relocation section is against a section which we + // discarded. + continue; + } + + assert((*pviews)[index].view != NULL); + + if (shdr.get_sh_link() != this->symtab_shnum_) + { + fprintf(stderr, + _("%s: %s: relocation section %u uses unexpected " + "symbol table %u\n"), + program_name, this->name().c_str(), i, shdr.get_sh_link()); + gold_exit(false); + } + + off_t sh_size = shdr.get_sh_size(); + const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(), + sh_size); + + unsigned int reloc_size; + if (sh_type == elfcpp::SHT_REL) + reloc_size = elfcpp::Elf_sizes<size>::rel_size; + else + reloc_size = elfcpp::Elf_sizes<size>::rela_size; + + if (reloc_size != shdr.get_sh_entsize()) + { + fprintf(stderr, + _("%s: %s: unexpected entsize for reloc section %u: " + "%lu != %u"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(shdr.get_sh_entsize()), + reloc_size); + gold_exit(false); + } + + size_t reloc_count = sh_size / reloc_size; + if (reloc_count * reloc_size != sh_size) + { + fprintf(stderr, _("%s: %s: reloc section %u size %lu uneven"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(sh_size)); + gold_exit(false); + } + + target->relocate_section(symtab, this, sh_type, prelocs, reloc_count, + this->local_symbol_count_, + this->values_, + this->symbols_, + (*pviews)[index].view, + (*pviews)[index].address, + (*pviews)[index].view_size); + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +void +Sized_object<32, false>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Stringpool* sympool, + Output_file* of); + +template +void +Sized_object<32, true>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Stringpool* sympool, + Output_file* of); + +template +void +Sized_object<64, false>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Stringpool* sympool, + Output_file* of); + +template +void +Sized_object<64, true>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Stringpool* sympool, + Output_file* of); + + +} // End namespace gold. diff --git a/gold/reloc.h b/gold/reloc.h new file mode 100644 index 0000000..287bb79 --- /dev/null +++ b/gold/reloc.h @@ -0,0 +1,45 @@ +// reloc.h -- relocate input files for gold -*- C++ -*- + +#ifndef GOLD_RELOC_H +#define GOLD_RELOC_H + +#include "workqueue.h" + +namespace gold +{ + +class Relocate_task : public Task +{ + public: + Relocate_task(const General_options& options, const Symbol_table* symtab, + const Stringpool* sympool, Object* object, Output_file* of, + Task_token* final_blocker) + : options_(options), symtab_(symtab), sympool_(sympool), object_(object), + of_(of), final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Relocate_locker; + + const General_options& options_; + const Symbol_table* symtab_; + const Stringpool* sympool_; + Object* object_; + Output_file* of_; + Task_token* final_blocker_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_RELOC_H) diff --git a/gold/resolve.cc b/gold/resolve.cc index 8252f5b..669fbaf 100644 --- a/gold/resolve.cc +++ b/gold/resolve.cc @@ -190,10 +190,9 @@ Symbol_table::resolve(Sized_symbol<size>* to, switch (tobits * 16 + frombits) { case DEF * 16 + DEF: - // Two definitions of the same symbol. - fprintf(stderr, "%s: %s: multiple definition of %s\n", - program_name, object->name().c_str(), to->name()); - // FIXME: Report locations. Record that we have seen an error. + // Two definitions of the same symbol. We can't give an error + // here, because we have not yet discarded linkonce and comdat + // sections. FIXME. return; case WEAK_DEF * 16 + DEF: diff --git a/gold/stringpool.cc b/gold/stringpool.cc index 6437d01..b1a2ce2 100644 --- a/gold/stringpool.cc +++ b/gold/stringpool.cc @@ -4,20 +4,23 @@ #include <cassert> #include <cstring> +#include <algorithm> +#include <vector> +#include "output.h" #include "stringpool.h" namespace gold { Stringpool::Stringpool() - : string_set_(), strings_() + : string_set_(), strings_(), strtab_size_(0) { } Stringpool::~Stringpool() { - for (std::list<stringdata*>::iterator p = this->strings_.begin(); + for (std::list<Stringdata*>::iterator p = this->strings_.begin(); p != this->strings_.end(); ++p) delete[] reinterpret_cast<char*>(*p); @@ -64,16 +67,16 @@ Stringpool::add_string(const char* s) bool front = true; if (len >= buffer_size) { - alc = sizeof(stringdata) + len; + alc = sizeof(Stringdata) + len; front = false; } else if (this->strings_.empty()) - alc = sizeof(stringdata) + buffer_size; + alc = sizeof(Stringdata) + buffer_size; else { - stringdata *psd = this->strings_.front(); + Stringdata *psd = this->strings_.front(); if (len >= psd->alc - psd->len) - alc = sizeof(stringdata) + buffer_size; + alc = sizeof(Stringdata) + buffer_size; else { char* ret = psd->data + psd->len; @@ -83,8 +86,8 @@ Stringpool::add_string(const char* s) } } - stringdata *psd = reinterpret_cast<stringdata*>(new char[alc]); - psd->alc = alc; + Stringdata *psd = reinterpret_cast<Stringdata*>(new char[alc]); + psd->alc = alc - sizeof(Stringdata); memcpy(psd->data, s, len + 1); psd->len = len + 1; if (front) @@ -102,16 +105,17 @@ Stringpool::add(const char* s) // FIXME: This will look up the entry twice in the hash table. The // problem is that we can't insert S before we canonicalize it. I // don't think there is a way to handle this correctly with - // unordered_set, so this should be replaced with custom code to do + // unordered_map, so this should be replaced with custom code to do // what we need, which is to return the empty slot. String_set_type::const_iterator p = this->string_set_.find(s); if (p != this->string_set_.end()) - return *p; + return p->first; const char* ret = this->add_string(s); + std::pair<const char*, off_t> val(ret, 0); std::pair<String_set_type::iterator, bool> ins = - this->string_set_.insert(ret); + this->string_set_.insert(val); assert(ins.second); return ret; } @@ -127,4 +131,121 @@ Stringpool::add(const char* s, size_t len) return this->add(st); } +const char* +Stringpool::find(const char* s) const +{ + String_set_type::const_iterator p = this->string_set_.find(s); + if (p == this->string_set_.end()) + return NULL; + return p->first; +} + +// Comparison routine used when sorting into an ELF strtab. We want +// to sort this so that when one string is a suffix of another, we +// always see the shorter string immediately after the longer string. +// For example, we want to see these strings in this order: +// abcd +// cd +// d +// When strings are not suffixes, we don't care what order they are +// in, but we need to ensure that suffixes wind up next to each other. +// So we do a reversed lexicographic sort on the reversed string. + +bool +Stringpool::Stringpool_sort_comparison::operator()( + String_set_type::iterator it1, + String_set_type::iterator it2) const +{ + const char* s1 = it1->first; + const char* s2 = it2->first; + int len1 = strlen(s1); + int len2 = strlen(s2); + int minlen = len1 < len2 ? len1 : len2; + const char* p1 = s1 + len1 - 1; + const char* p2 = s2 + len2 - 1; + for (int i = minlen - 1; i >= 0; --i, --p1, --p2) + { + if (*p1 != *p2) + return *p1 > *p2; + } + return len1 > len2; +} + +// Return whether s1 is a suffix of s2. + +bool +Stringpool::is_suffix(const char* s1, const char* s2) +{ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + if (len1 > len2) + return false; + return strcmp(s1, s2 + len2 - len1) == 0; +} + +// Turn the stringpool into an ELF strtab: determine the offsets of +// each string in the table. + +void +Stringpool::set_string_offsets() +{ + size_t count = this->string_set_.size(); + + std::vector<String_set_type::iterator> v; + v.reserve(count); + + for (String_set_type::iterator p = this->string_set_.begin(); + p != this->string_set_.end(); + ++p) + v.push_back(p); + + std::sort(v.begin(), v.end(), Stringpool_sort_comparison()); + + // Offset 0 is reserved for the empty string. + off_t offset = 1; + for (size_t i = 0; i < count; ++i) + { + if (v[i]->first[0] == '\0') + v[i]->second = 0; + else if (i > 0 && Stringpool::is_suffix(v[i]->first, v[i - 1]->first)) + v[i]->second = (v[i - 1]->second + + strlen(v[i - 1]->first) + - strlen(v[i]->first)); + else + { + v[i]->second = offset; + offset += strlen(v[i]->first) + 1; + } + } + + this->strtab_size_ = offset; +} + +// Get the offset of a string in the ELF strtab. The string must +// exist. + +off_t +Stringpool::get_offset(const char* s) const +{ + String_set_type::const_iterator p = this->string_set_.find(s); + if (p != this->string_set_.end()) + return p->second; + abort(); +} + +// Write the ELF strtab into the output file at the specified offset. + +void +Stringpool::write(Output_file* of, off_t offset) +{ + unsigned char* viewu = of->get_output_view(offset, this->strtab_size_); + char* view = reinterpret_cast<char*>(viewu); + view[0] = '\0'; + for (String_set_type::const_iterator p = this->string_set_.begin(); + p != this->string_set_.end(); + ++p) + strcpy(view + p->second, p->first); + of->write_output_view(offset, this->strtab_size_, viewu); +} + } // End namespace gold. diff --git a/gold/stringpool.h b/gold/stringpool.h index 79632e0..01c71a1 100644 --- a/gold/stringpool.h +++ b/gold/stringpool.h @@ -12,6 +12,8 @@ namespace gold { +class Output_file; + class Stringpool { public: @@ -21,19 +23,50 @@ class Stringpool // Add a string to the pool. This returns a canonical permanent // pointer to the string. - const char* add(const char*); + const char* + add(const char*); - const char* add(const std::string& s) + const char* + add(const std::string& s) { return this->add(s.c_str()); } // Add the prefix of a string to the pool. - const char* add(const char *, size_t); + const char* + add(const char *, size_t); + + // If a string is present, return the canonical string. Otherwise, + // return NULL. + const char* + find(const char*) const; + + // Turn the stringpool into an ELF strtab: determine the offsets of + // all the strings. + void + set_string_offsets(); + + // Get the offset of a string. + off_t + get_offset(const char*) const; + + off_t + get_offset(const std::string& s) const + { return this->get_offset(s.c_str()); } + + // Get the size of the ELF strtab. + off_t + get_strtab_size() const + { return this->strtab_size_; } + + // Write the strtab into the output file at the specified offset. + void + write(Output_file*, off_t offset); private: Stringpool(const Stringpool&); Stringpool& operator=(const Stringpool&); - struct stringdata + // We store the actual data in a list of these buffers. + struct Stringdata { // Length of data in buffer. size_t len; @@ -43,7 +76,9 @@ class Stringpool char data[1]; }; - const char* add_string(const char*); + // Copy a string into the buffers, returning a canonical string. + const char* + add_string(const char*); struct Stringpool_hash { @@ -58,17 +93,34 @@ class Stringpool { return strcmp(p1, p2) == 0; } }; + // Return whether s1 is a suffix of s2. + static bool is_suffix(const char* s1, const char* s2); + + // The hash table is a map from string names to offsets. We only + // use the offsets if we turn this into an ELF strtab section. + #ifdef HAVE_TR1_UNORDERED_SET - typedef Unordered_set<const char*, Stringpool_hash, Stringpool_eq, - std::allocator<const char*>, + typedef Unordered_map<const char*, off_t, Stringpool_hash, + Stringpool_eq, + std::allocator<std::pair<const char* const, off_t> >, true> String_set_type; #else - typedef Unordered_set<const char*, Stringpool_hash, Stringpool_eq, - std::allocator<const char*> > String_set_type; + typedef Unordered_map<const char*, off_t, Stringpool_hash, + Stringpool_eq> String_set_type; #endif + // Comparison routine used when sorting into an ELF strtab. + + struct Stringpool_sort_comparison + { + bool + operator()(String_set_type::iterator, + String_set_type::iterator) const; + }; + String_set_type string_set_; - std::list<stringdata*> strings_; + std::list<Stringdata*> strings_; + off_t strtab_size_; }; } // End namespace gold. diff --git a/gold/symtab.cc b/gold/symtab.cc index a317f99..cc77b37 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -9,6 +9,7 @@ #include "object.h" #include "output.h" +#include "target.h" #include "symtab.h" namespace gold @@ -92,6 +93,8 @@ Symbol_table::make_forwarder(Symbol* from, Symbol* to) from->set_forwarder(); } +// Resolve the forwards from FROM, returning the real symbol. + Symbol* Symbol_table::resolve_forwards(Symbol* from) const { @@ -102,6 +105,28 @@ Symbol_table::resolve_forwards(Symbol* from) const return p->second; } +// Look up a symbol by name. + +Symbol* +Symbol_table::lookup(const char* name, const char* version) const +{ + name = this->namepool_.find(name); + if (name == NULL) + return NULL; + if (version != NULL) + { + version = this->namepool_.find(version); + if (version == NULL) + return NULL; + } + + Symbol_table_key key(name, version); + Symbol_table::Symbol_table_type::const_iterator p = this->table_.find(key); + if (p == this->table_.end()) + return NULL; + return p->second; +} + // Resolve a Symbol with another Symbol. This is only used in the // unusual case where there are references to both an unversioned // symbol and a symbol with a version, and we then discover that that @@ -380,8 +405,10 @@ Symbol_table::finalize(off_t off, Stringpool* pool) { if (this->size_ == 32) return this->sized_finalize<32>(off, pool); - else + else if (this->size_ == 64) return this->sized_finalize<64>(off, pool); + else + abort(); } // Set the final value for all the symbols. @@ -390,11 +417,12 @@ template<int size> off_t Symbol_table::sized_finalize(off_t off, Stringpool* pool) { - off = (off + size - 1) & ~ (size - 1); + off = (off + (size >> 3) - 1) & ~ ((size >> 3) - 1); this->offset_ = off; const int sym_size = elfcpp::Elf_sizes<size>::sym_size; Symbol_table_type::iterator p = this->table_.begin(); + size_t count = 0; while (p != this->table_.end()) { Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(p->second); @@ -402,6 +430,13 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool) // FIXME: Here we need to decide which symbols should go into // the output file. + // FIXME: This is wrong. + if (sym->shnum() >= elfcpp::SHN_LORESERVE) + { + ++p; + continue; + } + const Object::Map_to_output* mo = sym->object()->section_output_info(sym->shnum()); @@ -416,16 +451,89 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool) } else { - sym->set_value(mo->output_section->address() + mo->offset); + sym->set_value(sym->value() + + mo->output_section->address() + + mo->offset); pool->add(sym->name()); ++p; + ++count; off += sym_size; } } + this->output_count_ = count; + return off; } +// Write out the global symbols. + +void +Symbol_table::write_globals(const Target* target, const Stringpool* sympool, + Output_file* of) const +{ + if (this->size_ == 32) + { + if (target->is_big_endian()) + this->sized_write_globals<32, true>(target, sympool, of); + else + this->sized_write_globals<32, false>(target, sympool, of); + } + else if (this->size_ == 64) + { + if (target->is_big_endian()) + this->sized_write_globals<64, true>(target, sympool, of); + else + this->sized_write_globals<64, false>(target, sympool, of); + } + else + abort(); +} + +// Write out the global symbols. + +template<int size, bool big_endian> +void +Symbol_table::sized_write_globals(const Target*, + const Stringpool* sympool, + Output_file* of) const +{ + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + unsigned char* psyms = of->get_output_view(this->offset_, + this->output_count_ * sym_size); + unsigned char* ps = psyms; + for (Symbol_table_type::const_iterator p = this->table_.begin(); + p != this->table_.end(); + ++p) + { + Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(p->second); + + // FIXME: This repeats sized_finalize(). + + // FIXME: This is wrong. + if (sym->shnum() >= elfcpp::SHN_LORESERVE) + continue; + + const Object::Map_to_output* mo = + sym->object()->section_output_info(sym->shnum()); + + if (mo->output_section == NULL) + continue; + + elfcpp::Sym_write<size, big_endian> osym(ps); + osym.put_st_name(sympool->get_offset(sym->name())); + osym.put_st_value(sym->value()); + osym.put_st_size(sym->symsize()); + osym.put_st_info(elfcpp::elf_st_info(sym->binding(), sym->type())); + osym.put_st_other(elfcpp::elf_st_other(sym->visibility(), sym->other())); + osym.put_st_shndx(mo->output_section->shndx()); + + ps += sym_size; + } + + of->write_output_view(this->offset_, this->output_count_ * sym_size, psyms); +} + // Instantiate the templates we need. We could use the configure // script to restrict this to only the ones needed for implemented // targets. diff --git a/gold/symtab.h b/gold/symtab.h index 91a1f4d..51bb269 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -17,6 +17,8 @@ namespace gold { class Object; +class Output_file; +class Target; template<int size, bool big_endian> class Sized_object; @@ -218,6 +220,10 @@ class Symbol_table size_t count, const char* sym_names, size_t sym_name_size, Symbol** sympointers); + // Look up a symbol. + Symbol* + lookup(const char*, const char* version = NULL) const; + // Return the real symbol associated with the forwarder symbol FROM. Symbol* resolve_forwards(Symbol* from) const; @@ -243,6 +249,10 @@ class Symbol_table off_t finalize(off_t, Stringpool*); + // Write out the global symbols. + void + write_globals(const Target*, const Stringpool*, Output_file*) const; + private: Symbol_table(const Symbol_table&); Symbol_table& operator=(const Symbol_table&); @@ -286,6 +296,11 @@ class Symbol_table off_t sized_finalize(off_t, Stringpool*); + // Write globals specialized for size and endianness. + template<int size, bool big_endian> + void + sized_write_globals(const Target*, const Stringpool*, Output_file*) const; + // The type of the symbol hash table. typedef std::pair<const char*, const char*> Symbol_table_key; @@ -312,6 +327,9 @@ class Symbol_table // write the table. off_t offset_; + // The number of global symbols we want to write out. + size_t output_count_; + // The symbol hash table. Symbol_table_type table_; diff --git a/gold/target-reloc.h b/gold/target-reloc.h new file mode 100644 index 0000000..66ff78c --- /dev/null +++ b/gold/target-reloc.h @@ -0,0 +1,119 @@ +// target-reloc.h -- target specific relocation support -*- C++ -*- + +#ifndef GOLD_TARGET_RELOC_H +#define GOLD_TARGET_RELOC_H + +#include "elfcpp.h" +#include "symtab.h" + +namespace gold +{ + +// Pick the ELF relocation accessor class and the size based on +// SH_TYPE, which is either SHT_REL or SHT_RELA. + +template<int sh_type, int size, bool big_endian> +struct Reloc_types; + +template<int size, bool big_endian> +struct Reloc_types<elfcpp::SHT_REL, size, big_endian> +{ + typedef typename elfcpp::Rel<size, big_endian> Reloc; + static const int reloc_size = elfcpp::Elf_sizes<size>::rel_size; +}; + +template<int size, bool big_endian> +struct Reloc_types<elfcpp::SHT_RELA, size, big_endian> +{ + typedef typename elfcpp::Rela<size, big_endian> Reloc; + static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; +}; + +// This function implements the generic part of relocation handling. +// This is an inline function which take a class whose operator() +// implements the machine specific part of relocation. We do it this +// way to avoid making a function call for each relocation, and to +// avoid repeating the generic relocation handling code for each +// target. + +// SIZE is the ELF size: 32 or 64. BIG_ENDIAN is the endianness of +// the data. SH_TYPE is the section type: SHT_REL or SHT_RELA. RELOC +// implements operator() to do a relocation. + +// OBJECT is the object for we are processing relocs. SH_TYPE is the +// type of relocation: SHT_REL or SHT_RELA. PRELOCS points to the +// relocation data. RELOC_COUNT is the number of relocs. LOCAL_COUNT +// is the number of local symbols. LOCAL_VALUES holds the values of +// the local symbols. GLOBAL_SYMS points to the global symbols. VIEW +// is the section data, VIEW_ADDRESS is its memory address, and +// VIEW_SIZE is the size. + +template<int size, bool big_endian, int sh_type, typename Relocate> +inline void +relocate_section( + const Symbol_table* symtab, + Sized_object<size, big_endian>* object, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_count, + const typename elfcpp::Elf_types<size>::Elf_Addr* local_values, + Symbol** global_syms, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr view_address, + off_t view_size) +{ + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; + const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; + Relocate relocate; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reltype reloc(prelocs); + + off_t offset = reloc.get_r_offset(); + if (offset < 0 || offset >= view_size) + { + fprintf(stderr, _("%s: %s: reloc %zu has bad offset %lu\n"), + program_name, object->name().c_str(), i, + static_cast<unsigned long>(offset)); + gold_exit(false); + } + + typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + unsigned int r_type = elfcpp::elf_r_type<size>(r_info); + + Sized_symbol<size>* sym; + typename elfcpp::Elf_types<size>::Elf_Addr value; + + if (r_sym < local_count) + { + sym = NULL; + value = local_values[r_sym]; + } + else + { + Symbol* gsym = global_syms[r_sym - local_count]; + if (gsym->is_forwarder()) + gsym = symtab->resolve_forwards(gsym); + + sym = static_cast<Sized_symbol<size>*>(gsym); + value = sym->value(); + + if (sym->shnum() == elfcpp::SHN_UNDEF + && sym->binding() != elfcpp::STB_WEAK) + { + fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"), + program_name, object->name().c_str(), sym->name()); + // gold_exit(false); + } + } + + relocate(object, reloc, r_type, sym, value, view + offset, + view_address + offset); + } +} + +} // End namespace gold. + +#endif // !defined(GOLD_TARGET_RELOC_H) diff --git a/gold/target.h b/gold/target.h index bba3d5a..5230bb2 100644 --- a/gold/target.h +++ b/gold/target.h @@ -15,13 +15,15 @@ #include <cassert> -#include "symtab.h" #include "elfcpp.h" +#include "symtab.h" namespace gold { class Object; +template<int size, bool big_endian> +class Sized_object; // The abstract class for target specific handling. @@ -42,6 +44,11 @@ class Target is_big_endian() const { return this->pti_->is_big_endian; } + // Machine code to store in e_machine field of ELF header. + elfcpp::EM + machine_code() const + { return this->pti_->machine_code; } + // Whether this target has a specific make_symbol function. bool has_make_symbol() const @@ -77,6 +84,8 @@ class Target int size; // Whether the target is big endian. bool is_big_endian; + // The code to store in the e_machine field of the ELF header. + elfcpp::EM machine_code; // Whether this target has a specific make_symbol function. bool has_make_symbol; // Whether this target has a specific resolve function. @@ -124,6 +133,29 @@ class Sized_target : public Target resolve(Symbol*, const elfcpp::Sym<size, big_endian>&, Object*) { abort(); } + // Relocate section data. SYMTAB is the symbol table. OBJECT is + // the object in which the section appears. SH_TYPE is the type of + // the relocation section, SHT_REL or SHT_RELA. PRELOCS points to + // the relocation information. RELOC_COUNT is the number of relocs. + // LOCAL_COUNT is the number of local symbols. The VALUES and + // GLOBAL_SYMS have symbol table information. VIEW is a view into + // the output file holding the section contents, VIEW_ADDRESS is the + // virtual address of the view, and VIEW_SIZE is the size of the + // view. + virtual void + relocate_section(const Symbol_table*, // symtab + Sized_object<size, big_endian>*, // object + unsigned int, // sh_type + const unsigned char*, // prelocs + size_t, // reloc_count + unsigned int, // local_count + const typename elfcpp::Elf_types<size>::Elf_Addr*, // values + Symbol**, // global_syms + unsigned char*, // view + typename elfcpp::Elf_types<size>::Elf_Addr, // view_address + off_t) // view_size + { abort(); } + protected: Sized_target(const Target::Target_info* pti) : Target(pti) |