1/*	$NetBSD: dwarf_loclist.c,v 1.5 2024/03/03 17:37:31 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2009,2014 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: dwarf_loclist.c,v 1.5 2024/03/03 17:37:31 christos Exp $");
32ELFTC_VCSID("Id: dwarf_loclist.c 3066 2014-06-06 19:36:06Z kaiwang27");
33
34static int
35copy_locdesc(Dwarf_Debug dbg, Dwarf_Locdesc *dst, Dwarf_Locdesc *src,
36    Dwarf_Error *error)
37{
38
39	assert(src != NULL && dst != NULL);
40
41	dst->ld_lopc = src->ld_lopc;
42	dst->ld_hipc = src->ld_hipc;
43	dst->ld_cents = src->ld_cents;
44
45	if (dst->ld_cents > 0) {
46		dst->ld_s = calloc(dst->ld_cents, sizeof(Dwarf_Loc));
47		if (dst->ld_s == NULL) {
48			DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
49			return (DW_DLE_MEMORY);
50		}
51		memcpy(dst->ld_s, src->ld_s, src->ld_cents *
52		    sizeof(Dwarf_Loc));
53	} else
54		dst->ld_s = NULL;
55
56	return (DW_DLE_NONE);
57}
58
59int
60dwarf_loclist_n(Dwarf_Attribute at, Dwarf_Locdesc ***llbuf,
61    Dwarf_Signed *listlen, Dwarf_Error *error)
62{
63	Dwarf_Debug dbg;
64	int ret;
65
66	dbg = at != NULL ? at->at_die->die_dbg : NULL;
67
68	if (at == NULL || llbuf == NULL || listlen == NULL) {
69		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
70		return (DW_DLV_ERROR);
71	}
72
73	switch (at->at_attrib) {
74	case DW_AT_location:
75	case DW_AT_string_length:
76	case DW_AT_return_addr:
77	case DW_AT_data_member_location:
78	case DW_AT_frame_base:
79	case DW_AT_segment:
80	case DW_AT_static_link:
81	case DW_AT_use_location:
82	case DW_AT_vtable_elem_location:
83		switch (at->at_form) {
84		case DW_FORM_data4:
85		case DW_FORM_data8:
86			/*
87			 * DW_FORM_data[48] can not be used as section offset
88			 * since DWARF4. For DWARF[23], the application needs
89			 * to determine if DW_FORM_data[48] is representing
90			 * a constant or a section offset.
91			 */
92			if (at->at_die->die_cu->cu_version >= 4) {
93				DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
94				return (DW_DLV_NO_ENTRY);
95			}
96			/* FALLTHROUGH */
97		case DW_FORM_sec_offset:
98			ret = _dwarf_loclist_find(dbg, at->at_die->die_cu,
99			    at->u[0].u64, llbuf, listlen, NULL, error);
100			if (ret == DW_DLE_NO_ENTRY) {
101				DWARF_SET_ERROR(dbg, error, ret);
102				return (DW_DLV_NO_ENTRY);
103			}
104			if (ret != DW_DLE_NONE)
105				return (DW_DLV_ERROR);
106			return (DW_DLV_OK);
107		case DW_FORM_block:
108		case DW_FORM_block1:
109		case DW_FORM_block2:
110		case DW_FORM_block4:
111		case DW_FORM_exprloc:
112			if (at->at_ld == NULL) {
113				ret = _dwarf_loc_add(at->at_die, at, error);
114				if (ret != DW_DLE_NONE)
115					return (DW_DLV_ERROR);
116			}
117			*llbuf = calloc(1, sizeof(Dwarf_Locdesc *));
118			if (*llbuf == NULL) {
119				DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
120				return (DW_DLV_ERROR);
121			}
122			(*llbuf)[0] = calloc(1, sizeof(Dwarf_Locdesc));
123			if ((*llbuf)[0] == NULL) {
124				free(*llbuf);
125				DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
126				return (DW_DLV_ERROR);
127			}
128			if (copy_locdesc(dbg, (*llbuf)[0], at->at_ld, error) !=
129			    DW_DLE_NONE) {
130				free((*llbuf)[0]);
131				free(*llbuf);
132				return (DW_DLV_ERROR);
133			}
134			*listlen = 1;
135			return (DW_DLV_OK);
136		default:
137			/* Malformed Attr? */
138			DWARF_SET_ERROR(dbg, error, DW_DLE_ATTR_FORM_BAD);
139			return (DW_DLV_NO_ENTRY);
140		}
141	default:
142		/* Wrong attr supplied. */
143		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
144		return (DW_DLV_ERROR);
145	}
146}
147
148int
149dwarf_loclist(Dwarf_Attribute at, Dwarf_Locdesc **llbuf,
150    Dwarf_Signed *listlen, Dwarf_Error *error)
151{
152	Dwarf_Locdesc **_llbuf;
153	int i, ret;
154
155	ret = dwarf_loclist_n(at, &_llbuf, listlen, error);
156	if (ret != DW_DLV_OK)
157		return (ret);
158
159	/* Only return the first location description of the list. */
160	*llbuf = _llbuf[0];
161
162	/* Free the rest of the list. */
163	for (i = 1; i < *listlen; i++) {
164		if (_llbuf[i]->ld_s)
165			free(_llbuf[i]->ld_s);
166		free(_llbuf[i]);
167	}
168	free(_llbuf);
169
170	*listlen = 1;
171
172	return (DW_DLV_OK);
173}
174
175int
176dwarf_get_loclist_entry(Dwarf_Debug dbg, Dwarf_Unsigned offset,
177    Dwarf_Addr *hipc, Dwarf_Addr *lopc, Dwarf_Ptr *data,
178    Dwarf_Unsigned *entry_len, Dwarf_Unsigned *next_entry,
179    Dwarf_Error *error)
180{
181	Dwarf_Locdesc *ld, **llbuf;
182	Dwarf_Section *ds;
183	Dwarf_Signed listlen;
184	int i, ret;
185
186	/*
187	 * Note that this API sometimes will not work correctly because
188	 * it assumes that all units have the same pointer size and offset
189	 * size.
190	 */
191
192	if (dbg == NULL || hipc == NULL || lopc == NULL || data == NULL ||
193	    entry_len == NULL || next_entry == NULL) {
194		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
195		return (DW_DLV_ERROR);
196	}
197
198	ret = _dwarf_loclist_find(dbg, STAILQ_FIRST(&dbg->dbg_cu), offset,
199	    &llbuf, &listlen, entry_len, error);
200	if (ret == DW_DLE_NO_ENTRY) {
201		DWARF_SET_ERROR(dbg, error, DW_DLV_NO_ENTRY);
202		return (DW_DLV_NO_ENTRY);
203	} else if (ret != DW_DLE_NONE)
204		return (DW_DLV_ERROR);
205
206	*hipc = *lopc = 0;
207	for (i = 0; i < listlen; i++) {
208		ld = llbuf[i];
209		if (i == 0) {
210			*hipc = ld->ld_hipc;
211			*lopc = ld->ld_lopc;
212		} else {
213			if (ld->ld_lopc < *lopc)
214				*lopc = ld->ld_lopc;
215			if (ld->ld_hipc > *hipc)
216				*hipc = ld->ld_hipc;
217		}
218	}
219
220	ds = _dwarf_find_section(dbg, ".debug_loc");
221	assert(ds != NULL);
222	*data = (uint8_t *) ds->ds_data + offset;
223	*next_entry = offset + *entry_len;
224
225	return (DW_DLV_OK);
226}
227
228int
229dwarf_loclist_from_expr(Dwarf_Debug dbg, Dwarf_Ptr bytes_in,
230    Dwarf_Unsigned bytes_len, Dwarf_Locdesc **llbuf, Dwarf_Signed *listlen,
231    Dwarf_Error *error)
232{
233
234	return (dwarf_loclist_from_expr_a(dbg, bytes_in, bytes_len,
235	    dbg->dbg_pointer_size, llbuf, listlen, error));
236}
237
238int
239dwarf_loclist_from_expr_a(Dwarf_Debug dbg, Dwarf_Ptr bytes_in,
240    Dwarf_Unsigned bytes_len, Dwarf_Half addr_size, Dwarf_Locdesc **llbuf,
241    Dwarf_Signed *listlen, Dwarf_Error *error)
242{
243	Dwarf_Half offset_size;
244	Dwarf_Small version;
245
246	/*
247	 * Obtain offset size and DWARF version from the current
248	 * Compilation Unit or Type Unit. These values are needed
249	 * for correctly parsing DW_OP_GNU_implicit_pointer operator.
250	 *
251	 * Note that dwarf_loclist_from_expr_b() should be used instead
252	 * if the application knows correct values for offset size
253	 * and DWARF version.
254	 */
255	if (dbg->dbg_cu_current) {
256		offset_size = dbg->dbg_cu_current->cu_length_size == 4 ? 4 : 8;
257		version = dbg->dbg_cu_current->cu_version;
258	} else if (dbg->dbg_tu_current) {
259		offset_size = dbg->dbg_tu_current->cu_length_size == 4 ? 4 : 8;
260		version = dbg->dbg_tu_current->cu_version;
261	} else {
262		/* Default values if no CU/TU context. */
263		offset_size = 4;
264		version = 2;	/* DWARF2 */
265	}
266
267	return (dwarf_loclist_from_expr_b(dbg, bytes_in, bytes_len, addr_size,
268	    offset_size, version, llbuf, listlen, error));
269}
270
271int
272dwarf_loclist_from_expr_b(Dwarf_Debug dbg, Dwarf_Ptr bytes_in,
273    Dwarf_Unsigned bytes_len, Dwarf_Half addr_size, Dwarf_Half offset_size,
274    Dwarf_Small version, Dwarf_Locdesc **llbuf, Dwarf_Signed *listlen,
275    Dwarf_Error *error)
276{
277	Dwarf_Locdesc *ld;
278	int ret;
279
280	if (dbg == NULL || bytes_in == NULL || bytes_len == 0 ||
281	    llbuf == NULL || listlen == NULL) {
282		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
283		return (DW_DLV_ERROR);
284	}
285
286	if (addr_size != 4 && addr_size != 8) {
287		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
288		return (DW_DLV_ERROR);
289	}
290
291	if (offset_size != 4 && offset_size != 8) {
292		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
293		return (DW_DLV_ERROR);
294	}
295
296	ret = _dwarf_loc_fill_locexpr(dbg, &ld, bytes_in, bytes_len, addr_size,
297	    offset_size, version, error);
298	if (ret != DW_DLE_NONE)
299		return (DW_DLV_ERROR);
300
301	*llbuf = ld;
302	*listlen = 1;
303
304	return (DW_DLV_OK);
305}
306