1 /* $NetBSD: libdwarf_reloc.c,v 1.8 2026/05/17 21:40:49 jkoshy 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.8 2026/05/17 21:40:49 jkoshy Exp $"); 32 ELFTC_VCSID("Id: libdwarf_reloc.c 4104 2025-01-25 18:05:14Z jkoshy"); 33 34 Dwarf_Unsigned 35 _dwarf_get_reloc_type(Dwarf_P_Debug dbg, int is64) 36 { 37 38 assert(dbg != NULL); 39 40 switch (dbg->dbgp_isa) { 41 case DW_ISA_AARCH64: 42 return (is64 ? R_AARCH64_ABS64 : R_AARCH64_ABS32); 43 case DW_ISA_ARM: 44 return (R_ARM_ABS32); 45 case DW_ISA_IA64: 46 return (is64 ? R_IA_64_DIR64LSB : R_IA_64_DIR32LSB); 47 case DW_ISA_LOONGARCH: 48 return (is64 ? R_LARCH_64 : R_LARCH_32); 49 case DW_ISA_MIPS: 50 return (is64 ? R_MIPS_64 : R_MIPS_32); 51 case DW_ISA_PPC: 52 return (is64 ? R_PPC64_ADDR64 : R_PPC_ADDR32); 53 case DW_ISA_RISCV: 54 return (is64 ? R_RISCV_64 : R_RISCV_32); 55 case DW_ISA_SPARC: 56 return (is64 ? R_SPARC_UA64 : R_SPARC_UA32); 57 case DW_ISA_X86: 58 return (R_386_32); 59 case DW_ISA_X86_64: 60 return (is64 ? R_X86_64_64 : R_X86_64_32); 61 default: 62 break; 63 } 64 return (0); /* NOT REACHED */ 65 } 66 67 int 68 _dwarf_get_reloc_size(Dwarf_Debug dbg, Dwarf_Unsigned rel_type) 69 { 70 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_IA_64: 90 if (rel_type == R_IA_64_SECREL32LSB) 91 return (4); 92 else if (rel_type == R_IA_64_DIR64LSB) 93 return (8); 94 break; 95 case EM_LOONGARCH: 96 if (rel_type == R_LARCH_32) 97 return (4); 98 else if (rel_type == R_LARCH_64) 99 return (8); 100 break; 101 case EM_MIPS: 102 if (rel_type == R_MIPS_32) 103 return (4); 104 else if (rel_type == R_MIPS_64) 105 return (8); 106 break; 107 case EM_PPC: 108 if (rel_type == R_PPC_ADDR32) 109 return (4); 110 break; 111 case EM_PPC64: 112 if (rel_type == R_PPC_ADDR32) 113 return (4); 114 else if (rel_type == R_PPC64_ADDR64) 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_SPARC: 124 if (rel_type == R_SPARC_UA32) 125 return (4); 126 else if (rel_type == R_SPARC_UA64) 127 return (8); 128 break; 129 case EM_X86_64: 130 if (rel_type == R_X86_64_32) 131 return (4); 132 else if (rel_type == R_X86_64_64) 133 return (8); 134 break; 135 default: 136 break; 137 } 138 139 /* unknown relocation. */ 140 return (0); 141 } 142 143 int 144 _dwarf_reloc_section_init(Dwarf_P_Debug dbg, Dwarf_Rel_Section *drsp, 145 Dwarf_P_Section ref, Dwarf_Error *error) 146 { 147 Dwarf_Rel_Section drs; 148 char name[128]; 149 int pseudo; 150 151 assert(dbg != NULL && drsp != NULL && ref != NULL); 152 153 if ((drs = calloc(1, sizeof(struct _Dwarf_Rel_Section))) == NULL) { 154 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 155 return (DW_DLE_MEMORY); 156 } 157 158 drs->drs_ref = ref; 159 160 /* 161 * FIXME The logic here is most likely wrong. It should 162 * be the ISA that determines relocation type. 163 */ 164 if (dbg->dbgp_flags & DW_DLC_SIZE_64) 165 drs->drs_addend = 1; 166 else 167 drs->drs_addend = 0; 168 169 if (dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) 170 pseudo = 1; 171 else 172 pseudo = 0; 173 174 snprintf(name, sizeof(name), "%s%s", 175 drs->drs_addend ? ".rela" : ".rel", ref->ds_name); 176 if (_dwarf_section_init(dbg, &drs->drs_ds, name, pseudo, error) != 177 DW_DLE_NONE) { 178 free(drs); 179 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 180 return (DW_DLE_MEMORY); 181 } 182 183 STAILQ_INIT(&drs->drs_dre); 184 STAILQ_INSERT_TAIL(&dbg->dbgp_drslist, drs, drs_next); 185 dbg->dbgp_drscnt++; 186 *drsp = drs; 187 188 return (DW_DLE_NONE); 189 } 190 191 void 192 _dwarf_reloc_section_free(Dwarf_P_Debug dbg, Dwarf_Rel_Section *drsp) 193 { 194 Dwarf_Rel_Section drs, tdrs; 195 Dwarf_Rel_Entry dre, tdre; 196 197 assert(dbg != NULL && drsp != NULL); 198 199 if (*drsp == NULL) 200 return; 201 202 STAILQ_FOREACH_SAFE(drs, &dbg->dbgp_drslist, drs_next, tdrs) { 203 if (drs != *drsp) 204 continue; 205 STAILQ_REMOVE(&dbg->dbgp_drslist, drs, _Dwarf_Rel_Section, 206 drs_next); 207 STAILQ_FOREACH_SAFE(dre, &drs->drs_dre, dre_next, tdre) { 208 STAILQ_REMOVE(&drs->drs_dre, dre, _Dwarf_Rel_Entry, 209 dre_next); 210 free(dre); 211 } 212 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0) 213 _dwarf_section_free(dbg, &drs->drs_ds); 214 else { 215 if (drs->drs_ds->ds_name) 216 free(drs->drs_ds->ds_name); 217 free(drs->drs_ds); 218 } 219 free(drs); 220 *drsp = NULL; 221 dbg->dbgp_drscnt--; 222 break; 223 } 224 } 225 226 int 227 _dwarf_reloc_entry_add(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 228 Dwarf_P_Section ds, unsigned char type, unsigned char length, 229 Dwarf_Unsigned offset, Dwarf_Unsigned symndx, Dwarf_Unsigned addend, 230 const char *secname, Dwarf_Error *error) 231 { 232 Dwarf_Rel_Entry dre; 233 Dwarf_Unsigned reloff; 234 int ret; 235 236 assert(drs != NULL); 237 assert(offset <= ds->ds_size); 238 reloff = offset; 239 240 /* 241 * If the DW_DLC_SYMBOLIC_RELOCATIONS flag is set or ElfXX_Rel 242 * is used instead of ELfXX_Rela, we need to write the addend 243 * in the storage unit to be relocated. Otherwise write 0 in the 244 * storage unit and the addend will be written into relocation 245 * section later. 246 */ 247 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) || 248 drs->drs_addend == 0) 249 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, &offset, 250 addend, length, error); 251 else 252 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, &offset, 253 0, length, error); 254 if (ret != DW_DLE_NONE) 255 return (ret); 256 if (offset > ds->ds_size) 257 ds->ds_size = offset; 258 259 if ((dre = calloc(1, sizeof(struct _Dwarf_Rel_Entry))) == NULL) { 260 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 261 return (DW_DLE_MEMORY); 262 } 263 STAILQ_INSERT_TAIL(&drs->drs_dre, dre, dre_next); 264 dre->dre_type = type; 265 dre->dre_length = length; 266 dre->dre_offset = reloff; 267 dre->dre_symndx = symndx; 268 dre->dre_addend = addend; 269 dre->dre_secname = secname; 270 drs->drs_drecnt++; 271 272 return (DW_DLE_NONE); 273 } 274 275 int 276 _dwarf_reloc_entry_add_pair(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 277 Dwarf_P_Section ds, unsigned char length, Dwarf_Unsigned offset, 278 Dwarf_Unsigned symndx, Dwarf_Unsigned esymndx, Dwarf_Unsigned symoff, 279 Dwarf_Unsigned esymoff, Dwarf_Error *error) 280 { 281 Dwarf_Rel_Entry dre; 282 Dwarf_Unsigned reloff; 283 int ret; 284 285 assert(drs != NULL); 286 assert(offset <= ds->ds_size); 287 assert(dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS); 288 reloff = offset; 289 290 /* Write net offset into section stream. */ 291 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, &offset, 292 esymoff - symoff, length, error); 293 if (ret != DW_DLE_NONE) 294 return (ret); 295 if (offset > ds->ds_size) 296 ds->ds_size = offset; 297 298 if ((dre = calloc(2, sizeof(struct _Dwarf_Rel_Entry))) == NULL) { 299 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 300 return (DW_DLE_MEMORY); 301 } 302 STAILQ_INSERT_TAIL(&drs->drs_dre, &dre[0], dre_next); 303 STAILQ_INSERT_TAIL(&drs->drs_dre, &dre[1], dre_next); 304 dre[0].dre_type = dwarf_drt_first_of_length_pair; 305 dre[0].dre_length = length; 306 dre[0].dre_offset = reloff; 307 dre[0].dre_symndx = symndx; 308 dre[0].dre_addend = 0; 309 dre[0].dre_secname = NULL; 310 dre[1].dre_type = dwarf_drt_second_of_length_pair; 311 dre[1].dre_length = length; 312 dre[1].dre_offset = reloff; 313 dre[1].dre_symndx = esymndx; 314 dre[1].dre_addend = 0; 315 dre[1].dre_secname = NULL; 316 drs->drs_drecnt += 2; 317 318 return (DW_DLE_NONE); 319 } 320 321 int 322 _dwarf_reloc_section_finalize(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 323 Dwarf_Error *error) 324 { 325 Dwarf_P_Section ds; 326 Dwarf_Unsigned unit; 327 int ret, size; 328 329 assert(dbg != NULL && drs != NULL && drs->drs_ds != NULL && 330 drs->drs_ref != NULL); 331 332 ds = drs->drs_ds; 333 334 /* 335 * Calculate the size (in bytes) of the relocation section. 336 */ 337 if (dbg->dbgp_flags & DW_DLC_SIZE_64) 338 unit = drs->drs_addend ? sizeof(Elf64_Rela) : sizeof(Elf64_Rel); 339 else 340 unit = drs->drs_addend ? sizeof(Elf32_Rela) : sizeof(Elf32_Rel); 341 assert(ds->ds_size == 0); 342 size = drs->drs_drecnt * unit; 343 344 /* 345 * Discard this relocation section if there is no entry in it. 346 */ 347 if (size == 0) { 348 _dwarf_reloc_section_free(dbg, &drs); 349 return (DW_DLE_NONE); 350 } 351 352 /* 353 * If we are under stream mode, realloc the section data block to 354 * this size. 355 */ 356 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0) { 357 ds->ds_cap = size; 358 if ((ds->ds_data = realloc(ds->ds_data, (size_t) ds->ds_cap)) == 359 NULL) { 360 DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY); 361 return (DW_DLE_MEMORY); 362 } 363 } 364 365 /* 366 * Notify the application the creation of this relocation section. 367 * Note that the section link here should point to the .symtab 368 * section, we set it to 0 since we have no way to know .symtab 369 * section index. 370 */ 371 ret = _dwarf_pro_callback(dbg, ds->ds_name, size, 372 drs->drs_addend ? SHT_RELA : SHT_REL, 0, 0, drs->drs_ref->ds_ndx, 373 &ds->ds_symndx, NULL); 374 if (ret < 0) { 375 DWARF_SET_ERROR(dbg, error, DW_DLE_ELF_SECT_ERR); 376 return (DW_DLE_ELF_SECT_ERR); 377 } 378 ds->ds_ndx = ret; 379 380 return (DW_DLE_NONE); 381 } 382 383 int 384 _dwarf_reloc_section_gen(Dwarf_P_Debug dbg, Dwarf_Rel_Section drs, 385 Dwarf_Error *error) 386 { 387 Dwarf_Rel_Entry dre; 388 Dwarf_P_Section ds; 389 Dwarf_Unsigned type; 390 int ret; 391 392 assert((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0); 393 assert(drs->drs_ds != NULL && drs->drs_ds->ds_size == 0); 394 assert(!STAILQ_EMPTY(&drs->drs_dre)); 395 ds = drs->drs_ds; 396 397 STAILQ_FOREACH(dre, &drs->drs_dre, dre_next) { 398 assert(dre->dre_length == 4 || dre->dre_length == 8); 399 type = _dwarf_get_reloc_type(dbg, dre->dre_length == 8); 400 if (dbg->dbgp_flags & DW_DLC_SIZE_64) { 401 /* Write r_offset (8 bytes) */ 402 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 403 &ds->ds_size, dre->dre_offset, 8, error); 404 if (ret != DW_DLE_NONE) 405 return (ret); 406 /* Write r_info (8 bytes) */ 407 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 408 &ds->ds_size, ELF64_R_INFO(dre->dre_symndx, type), 409 8, error); 410 if (ret != DW_DLE_NONE) 411 return (ret); 412 /* Write r_addend (8 bytes) */ 413 if (drs->drs_addend) { 414 ret = dbg->write_alloc(&ds->ds_data, 415 &ds->ds_cap, &ds->ds_size, dre->dre_addend, 416 8, error); 417 if (ret != DW_DLE_NONE) 418 return (ret); 419 } 420 } else { 421 /* Write r_offset (4 bytes) */ 422 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 423 &ds->ds_size, dre->dre_offset, 4, error); 424 if (ret != DW_DLE_NONE) 425 return (ret); 426 /* Write r_info (4 bytes) */ 427 ret = dbg->write_alloc(&ds->ds_data, &ds->ds_cap, 428 &ds->ds_size, ELF32_R_INFO(dre->dre_symndx, type), 429 4, error); 430 if (ret != DW_DLE_NONE) 431 return (ret); 432 /* Write r_addend (4 bytes) */ 433 if (drs->drs_addend) { 434 ret = dbg->write_alloc(&ds->ds_data, 435 &ds->ds_cap, &ds->ds_size, dre->dre_addend, 436 4, error); 437 if (ret != DW_DLE_NONE) 438 return (ret); 439 } 440 } 441 } 442 assert(ds->ds_size == ds->ds_cap); 443 444 return (DW_DLE_NONE); 445 } 446 447 int 448 _dwarf_reloc_gen(Dwarf_P_Debug dbg, Dwarf_Error *error) 449 { 450 Dwarf_Rel_Section drs; 451 Dwarf_Rel_Entry dre; 452 Dwarf_P_Section ds; 453 int ret; 454 455 STAILQ_FOREACH(drs, &dbg->dbgp_drslist, drs_next) { 456 /* 457 * Update relocation entries: translate any section name 458 * reference to section symbol index. 459 */ 460 STAILQ_FOREACH(dre, &drs->drs_dre, dre_next) { 461 if (dre->dre_secname == NULL) 462 continue; 463 ds = _dwarf_pro_find_section(dbg, dre->dre_secname); 464 assert(ds != NULL && ds->ds_symndx != 0); 465 dre->dre_symndx = ds->ds_symndx; 466 } 467 468 /* 469 * Generate ELF relocation section if we are under stream 470 * mode. 471 */ 472 if ((dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) == 0) { 473 ret = _dwarf_reloc_section_gen(dbg, drs, error); 474 if (ret != DW_DLE_NONE) 475 return (ret); 476 } 477 } 478 479 return (DW_DLE_NONE); 480 } 481 482 void 483 _dwarf_reloc_cleanup(Dwarf_P_Debug dbg) 484 { 485 Dwarf_Rel_Section drs, tdrs; 486 Dwarf_Rel_Entry dre, tdre; 487 488 assert(dbg != NULL && dbg->dbg_mode == DW_DLC_WRITE); 489 490 STAILQ_FOREACH_SAFE(drs, &dbg->dbgp_drslist, drs_next, tdrs) { 491 STAILQ_REMOVE(&dbg->dbgp_drslist, drs, _Dwarf_Rel_Section, 492 drs_next); 493 free(drs->drs_drd); 494 STAILQ_FOREACH_SAFE(dre, &drs->drs_dre, dre_next, tdre) { 495 STAILQ_REMOVE(&drs->drs_dre, dre, _Dwarf_Rel_Entry, 496 dre_next); 497 free(dre); 498 } 499 if (dbg->dbgp_flags & DW_DLC_SYMBOLIC_RELOCATIONS) { 500 if (drs->drs_ds) { 501 if (drs->drs_ds->ds_name) 502 free(drs->drs_ds->ds_name); 503 free(drs->drs_ds); 504 } 505 } 506 free(drs); 507 } 508 dbg->dbgp_drscnt = 0; 509 dbg->dbgp_drspos = NULL; 510 } 511