| 1 | // copy-relocs.cc -- handle COPY relocations for gold. |
| 2 | |
| 3 | // Copyright 2006, 2007, 2008 Free Software Foundation, Inc. |
| 4 | // Written by Ian Lance Taylor <iant@google.com>. |
| 5 | |
| 6 | // This file is part of gold. |
| 7 | |
| 8 | // This program is free software; you can redistribute it and/or modify |
| 9 | // it under the terms of the GNU General Public License as published by |
| 10 | // the Free Software Foundation; either version 3 of the License, or |
| 11 | // (at your option) any later version. |
| 12 | |
| 13 | // This program is distributed in the hope that it will be useful, |
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | // GNU General Public License for more details. |
| 17 | |
| 18 | // You should have received a copy of the GNU General Public License |
| 19 | // along with this program; if not, write to the Free Software |
| 20 | // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| 21 | // MA 02110-1301, USA. |
| 22 | |
| 23 | #include "gold.h" |
| 24 | |
| 25 | #include "symtab.h" |
| 26 | #include "copy-relocs.h" |
| 27 | |
| 28 | namespace gold |
| 29 | { |
| 30 | |
| 31 | // Copy_relocs::Copy_reloc_entry methods. |
| 32 | |
| 33 | // Emit the reloc if appropriate. |
| 34 | |
| 35 | template<int sh_type, int size, bool big_endian> |
| 36 | void |
| 37 | Copy_relocs<sh_type, size, big_endian>::Copy_reloc_entry::emit( |
| 38 | Output_data_reloc<sh_type, true, size, big_endian>* reloc_section) |
| 39 | { |
| 40 | // If the symbol is no longer defined in a dynamic object, then we |
| 41 | // emitted a COPY relocation, and we do not want to emit this |
| 42 | // dynamic relocation. |
| 43 | if (this->sym_->is_from_dynobj()) |
| 44 | reloc_section->add_global(this->sym_, this->reloc_type_, |
| 45 | this->output_section_, this->relobj_, |
| 46 | this->shndx_, this->address_, |
| 47 | this->addend_); |
| 48 | } |
| 49 | |
| 50 | // Copy_relocs methods. |
| 51 | |
| 52 | // Handle a relocation against a symbol which may force us to generate |
| 53 | // a COPY reloc. |
| 54 | |
| 55 | template<int sh_type, int size, bool big_endian> |
| 56 | void |
| 57 | Copy_relocs<sh_type, size, big_endian>::copy_reloc( |
| 58 | Symbol_table* symtab, |
| 59 | Layout* layout, |
| 60 | Sized_symbol<size>* sym, |
| 61 | Sized_relobj<size, big_endian>* object, |
| 62 | unsigned int shndx, |
| 63 | Output_section *output_section, |
| 64 | const Reloc& rel, |
| 65 | Output_data_reloc<sh_type, true, size, big_endian>* reloc_section) |
| 66 | { |
| 67 | if (this->need_copy_reloc(sym, object, shndx)) |
| 68 | this->emit_copy_reloc(symtab, layout, sym, reloc_section); |
| 69 | else |
| 70 | { |
| 71 | // We may not need a COPY relocation. Save this relocation to |
| 72 | // possibly be emitted later. |
| 73 | this->save(sym, object, shndx, output_section, rel); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // Return whether we need a COPY reloc for a relocation against SYM. |
| 78 | // The relocation is begin applied to section SHNDX in OBJECT. |
| 79 | |
| 80 | template<int sh_type, int size, bool big_endian> |
| 81 | bool |
| 82 | Copy_relocs<sh_type, size, big_endian>::need_copy_reloc( |
| 83 | Sized_symbol<size>* sym, |
| 84 | Sized_relobj<size, big_endian>* object, |
| 85 | unsigned int shndx) const |
| 86 | { |
| 87 | // FIXME: Handle -z nocopyrelocs. |
| 88 | |
| 89 | if (sym->symsize() == 0) |
| 90 | return false; |
| 91 | |
| 92 | // If this is a readonly section, then we need a COPY reloc. |
| 93 | // Otherwise we can use a dynamic reloc. Note that calling |
| 94 | // section_flags here can be slow, as the information is not cached; |
| 95 | // fortunately we shouldn't see too many potential COPY relocs. |
| 96 | if ((object->section_flags(shndx) & elfcpp::SHF_WRITE) == 0) |
| 97 | return true; |
| 98 | |
| 99 | return false; |
| 100 | } |
| 101 | |
| 102 | // Emit a COPY relocation for SYM. |
| 103 | |
| 104 | template<int sh_type, int size, bool big_endian> |
| 105 | void |
| 106 | Copy_relocs<sh_type, size, big_endian>::emit_copy_reloc( |
| 107 | Symbol_table* symtab, |
| 108 | Layout* layout, |
| 109 | Sized_symbol<size>* sym, |
| 110 | Output_data_reloc<sh_type, true, size, big_endian>* reloc_section) |
| 111 | { |
| 112 | typename elfcpp::Elf_types<size>::Elf_WXword symsize = sym->symsize(); |
| 113 | |
| 114 | // There is no defined way to determine the required alignment of |
| 115 | // the symbol. We know that the symbol is defined in a dynamic |
| 116 | // object. We start with the alignment of the section in which it |
| 117 | // is defined; presumably we do not require an alignment larger than |
| 118 | // that. Then we reduce that alignment if the symbol is not aligned |
| 119 | // within the section. |
| 120 | gold_assert(sym->is_from_dynobj()); |
| 121 | bool is_ordinary; |
| 122 | unsigned int shndx = sym->shndx(&is_ordinary); |
| 123 | gold_assert(is_ordinary); |
| 124 | typename elfcpp::Elf_types<size>::Elf_WXword addralign = |
| 125 | sym->object()->section_addralign(shndx); |
| 126 | |
| 127 | typename Sized_symbol<size>::Value_type value = sym->value(); |
| 128 | while ((value & (addralign - 1)) != 0) |
| 129 | addralign >>= 1; |
| 130 | |
| 131 | if (this->dynbss_ == NULL) |
| 132 | { |
| 133 | this->dynbss_ = new Output_data_space(addralign, "** dynbss"); |
| 134 | layout->add_output_section_data(".bss", |
| 135 | elfcpp::SHT_NOBITS, |
| 136 | elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, |
| 137 | this->dynbss_); |
| 138 | } |
| 139 | |
| 140 | Output_data_space* dynbss = this->dynbss_; |
| 141 | |
| 142 | if (addralign > dynbss->addralign()) |
| 143 | dynbss->set_space_alignment(addralign); |
| 144 | |
| 145 | section_size_type dynbss_size = |
| 146 | convert_to_section_size_type(dynbss->current_data_size()); |
| 147 | dynbss_size = align_address(dynbss_size, addralign); |
| 148 | section_size_type offset = dynbss_size; |
| 149 | dynbss->set_current_data_size(dynbss_size + symsize); |
| 150 | |
| 151 | // Define the symbol as being copied. |
| 152 | symtab->define_with_copy_reloc(sym, dynbss, offset); |
| 153 | |
| 154 | // Add the COPY relocation to the dynamic reloc section. |
| 155 | this->add_copy_reloc(sym, offset, reloc_section); |
| 156 | } |
| 157 | |
| 158 | // Add a COPY relocation for SYM to RELOC_SECTION. |
| 159 | |
| 160 | template<int sh_type, int size, bool big_endian> |
| 161 | void |
| 162 | Copy_relocs<sh_type, size, big_endian>::add_copy_reloc( |
| 163 | Symbol* sym, |
| 164 | section_size_type offset, |
| 165 | Output_data_reloc<sh_type, true, size, big_endian>* reloc_section) |
| 166 | { |
| 167 | reloc_section->add_global(sym, this->copy_reloc_type_, this->dynbss_, |
| 168 | offset, 0); |
| 169 | } |
| 170 | |
| 171 | // Save a relocation to possibly be emitted later. |
| 172 | |
| 173 | template<int sh_type, int size, bool big_endian> |
| 174 | void |
| 175 | Copy_relocs<sh_type, size, big_endian>::save( |
| 176 | Symbol* sym, |
| 177 | Sized_relobj<size, big_endian>* object, |
| 178 | unsigned int shndx, |
| 179 | Output_section* output_section, |
| 180 | const Reloc& rel) |
| 181 | { |
| 182 | unsigned int reloc_type = elfcpp::elf_r_type<size>(rel.get_r_info()); |
| 183 | typename elfcpp::Elf_types<size>::Elf_Addr addend = |
| 184 | Reloc_types<sh_type, size, big_endian>::get_reloc_addend_noerror(&rel); |
| 185 | this->entries_.push_back(Copy_reloc_entry(sym, reloc_type, object, shndx, |
| 186 | output_section, rel.get_r_offset(), |
| 187 | addend)); |
| 188 | } |
| 189 | |
| 190 | // Emit any saved relocs. |
| 191 | |
| 192 | template<int sh_type, int size, bool big_endian> |
| 193 | void |
| 194 | Copy_relocs<sh_type, size, big_endian>::emit( |
| 195 | Output_data_reloc<sh_type, true, size, big_endian>* reloc_section) |
| 196 | { |
| 197 | for (typename Copy_reloc_entries::iterator p = this->entries_.begin(); |
| 198 | p != this->entries_.end(); |
| 199 | ++p) |
| 200 | p->emit(reloc_section); |
| 201 | |
| 202 | // We no longer need the saved information. |
| 203 | this->entries_.clear(); |
| 204 | } |
| 205 | |
| 206 | // Instantiate the templates we need. |
| 207 | |
| 208 | #ifdef HAVE_TARGET_32_LITTLE |
| 209 | template |
| 210 | class Copy_relocs<elfcpp::SHT_REL, 32, false>; |
| 211 | |
| 212 | template |
| 213 | class Copy_relocs<elfcpp::SHT_RELA, 32, false>; |
| 214 | #endif |
| 215 | |
| 216 | #ifdef HAVE_TARGET_32_BIG |
| 217 | template |
| 218 | class Copy_relocs<elfcpp::SHT_REL, 32, true>; |
| 219 | |
| 220 | template |
| 221 | class Copy_relocs<elfcpp::SHT_RELA, 32, true>; |
| 222 | #endif |
| 223 | |
| 224 | #ifdef HAVE_TARGET_64_LITTLE |
| 225 | template |
| 226 | class Copy_relocs<elfcpp::SHT_REL, 64, false>; |
| 227 | |
| 228 | template |
| 229 | class Copy_relocs<elfcpp::SHT_RELA, 64, false>; |
| 230 | #endif |
| 231 | |
| 232 | #ifdef HAVE_TARGET_64_BIG |
| 233 | template |
| 234 | class Copy_relocs<elfcpp::SHT_REL, 64, true>; |
| 235 | |
| 236 | template |
| 237 | class Copy_relocs<elfcpp::SHT_RELA, 64, true>; |
| 238 | #endif |
| 239 | |
| 240 | } // End namespace gold. |