1/*	$NetBSD: elf_data.c,v 1.5 2024/03/03 17:37:33 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2006,2008,2011 Joseph Koshy
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#if HAVE_NBTOOL_CONFIG_H
30# include "nbtool_config.h"
31#endif
32
33#include <sys/cdefs.h>
34
35#include <assert.h>
36#include <errno.h>
37#include <libelf.h>
38#include <stdint.h>
39#include <stdlib.h>
40
41#include "_libelf.h"
42
43__RCSID("$NetBSD: elf_data.c,v 1.5 2024/03/03 17:37:33 christos Exp $");
44ELFTC_VCSID("Id: elf_data.c 3977 2022-05-01 06:45:34Z jkoshy");
45
46Elf_Data *
47elf_getdata(Elf_Scn *s, Elf_Data *ed)
48{
49	Elf *e;
50	unsigned int sh_type;
51	int elfclass, elftype;
52	size_t count, fsz, msz;
53	struct _Libelf_Data *d;
54	uint64_t sh_align, sh_offset, sh_size, raw_size;
55	_libelf_translator_function *xlate;
56
57	d = (struct _Libelf_Data *) ed;
58
59	if (s == NULL || (e = s->s_elf) == NULL ||
60	    (d != NULL && s != d->d_scn)) {
61		LIBELF_SET_ERROR(ARGUMENT, 0);
62		return (NULL);
63	}
64
65	assert(e->e_kind == ELF_K_ELF);
66
67	if (d == NULL && (d = STAILQ_FIRST(&s->s_data)) != NULL)
68		return (&d->d_data);
69
70	if (d != NULL)
71		return (STAILQ_NEXT(d, d_next) ?
72		    &STAILQ_NEXT(d, d_next)->d_data : NULL);
73
74	if (e->e_rawfile == NULL) {
75		/*
76		 * In the ELF_C_WRITE case, there is no source that
77		 * can provide data for the section.
78		 */
79		LIBELF_SET_ERROR(ARGUMENT, 0);
80		return (NULL);
81	}
82
83	elfclass = e->e_class;
84
85	assert(elfclass == ELFCLASS32 || elfclass == ELFCLASS64);
86
87	if (elfclass == ELFCLASS32) {
88		sh_type   = s->s_shdr.s_shdr32.sh_type;
89		sh_offset = (uint64_t) s->s_shdr.s_shdr32.sh_offset;
90		sh_size   = (uint64_t) s->s_shdr.s_shdr32.sh_size;
91		sh_align  = (uint64_t) s->s_shdr.s_shdr32.sh_addralign;
92	} else {
93		sh_type   = s->s_shdr.s_shdr64.sh_type;
94		sh_offset = s->s_shdr.s_shdr64.sh_offset;
95		sh_size   = s->s_shdr.s_shdr64.sh_size;
96		sh_align  = s->s_shdr.s_shdr64.sh_addralign;
97	}
98
99	if (sh_type == SHT_NULL) {
100		LIBELF_SET_ERROR(SECTION, 0);
101		return (NULL);
102	}
103
104	raw_size = (uint64_t) e->e_rawsize;
105	if ((elftype = _libelf_xlate_shtype(sh_type)) < ELF_T_FIRST ||
106	    elftype > ELF_T_LAST || (sh_type != SHT_NOBITS &&
107	    (sh_offset > raw_size || sh_size > raw_size - sh_offset))) {
108		LIBELF_SET_ERROR(SECTION, 0);
109		return (NULL);
110	}
111
112	if ((fsz = (elfclass == ELFCLASS32 ? elf32_fsize : elf64_fsize)
113            (elftype, (size_t) 1, e->e_version)) == 0) {
114		LIBELF_SET_ERROR(UNIMPL, 0);
115		return (NULL);
116	}
117
118	if (sh_size % fsz) {
119		LIBELF_SET_ERROR(SECTION, 0);
120		return (NULL);
121	}
122
123	if (sh_size / fsz > SIZE_MAX) {
124		LIBELF_SET_ERROR(RANGE, 0);
125		return (NULL);
126	}
127
128	count = (size_t) (sh_size / fsz);
129
130	if ((msz = _libelf_msize(elftype, elfclass, e->e_version)) == 0)
131		return (NULL);
132
133	if (count > 0 && msz > SIZE_MAX / count) {
134		LIBELF_SET_ERROR(RANGE, 0);
135		return (NULL);
136	}
137
138	assert(msz > 0);
139	assert(count <= SIZE_MAX);
140	assert(msz * count <= SIZE_MAX);
141
142	if ((d = _libelf_allocate_data(s)) == NULL)
143		return (NULL);
144
145	d->d_data.d_buf     = NULL;
146	d->d_data.d_off     = 0;
147	d->d_data.d_align   = sh_align;
148	d->d_data.d_size    = msz * count;
149	d->d_data.d_type    = elftype;
150	d->d_data.d_version = e->e_version;
151
152	if (sh_type == SHT_NOBITS || sh_size == 0) {
153	        STAILQ_INSERT_TAIL(&s->s_data, d, d_next);
154		return (&d->d_data);
155        }
156
157	if ((d->d_data.d_buf = malloc(msz * count)) == NULL) {
158		(void) _libelf_release_data(d);
159		LIBELF_SET_ERROR(RESOURCE, 0);
160		return (NULL);
161	}
162
163	d->d_flags  |= LIBELF_F_DATA_MALLOCED;
164
165	xlate = _libelf_get_translator(elftype, ELF_TOMEMORY, elfclass,
166	    _libelf_elfmachine(e));
167	if (!(*xlate)(d->d_data.d_buf, (size_t) d->d_data.d_size,
168	    e->e_rawfile + sh_offset, count,
169	    e->e_byteorder != LIBELF_PRIVATE(byteorder))) {
170		_libelf_release_data(d);
171		LIBELF_SET_ERROR(DATA, 0);
172		return (NULL);
173	}
174
175	STAILQ_INSERT_TAIL(&s->s_data, d, d_next);
176
177	return (&d->d_data);
178}
179
180Elf_Data *
181elf_newdata(Elf_Scn *s)
182{
183	Elf *e;
184	struct _Libelf_Data *d;
185
186	if (s == NULL || (e = s->s_elf) == NULL) {
187		LIBELF_SET_ERROR(ARGUMENT, 0);
188		return (NULL);
189	}
190
191	assert(e->e_kind == ELF_K_ELF);
192
193	/*
194	 * elf_newdata() has to append a data descriptor, so
195	 * bring in existing section data if not already present.
196	 */
197	if (e->e_rawfile && s->s_size > 0 && STAILQ_EMPTY(&s->s_data))
198		if (elf_getdata(s, NULL) == NULL)
199			return (NULL);
200
201	if ((d = _libelf_allocate_data(s)) == NULL)
202		return (NULL);
203
204	STAILQ_INSERT_TAIL(&s->s_data, d, d_next);
205
206	d->d_data.d_align = 1;
207	d->d_data.d_buf = NULL;
208	d->d_data.d_off = (uint64_t) ~0;
209	d->d_data.d_size = 0;
210	d->d_data.d_type = ELF_T_BYTE;
211	d->d_data.d_version = LIBELF_PRIVATE(version);
212
213	(void) elf_flagscn(s, ELF_C_SET, ELF_F_DIRTY);
214
215	return (&d->d_data);
216}
217
218/*
219 * Retrieve a data descriptor for raw (untranslated) data for section
220 * `s'.
221 */
222
223Elf_Data *
224elf_rawdata(Elf_Scn *s, Elf_Data *ed)
225{
226	Elf *e;
227	int elf_class;
228	uint32_t sh_type;
229	struct _Libelf_Data *d;
230	uint64_t sh_align, sh_offset, sh_size, raw_size;
231
232	if (s == NULL || (e = s->s_elf) == NULL || e->e_rawfile == NULL) {
233		LIBELF_SET_ERROR(ARGUMENT, 0);
234		return (NULL);
235	}
236
237	assert(e->e_kind == ELF_K_ELF);
238
239	d = (struct _Libelf_Data *) ed;
240
241	if (d == NULL && (d = STAILQ_FIRST(&s->s_rawdata)) != NULL)
242		return (&d->d_data);
243
244	if (d != NULL)
245		return (&STAILQ_NEXT(d, d_next)->d_data);
246
247	elf_class = e->e_class;
248
249	assert(elf_class == ELFCLASS32 || elf_class == ELFCLASS64);
250
251	if (elf_class == ELFCLASS32) {
252		sh_type   = s->s_shdr.s_shdr32.sh_type;
253		sh_offset = (uint64_t) s->s_shdr.s_shdr32.sh_offset;
254		sh_size   = (uint64_t) s->s_shdr.s_shdr32.sh_size;
255		sh_align  = (uint64_t) s->s_shdr.s_shdr32.sh_addralign;
256	} else {
257		sh_type   = s->s_shdr.s_shdr64.sh_type;
258		sh_offset = s->s_shdr.s_shdr64.sh_offset;
259		sh_size   = s->s_shdr.s_shdr64.sh_size;
260		sh_align  = s->s_shdr.s_shdr64.sh_addralign;
261	}
262
263	if (sh_type == SHT_NULL) {
264		LIBELF_SET_ERROR(SECTION, 0);
265		return (NULL);
266	}
267
268	raw_size = (uint64_t) e->e_rawsize;
269	if (sh_type != SHT_NOBITS &&
270	    (sh_offset > raw_size || sh_size > raw_size - sh_offset)) {
271		LIBELF_SET_ERROR(SECTION, 0);
272		return (NULL);
273	}
274
275	if ((d = _libelf_allocate_data(s)) == NULL)
276		return (NULL);
277
278	d->d_data.d_buf = (sh_type == SHT_NOBITS || sh_size == 0) ? NULL :
279	    e->e_rawfile + sh_offset;
280	d->d_data.d_off     = 0;
281	d->d_data.d_align   = sh_align;
282	d->d_data.d_size    = sh_size;
283	d->d_data.d_type    = ELF_T_BYTE;
284	d->d_data.d_version = e->e_version;
285
286	STAILQ_INSERT_TAIL(&s->s_rawdata, d, d_next);
287
288	return (&d->d_data);
289}
290