// target-reloc.h -- target specific relocation support -*- C++ -*- #ifndef GOLD_TARGET_RELOC_H #define GOLD_TARGET_RELOC_H #include "elfcpp.h" #include "object.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 struct Reloc_types; template struct Reloc_types { typedef typename elfcpp::Rel Reloc; static const int reloc_size = elfcpp::Elf_sizes::rel_size; }; template struct Reloc_types { typedef typename elfcpp::Rela Reloc; static const int reloc_size = elfcpp::Elf_sizes::rela_size; }; // This function implements the generic part of reloc scanning. This // is an inline function which takes a class whose operator() // implements the machine specific part of scanning. We do it this // way to avoidmaking a function call for each relocation, and to // avoid repeating the generic code for each target. template inline void scan_relocs( const General_options& options, Symbol_table* symtab, Sized_object* object, const unsigned char* prelocs, size_t reloc_count, size_t local_count, const unsigned char* plocal_syms, Symbol** global_syms) { typedef typename Reloc_types::Reloc Reltype; const int reloc_size = Reloc_types::reloc_size; const int sym_size = elfcpp::Elf_sizes::sym_size; Scan scan; for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) { Reltype reloc(prelocs); typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); unsigned int r_sym = elfcpp::elf_r_sym(r_info); unsigned int r_type = elfcpp::elf_r_type(r_info); if (r_sym < local_count) { assert(plocal_syms != NULL); typename elfcpp::Sym lsym(plocal_syms + r_sym * sym_size); const unsigned int shndx = lsym.get_st_shndx(); if (shndx < elfcpp::SHN_LORESERVE && !object->is_section_included(lsym.get_st_shndx())) { // RELOC is a relocation against a local symbol in a // section we are discarding. We can ignore this // relocation. It will eventually become a reloc // against the value zero. // // FIXME: We should issue a warning if this is an // allocated section; is this the best place to do it? // // FIXME: The old GNU linker would in some cases look // for the linkonce section which caused this section to // be discarded, and, if the other section was the same // size, change the reloc to refer to the other section. // That seems risky and weird to me, and I don't know of // any case where it is actually required. continue; } scan.local(options, object, reloc, r_type, lsym); } else { Symbol* gsym = global_syms[r_sym - local_count]; assert(gsym != NULL); if (gsym->is_forwarder()) gsym = symtab->resolve_forwards(gsym); scan.global(options, object, reloc, r_type, gsym); } } } // This function implements the generic part of relocation processing. // 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. // RELOCATE implements operator() to do a relocation. // PRELOCS points to the relocation data. RELOC_COUNT is the number // of relocs. VIEW is the section data, VIEW_ADDRESS is its memory // address, and VIEW_SIZE is the size. template inline void relocate_section( const Relocate_info* relinfo, const unsigned char* prelocs, size_t reloc_count, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr view_address, off_t view_size) { typedef typename Reloc_types::Reloc Reltype; const int reloc_size = Reloc_types::reloc_size; Relocate relocate; unsigned int local_count = relinfo->local_symbol_count; typename elfcpp::Elf_types::Elf_Addr *local_values = relinfo->values; Symbol** global_syms = relinfo->symbols; 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 has bad offset %zu\n"), program_name, relinfo->location(i, offset).c_str(), static_cast(offset)); gold_exit(false); } typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); unsigned int r_sym = elfcpp::elf_r_sym(r_info); unsigned int r_type = elfcpp::elf_r_type(r_info); Sized_symbol* sym; typename elfcpp::Elf_types::Elf_Addr value; if (r_sym < local_count) { sym = NULL; value = local_values[r_sym]; } else { Symbol* gsym = global_syms[r_sym - local_count]; assert(gsym != NULL); if (gsym->is_forwarder()) gsym = relinfo->symtab->resolve_forwards(gsym); sym = static_cast*>(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, relinfo->location(i, offset).c_str(), sym->name()); // gold_exit(false); } } relocate.relocate(relinfo, i, reloc, r_type, sym, value, view + offset, view_address + offset, view_size); } } } // End namespace gold. #endif // !defined(GOLD_TARGET_RELOC_H)