OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "crazy_linker_elf_relocator.h" |
| 6 |
| 7 #include <errno.h> |
| 8 #include <string.h> |
| 9 |
| 10 #include "crazy_linker_debug.h" |
| 11 #include "crazy_linker_util.h" |
| 12 #include "linker_phdr.h" |
| 13 |
| 14 // Set to 1 to print debug traces during relocations. |
| 15 // Default is 0 since there are _tons_ of them. |
| 16 #define DEBUG_RELOCATIONS 0 |
| 17 |
| 18 #define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__) |
| 19 #define RLOG_ERRNO(...) LOG_IF_ERRNO(DEBUG_RELOCATIONS, __VA_ARGS__) |
| 20 |
| 21 |
| 22 #ifndef DF_SYMBOLIC |
| 23 #define DF_SYMBOLIC 2 |
| 24 #endif |
| 25 |
| 26 #ifndef DF_TEXTREL |
| 27 #define DF_TEXTREL 4 |
| 28 #endif |
| 29 |
| 30 #ifndef DT_FLAGS |
| 31 #define DT_FLAGS 30 |
| 32 #endif |
| 33 |
| 34 // Processor-specific relocation types supported by the linker. |
| 35 #ifdef __arm__ |
| 36 |
| 37 #define R_ARM_ABS32 2 |
| 38 #define R_ARM_REL32 3 |
| 39 #define R_ARM_GLOB_DAT 21 |
| 40 #define R_ARM_JUMP_SLOT 22 |
| 41 #define R_ARM_COPY 20 |
| 42 #define R_ARM_RELATIVE 23 |
| 43 |
| 44 #endif // __arm__ |
| 45 |
| 46 #ifdef __i386__ |
| 47 |
| 48 /* i386 relocations */ |
| 49 #define R_386_32 1 |
| 50 #define R_386_PC32 2 |
| 51 #define R_386_GLOB_DAT 6 |
| 52 #define R_386_JMP_SLOT 7 |
| 53 #define R_386_RELATIVE 8 |
| 54 |
| 55 #endif // __i386__ |
| 56 |
| 57 namespace crazy { |
| 58 |
| 59 namespace { |
| 60 |
| 61 // List of known relocation types the relocator knows about. |
| 62 enum RelocationType { |
| 63 RELOCATION_TYPE_UNKNOWN = 0, |
| 64 RELOCATION_TYPE_ABSOLUTE = 1, |
| 65 RELOCATION_TYPE_RELATIVE = 2, |
| 66 RELOCATION_TYPE_PC_RELATIVE = 3, |
| 67 RELOCATION_TYPE_COPY = 4, |
| 68 }; |
| 69 |
| 70 // Convert an ELF relocation type info a RelocationType value. |
| 71 RelocationType GetRelocationType(unsigned r_type) { |
| 72 switch (r_type) { |
| 73 #ifdef __arm__ |
| 74 case R_ARM_JUMP_SLOT: |
| 75 case R_ARM_GLOB_DAT: |
| 76 case R_ARM_ABS32: |
| 77 return RELOCATION_TYPE_ABSOLUTE; |
| 78 |
| 79 case R_ARM_REL32: |
| 80 case R_ARM_RELATIVE: |
| 81 return RELOCATION_TYPE_RELATIVE; |
| 82 |
| 83 case R_ARM_COPY: |
| 84 return RELOCATION_TYPE_COPY; |
| 85 #endif |
| 86 |
| 87 #ifdef __i386__ |
| 88 case R_386_JMP_SLOT: |
| 89 case R_386_GLOB_DAT: |
| 90 case R_386_32: |
| 91 return RELOCATION_TYPE_ABSOLUTE; |
| 92 |
| 93 case R_386_RELATIVE: |
| 94 return RELOCATION_TYPE_RELATIVE; |
| 95 |
| 96 case R_386_PC32: |
| 97 return RELOCATION_TYPE_PC_RELATIVE; |
| 98 #endif |
| 99 |
| 100 #ifdef __mips__ |
| 101 case R_MIPS_REL32: |
| 102 return RELOCATION_TYPE_RELATIVE; |
| 103 #endif |
| 104 |
| 105 default: |
| 106 return RELOCATION_TYPE_UNKNOWN; |
| 107 } |
| 108 } |
| 109 |
| 110 } // namespace |
| 111 |
| 112 ElfRelocator::ElfRelocator() { |
| 113 } |
| 114 |
| 115 bool ElfRelocator::Init(const ELF::Phdr* phdr, |
| 116 size_t phdr_count, |
| 117 size_t load_bias, |
| 118 const ELF::Dyn* dyn, |
| 119 size_t count, |
| 120 Error* error) { |
| 121 phdr_ = phdr; |
| 122 phdr_count_ = phdr_count; |
| 123 load_bias_ = load_bias; |
| 124 |
| 125 LOG("%s: phdr=%p phdr_count=%d load_bias=%x dyn_table=%p dyn_count=%d\n", |
| 126 __FUNCTION__, phdr, (int)phdr_count, (int)load_bias, dyn, (int)count); |
| 127 |
| 128 for ( ; count > 0; dyn++, count-- ) { |
| 129 // Be paranoid. |
| 130 if (dyn->d_tag == DT_NULL) |
| 131 break; |
| 132 |
| 133 ELF::Addr dyn_value = dyn->d_un.d_val; |
| 134 uintptr_t dyn_addr = load_bias_ + dyn->d_un.d_ptr; |
| 135 |
| 136 switch (dyn->d_tag) { |
| 137 case DT_PLTREL: |
| 138 // NOTE: Yes, there is nothing to record here, the content of |
| 139 // plt_rel_ will come from DT_JMPREL instead. |
| 140 RLOG(" DT_PLTREL"); |
| 141 if (dyn_value != DT_REL) { |
| 142 *error = "Unsupported DT_RELA entry in dynamic section"; |
| 143 return false; |
| 144 } |
| 145 break; |
| 146 case DT_JMPREL: |
| 147 RLOG(" DT_JMPREL addr=%p\n", dyn_addr); |
| 148 plt_rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr); |
| 149 break; |
| 150 case DT_PLTRELSZ: |
| 151 plt_rel_count_ = dyn_value / sizeof(ELF::Rel); |
| 152 RLOG(" DT_PLTRELSZ size=%d count=%d\n", dyn_value, plt_rel_count_); |
| 153 break; |
| 154 case DT_REL: |
| 155 RLOG(" DT_REL addr=%p\n", dyn_addr); |
| 156 rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr); |
| 157 break; |
| 158 case DT_RELSZ: |
| 159 rel_count_ = dyn_value / sizeof(ELF::Rel); |
| 160 RLOG(" DT_RELSZ size=%d count=%d\n", dyn_value, rel_count_); |
| 161 break; |
| 162 case DT_PLTGOT: |
| 163 // NOTE from original linker: |
| 164 // Save this in case we decide to do lazy binding. We don't yet. |
| 165 RLOG(" DT_PLTGOT addr=%p\n", dyn_addr); |
| 166 plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr); |
| 167 break; |
| 168 case DT_RELA: |
| 169 *error = "Unsupported DT_RELA entry in dynamic section"; |
| 170 return false; |
| 171 case DT_TEXTREL: |
| 172 LOG(" DT_TEXTREL\n"); |
| 173 has_text_relocations_ = true; |
| 174 break; |
| 175 case DT_SYMBOLIC: |
| 176 RLOG(" DT_SYMBOLIC\n"); |
| 177 has_symbolic_ = true; |
| 178 break; |
| 179 case DT_FLAGS: |
| 180 if (dyn_value & DF_TEXTREL) |
| 181 has_text_relocations_ = true; |
| 182 if (dyn_value & DF_SYMBOLIC) |
| 183 has_symbolic_ = true; |
| 184 RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n", |
| 185 has_text_relocations_ ? "true" : "false", |
| 186 has_symbolic_ ? "true" : "false"); |
| 187 break; |
| 188 #if defined(__mips__) |
| 189 case DT_MIPS_SYMTABNO: |
| 190 RLOG(" DT_MIPS_SYMTABNO value=%d\n", dyn_value); |
| 191 mips_symtabno_ = dyn_value; |
| 192 break; |
| 193 |
| 194 case DT_MIPS_LOCAL_GOTNO: |
| 195 RLOG(" DT_MIPS_LOCAL_GOTNO value=%d\n", dyn_value); |
| 196 mips_local_gotno_ = dyn_value; |
| 197 break; |
| 198 |
| 199 case DT_MIPS_GOTSYM: |
| 200 RLOG(" DT_MIPS_GOTSYM value=%d\n", dyn_value); |
| 201 mips_gotsym_ = dyn_value; |
| 202 break; |
| 203 #endif |
| 204 default: |
| 205 RLOG(" UNKNOWN tag=%d value=%08x addr=%p\n", |
| 206 dyn->d_tag, dyn_value, (void*)dyn_addr); |
| 207 ; |
| 208 } |
| 209 } |
| 210 LOG("%s: Done!\n", __FUNCTION__); |
| 211 return true; |
| 212 } |
| 213 |
| 214 bool ElfRelocator::Apply(SymbolResolver* resolver, |
| 215 const char* string_table, |
| 216 const ELF::Sym* symbol_table, |
| 217 Error* error) { |
| 218 resolver_ = resolver; |
| 219 string_table_ = string_table; |
| 220 symbol_table_ = symbol_table; |
| 221 |
| 222 LOG("%s: string_table=%p sybol_table=%p\n", __FUNCTION__, |
| 223 string_table, symbol_table); |
| 224 |
| 225 if (has_text_relocations_) { |
| 226 if (phdr_table_unprotect_segments(phdr_, |
| 227 phdr_count_, |
| 228 load_bias_) < 0) { |
| 229 error->Format("Can't unprotect loadable segments: %s", |
| 230 strerror(errno)); |
| 231 return false; |
| 232 } |
| 233 } |
| 234 |
| 235 if (!ApplyRelocs(plt_rel_, plt_rel_count_, error) || |
| 236 !ApplyRelocs(rel_, rel_count_, error)) { |
| 237 return false; |
| 238 } |
| 239 |
| 240 #ifdef __mips__ |
| 241 if (!RelocateMipsGot(error)) |
| 242 return false; |
| 243 #endif |
| 244 |
| 245 if (has_text_relocations_) { |
| 246 if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) { |
| 247 error->Format("Can't protect loadable segments: %s", |
| 248 strerror(errno)); |
| 249 return false; |
| 250 } |
| 251 } |
| 252 |
| 253 // Turn on GNU RELRO protection now. |
| 254 LOG("%s: Enabling GNU RELRO protection\n", __FUNCTION__); |
| 255 |
| 256 if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) { |
| 257 error->Format("Can't enable GNU RELRO protection: %s", |
| 258 strerror(errno)); |
| 259 return false; |
| 260 } |
| 261 |
| 262 LOG("%s: Done\n", __FUNCTION__); |
| 263 return true; |
| 264 } |
| 265 |
| 266 bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel, |
| 267 size_t rel_count, |
| 268 Error* error) { |
| 269 RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count); |
| 270 |
| 271 if (!rel) |
| 272 return true; |
| 273 |
| 274 for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) { |
| 275 unsigned rel_type = ELF_R_TYPE(rel->r_info); |
| 276 unsigned rel_symbol = ELF_R_SYM(rel->r_info); |
| 277 |
| 278 ELF::Addr sym_addr = 0; |
| 279 ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_); |
| 280 LOG(" %d/%d reloc=%p offset=%p type=%d symbol=%d\n", |
| 281 rel_n + 1, rel_count, reloc, rel->r_offset, rel_type, rel_symbol); |
| 282 |
| 283 if (rel_type == 0) |
| 284 continue; |
| 285 |
| 286 RelocationType r_type = GetRelocationType(rel_type); |
| 287 bool CRAZY_UNUSED resolved = false; |
| 288 |
| 289 // If this is a symbolic relocation, compute the symbol's address. |
| 290 if (rel_symbol != 0) { |
| 291 const char* sym_name = |
| 292 string_table_ + symbol_table_[rel_symbol].st_name; |
| 293 RLOG(" symbol name='%s'\n", sym_name); |
| 294 void* address = resolver_->Lookup(sym_name); |
| 295 if (address) { |
| 296 // The symbol was found, so compute its address. |
| 297 RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, |
| 298 sym_name, address); |
| 299 resolved = true; |
| 300 sym_addr = reinterpret_cast<ELF::Addr>(address); |
| 301 } else { |
| 302 // The symbol was not found. Normally this is an error except |
| 303 // if this is a weak reference. |
| 304 if (ELF_ST_BIND(symbol_table_[rel_symbol].st_info) != STB_WEAK) { |
| 305 error->Format("Could not find symbol '%s'", sym_name); |
| 306 return false; |
| 307 } |
| 308 |
| 309 resolved = true; |
| 310 RLOG("%s: weak reference to unresolved symbol %s\n", |
| 311 __FUNCTION__, sym_name); |
| 312 |
| 313 // IHI0044C AAELF 4.5.1.1: |
| 314 // Libraries are not searched to resolve weak references. |
| 315 // It is not an error for a weak reference to remain |
| 316 // unsatisfied. |
| 317 // |
| 318 // During linking, the value of an undefined weak reference is: |
| 319 // - Zero if the relocation type is absolute |
| 320 // - The address of the place if the relocation is pc-relative |
| 321 // - The address of nominal base address if the relocation |
| 322 // type is base-relative. |
| 323 RelocationType r = GetRelocationType(rel_type); |
| 324 if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE) |
| 325 sym_addr = 0; |
| 326 else if (r == RELOCATION_TYPE_PC_RELATIVE) |
| 327 sym_addr = reloc; |
| 328 else { |
| 329 error->Format( |
| 330 "Invalid weak relocation type (%d) for unknown symbol '%s'", |
| 331 r_type, sym_name); |
| 332 return false; |
| 333 } |
| 334 } |
| 335 } |
| 336 |
| 337 // Apply the relocation. |
| 338 ELF::Addr* target = reinterpret_cast<ELF::Addr*>(reloc); |
| 339 switch (rel_type) { |
| 340 #ifdef __arm__ |
| 341 case R_ARM_JUMP_SLOT: |
| 342 RLOG(" R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr); |
| 343 *target = sym_addr; |
| 344 break; |
| 345 |
| 346 case R_ARM_GLOB_DAT: |
| 347 RLOG(" R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr); |
| 348 *target = sym_addr; |
| 349 break; |
| 350 |
| 351 case R_ARM_ABS32: |
| 352 RLOG(" R_ARM_ABS32 target=%p (%p) addr=%p\n", target, *target, sym_addr
); |
| 353 *target += sym_addr; |
| 354 break; |
| 355 |
| 356 case R_ARM_REL32: |
| 357 RLOG(" R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n", target, *target
, sym_addr, rel->r_offset); |
| 358 *target += sym_addr - rel->r_offset; |
| 359 break; |
| 360 |
| 361 case R_ARM_RELATIVE: |
| 362 RLOG(" R_ARM_RELATIVE target=%p (%p) bias=%p\n", target, *target, load_
bias_); |
| 363 if (rel_symbol) { |
| 364 *error = "Invalid relative relocation with symbol"; |
| 365 return false; |
| 366 } |
| 367 *target += load_bias_; |
| 368 break; |
| 369 |
| 370 case R_ARM_COPY: |
| 371 // NOTE: These relocations are forbidden in shared libraries. |
| 372 // The Android linker has special code to deal with this, which |
| 373 // is not needed here. |
| 374 RLOG(" R_ARM_COPY\n"); |
| 375 *error = "Invalid R_ARM_COPY relocation in shared library"; |
| 376 return false; |
| 377 #endif // __arm__ |
| 378 |
| 379 #ifdef __i386__ |
| 380 case R_386_JMP_SLOT: |
| 381 *target = sym_addr; |
| 382 break; |
| 383 |
| 384 case R_386_GLOB_DAT: |
| 385 *target = sym_addr; |
| 386 break; |
| 387 |
| 388 case R_386_RELATIVE: |
| 389 if (rel_symbol) { |
| 390 *error = "Invalid relative relocation with symbol"; |
| 391 return false; |
| 392 } |
| 393 *target += load_bias_; |
| 394 break; |
| 395 |
| 396 case R_386_32: |
| 397 *target += sym_addr; |
| 398 break; |
| 399 |
| 400 case R_386_PC32: |
| 401 *target += (sym_addr - reloc); |
| 402 break; |
| 403 #endif // __i386__ |
| 404 |
| 405 #ifdef __mips__ |
| 406 case R_MIPS_REL32: |
| 407 if (resolved) |
| 408 *target += sym_addr; |
| 409 else |
| 410 *target += load_bias_; |
| 411 break; |
| 412 #endif // __mips__ |
| 413 |
| 414 default: |
| 415 error->Format("Invalid relocation type (%d)", rel_type); |
| 416 return false; |
| 417 } |
| 418 } |
| 419 |
| 420 return true; |
| 421 } |
| 422 |
| 423 #ifdef __mips__ |
| 424 bool ElfRelocator::RelocateMipsGot(Error* error) { |
| 425 // TODO(digit): Implement this. |
| 426 *error = "Relocating the MIPS GOT is not implemented"; |
| 427 return false; |
| 428 } |
| 429 #endif // __mips__ |
| 430 |
| 431 } // namespace crazy |
OLD | NEW |