|  | // gc.h -- garbage collection of unused sections | 
|  |  | 
|  | // Copyright (C) 2009-2023 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(static_cast<Relobj*>(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(static_cast<Relobj*>(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 |