xref: /illumos-gate/usr/src/lib/libdwarf/common/dwarf_ranges.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
1 /*
2   Copyright (C) 2008-2020 David Anderson. All Rights Reserved.
3   Portions Copyright 2012 SN Systems Ltd. All rights reserved.
4 
5   This program is free software; you can redistribute it
6   and/or modify it under the terms of version 2.1 of the
7   GNU Lesser General Public License as published by the Free
8   Software Foundation.
9 
10   This program is distributed in the hope that it would be
11   useful, but WITHOUT ANY WARRANTY; without even the implied
12   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13   PURPOSE.
14 
15   Further, this software is distributed without any warranty
16   that it is free of the rightful claim of any third person
17   regarding infringement or the like.  Any license provided
18   herein, whether implied or otherwise, applies only to this
19   software file.  Patent licenses, if any, provided herein
20   do not apply to combinations of this program with other
21   software, or any other product whatsoever.
22 
23   You should have received a copy of the GNU Lesser General
24   Public License along with this program; if not, write the
25   Free Software Foundation, Inc., 51 Franklin Street - Fifth
26   Floor, Boston MA 02110-1301, USA.
27 
28 */
29 
30 #include "config.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include "dwarf_incl.h"
34 #include "dwarf_alloc.h"
35 #include "dwarf_error.h"
36 #include "dwarf_util.h"
37 #include "dwarfstring.h"
38 
39 struct ranges_entry {
40    struct ranges_entry *next;
41    Dwarf_Ranges cur;
42 };
43 
44 
45 static void
46 free_allocated_ranges( struct ranges_entry *base)
47 {
48     struct ranges_entry *cur = 0;
49     struct ranges_entry *next = 0;
50     for ( cur = base ; cur ; cur = next ) {
51         next = cur->next;
52         free(cur);
53     }
54 }
55 
56 /*  We encapsulate the macro use so we can
57     free local malloc resources that would otherwise
58     leak. See the call points below. */
59 static int
60 read_unaligned_addr_check(Dwarf_Debug dbg,
61     Dwarf_Addr *addr_out,
62     Dwarf_Small *rangeptr,
63     unsigned address_size,
64     Dwarf_Error *error,
65     Dwarf_Small *section_end)
66 {
67     Dwarf_Addr a = 0;
68 
69     READ_UNALIGNED_CK(dbg,a,
70         Dwarf_Addr, rangeptr,
71         address_size,
72         error,section_end);
73     *addr_out = a;
74     return DW_DLV_OK;
75 }
76 /*  As of DWARF5 the ranges section each range list set has
77     a range-list-table header. See "7.28 Range List Table"
78     in the DWARF5 standard.
79     For DWARF5 the offset should be the offset of
80     the range-list-table-header for that range list.
81     For DWARF3 and DWARF4 the offset has to be that
82     of a range list.
83 */
84 /*  Ranges and pc values can be in a split dwarf object.
85     In that case the appropriate values need to be
86     incremented by data from the executable in
87     the compilation unit with the same dwo_id.
88 
89     We return an error which is on the incoming dbg, not
90     the possibly-tied-dbg localdbg.
91     If incoming die is NULL there is no context, so do not look
92     for a tied file, and address_size is the size
93     of the overall object, not the address_size of the context. */
94 #define MAX_ADDR ((address_size == 8)?0xffffffffffffffffULL:0xffffffff)
95 int dwarf_get_ranges_a(Dwarf_Debug dbg,
96     Dwarf_Off rangesoffset,
97     Dwarf_Die die,
98     Dwarf_Ranges ** rangesbuf,
99     Dwarf_Signed * listlen,
100     Dwarf_Unsigned * bytecount,
101     Dwarf_Error * error)
102 {
103     Dwarf_Small *rangeptr = 0;
104     Dwarf_Small *beginrangeptr = 0;
105     Dwarf_Small *section_end = 0;
106     unsigned entry_count = 0;
107     struct ranges_entry *base = 0;
108     struct ranges_entry *last = 0;
109     struct ranges_entry *curre = 0;
110     Dwarf_Ranges * ranges_data_out = 0;
111     unsigned copyindex = 0;
112     Dwarf_Half address_size = 0;
113     int res = DW_DLV_ERROR;
114     Dwarf_Unsigned ranges_base = 0;
115     Dwarf_Unsigned addr_base = 0;
116     Dwarf_Debug localdbg = dbg;
117     Dwarf_Error localerror = 0;
118     Dwarf_Half die_version = 3; /* default for dwarf_get_ranges() */
119     UNUSEDARG Dwarf_Half offset_size = 4;
120     Dwarf_CU_Context cucontext = 0;
121 
122     if (!dbg) {
123         _dwarf_error(NULL, error, DW_DLE_DBG_NULL);
124         return DW_DLV_ERROR;
125     }
126     address_size = localdbg->de_pointer_size; /* default  */
127     if (die) {
128         /*  If we wind up using the tied file the die_version
129             had better match! It cannot be other than a match. */
130         /*  Can return DW_DLV_ERROR, not DW_DLV_NO_ENTRY.
131             Add err code if error.  Version comes from the
132             cu context, not the DIE itself. */
133         res = dwarf_get_version_of_die(die,&die_version,
134             &offset_size);
135         if (res == DW_DLV_ERROR) {
136             _dwarf_error(dbg, error, DW_DLE_DIE_NO_CU_CONTEXT);
137             return DW_DLV_ERROR;
138         }
139         if (!die->di_cu_context) {
140             _dwarf_error(dbg, error, DW_DLE_DIE_NO_CU_CONTEXT);
141             return DW_DLV_ERROR;
142         }
143         cucontext = die->di_cu_context;
144         /*  The DW4 ranges base was never used in GNU
145             but did get emitted.
146             http://llvm.1065342.n5.nabble.com/DebugInfo-DW-AT-GNU-ranges-base-in-non-fission-td64194.html
147             */
148 #if 0
149         if (cucontext->cc_unit_type != DW_UT_skeleton &&
150             cucontext->cc_version_stamp != DW_CU_VERSION4 &&
151             cucontext->cc_ranges_base_present) {
152             /* Never needed. See DW_AT_GNU_ranges_base
153             in find_cu_die_base_fields() in dwarf_die_deliv.c */
154             ranges_base = cucontext->cc_ranges_base;
155         }
156 #endif
157         address_size = cucontext->cc_address_size;
158     }
159 
160     /*  If tied object exists,
161         base object is DWP and tied object is the
162         skeleton/executabl CU containing a skeleton CU
163         with DW_AT_addr_base/DW_AT_GNU_addr_base and
164         DW_AT_rnglists_base/DW_AT_GNU_ranges base. */
165     if (die &&dbg->de_tied_data.td_tied_object) {
166 
167         int restied = 0;
168 
169         /*  dwo/dwp CU  may have (DW5 pg 63)
170             DW_AT_name
171             DW_AT_language
172             DW_AT_macros
173             DW_AT_producer
174             DW_AT_identifier_case
175             DW_AT_main_subprogram
176             DW_AT_entry_pc
177             DW_AT_ranges if DW5 offset of header (see
178                 ranges_base/rnglists_base below),
179                 else of ranges
180             DW_AT_GNU_pubnames
181             DW_AT_stmt_list, a trivial stmt list with
182                 the list of directories and file names
183                 where needed for Type Units (DW5 pg 64)
184             and must have
185             DW_AT_[GNU_]dwo_id
186             and inherited from skeleton:
187             DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges,
188             DW_AT_stmt_list, DW_AT_comp_dir,
189             DW_AT_use_UTF8, DW_AT_str_offsets_base,
190             DW_AT_addr_base and DW_AT_rnglists_base.
191 
192             skeleton CU may have (DW5 pg 62)
193             DW_AT_[GNU_]addr_base   possibly needed by dwp
194             DW_AT_str_offsets_base  possibly needed by dwp
195             DW_AT_GNU_ranges_base (GNU)possibly needed by dwp
196             DW_AT_rnglists_base   (DWARF5)possibly needed by dwp
197             DW_AT_low_pc
198             DW_AT_high_pc
199             DW_AT_ranges
200             DW_AT_str_offsets_base
201             DW_AT_[GNU_]dwo_name
202             DW_AT_stmt_list
203             DW_AT_comp_dir
204             DW_AT_use_UTF8
205             and must have
206             DW_AT_[GNU_]dwo_id
207             .debug_ranges does not
208             exist in the DWP, it is only in the executable.
209         */
210         restied = _dwarf_get_ranges_base_attr_from_tied(dbg,
211             cucontext,
212             &ranges_base,
213             &addr_base,
214             error);
215         if (restied == DW_DLV_ERROR ) {
216             if(!error) {
217                 return restied;
218             }
219             dwarf_dealloc(localdbg,*error,DW_DLA_ERROR);
220             *error = 0;
221             /* Nothing else to do. Look in original dbg. */
222         } else if (restied == DW_DLV_NO_ENTRY ) {
223             /* Nothing else to do. Look in original dbg. */
224         } else {
225             /*  Ranges are never in a split dwarf object.
226                 In the base object
227                 instead. Use the tied_object */
228             localdbg = dbg->de_tied_data.td_tied_object;
229         }
230     }
231 #if 0
232 FIXME Implement debug_rnglists!
233     /*  de_debug_ranges is DW 3,4.
234         de_debug_rnglists is DW5. */
235     if (die_version >= DW_CU_VERSION5) {
236         res = _dwarf_load_section(localdbg,
237             &localdbg->de_debug_rngslists,&localerror);
238     } else {
239         res = _dwarf_load_section(localdbg,
240             &localdbg->de_debug_ranges,&localerror);
241     }
242 #endif
243     res = _dwarf_load_section(localdbg, &localdbg->de_debug_ranges,&localerror);
244     if (res == DW_DLV_ERROR) {
245         _dwarf_error_mv_s_to_t(localdbg,&localerror,dbg,error);
246         return res;
247     } else if (res == DW_DLV_NO_ENTRY) {
248         return res;
249     }
250 
251     /*  Be safe in case adding rangesoffset and rangebase
252         overflows. */
253     if (rangesoffset  == localdbg->de_debug_ranges.dss_size) {
254         return DW_DLV_NO_ENTRY;
255     }
256     if (rangesoffset  > localdbg->de_debug_ranges.dss_size) {
257         dwarfstring m;
258 
259         dwarfstring_constructor(&m);
260         dwarfstring_append_printf_u(&m,
261             "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
262             " rangesoffset is 0x%lx ",rangesoffset);
263         dwarfstring_append_printf_u(&m,
264             " and section size is 0x%lx.",
265             localdbg->de_debug_ranges.dss_size);
266         _dwarf_error_string(dbg, error,
267             DW_DLE_DEBUG_RANGES_OFFSET_BAD,
268             dwarfstring_string(&m));
269         dwarfstring_destructor(&m);
270         return DW_DLV_ERROR;
271     }
272     if (ranges_base >= localdbg->de_debug_ranges.dss_size) {
273         dwarfstring m;
274 
275         dwarfstring_constructor(&m);
276         dwarfstring_append_printf_u(&m,
277             "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
278             " ranges base is 0x%lx ",ranges_base);
279         dwarfstring_append_printf_u(&m,
280             " and section size is 0x%lx.",
281             localdbg->de_debug_ranges.dss_size);
282         _dwarf_error_string(dbg, error,
283             DW_DLE_DEBUG_RANGES_OFFSET_BAD,
284             dwarfstring_string(&m));
285         dwarfstring_destructor(&m);
286         return DW_DLV_ERROR;
287     }
288     if ((rangesoffset +ranges_base) >=
289         localdbg->de_debug_ranges.dss_size) {
290         dwarfstring m;
291 
292         dwarfstring_constructor(&m);
293         dwarfstring_append_printf_u(&m,
294             "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
295             " ranges base+offset  is 0x%lx ",
296             ranges_base+rangesoffset);
297         dwarfstring_append_printf_u(&m,
298             " and section size is 0x%lx.",
299             localdbg->de_debug_ranges.dss_size);
300         _dwarf_error_string(dbg, error,
301             DW_DLE_DEBUG_RANGES_OFFSET_BAD,
302             dwarfstring_string(&m));
303         dwarfstring_destructor(&m);
304         return DW_DLV_ERROR;
305     }
306 
307     /*  tied address_size must match the dwo address_size */
308     section_end = localdbg->de_debug_ranges.dss_data +
309         localdbg->de_debug_ranges.dss_size;
310     rangeptr = localdbg->de_debug_ranges.dss_data +
311         rangesoffset + ranges_base;
312     beginrangeptr = rangeptr;
313 
314     for (;;) {
315         struct ranges_entry * re = 0;
316 
317         if (rangeptr == section_end) {
318             break;
319         }
320         if (rangeptr  > section_end) {
321             dwarfstring m;
322 
323             free_allocated_ranges(base);
324             dwarfstring_constructor(&m);
325             dwarfstring_append(&m,
326                 "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
327                 " ranges pointer ran off the end "
328                 "of the  section");
329             _dwarf_error_string(dbg, error,
330                 DW_DLE_DEBUG_RANGES_OFFSET_BAD,
331                 dwarfstring_string(&m));
332             dwarfstring_destructor(&m);
333             return DW_DLV_ERROR;
334         }
335         re = calloc(sizeof(struct ranges_entry),1);
336         if (!re) {
337             free_allocated_ranges(base);
338             _dwarf_error(dbg, error, DW_DLE_DEBUG_RANGES_OUT_OF_MEM);
339             return DW_DLV_ERROR;
340         }
341         if ((rangeptr + (2*address_size)) > section_end) {
342             free(re);
343             free_allocated_ranges(base);
344             _dwarf_error_string(dbg, error,
345                 DW_DLE_DEBUG_RANGES_OFFSET_BAD,
346                 "DW_DLE_DEBUG_RANGES_OFFSET_BAD: "
347                 " Not at the end of the ranges section "
348                 " but there is not enough room in the section "
349                 " for the next ranges entry");
350             return  DW_DLV_ERROR;
351         }
352         entry_count++;
353         res = read_unaligned_addr_check(localdbg,&re->cur.dwr_addr1,
354             rangeptr, address_size,error,section_end);
355         if (res != DW_DLV_OK) {
356             free(re);
357             free_allocated_ranges(base);
358             return res;
359         }
360         rangeptr +=  address_size;
361         res = read_unaligned_addr_check(localdbg,&re->cur.dwr_addr2,
362             rangeptr, address_size,error,section_end);
363         if (res != DW_DLV_OK) {
364             free(re);
365             free_allocated_ranges(base);
366             return res;
367         }
368         rangeptr +=  address_size;
369         if (!base) {
370             base = re;
371             last = re;
372         } else {
373             last->next = re;
374             last = re;
375         }
376         if (re->cur.dwr_addr1 == 0 && re->cur.dwr_addr2 == 0) {
377             re->cur.dwr_type =  DW_RANGES_END;
378             break;
379         } else if (re->cur.dwr_addr1 == MAX_ADDR) {
380             re->cur.dwr_type =  DW_RANGES_ADDRESS_SELECTION;
381         } else {
382             re->cur.dwr_type =  DW_RANGES_ENTRY;
383         }
384     }
385 
386     /* We return ranges on dbg, so use that to allocate. */
387     ranges_data_out =   (Dwarf_Ranges *)
388         _dwarf_get_alloc(dbg,DW_DLA_RANGES,entry_count);
389     if (!ranges_data_out) {
390         /* Error, apply to original, not local dbg. */
391         free_allocated_ranges(base);
392         _dwarf_error(dbg, error, DW_DLE_DEBUG_RANGES_OUT_OF_MEM);
393         return (DW_DLV_ERROR);
394     }
395     curre = base;
396     *rangesbuf = ranges_data_out;
397     *listlen = entry_count;
398     for (copyindex = 0; curre && (copyindex < entry_count);
399         ++copyindex,++ranges_data_out) {
400 
401         *ranges_data_out = curre->cur;
402         curre = curre->next;
403     }
404     /* ASSERT: curre == NULL */
405     free_allocated_ranges(base);
406     base = 0;
407     /* Callers will often not care about the bytes used. */
408     if (bytecount) {
409         *bytecount = rangeptr - beginrangeptr;
410     }
411     return DW_DLV_OK;
412 }
413 
414 int dwarf_get_ranges(Dwarf_Debug dbg,
415     Dwarf_Off rangesoffset,
416     Dwarf_Ranges ** rangesbuf,
417     Dwarf_Signed * listlen,
418     Dwarf_Unsigned * bytecount,
419     Dwarf_Error * error)
420 {
421     Dwarf_Die die = 0;
422     int res = dwarf_get_ranges_a(dbg,rangesoffset,die,
423         rangesbuf,listlen,bytecount,error);
424     return res;
425 }
426 
427 void
428 dwarf_ranges_dealloc(Dwarf_Debug dbg, Dwarf_Ranges * rangesbuf,
429     UNUSEDARG Dwarf_Signed rangecount)
430 {
431     dwarf_dealloc(dbg,rangesbuf, DW_DLA_RANGES);
432 }
433