1/*-
2 * Copyright (c) 2009,2011 Kai Wang
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include "_libdwarf.h"
28
29ELFTC_VCSID("$Id: libdwarf_loclist.c 3061 2014-06-02 00:42:41Z kaiwang27 $");
30
31static int
32_dwarf_loclist_add_locdesc(Dwarf_Debug dbg, Dwarf_CU cu, Dwarf_Section *ds,
33    Dwarf_Unsigned *off, Dwarf_Locdesc **ld, Dwarf_Signed *ldlen,
34    Dwarf_Unsigned *total_len, Dwarf_Error *error)
35{
36	uint64_t start, end;
37	int i, len, ret;
38
39	if (total_len != NULL)
40		*total_len = 0;
41
42	for (i = 0; *off < ds->ds_size; i++) {
43		start = dbg->read(ds->ds_data, off, cu->cu_pointer_size);
44		end = dbg->read(ds->ds_data, off, cu->cu_pointer_size);
45		if (ld != NULL) {
46			ld[i]->ld_lopc = start;
47			ld[i]->ld_hipc = end;
48		}
49
50		if (total_len != NULL)
51			*total_len += 2 * cu->cu_pointer_size;
52
53		/* Check if it is the end entry. */
54		if (start == 0 && end ==0) {
55			i++;
56			break;
57		}
58
59		/* Check if it is base-select entry. */
60		if ((cu->cu_pointer_size == 4 && start == ~0U) ||
61		    (cu->cu_pointer_size == 8 && start == ~0ULL))
62			continue;
63
64		/* Otherwise it's normal entry. */
65		len = dbg->read(ds->ds_data, off, 2);
66		if (*off + len > ds->ds_size) {
67			DWARF_SET_ERROR(dbg, error,
68			    DW_DLE_DEBUG_LOC_SECTION_SHORT);
69			return (DW_DLE_DEBUG_LOC_SECTION_SHORT);
70		}
71
72		if (total_len != NULL)
73			*total_len += len;
74
75		if (ld != NULL) {
76			ret = _dwarf_loc_fill_locdesc(dbg, ld[i],
77			    ds->ds_data + *off, len, cu->cu_pointer_size,
78			    cu->cu_length_size == 4 ? 4 : 8, cu->cu_version,
79			    error);
80			if (ret != DW_DLE_NONE)
81				return (ret);
82		}
83
84		*off += len;
85	}
86
87	if (ldlen != NULL)
88		*ldlen = i;
89
90	return (DW_DLE_NONE);
91}
92
93int
94_dwarf_loclist_find(Dwarf_Debug dbg, Dwarf_CU cu, uint64_t lloff,
95    Dwarf_Locdesc ***ret_llbuf, Dwarf_Signed *listlen,
96    Dwarf_Unsigned *entry_len, Dwarf_Error *error)
97{
98	Dwarf_Locdesc **llbuf;
99	Dwarf_Section *ds;
100	Dwarf_Signed ldlen;
101	Dwarf_Unsigned off;
102	int i, ret;
103
104	if ((ds = _dwarf_find_section(dbg, ".debug_loc")) == NULL) {
105		DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
106		return (DW_DLE_NO_ENTRY);
107	}
108
109	if (lloff >= ds->ds_size) {
110		DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
111		return (DW_DLE_NO_ENTRY);
112	}
113
114	/* Get the number of locdesc the first round. */
115	off = lloff;
116	ret = _dwarf_loclist_add_locdesc(dbg, cu, ds, &off, NULL, &ldlen,
117	    NULL, error);
118	if (ret != DW_DLE_NONE)
119		return (ret);
120
121	if (ldlen == 0)
122		return (DW_DLE_NO_ENTRY);
123
124	/*
125	 * Dwarf_Locdesc list memory is allocated in this way (one more level
126	 * of indirect) to make the loclist API be compatible with SGI libdwarf.
127	 */
128	if ((llbuf = calloc(ldlen, sizeof(Dwarf_Locdesc *))) == NULL) {
129		DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
130		return (DW_DLE_MEMORY);
131	}
132	for (i = 0; i < ldlen; i++) {
133		if ((llbuf[i] = calloc(1, sizeof(Dwarf_Locdesc))) == NULL) {
134			DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
135			ret = DW_DLE_MEMORY;
136			goto fail_cleanup;
137		}
138	}
139
140	off = lloff;
141
142	/* Fill in locdesc. */
143	ret = _dwarf_loclist_add_locdesc(dbg, cu, ds, &off, llbuf, NULL,
144	    entry_len, error);
145	if (ret != DW_DLE_NONE)
146		goto fail_cleanup;
147
148	*ret_llbuf = llbuf;
149	*listlen = ldlen;
150
151	return (DW_DLE_NONE);
152
153fail_cleanup:
154
155	if (llbuf != NULL) {
156		for (i = 0; i < ldlen; i++) {
157			if (llbuf[i]->ld_s)
158				free(llbuf[i]->ld_s);
159			free(llbuf[i]);
160		}
161		free(llbuf);
162	}
163
164	return (ret);
165}
166