| // gc.h -- garbage collection of unused sections | 
 |  | 
 | // Copyright (C) 2009-2020 Free Software Foundation, Inc. | 
 | // Written by Sriraman Tallam <tmsriram@google.com>. | 
 |  | 
 | // This file is part of gold. | 
 |  | 
 | // This program is free software; you can redistribute it and/or modify | 
 | // it under the terms of the GNU General Public License as published by | 
 | // the Free Software Foundation; either version 3 of the License, or | 
 | // (at your option) any later version. | 
 |  | 
 | // This program is distributed in the hope that it will be useful, | 
 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | // GNU General Public License for more details. | 
 |  | 
 | // You should have received a copy of the GNU General Public License | 
 | // along with this program; if not, write to the Free Software | 
 | // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | 
 | // MA 02110-1301, USA. | 
 |  | 
 | #ifndef GOLD_GC_H | 
 | #define GOLD_GC_H | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "elfcpp.h" | 
 | #include "symtab.h" | 
 | #include "object.h" | 
 | #include "icf.h" | 
 |  | 
 | namespace gold | 
 | { | 
 |  | 
 | class Object; | 
 |  | 
 | template<int size, bool big_endian> | 
 | class Sized_relobj_file; | 
 |  | 
 | class Output_section; | 
 | class General_options; | 
 | class Layout; | 
 |  | 
 | class Garbage_collection | 
 | { | 
 |  public: | 
 |  | 
 |   typedef Unordered_set<Section_id, Section_id_hash> Sections_reachable; | 
 |   typedef std::map<Section_id, Sections_reachable> Section_ref; | 
 |   typedef std::vector<Section_id> Worklist_type; | 
 |   // This maps the name of the section which can be represented as a C | 
 |   // identifier (cident) to the list of sections that have that name. | 
 |   // Different object files can have cident sections with the same name. | 
 |   typedef std::map<std::string, Sections_reachable> Cident_section_map; | 
 |  | 
 |   Garbage_collection() | 
 |   : is_worklist_ready_(false) | 
 |   { } | 
 |  | 
 |   // Accessor methods for the private members. | 
 |  | 
 |   Sections_reachable& | 
 |   referenced_list() | 
 |   { return referenced_list_; } | 
 |  | 
 |   Section_ref& | 
 |   section_reloc_map() | 
 |   { return this->section_reloc_map_; } | 
 |  | 
 |   Worklist_type& | 
 |   worklist() | 
 |   { return this->work_list_; } | 
 |  | 
 |   bool | 
 |   is_worklist_ready() | 
 |   { return this->is_worklist_ready_; } | 
 |  | 
 |   void | 
 |   worklist_ready() | 
 |   { this->is_worklist_ready_ = true; } | 
 |  | 
 |   void | 
 |   do_transitive_closure(); | 
 |  | 
 |   bool | 
 |   is_section_garbage(Relobj* obj, unsigned int shndx) | 
 |   { return (this->referenced_list().find(Section_id(obj, shndx)) | 
 |             == this->referenced_list().end()); } | 
 |  | 
 |   Cident_section_map* | 
 |   cident_sections() | 
 |   { return &cident_sections_; } | 
 |  | 
 |   void | 
 |   add_cident_section(std::string section_name, | 
 | 		     Section_id secn) | 
 |   { this->cident_sections_[section_name].insert(secn); } | 
 |  | 
 |   // Add a reference from the SRC_SHNDX-th section of SRC_OBJECT to | 
 |   // DST_SHNDX-th section of DST_OBJECT. | 
 |   void | 
 |   add_reference(Relobj* src_object, unsigned int src_shndx, | 
 | 		Relobj* dst_object, unsigned int dst_shndx) | 
 |   { | 
 |     Section_id src_id(src_object, src_shndx); | 
 |     Section_id dst_id(dst_object, dst_shndx); | 
 |     Sections_reachable& reachable = this->section_reloc_map_[src_id]; | 
 |     reachable.insert(dst_id); | 
 |   } | 
 |  | 
 |  private: | 
 |  | 
 |   Worklist_type work_list_; | 
 |   bool is_worklist_ready_; | 
 |   Section_ref section_reloc_map_; | 
 |   Sections_reachable referenced_list_; | 
 |   Cident_section_map cident_sections_; | 
 | }; | 
 |  | 
 | // Data to pass between successive invocations of do_layout | 
 | // in object.cc while garbage collecting.  This data structure | 
 | // is filled by using the data from Read_symbols_data. | 
 |  | 
 | struct Symbols_data | 
 | { | 
 |   // Section headers. | 
 |   unsigned char* section_headers_data; | 
 |   // Section names. | 
 |   unsigned char* section_names_data; | 
 |   // Size of section name data in bytes. | 
 |   section_size_type section_names_size; | 
 |   // Symbol data. | 
 |   unsigned char* symbols_data; | 
 |   // Size of symbol data in bytes. | 
 |   section_size_type symbols_size; | 
 |   // Offset of external symbols within symbol data.  This structure | 
 |   // sometimes contains only external symbols, in which case this will | 
 |   // be zero.  Sometimes it contains all symbols. | 
 |   section_offset_type external_symbols_offset; | 
 |   // Symbol names. | 
 |   unsigned char* symbol_names_data; | 
 |   // Size of symbol name data in bytes. | 
 |   section_size_type symbol_names_size; | 
 | }; | 
 |  | 
 | // Relocations of type SHT_REL store the addend value in their bytes. | 
 | // This function returns the size of the embedded addend which is | 
 | // nothing but the size of the relocation. | 
 |  | 
 | template<typename Classify_reloc> | 
 | inline unsigned int | 
 | get_embedded_addend_size(int r_type, Relobj* obj) | 
 | { | 
 |   if (Classify_reloc::sh_type == elfcpp::SHT_REL) | 
 |     return Classify_reloc::get_size_for_reloc(r_type, obj); | 
 |   return 0; | 
 | } | 
 |  | 
 | // This function implements the generic part of reloc | 
 | // processing to map a section to all the sections it | 
 | // references through relocs.  It is called only during | 
 | // garbage collection (--gc-sections) and identical code | 
 | // folding (--icf). | 
 |  | 
 | template<int size, bool big_endian, typename Target_type, | 
 | 	 typename Scan, typename Classify_reloc> | 
 | inline void | 
 | gc_process_relocs( | 
 |     Symbol_table* symtab, | 
 |     Layout*, | 
 |     Target_type* target, | 
 |     Sized_relobj_file<size, big_endian>* src_obj, | 
 |     unsigned int src_indx, | 
 |     const unsigned char* prelocs, | 
 |     size_t reloc_count, | 
 |     Output_section*, | 
 |     bool, | 
 |     size_t local_count, | 
 |     const unsigned char* plocal_syms) | 
 | { | 
 |   Scan scan; | 
 |  | 
 |   typedef typename Classify_reloc::Reltype Reltype; | 
 |   const int reloc_size = Classify_reloc::reloc_size; | 
 |   const int sym_size = elfcpp::Elf_sizes<size>::sym_size; | 
 |  | 
 |   Icf::Sections_reachable_info* secvec = NULL; | 
 |   Icf::Symbol_info* symvec = NULL; | 
 |   Icf::Addend_info* addendvec = NULL; | 
 |   Icf::Offset_info* offsetvec = NULL; | 
 |   Icf::Reloc_addend_size_info* reloc_addend_size_vec = NULL; | 
 |   bool is_icf_tracked = false; | 
 |   const char* cident_section_name = NULL; | 
 |  | 
 |   std::string src_section_name = (parameters->options().icf_enabled() | 
 |                                   ? src_obj->section_name(src_indx) | 
 |                                   : ""); | 
 |  | 
 |   bool check_section_for_function_pointers = false; | 
 |  | 
 |   if (parameters->options().icf_enabled() | 
 |       && (is_section_foldable_candidate(src_section_name) | 
 |           || is_prefix_of(".eh_frame", src_section_name.c_str()))) | 
 |     { | 
 |       is_icf_tracked = true; | 
 |       Section_id src_id(src_obj, src_indx); | 
 |       Icf::Reloc_info* reloc_info = | 
 |         &symtab->icf()->reloc_info_list()[src_id]; | 
 |       secvec = &reloc_info->section_info; | 
 |       symvec = &reloc_info->symbol_info; | 
 |       addendvec = &reloc_info->addend_info; | 
 |       offsetvec = &reloc_info->offset_info; | 
 |       reloc_addend_size_vec = &reloc_info->reloc_addend_size_info; | 
 |     } | 
 |  | 
 |   check_section_for_function_pointers = | 
 |     symtab->icf()->check_section_for_function_pointers(src_section_name, | 
 |                                                        target); | 
 |  | 
 |   for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) | 
 |     { | 
 |       Reltype reloc(prelocs); | 
 |       unsigned int r_sym = Classify_reloc::get_r_sym(&reloc); | 
 |       unsigned int r_type = Classify_reloc::get_r_type(&reloc); | 
 |       typename elfcpp::Elf_types<size>::Elf_Swxword addend = | 
 | 	  Classify_reloc::get_r_addend(&reloc); | 
 |       Relobj* dst_obj; | 
 |       unsigned int dst_indx; | 
 |       typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; | 
 |       Address dst_off; | 
 |  | 
 |       if (r_sym < local_count) | 
 |         { | 
 |           gold_assert(plocal_syms != NULL); | 
 |           typename elfcpp::Sym<size, big_endian> lsym(plocal_syms | 
 |                                                       + r_sym * sym_size); | 
 | 	  dst_indx = lsym.get_st_shndx(); | 
 |           bool is_ordinary; | 
 | 	  dst_indx = src_obj->adjust_sym_shndx(r_sym, dst_indx, &is_ordinary); | 
 |           dst_obj = src_obj; | 
 | 	  dst_off = lsym.get_st_value() + addend; | 
 |  | 
 |           if (is_icf_tracked) | 
 |             { | 
 | 	      Address symvalue = dst_off - addend; | 
 | 	      if (is_ordinary)  | 
 | 		(*secvec).push_back(Section_id(src_obj, dst_indx)); | 
 | 	      else | 
 |                 (*secvec).push_back(Section_id(NULL, 0)); | 
 |               // If the target of the relocation is an STT_SECTION symbol, | 
 |               // make a note of that by storing -1 in the symbol vector. | 
 |               if (lsym.get_st_type() == elfcpp::STT_SECTION) | 
 | 		(*symvec).push_back(reinterpret_cast<Symbol*>(-1)); | 
 | 	      else | 
 | 		(*symvec).push_back(NULL); | 
 | 	      (*addendvec).push_back(std::make_pair( | 
 | 					static_cast<long long>(symvalue), | 
 | 					static_cast<long long>(addend))); | 
 |               uint64_t reloc_offset = | 
 |                 convert_to_section_size_type(reloc.get_r_offset()); | 
 | 	      (*offsetvec).push_back(reloc_offset); | 
 |               (*reloc_addend_size_vec).push_back( | 
 |                 get_embedded_addend_size<Classify_reloc>(r_type, src_obj)); | 
 |             } | 
 |  | 
 | 	  // When doing safe folding, check to see if this relocation is that | 
 | 	  // of a function pointer being taken. | 
 | 	  if (is_ordinary | 
 | 	      && check_section_for_function_pointers | 
 |               && lsym.get_st_type() != elfcpp::STT_OBJECT | 
 |  	      && scan.local_reloc_may_be_function_pointer(symtab, NULL, target, | 
 | 							  src_obj, src_indx, | 
 | 			                       		  NULL, reloc, r_type, | 
 | 							  lsym)) | 
 |             symtab->icf()->set_section_has_function_pointers( | 
 |               src_obj, lsym.get_st_shndx()); | 
 |  | 
 |           if (!is_ordinary || dst_indx == src_indx) | 
 |             continue; | 
 |         } | 
 |       else | 
 |         { | 
 |           Symbol* gsym = src_obj->global_symbol(r_sym); | 
 |           gold_assert(gsym != NULL); | 
 |           if (gsym->is_forwarder()) | 
 |             gsym = symtab->resolve_forwards(gsym); | 
 |  | 
 |           dst_obj = NULL; | 
 |           dst_indx = 0; | 
 |           bool is_ordinary = false; | 
 |           if (gsym->source() == Symbol::FROM_OBJECT | 
 | 	      && !gsym->object()->is_dynamic()) | 
 |             { | 
 |               dst_obj = static_cast<Relobj*>(gsym->object()); | 
 |               dst_indx = gsym->shndx(&is_ordinary); | 
 |             } | 
 | 	  dst_off = static_cast<const Sized_symbol<size>*>(gsym)->value(); | 
 | 	  dst_off += addend; | 
 |  | 
 | 	  // When doing safe folding, check to see if this relocation is that | 
 | 	  // of a function pointer being taken. | 
 | 	  if (gsym->source() == Symbol::FROM_OBJECT | 
 |               && gsym->type() == elfcpp::STT_FUNC | 
 |               && check_section_for_function_pointers | 
 |               && dst_obj != NULL | 
 |               && (!is_ordinary | 
 |                   || scan.global_reloc_may_be_function_pointer( | 
 |                        symtab, NULL, target, src_obj, src_indx, NULL, reloc, | 
 |                        r_type, gsym))) | 
 |             symtab->icf()->set_section_has_function_pointers(dst_obj, dst_indx); | 
 |  | 
 |           // If the symbol name matches '__start_XXX' then the section with | 
 |           // the C identifier like name 'XXX' should not be garbage collected. | 
 |           // A similar treatment to symbols with the name '__stop_XXX'. | 
 |           if (is_prefix_of(cident_section_start_prefix, gsym->name())) | 
 |             { | 
 |               cident_section_name = (gsym->name()  | 
 |                                      + strlen(cident_section_start_prefix)); | 
 |             } | 
 |           else if (is_prefix_of(cident_section_stop_prefix, gsym->name())) | 
 |             { | 
 |               cident_section_name = (gsym->name()  | 
 |                                      + strlen(cident_section_stop_prefix)); | 
 |             } | 
 |           if (is_icf_tracked) | 
 |             { | 
 | 	      Address symvalue = dst_off - addend; | 
 |               if (is_ordinary && dst_obj != NULL) | 
 | 		(*secvec).push_back(Section_id(dst_obj, dst_indx)); | 
 | 	      else | 
 |                 (*secvec).push_back(Section_id(NULL, 0)); | 
 |               (*symvec).push_back(gsym); | 
 | 	      (*addendvec).push_back(std::make_pair( | 
 | 					static_cast<long long>(symvalue), | 
 | 					static_cast<long long>(addend))); | 
 |               uint64_t reloc_offset = | 
 |                 convert_to_section_size_type(reloc.get_r_offset()); | 
 | 	      (*offsetvec).push_back(reloc_offset); | 
 |               (*reloc_addend_size_vec).push_back( | 
 |                 get_embedded_addend_size<Classify_reloc>(r_type, src_obj)); | 
 | 	    } | 
 |  | 
 |           if (dst_obj == NULL) | 
 |             continue; | 
 |           if (!is_ordinary) | 
 |             continue; | 
 |         } | 
 |       if (parameters->options().gc_sections()) | 
 |         { | 
 | 	  symtab->gc()->add_reference(src_obj, src_indx, dst_obj, dst_indx); | 
 | 	  parameters->sized_target<size, big_endian>() | 
 | 	    ->gc_add_reference(symtab, src_obj, src_indx, dst_obj, dst_indx, | 
 | 			       dst_off); | 
 |           if (cident_section_name != NULL) | 
 |             { | 
 |               Garbage_collection::Cident_section_map::iterator ele = | 
 |                 symtab->gc()->cident_sections()->find(std::string(cident_section_name)); | 
 |               if (ele == symtab->gc()->cident_sections()->end()) | 
 |                 continue; | 
 | 	      Section_id src_id(src_obj, src_indx); | 
 |               Garbage_collection::Sections_reachable& | 
 |                 v(symtab->gc()->section_reloc_map()[src_id]); | 
 |               Garbage_collection::Sections_reachable& cident_secn(ele->second); | 
 |               for (Garbage_collection::Sections_reachable::iterator it_v | 
 |                      = cident_secn.begin(); | 
 |                    it_v != cident_secn.end(); | 
 |                    ++it_v) | 
 |                 { | 
 |                   v.insert(*it_v); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |   return; | 
 | } | 
 |  | 
 | } // End of namespace gold. | 
 |  | 
 | #endif |