xref: /illumos-gate/usr/src/lib/libdwarf/common/dwarf_str_offsets.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2     Copyright (C) 2018-2018 David Anderson. All Rights Reserved.
3 
4     This program is free software; you can redistribute it
5     and/or modify it under the terms of version 2.1 of
6     the GNU Lesser General Public License
7     as published by the Free Software Foundation.
8 
9     This program is distributed in the hope that it would be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13     Further, this software is distributed without any warranty
14     that it is free of the rightful claim of any third person
15     regarding infringement or the like.
16     Any license provided herein, whether implied or
17     otherwise, applies only to this software file.
18     Patent licenses, if any, provided herein do not
19     apply to combinations of this program with
20     other software, or any other product whatsoever.
21 
22     You should have received a copy of the GNU Lesser General Public
23     License along with this program; if not, write the Free Software
24     Foundation, Inc., 51 Franklin Street - Fifth Floor,
25     Boston MA 02110-1301,
26     USA.
27 */
28 
29 #include "config.h"
30 #include <stdio.h>
31 #include "dwarf_incl.h"
32 #include "dwarf_alloc.h"
33 #include "dwarf_error.h"
34 #include "dwarf_util.h"
35 #include "dwarfstring.h"
36 #include "dwarf_str_offsets.h"
37 
38 #define TRUE 1
39 #define FALSE 0
40 
41 #define STR_OFFSETS_MAGIC 0x2feed2
42 
43 #define VALIDATE_SOT(xsot)                                            \
44     if (!xsot) {                                                      \
45         _dwarf_error(NULL,error,DW_DLE_STR_OFFSETS_NULLARGUMENT);     \
46         return DW_DLV_ERROR;                                          \
47     }                                                                 \
48     if (!xsot->so_dbg) {                                              \
49         _dwarf_error(NULL,error,DW_DLE_STR_OFFSETS_NULL_DBG);         \
50         return DW_DLV_ERROR;                                          \
51     }                                                                 \
52     if (xsot->so_magic_value !=  STR_OFFSETS_MAGIC) {                 \
53         _dwarf_error(xsot->so_dbg,error,DW_DLE_STR_OFFSETS_NO_MAGIC); \
54         return DW_DLV_ERROR;                                          \
55     }
56 
57 #if 0
58 static void
59 dump_bytes(char * msg,Dwarf_Small * start, long len)
60 {
61     Dwarf_Small *end = start + len;
62     Dwarf_Small *cur = start;
63 
64     printf("%s ",msg);
65     for (; cur < end; cur++) {
66         printf("%02x ", *cur);
67     }
68     printf("\n");
69 }
70 #endif
71 
72 int
73 dwarf_open_str_offsets_table_access(Dwarf_Debug dbg,
74     Dwarf_Str_Offsets_Table * table_data,
75     Dwarf_Error             * error)
76 {
77     int res = 0;
78     Dwarf_Str_Offsets_Table local_table_data = 0;
79     Dwarf_Small *offsets_start_ptr = 0;
80     Dwarf_Unsigned sec_size = 0;
81 
82     if (!dbg) {
83         _dwarf_error(NULL,error,DW_DLE_STR_OFFSETS_NULL_DBG);         \
84         return DW_DLV_ERROR;                                          \
85     }
86     if (!table_data) {
87         _dwarf_error(dbg,error,DW_DLE_STR_OFFSETS_NULLARGUMENT);     \
88         return DW_DLV_ERROR;                                          \
89     }
90     /*  Considered testing for *table_data being NULL, but
91         not doing such a test. */
92 
93     res = _dwarf_load_section(dbg, &dbg->de_debug_str_offsets,error);
94     if (res != DW_DLV_OK) {
95         return res;
96     }
97     offsets_start_ptr = dbg->de_debug_str_offsets.dss_data;
98     if (!offsets_start_ptr) {
99         return DW_DLV_NO_ENTRY;
100     }
101     sec_size = dbg->de_debug_str_offsets.dss_size;
102     local_table_data = (Dwarf_Str_Offsets_Table)_dwarf_get_alloc(dbg,
103         DW_DLA_STR_OFFSETS,1);
104     if(!local_table_data) {
105         _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
106         return DW_DLV_ERROR;
107     }
108     local_table_data->so_dbg = dbg;
109     local_table_data->so_magic_value  = STR_OFFSETS_MAGIC;
110     local_table_data->so_section_start_ptr = offsets_start_ptr;
111     local_table_data->so_section_end_ptr = offsets_start_ptr +sec_size;
112     local_table_data->so_section_size = sec_size;
113     local_table_data->so_next_table_offset = 0;
114     local_table_data->so_wasted_section_bytes = 0;
115     /*  get_alloc zeroed all the bits, no need to repeat that here. */
116     *table_data = local_table_data;
117     return DW_DLV_OK;
118 }
119 
120 int
121 dwarf_close_str_offsets_table_access(
122     Dwarf_Str_Offsets_Table  table_data,
123     Dwarf_Error             * error)
124 {
125     Dwarf_Debug dbg = 0;
126 
127     VALIDATE_SOT(table_data)
128     dbg = table_data->so_dbg;
129     table_data->so_magic_value = 0xdead;
130     dwarf_dealloc(dbg,table_data, DW_DLA_STR_OFFSETS);
131     return DW_DLV_OK;
132 }
133 
134 
135 int
136 dwarf_str_offsets_value_by_index(Dwarf_Str_Offsets_Table sot,
137     Dwarf_Unsigned index,
138     Dwarf_Unsigned *stroffset,
139     Dwarf_Error *error)
140 {
141     Dwarf_Small *entryptr = 0;
142     Dwarf_Unsigned val = 0;
143 
144     VALIDATE_SOT(sot)
145     if (index >= sot->so_array_entry_count) {
146         _dwarf_error(sot->so_dbg,error, DW_DLE_STR_OFFSETS_ARRAY_INDEX_WRONG);
147         return DW_DLV_ERROR;
148     }
149     entryptr = sot->so_array_ptr + (index * sot->so_array_entry_size);
150     READ_UNALIGNED_CK(sot->so_dbg, val, Dwarf_Unsigned,
151         entryptr, sot->so_array_entry_size,error,sot->so_end_cu_ptr);
152     *stroffset = val;
153     return DW_DLV_OK;
154 }
155 
156 /*  We are assuming we can look for a str_offsets table
157     header on 32bit boundaries.  Any non-zero value
158     suggests we are at a table.  So we assume it is a table.
159     Later we'll check for validity.
160     This is just so that unused zeroed 32bit words, if any,
161     do not stop our processing. */
162 static int
163 find_next_str_offsets_tab(Dwarf_Str_Offsets_Table sot,
164     Dwarf_Unsigned starting_offset,
165     Dwarf_Unsigned *table_offset_out,
166     Dwarf_Error *error)
167 {
168     Dwarf_Small *word_ptra = 0;
169     Dwarf_Small *word_ptrb = 0;
170     Dwarf_Unsigned offset  = starting_offset;
171 
172     word_ptra = word_ptrb = sot->so_section_start_ptr +offset;
173     for ( ; ; word_ptrb += DWARF_32BIT_SIZE) {
174         Dwarf_Unsigned one32bit = 0;
175         if (word_ptrb >= sot->so_section_end_ptr) {
176             sot->so_wasted_section_bytes += (word_ptrb - word_ptra);
177             return DW_DLV_NO_ENTRY;
178         }
179         READ_UNALIGNED_CK(sot->so_dbg, one32bit, Dwarf_Unsigned,
180             word_ptrb, DWARF_32BIT_SIZE,
181             error,sot->so_section_end_ptr);
182         if (one32bit) {
183             /* Found a value */
184             break;
185         }
186         offset += DWARF_32BIT_SIZE;
187     }
188     sot->so_wasted_section_bytes += (word_ptrb - word_ptra);
189     *table_offset_out = offset;
190     return DW_DLV_OK;
191 }
192 
193 /* The minimum possible area .debug_str_offsets header . */
194 #define MIN_HEADER_LENGTH  8
195 
196 /*  New April 2018.
197     Beginning at starting_offset zero,
198     returns data about the first table found.
199     The value *next_table_offset is the value
200     of the next table (if any), one byte past
201     the end of the table whose data is returned..
202     Returns DW_DLV_NO_ENTRY if the starting offset
203     is past the end of valid data.
204 
205     There is no guarantee that there are no non-0 nonsense
206     bytes in the section outside of useful tables,
207     so this can fail and return nonsense or
208     DW_DLV_ERROR  if such garbage exists.
209 */
210 
211 static int
212 is_all_zeroes(Dwarf_Small*start,
213     Dwarf_Small*end)
214 {
215     if (start >= end) {
216         /*  We should not get here, this is just
217             a defensive test. */
218         return TRUE;
219     }
220     for( ; start < end; ++start) {
221         if (!*start) {
222             /* There is some garbage here. */
223             return FALSE;
224         }
225     }
226     /* All just zero bytes. */
227     return TRUE;
228 }
229 
230 int
231 dwarf_next_str_offsets_table(Dwarf_Str_Offsets_Table sot,
232     Dwarf_Unsigned *unit_length_out,
233     Dwarf_Unsigned *unit_length_offset_out,
234     Dwarf_Unsigned *table_start_offset_out,
235     Dwarf_Half     *entry_size_out,
236     Dwarf_Half     *version_out,
237     Dwarf_Half     *padding_out,
238     Dwarf_Unsigned *table_value_count_out,
239     Dwarf_Error    * error)
240 {
241 
242     Dwarf_Small *table_start_ptr = 0;
243     Dwarf_Small *table_end_ptr = 0;
244     Dwarf_Unsigned table_offset = 0;
245     Dwarf_Unsigned local_length_size = 0;
246     UNUSEDARG Dwarf_Unsigned local_extension_size = 0;
247     Dwarf_Unsigned length = 0;
248     Dwarf_Half version = 0;
249     Dwarf_Half padding = 0;
250     int res = 0;
251 
252     VALIDATE_SOT(sot)
253 
254     res = find_next_str_offsets_tab(sot,
255         sot->so_next_table_offset,
256         &table_offset,error);
257     if (res != DW_DLV_OK) {
258         /*  As a special case, if unused zero bytess at the end,
259             count as wasted space. */
260         if (res == DW_DLV_NO_ENTRY) {
261             if (table_offset > sot->so_next_table_offset) {
262                 sot->so_wasted_section_bytes +=
263                     (table_offset - sot->so_next_table_offset);
264             }
265         }
266         return res;
267     }
268     if (table_offset > sot->so_next_table_offset) {
269         sot->so_wasted_section_bytes +=
270             (table_offset - sot->so_next_table_offset);
271     }
272     table_start_ptr = sot->so_section_start_ptr + table_offset;
273     sot->so_header_ptr = table_start_ptr;
274     if (table_start_ptr >= sot->so_section_end_ptr) {
275         if (table_start_ptr == sot->so_section_end_ptr) {
276             /* At end of section. Done. */
277             return DW_DLV_NO_ENTRY;
278         } else {
279             /* bogus table offset. */
280             ptrdiff_t len = table_start_ptr -
281                 sot->so_section_end_ptr;
282             dwarfstring m;
283 
284             dwarfstring_constructor(&m);
285             dwarfstring_append_printf_i(&m,
286                 "DW_DLE_STR_OFFSETS_EXTRA_BYTES: "
287                 "Table Offset is %"   DW_PR_DSd
288                 " bytes past end of section",len);
289             _dwarf_error_string(sot->so_dbg,error,
290                 DW_DLE_STR_OFFSETS_EXTRA_BYTES,
291                 dwarfstring_string(&m));
292             dwarfstring_destructor(&m);
293         }
294     }
295 
296     if ((table_start_ptr + MIN_HEADER_LENGTH) >
297         sot->so_section_end_ptr) {
298 
299         /*  We have a too-short section it appears.
300             Should we generate error? Or ignore?
301             As of March 10 2020 we check for garbage
302             bytes in-section. */
303         ptrdiff_t len = 0;
304         dwarfstring m;
305 
306         if (is_all_zeroes(table_start_ptr,sot->so_section_end_ptr)){
307             return DW_DLV_NO_ENTRY;
308         }
309         len = sot->so_section_end_ptr -
310             table_start_ptr;
311         dwarfstring_constructor(&m);
312         dwarfstring_append_printf_i(&m,
313             "DW_DLE_STR_OFFSETS_EXTRA_BYTES: "
314             "Table Offset plus a minimal header is %"
315             DW_PR_DSd
316             " bytes past end of section"
317             " and some bytes in-section are non-zero",len);
318         _dwarf_error_string(sot->so_dbg,error,
319             DW_DLE_STR_OFFSETS_EXTRA_BYTES,
320             dwarfstring_string(&m));
321         dwarfstring_destructor(&m);
322         return DW_DLV_ERROR;
323     }
324     READ_AREA_LENGTH_CK(sot->so_dbg,length,Dwarf_Unsigned,
325         table_start_ptr,local_length_size,
326         local_extension_size,error,
327         sot->so_section_size,sot->so_section_end_ptr);
328 
329     /*  The 'length' part of any header is
330         local_extension_size + local_length_size.
331         Standard DWARF2 sums to 4.
332         Standard DWARF3,4,5 sums to 4 or 12.
333         Nonstandard SGI IRIX 64bit dwarf sums to 8 (SGI IRIX
334         was all DWARF2 and could not have a .debug_str_offsets
335         section).
336         The header includes 2 bytes of version and two bytes
337         of padding. */
338 
339     /*  table_start_ptr was incremented past the length data. */
340     table_end_ptr = table_start_ptr + length;
341     READ_UNALIGNED_CK(sot->so_dbg, version, Dwarf_Half,
342         table_start_ptr, DWARF_HALF_SIZE,
343         error,sot->so_section_end_ptr);
344     table_start_ptr += DWARF_HALF_SIZE;
345     READ_UNALIGNED_CK(sot->so_dbg, padding, Dwarf_Half,
346         table_start_ptr, DWARF_HALF_SIZE,
347         error,sot->so_section_end_ptr);
348     table_start_ptr += DWARF_HALF_SIZE;
349 
350     if (version != DW_STR_OFFSETS_VERSION5) {
351         _dwarf_error(sot->so_dbg,error,
352             DW_DLE_STR_OFFSETS_VERSION_WRONG);
353         return DW_DLV_ERROR;
354     }
355 
356     /*  So now table_start_ptr points to a table of local_length_size
357         entries.
358         Each entry in this table is local_length_size bytes
359         long: 4 or 8. */
360     {
361         Dwarf_Unsigned entrycount = 0;
362         Dwarf_Unsigned entrybytes = 0;
363 
364         entrybytes = table_end_ptr - table_start_ptr;
365         if (entrybytes % local_length_size) {
366             _dwarf_error(sot->so_dbg,error,
367                 DW_DLE_STR_OFFSETS_ARRAY_SIZE);
368             return DW_DLV_ERROR;
369         }
370         entrycount = entrybytes/local_length_size;
371         sot->so_next_table_offset = table_end_ptr -
372             sot->so_section_start_ptr;
373 
374         sot->so_end_cu_ptr =  table_end_ptr;
375         sot->so_array_ptr = table_start_ptr;
376         sot->so_table_start_offset = table_offset;
377         sot->so_array_start_offset = table_start_ptr -
378             sot->so_section_start_ptr;
379         sot->so_array_entry_count = entrycount;
380         sot->so_array_entry_size = local_length_size;
381         sot->so_table_count += 1;
382 
383         /*  The data length  in bytes following the unit_length field
384             of the header. */
385         *unit_length_out = length;
386 
387         /*  Where the unit_length field starts in the section. */
388         *unit_length_offset_out = sot->so_table_start_offset;
389 
390         /*  Where the table of offsets starts in the section. */
391         *table_start_offset_out = sot->so_array_start_offset;
392 
393         /*   Entrysize: 4 or 8 */
394         *entry_size_out  = local_length_size;
395 
396         /*   Version is 5. */
397         *version_out  = version;
398 
399         /*   This is supposed to be zero. */
400         *padding_out  = padding;
401 
402         /*  How many entry_size entries are in the array. */
403         *table_value_count_out = entrycount;
404         return DW_DLV_OK;
405     }
406 }
407 
408 /*  Meant to be called after all tables accessed using
409     dwarf_next_str_offsets_table(). */
410 int
411 dwarf_str_offsets_statistics(Dwarf_Str_Offsets_Table table_data,
412     Dwarf_Unsigned * wasted_byte_count,
413     Dwarf_Unsigned * table_count,
414     Dwarf_Error    * error)
415 {
416     VALIDATE_SOT(table_data)
417     if(wasted_byte_count) {
418         *wasted_byte_count = table_data->so_wasted_section_bytes;
419     }
420     if(table_count) {
421         *table_count = table_data->so_table_count;
422     }
423     return DW_DLV_OK;
424 }
425