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
dwarf_open_str_offsets_table_access(Dwarf_Debug dbg,Dwarf_Str_Offsets_Table * table_data,Dwarf_Error * error)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
dwarf_close_str_offsets_table_access(Dwarf_Str_Offsets_Table table_data,Dwarf_Error * error)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
dwarf_str_offsets_value_by_index(Dwarf_Str_Offsets_Table sot,Dwarf_Unsigned index,Dwarf_Unsigned * stroffset,Dwarf_Error * error)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
find_next_str_offsets_tab(Dwarf_Str_Offsets_Table sot,Dwarf_Unsigned starting_offset,Dwarf_Unsigned * table_offset_out,Dwarf_Error * error)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
is_all_zeroes(Dwarf_Small * start,Dwarf_Small * end)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
dwarf_next_str_offsets_table(Dwarf_Str_Offsets_Table sot,Dwarf_Unsigned * unit_length_out,Dwarf_Unsigned * unit_length_offset_out,Dwarf_Unsigned * table_start_offset_out,Dwarf_Half * entry_size_out,Dwarf_Half * version_out,Dwarf_Half * padding_out,Dwarf_Unsigned * table_value_count_out,Dwarf_Error * error)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
dwarf_str_offsets_statistics(Dwarf_Str_Offsets_Table table_data,Dwarf_Unsigned * wasted_byte_count,Dwarf_Unsigned * table_count,Dwarf_Error * error)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