1 /* $NetBSD: libdwarf_reloc.c,v 1.6 2024/03/27 21:53:06 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Kai Wang 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "_libdwarf.h" 30 31 __RCSID("$NetBSD: libdwarf_reloc.c,v 1.6 2024/03/27 21:53:06 christos Exp $"); 32 ELFTC_VCSID("Id: libdwarf_reloc.c 3741 2019-06-07 06:32:01Z jkoshy"); 33 34 Dwarf_Unsigned 35 _dwarf_get_reloc_type(Dwarf_P_Debug dbg, int is64) 36 { 37 38 assert(dbg != NULL); 39 #ifdef BUILTIN_ELF_HEADERS 40 switch (dbg->dbgp_isa) { 41 case DW_ISA_AARCH64: 42 return (is64 ? R_AARCH64_ABS64 : R_AARCH64_ABS32); 43 case DW_ISA_X86: 44 return (R_386_32); 45 case DW_ISA_X86_64: 46 return (is64 ? R_X86_64_64 : R_X86_64_32); 47 case DW_ISA_SPARC: 48 return (is64 ? R_SPARC_UA64 : R_SPARC_UA32); 49 case DW_ISA_PPC: 50 return (is64 ? R_PPC64_ADDR64 : R_PPC_ADDR32); 51 case DW_ISA_ARM: 52 return (R_ARM_ABS32); 53 case DW_ISA_MIPS: 54 return (is64 ? R_MIPS_64 : R_MIPS_32); 55 case DW_ISA_RISCV: 56 return (is64 ? R_RISCV_64 : R_RISCV_32); 57 case DW_ISA_IA64: 58 return (is64 ? R_IA_64_DIR64LSB : R_IA_64_DIR32LSB); 59 default: 60 break; 61 } 62 #endif 63 return (0); /* NOT REACHED */ 64 } 65 66 int 67 _dwarf_get_reloc_size(Dwarf_Debug dbg, Dwarf_Unsigned rel_type) 68 { 69 70 #ifdef BUILTIN_ELF_HEADERS 71 switch (dbg->dbg_machine) { 72 case EM_NONE: 73 break; 74 case EM_AARCH64: 75 if (rel_type == R_AARCH64_ABS32) 76 return (4); 77 else if (rel_type == R_AARCH64_ABS64) 78 return (8); 79 break; 80 case EM_ARM: 81 if (rel_type == R_ARM_ABS32) 82 return (4); 83 break; 84 case EM_386: 85 case EM_IAMCU: 86 if (rel_type == R_386_32) 87 return (4); 88 break; 89 case EM_X86_64: 90 if (rel_type == R_X86_64_32) 91 return (4); 92 else if (rel_type == R_X86_64_64) 93 return (8); 94 break; 95 case EM_SPARC: 96 if (rel_type == R_SPARC_UA32) 97 return (4); 98 else if (rel_type == R_SPARC_UA64) 99 return (8); 100 break; 101 case EM_PPC: 102 if (rel_type == R_PPC_ADDR32) 103 return (4); 104 break; 105 case EM_PPC64: 106 if (rel_type == R_PPC_ADDR32) 107 return (4); 108 else if (rel_type == R_PPC64_ADDR64) 109 return (8); 110 break; 111 case EM_MIPS: 112 if (rel_type == R_MIPS_32) 113 return (4); 114 else if (rel_type == R_MIPS_64) 115 return (8); 116 break; 117 case EM_RISCV: 118 if (rel_type == R_RISCV_32) 119 return (4); 120 else if (rel_type == R_RISCV_64) 121 return (8); 122 break; 123 case EM_IA_64: 124 if (rel_type == R_IA_64_SECREL32LSB) 125 return (4); 126 else if (rel_type == R_IA_64_DIR64LSB) 127 return (8); 128 break; 129 default: 130 break; 131 } 132 #endif 133 134 /* unknown relocation. */ 135 return (0); 136 } 137 138 int 139 _dwarf_reloc_section_init(Dwarf_P_Debug dbg, Dwarf_Rel_Section *drsp, 140 Dwarf_P_Section ref, Dwarf_Error *error) 141 { 142 Dwarf_Rel_Section drs; 143 char name[128]; 144 int pseudo; 145 146 assert(dbg != NULL && drsp != NULL && ref != NULL); 147 148 if ((drs = calloc(1, sizeof(struct _Dwarf_Rel_Section))) == NULL) { 149 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 150 return (DW_DLE_MEMORY); 151 } 152 153 drs->drs_ref = ref; 154 155 /* 156 * FIXME The logic here is most likely wrong. It should 157 * be the ISA that determines relocation type. 158 */ 159 if (dbg->dbgp_flags & DW_DLC_SIZE_64) 160 drs->drs_addend = 1; 161 else 162 drs->drs_addend = 0; 163 164 if (dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) 165 pseudo = 1; 166 else 167 pseudo = 0; 168 169 snprintf(name, sizeof(name), "%s%s", 170 drs->drs_addend ? ".rela" : ".rel", ref->ds_name); 171 if (_dwarf_section_init(dbg, &drs->drs_ds, name, pseudo, error) != 172 DW_DLE_NONE) { 173 free(drs); 174 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 175 return (DW_DLE_MEMORY); 176 } 177 178 STAILQ_INIT(&drs->drs_dre); 179 STAILQ_INSERT_TAIL(&dbg->dbgp_drslist, drs, drs_next); 180 dbg->dbgp_drscnt++; 181 *drsp = drs; 182 183 return (DW_DLE_NONE); 184 } 185 186 void 187 _dwarf_reloc_section_free(Dwarf_P_Debug dbg, Dwarf_Rel_Section *drsp) 188 { 189 Dwarf_Rel_Section drs, tdrs; 190 Dwarf_Rel_Entry dre, tdre; 191 192 assert(dbg != NULL && drsp != NULL); 193 194 if (*drsp == NULL) 195 return; 196 197 STAILQ_FOREACH_SAFE(drs, &dbg->dbgp_drslist, drs_next, tdrs) { 198 if (drs != *drsp) 199 continue; 200 STAILQ_REMOVE(&dbg->dbgp_drslist, drs, _Dwarf_Rel_Section, 201 drs_next); 202 STAILQ_FOREACH_SAFE(dre, &drs->drs_dre, dre_next, tdre) { 203 STAILQ_REMOVE(&drs->drs_dre, dre, _Dwarf_Rel_Entry, 204 dre_next); 205 free(dre); 206 } 207 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0) 208 _dwarf_section_free(dbg, &drs->drs_ds); 209 else { 210 if (drs->drs_ds->ds_name) 211 free(drs->drs_ds->ds_name); 212 free(drs->drs_ds); 213 } 214 free(drs); 215 *drsp = NULL; 216 dbg->dbgp_drscnt--; 217 break; 218 } 219 } 220 221 int 222 _dwarf_reloc_entry_add(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 223 Dwarf_P_Section ds, unsigned char type, unsigned char length, 224 Dwarf_Unsigned offset, Dwarf_Unsigned symndx, Dwarf_Unsigned addend, 225 const char *secname, Dwarf_Error *error) 226 { 227 Dwarf_Rel_Entry dre; 228 Dwarf_Unsigned reloff; 229 int ret; 230 231 assert(drs != NULL); 232 assert(offset <= ds->ds_size); 233 reloff = offset; 234 235 /* 236 * If the DW_DLC_SYMBOLIC_RELOCATIONS flag is set or ElfXX_Rel 237 * is used instead of ELfXX_Rela, we need to write the addend 238 * in the storage unit to be relocated. Otherwise write 0 in the 239 * storage unit and the addend will be written into relocation 240 * section later. 241 */ 242 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) || 243 drs->drs_addend == 0) 244 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, &offset, 245 addend, length, error); 246 else 247 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, &offset, 248 0, length, error); 249 if (ret != DW_DLE_NONE) 250 return (ret); 251 if (offset > ds->ds_size) 252 ds->ds_size = offset; 253 254 if ((dre = calloc(1, sizeof(struct _Dwarf_Rel_Entry))) == NULL) { 255 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 256 return (DW_DLE_MEMORY); 257 } 258 STAILQ_INSERT_TAIL(&drs->drs_dre, dre, dre_next); 259 dre->dre_type = type; 260 dre->dre_length = length; 261 dre->dre_offset = reloff; 262 dre->dre_symndx = symndx; 263 dre->dre_addend = addend; 264 dre->dre_secname = secname; 265 drs->drs_drecnt++; 266 267 return (DW_DLE_NONE); 268 } 269 270 int 271 _dwarf_reloc_entry_add_pair(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 272 Dwarf_P_Section ds, unsigned char length, Dwarf_Unsigned offset, 273 Dwarf_Unsigned symndx, Dwarf_Unsigned esymndx, Dwarf_Unsigned symoff, 274 Dwarf_Unsigned esymoff, Dwarf_Error *error) 275 { 276 Dwarf_Rel_Entry dre; 277 Dwarf_Unsigned reloff; 278 int ret; 279 280 assert(drs != NULL); 281 assert(offset <= ds->ds_size); 282 assert(dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS); 283 reloff = offset; 284 285 /* Write net offset into section stream. */ 286 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, &offset, 287 esymoff - symoff, length, error); 288 if (ret != DW_DLE_NONE) 289 return (ret); 290 if (offset > ds->ds_size) 291 ds->ds_size = offset; 292 293 if ((dre = calloc(2, sizeof(struct _Dwarf_Rel_Entry))) == NULL) { 294 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 295 return (DW_DLE_MEMORY); 296 } 297 STAILQ_INSERT_TAIL(&drs->drs_dre, &dre[0], dre_next); 298 STAILQ_INSERT_TAIL(&drs->drs_dre, &dre[1], dre_next); 299 dre[0].dre_type = dwarf_drt_first_of_length_pair; 300 dre[0].dre_length = length; 301 dre[0].dre_offset = reloff; 302 dre[0].dre_symndx = symndx; 303 dre[0].dre_addend = 0; 304 dre[0].dre_secname = NULL; 305 dre[1].dre_type = dwarf_drt_second_of_length_pair; 306 dre[1].dre_length = length; 307 dre[1].dre_offset = reloff; 308 dre[1].dre_symndx = esymndx; 309 dre[1].dre_addend = 0; 310 dre[1].dre_secname = NULL; 311 drs->drs_drecnt += 2; 312 313 return (DW_DLE_NONE); 314 } 315 316 int 317 _dwarf_reloc_section_finalize(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 318 Dwarf_Error *error) 319 { 320 Dwarf_P_Section ds; 321 Dwarf_Unsigned unit; 322 int ret, size; 323 324 assert(dbg != NULL && drs != NULL && drs->drs_ds != NULL && 325 drs->drs_ref != NULL); 326 327 ds = drs->drs_ds; 328 329 /* 330 * Calculate the size (in bytes) of the relocation section. 331 */ 332 if (dbg->dbgp_flags & DW_DLC_SIZE_64) 333 unit = drs->drs_addend ? sizeof(Elf64_Rela) : sizeof(Elf64_Rel); 334 else 335 unit = drs->drs_addend ? sizeof(Elf32_Rela) : sizeof(Elf32_Rel); 336 assert(ds->ds_size == 0); 337 size = drs->drs_drecnt * unit; 338 339 /* 340 * Discard this relocation section if there is no entry in it. 341 */ 342 if (size == 0) { 343 _dwarf_reloc_section_free(dbg, &drs); 344 return (DW_DLE_NONE); 345 } 346 347 /* 348 * If we are under stream mode, realloc the section data block to 349 * this size. 350 */ 351 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0) { 352 ds->ds_cap = size; 353 if ((ds->ds_data = realloc(ds->ds_data, (size_t) ds->ds_cap)) == 354 NULL) { 355 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 356 return (DW_DLE_MEMORY); 357 } 358 } 359 360 /* 361 * Notify the application the creation of this relocation section. 362 * Note that the section link here should point to the .symtab 363 * section, we set it to 0 since we have no way to know .symtab 364 * section index. 365 */ 366 ret = _dwarf_pro_callback(dbg, ds->ds_name, size, 367 drs->drs_addend ? SHT_RELA : SHT_REL, 0, 0, drs->drs_ref->ds_ndx, 368 &ds->ds_symndx, NULL); 369 if (ret < 0) { 370 DWARF_SET_ERROR(dbg, error, DW_DLE_ELF_SECT_ERR); 371 return (DW_DLE_ELF_SECT_ERR); 372 } 373 ds->ds_ndx = ret; 374 375 return (DW_DLE_NONE); 376 } 377 378 int 379 _dwarf_reloc_section_gen(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 380 Dwarf_Error *error) 381 { 382 Dwarf_Rel_Entry dre; 383 Dwarf_P_Section ds; 384 Dwarf_Unsigned type; 385 int ret; 386 387 assert((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0); 388 assert(drs->drs_ds != NULL && drs->drs_ds->ds_size == 0); 389 assert(!STAILQ_EMPTY(&drs->drs_dre)); 390 ds = drs->drs_ds; 391 392 STAILQ_FOREACH(dre, &drs->drs_dre, dre_next) { 393 assert(dre->dre_length == 4 || dre->dre_length == 8); 394 type = _dwarf_get_reloc_type(dbg, dre->dre_length == 8); 395 if (dbg->dbgp_flags & DW_DLC_SIZE_64) { 396 /* Write r_offset (8 bytes) */ 397 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 398 &ds->ds_size, dre->dre_offset, 8, error); 399 if (ret != DW_DLE_NONE) 400 return (ret); 401 /* Write r_info (8 bytes) */ 402 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 403 &ds->ds_size, ELF64_R_INFO(dre->dre_symndx, type), 404 8, error); 405 if (ret != DW_DLE_NONE) 406 return (ret); 407 /* Write r_addend (8 bytes) */ 408 if (drs->drs_addend) { 409 ret = dbg->write_alloc(&ds->ds_data, 410 &ds->ds_cap, &ds->ds_size, dre->dre_addend, 411 8, error); 412 if (ret != DW_DLE_NONE) 413 return (ret); 414 } 415 } else { 416 /* Write r_offset (4 bytes) */ 417 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 418 &ds->ds_size, dre->dre_offset, 4, error); 419 if (ret != DW_DLE_NONE) 420 return (ret); 421 /* Write r_info (4 bytes) */ 422 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 423 &ds->ds_size, ELF32_R_INFO(dre->dre_symndx, type), 424 4, error); 425 if (ret != DW_DLE_NONE) 426 return (ret); 427 /* Write r_addend (4 bytes) */ 428 if (drs->drs_addend) { 429 ret = dbg->write_alloc(&ds->ds_data, 430 &ds->ds_cap, &ds->ds_size, dre->dre_addend, 431 4, error); 432 if (ret != DW_DLE_NONE) 433 return (ret); 434 } 435 } 436 } 437 assert(ds->ds_size == ds->ds_cap); 438 439 return (DW_DLE_NONE); 440 } 441 442 int 443 _dwarf_reloc_gen(Dwarf_P_Debug dbg, Dwarf_Error *error) 444 { 445 Dwarf_Rel_Section drs; 446 Dwarf_Rel_Entry dre; 447 Dwarf_P_Section ds; 448 int ret; 449 450 STAILQ_FOREACH(drs, &dbg->dbgp_drslist, drs_next) { 451 /* 452 * Update relocation entries: translate any section name 453 * reference to section symbol index. 454 */ 455 STAILQ_FOREACH(dre, &drs->drs_dre, dre_next) { 456 if (dre->dre_secname == NULL) 457 continue; 458 ds = _dwarf_pro_find_section(dbg, dre->dre_secname); 459 assert(ds != NULL && ds->ds_symndx != 0); 460 dre->dre_symndx = ds->ds_symndx; 461 } 462 463 /* 464 * Generate ELF relocation section if we are under stream 465 * mode. 466 */ 467 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0) { 468 ret = _dwarf_reloc_section_gen(dbg, drs, error); 469 if (ret != DW_DLE_NONE) 470 return (ret); 471 } 472 } 473 474 return (DW_DLE_NONE); 475 } 476 477 void 478 _dwarf_reloc_cleanup(Dwarf_P_Debug dbg) 479 { 480 Dwarf_Rel_Section drs, tdrs; 481 Dwarf_Rel_Entry dre, tdre; 482 483 assert(dbg != NULL && dbg->dbg_mode == DW_DLC_WRITE); 484 485 STAILQ_FOREACH_SAFE(drs, &dbg->dbgp_drslist, drs_next, tdrs) { 486 STAILQ_REMOVE(&dbg->dbgp_drslist, drs, _Dwarf_Rel_Section, 487 drs_next); 488 free(drs->drs_drd); 489 STAILQ_FOREACH_SAFE(dre, &drs->drs_dre, dre_next, tdre) { 490 STAILQ_REMOVE(&drs->drs_dre, dre, _Dwarf_Rel_Entry, 491 dre_next); 492 free(dre); 493 } 494 if (dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) { 495 if (drs->drs_ds) { 496 if (drs->drs_ds->ds_name) 497 free(drs->drs_ds->ds_name); 498 free(drs->drs_ds); 499 } 500 } 501 free(drs); 502 } 503 dbg->dbgp_drscnt = 0; 504 dbg->dbgp_drspos = NULL; 505 } 506