Home | History | Annotate | Line # | Download | only in libdwarf
      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