diff options
Diffstat (limited to 'gold/dynobj.cc')
-rw-r--r-- | gold/dynobj.cc | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/gold/dynobj.cc b/gold/dynobj.cc new file mode 100644 index 0000000..ba1fb15 --- /dev/null +++ b/gold/dynobj.cc @@ -0,0 +1,670 @@ +// dynobj.cc -- dynamic object support for gold + +#include "gold.h" + +#include <vector> +#include <cstring> + +#include "symtab.h" +#include "dynobj.h" + +namespace gold +{ + +// Class Sized_dynobj. + +template<int size, bool big_endian> +Sized_dynobj<size, big_endian>::Sized_dynobj( + const std::string& name, + Input_file* input_file, + off_t offset, + const elfcpp::Ehdr<size, big_endian>& ehdr) + : Dynobj(name, input_file, offset), + elf_file_(this, ehdr), + soname_() +{ +} + +// Set up the object. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::setup( + const elfcpp::Ehdr<size, big_endian>& ehdr) +{ + this->set_target(ehdr.get_e_machine(), size, big_endian, + ehdr.get_e_ident()[elfcpp::EI_OSABI], + ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]); + + const unsigned int shnum = this->elf_file_.shnum(); + this->set_shnum(shnum); +} + +// Find the SHT_DYNSYM section and the various version sections, and +// the dynamic section, given the section headers. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::find_dynsym_sections( + const unsigned char* pshdrs, + unsigned int* pdynsym_shndx, + unsigned int* pversym_shndx, + unsigned int* pverdef_shndx, + unsigned int* pverneed_shndx, + unsigned int* pdynamic_shndx) +{ + *pdynsym_shndx = -1U; + *pversym_shndx = -1U; + *pverdef_shndx = -1U; + *pverneed_shndx = -1U; + *pdynamic_shndx = -1U; + + const unsigned int shnum = this->shnum(); + const unsigned char* p = pshdrs; + for (unsigned int i = 0; i < shnum; ++i, p += This::shdr_size) + { + typename This::Shdr shdr(p); + + unsigned int* pi; + switch (shdr.get_sh_type()) + { + case elfcpp::SHT_DYNSYM: + pi = pdynsym_shndx; + break; + case elfcpp::SHT_GNU_versym: + pi = pversym_shndx; + break; + case elfcpp::SHT_GNU_verdef: + pi = pverdef_shndx; + break; + case elfcpp::SHT_GNU_verneed: + pi = pverneed_shndx; + break; + case elfcpp::SHT_DYNAMIC: + pi = pdynamic_shndx; + break; + default: + pi = NULL; + break; + } + + if (pi == NULL) + continue; + + if (*pi != -1U) + { + fprintf(stderr, + _("%s: %s: unexpected duplicate type %u section: %u, %u\n"), + program_name, this->name().c_str(), shdr.get_sh_type(), + *pi, i); + gold_exit(false); + } + + *pi = i; + } +} + +// Read the contents of section SHNDX. PSHDRS points to the section +// headers. TYPE is the expected section type. LINK is the expected +// section link. Store the data in *VIEW and *VIEW_SIZE. The +// section's sh_info field is stored in *VIEW_INFO. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::read_dynsym_section( + const unsigned char* pshdrs, + unsigned int shndx, + elfcpp::SHT type, + unsigned int link, + File_view** view, + off_t* view_size, + unsigned int* view_info) +{ + if (shndx == -1U) + { + *view = NULL; + *view_size = 0; + *view_info = 0; + return; + } + + typename This::Shdr shdr(pshdrs + shndx * This::shdr_size); + + assert(shdr.get_sh_type() == type); + + if (shdr.get_sh_link() != link) + { + fprintf(stderr, + _("%s: %s: unexpected link in section %u header: %u != %u\n"), + program_name, this->name().c_str(), shndx, + shdr.get_sh_link(), link); + gold_exit(false); + } + + *view = this->get_lasting_view(shdr.get_sh_offset(), shdr.get_sh_size()); + *view_size = shdr.get_sh_size(); + *view_info = shdr.get_sh_info(); +} + +// Set soname_ if this shared object has a DT_SONAME tag. PSHDRS +// points to the section headers. DYNAMIC_SHNDX is the section index +// of the SHT_DYNAMIC section. STRTAB_SHNDX, STRTAB, and STRTAB_SIZE +// are the section index and contents of a string table which may be +// the one associated with the SHT_DYNAMIC section. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::set_soname(const unsigned char* pshdrs, + unsigned int dynamic_shndx, + unsigned int strtab_shndx, + const unsigned char* strtabu, + off_t strtab_size) +{ + typename This::Shdr dynamicshdr(pshdrs + dynamic_shndx * This::shdr_size); + assert(dynamicshdr.get_sh_type() == elfcpp::SHT_DYNAMIC); + + const off_t dynamic_size = dynamicshdr.get_sh_size(); + const unsigned char* pdynamic = this->get_view(dynamicshdr.get_sh_offset(), + dynamic_size); + + const unsigned int link = dynamicshdr.get_sh_link(); + if (link != strtab_shndx) + { + if (link >= this->shnum()) + { + fprintf(stderr, + _("%s: %s: DYNAMIC section %u link out of range: %u\n"), + program_name, this->name().c_str(), + dynamic_shndx, link); + gold_exit(false); + } + + typename This::Shdr strtabshdr(pshdrs + link * This::shdr_size); + if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB) + { + fprintf(stderr, + _("%s: %s: DYNAMIC section %u link %u is not a strtab\n"), + program_name, this->name().c_str(), + dynamic_shndx, link); + gold_exit(false); + } + + strtab_size = strtabshdr.get_sh_size(); + strtabu = this->get_view(strtabshdr.get_sh_offset(), strtab_size); + } + + for (const unsigned char* p = pdynamic; + p < pdynamic + dynamic_size; + p += This::dyn_size) + { + typename This::Dyn dyn(p); + + if (dyn.get_d_tag() == elfcpp::DT_SONAME) + { + off_t val = dyn.get_d_val(); + if (val >= strtab_size) + { + fprintf(stderr, + _("%s: %s: DT_SONAME value out of range: " + "%lld >= %lld\n"), + program_name, this->name().c_str(), + static_cast<long long>(val), + static_cast<long long>(strtab_size)); + gold_exit(false); + } + + const char* strtab = reinterpret_cast<const char*>(strtabu); + this->soname_ = std::string(strtab + val); + return; + } + + if (dyn.get_d_tag() == elfcpp::DT_NULL) + return; + } + + fprintf(stderr, _("%s: %s: missing DT_NULL in dynamic segment\n"), + program_name, this->name().c_str()); + gold_exit(false); +} + +// Read the symbols and sections from a dynamic object. We read the +// dynamic symbols, not the normal symbols. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) +{ + this->read_section_data(&this->elf_file_, sd); + + const unsigned char* const pshdrs = sd->section_headers->data(); + + unsigned int dynsym_shndx; + unsigned int versym_shndx; + unsigned int verdef_shndx; + unsigned int verneed_shndx; + unsigned int dynamic_shndx; + this->find_dynsym_sections(pshdrs, &dynsym_shndx, &versym_shndx, + &verdef_shndx, &verneed_shndx, &dynamic_shndx); + + unsigned int strtab_shndx = -1U; + + if (dynsym_shndx == -1U) + { + sd->symbols = NULL; + sd->symbols_size = 0; + sd->symbol_names = NULL; + sd->symbol_names_size = 0; + } + else + { + // Get the dynamic symbols. + typename This::Shdr dynsymshdr(pshdrs + dynsym_shndx * This::shdr_size); + assert(dynsymshdr.get_sh_type() == elfcpp::SHT_DYNSYM); + + sd->symbols = this->get_lasting_view(dynsymshdr.get_sh_offset(), + dynsymshdr.get_sh_size()); + sd->symbols_size = dynsymshdr.get_sh_size(); + + // Get the symbol names. + strtab_shndx = dynsymshdr.get_sh_link(); + if (strtab_shndx >= this->shnum()) + { + fprintf(stderr, + _("%s: %s: invalid dynamic symbol table name index: %u\n"), + program_name, this->name().c_str(), strtab_shndx); + gold_exit(false); + } + typename This::Shdr strtabshdr(pshdrs + strtab_shndx * This::shdr_size); + if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB) + { + fprintf(stderr, + _("%s: %s: dynamic symbol table name section " + "has wrong type: %u\n"), + program_name, this->name().c_str(), + static_cast<unsigned int>(strtabshdr.get_sh_type())); + gold_exit(false); + } + + sd->symbol_names = this->get_lasting_view(strtabshdr.get_sh_offset(), + strtabshdr.get_sh_size()); + sd->symbol_names_size = strtabshdr.get_sh_size(); + + // Get the version information. + + unsigned int dummy; + this->read_dynsym_section(pshdrs, versym_shndx, elfcpp::SHT_GNU_versym, + dynsym_shndx, &sd->versym, &sd->versym_size, + &dummy); + + // We require that the version definition and need section link + // to the same string table as the dynamic symbol table. This + // is not a technical requirement, but it always happens in + // practice. We could change this if necessary. + + this->read_dynsym_section(pshdrs, verdef_shndx, elfcpp::SHT_GNU_verdef, + strtab_shndx, &sd->verdef, &sd->verdef_size, + &sd->verdef_info); + + this->read_dynsym_section(pshdrs, verneed_shndx, elfcpp::SHT_GNU_verneed, + strtab_shndx, &sd->verneed, &sd->verneed_size, + &sd->verneed_info); + } + + // Read the SHT_DYNAMIC section to find whether this shared object + // has a DT_SONAME tag. This doesn't really have anything to do + // with reading the symbols, but this is a convenient place to do + // it. + if (dynamic_shndx != -1U) + this->set_soname(pshdrs, dynamic_shndx, strtab_shndx, + (sd->symbol_names == NULL + ? NULL + : sd->symbol_names->data()), + sd->symbol_names_size); +} + +// Lay out the input sections for a dynamic object. We don't want to +// include sections from a dynamic object, so all that we actually do +// here is check for .gnu.warning sections. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::do_layout(const General_options&, + Symbol_table* symtab, + Layout*, + Read_symbols_data* sd) +{ + const unsigned int shnum = this->shnum(); + if (shnum == 0) + return; + + // Get the section headers. + const unsigned char* pshdrs = sd->section_headers->data(); + + // Get the section names. + const unsigned char* pnamesu = sd->section_names->data(); + const char* pnames = reinterpret_cast<const char*>(pnamesu); + + // Skip the first, dummy, section. + pshdrs += This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size) + { + typename This::Shdr shdr(pshdrs); + + if (shdr.get_sh_name() >= sd->section_names_size) + { + fprintf(stderr, + _("%s: %s: bad section name offset for section %u: %lu\n"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(shdr.get_sh_name())); + gold_exit(false); + } + + const char* name = pnames + shdr.get_sh_name(); + + this->handle_gnu_warning_section(name, i, symtab); + } + + delete sd->section_headers; + sd->section_headers = NULL; + delete sd->section_names; + sd->section_names = NULL; +} + +// Add an entry to the vector mapping version numbers to version +// strings. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::set_version_map( + Version_map* version_map, + unsigned int ndx, + const char* name) const +{ + assert(ndx < version_map->size()); + if ((*version_map)[ndx] != NULL) + { + fprintf(stderr, _("%s: %s: duplicate definition for version %u\n"), + program_name, this->name().c_str(), ndx); + gold_exit(false); + } + (*version_map)[ndx] = name; +} + +// Create a vector mapping version numbers to version strings. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::make_version_map( + Read_symbols_data* sd, + Version_map* version_map) const +{ + if (sd->verdef == NULL && sd->verneed == NULL) + return; + + // First find the largest version index. + unsigned int maxver = 0; + + if (sd->verdef != NULL) + { + const unsigned char* pverdef = sd->verdef->data(); + off_t verdef_size = sd->verdef_size; + const unsigned int count = sd->verdef_info; + + const unsigned char* p = pverdef; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verdef<size, big_endian> verdef(p); + + const unsigned int vd_ndx = verdef.get_vd_ndx(); + + // The GNU linker clears the VERSYM_HIDDEN bit. I'm not + // sure why. + + if (vd_ndx > maxver) + maxver = vd_ndx; + + const unsigned int vd_next = verdef.get_vd_next(); + if ((p - pverdef) + vd_next >= verdef_size) + { + fprintf(stderr, + _("%s: %s: verdef vd_next field out of range: %u\n"), + program_name, this->name().c_str(), vd_next); + gold_exit(false); + } + + p += vd_next; + } + } + + if (sd->verneed != NULL) + { + const unsigned char* pverneed = sd->verneed->data(); + off_t verneed_size = sd->verneed_size; + const unsigned int count = sd->verneed_info; + + const unsigned char* p = pverneed; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verneed<size, big_endian> verneed(p); + + const unsigned int vn_aux = verneed.get_vn_aux(); + if ((p - pverneed) + vn_aux >= verneed_size) + { + fprintf(stderr, + _("%s: %s: verneed vn_aux field out of range: %u\n"), + program_name, this->name().c_str(), vn_aux); + gold_exit(false); + } + + const unsigned int vn_cnt = verneed.get_vn_cnt(); + const unsigned char* pvna = p + vn_aux; + for (unsigned int j = 0; j < vn_cnt; ++j) + { + elfcpp::Vernaux<size, big_endian> vernaux(pvna); + + const unsigned int vna_other = vernaux.get_vna_other(); + if (vna_other > maxver) + maxver = vna_other; + + const unsigned int vna_next = vernaux.get_vna_next(); + if ((pvna - pverneed) + vna_next >= verneed_size) + { + fprintf(stderr, + _("%s: %s: verneed vna_next field " + "out of range: %u\n"), + program_name, this->name().c_str(), vna_next); + gold_exit(false); + } + + pvna += vna_next; + } + + const unsigned int vn_next = verneed.get_vn_next(); + if ((p - pverneed) + vn_next >= verneed_size) + { + fprintf(stderr, + _("%s: %s: verneed vn_next field out of range: %u\n"), + program_name, this->name().c_str(), vn_next); + gold_exit(false); + } + + p += vn_next; + } + } + + // Now MAXVER is the largest version index we have seen. + + version_map->resize(maxver + 1); + + const char* names = reinterpret_cast<const char*>(sd->symbol_names->data()); + off_t names_size = sd->symbol_names_size; + + if (sd->verdef != NULL) + { + const unsigned char* pverdef = sd->verdef->data(); + off_t verdef_size = sd->verdef_size; + const unsigned int count = sd->verdef_info; + + const unsigned char* p = pverdef; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verdef<size, big_endian> verdef(p); + + const unsigned int vd_cnt = verdef.get_vd_cnt(); + if (vd_cnt < 1) + { + fprintf(stderr, _("%s: %s: verdef vd_cnt field too small: %u\n"), + program_name, this->name().c_str(), vd_cnt); + gold_exit(false); + } + + const unsigned int vd_aux = verdef.get_vd_aux(); + if ((p - pverdef) + vd_aux >= verdef_size) + { + fprintf(stderr, + _("%s: %s: verdef vd_aux field out of range: %u\n"), + program_name, this->name().c_str(), vd_aux); + gold_exit(false); + } + + const unsigned char* pvda = p + vd_aux; + elfcpp::Verdaux<size, big_endian> verdaux(pvda); + + const unsigned int vda_name = verdaux.get_vda_name(); + if (vda_name >= names_size) + { + fprintf(stderr, + _("%s: %s: verdaux vda_name field out of range: %u\n"), + program_name, this->name().c_str(), vda_name); + gold_exit(false); + } + + this->set_version_map(version_map, verdef.get_vd_ndx(), + names + vda_name); + + const unsigned int vd_next = verdef.get_vd_next(); + if ((p - pverdef) + vd_next >= verdef_size) + { + fprintf(stderr, + _("%s: %s: verdef vd_next field out of range: %u\n"), + program_name, this->name().c_str(), vd_next); + gold_exit(false); + } + + p += vd_next; + } + } + + if (sd->verneed != NULL) + { + const unsigned char* pverneed = sd->verneed->data(); + const unsigned int count = sd->verneed_info; + + const unsigned char* p = pverneed; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verneed<size, big_endian> verneed(p); + + const unsigned int vn_aux = verneed.get_vn_aux(); + const unsigned int vn_cnt = verneed.get_vn_cnt(); + const unsigned char* pvna = p + vn_aux; + for (unsigned int j = 0; j < vn_cnt; ++j) + { + elfcpp::Vernaux<size, big_endian> vernaux(pvna); + + const unsigned int vna_name = vernaux.get_vna_name(); + if (vna_name >= names_size) + { + fprintf(stderr, + _("%s: %s: vernaux vna_name field " + "out of range: %u\n"), + program_name, this->name().c_str(), vna_name); + gold_exit(false); + } + + this->set_version_map(version_map, vernaux.get_vna_other(), + names + vna_name); + + pvna += vernaux.get_vna_next(); + } + + p += verneed.get_vn_next(); + } + } +} + +// Add the dynamic symbols to the symbol table. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::do_add_symbols(Symbol_table* symtab, + Read_symbols_data* sd) +{ + if (sd->symbols == NULL) + { + assert(sd->symbol_names == NULL); + assert(sd->versym == NULL && sd->verdef == NULL && sd->verneed == NULL); + return; + } + + const int sym_size = This::sym_size; + const size_t symcount = sd->symbols_size / sym_size; + if (symcount * sym_size != sd->symbols_size) + { + fprintf(stderr, + _("%s: %s: size of dynamic symbols is not " + "multiple of symbol size\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + Version_map version_map; + this->make_version_map(sd, &version_map); + + const char* sym_names = + reinterpret_cast<const char*>(sd->symbol_names->data()); + symtab->add_from_dynobj(this, sd->symbols->data(), symcount, + sym_names, sd->symbol_names_size, + (sd->versym == NULL + ? NULL + : sd->versym->data()), + sd->versym_size, + &version_map); + + delete sd->symbols; + sd->symbols = NULL; + delete sd->symbol_names; + sd->symbol_names = NULL; + if (sd->versym != NULL) + { + delete sd->versym; + sd->versym = NULL; + } + if (sd->verdef != NULL) + { + delete sd->verdef; + sd->verdef = NULL; + } + if (sd->verneed != NULL) + { + delete sd->verneed; + sd->verneed = NULL; + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +class Sized_dynobj<32, false>; + +template +class Sized_dynobj<32, true>; + +template +class Sized_dynobj<64, false>; + +template +class Sized_dynobj<64, true>; + +} // End namespace gold. |