summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdwarf/common/dwarf_str_offsets.c
blob: aef7d000a796c134daace18df776cf0bc38c84b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
    Copyright (C) 2018-2018 David Anderson. All Rights Reserved.

    This program is free software; you can redistribute it
    and/or modify it under the terms of version 2.1 of
    the GNU Lesser General Public License
    as published by the Free Software Foundation.

    This program is distributed in the hope that it would be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    Further, this software is distributed without any warranty
    that it is free of the rightful claim of any third person
    regarding infringement or the like.
    Any license provided herein, whether implied or
    otherwise, applies only to this software file.
    Patent licenses, if any, provided herein do not
    apply to combinations of this program with
    other software, or any other product whatsoever.

    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write the Free Software
    Foundation, Inc., 51 Franklin Street - Fifth Floor,
    Boston MA 02110-1301,
    USA.
*/

#include "config.h"
#include <stdio.h>
#include "dwarf_incl.h"
#include "dwarf_alloc.h"
#include "dwarf_error.h"
#include "dwarf_util.h"
#include "dwarfstring.h"
#include "dwarf_str_offsets.h"

#define TRUE 1
#define FALSE 0

#define STR_OFFSETS_MAGIC 0x2feed2

#define VALIDATE_SOT(xsot)                                            \
    if (!xsot) {                                                      \
        _dwarf_error(NULL,error,DW_DLE_STR_OFFSETS_NULLARGUMENT);     \
        return DW_DLV_ERROR;                                          \
    }                                                                 \
    if (!xsot->so_dbg) {                                              \
        _dwarf_error(NULL,error,DW_DLE_STR_OFFSETS_NULL_DBG);         \
        return DW_DLV_ERROR;                                          \
    }                                                                 \
    if (xsot->so_magic_value !=  STR_OFFSETS_MAGIC) {                 \
        _dwarf_error(xsot->so_dbg,error,DW_DLE_STR_OFFSETS_NO_MAGIC); \
        return DW_DLV_ERROR;                                          \
    }

#if 0
static void
dump_bytes(char * msg,Dwarf_Small * start, long len)
{
    Dwarf_Small *end = start + len;
    Dwarf_Small *cur = start;

    printf("%s ",msg);
    for (; cur < end; cur++) {
        printf("%02x ", *cur);
    }
    printf("\n");
}
#endif

int
dwarf_open_str_offsets_table_access(Dwarf_Debug dbg,
    Dwarf_Str_Offsets_Table * table_data,
    Dwarf_Error             * error)
{
    int res = 0;
    Dwarf_Str_Offsets_Table local_table_data = 0;
    Dwarf_Small *offsets_start_ptr = 0;
    Dwarf_Unsigned sec_size = 0;

    if (!dbg) {
        _dwarf_error(NULL,error,DW_DLE_STR_OFFSETS_NULL_DBG);         \
        return DW_DLV_ERROR;                                          \
    }
    if (!table_data) {
        _dwarf_error(dbg,error,DW_DLE_STR_OFFSETS_NULLARGUMENT);     \
        return DW_DLV_ERROR;                                          \
    }
    /*  Considered testing for *table_data being NULL, but
        not doing such a test. */

    res = _dwarf_load_section(dbg, &dbg->de_debug_str_offsets,error);
    if (res != DW_DLV_OK) {
        return res;
    }
    offsets_start_ptr = dbg->de_debug_str_offsets.dss_data;
    if (!offsets_start_ptr) {
        return DW_DLV_NO_ENTRY;
    }
    sec_size = dbg->de_debug_str_offsets.dss_size;
    local_table_data = (Dwarf_Str_Offsets_Table)_dwarf_get_alloc(dbg,
        DW_DLA_STR_OFFSETS,1);
    if(!local_table_data) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }
    local_table_data->so_dbg = dbg;
    local_table_data->so_magic_value  = STR_OFFSETS_MAGIC;
    local_table_data->so_section_start_ptr = offsets_start_ptr;
    local_table_data->so_section_end_ptr = offsets_start_ptr +sec_size;
    local_table_data->so_section_size = sec_size;
    local_table_data->so_next_table_offset = 0;
    local_table_data->so_wasted_section_bytes = 0;
    /*  get_alloc zeroed all the bits, no need to repeat that here. */
    *table_data = local_table_data;
    return DW_DLV_OK;
}

int
dwarf_close_str_offsets_table_access(
    Dwarf_Str_Offsets_Table  table_data,
    Dwarf_Error             * error)
{
    Dwarf_Debug dbg = 0;

    VALIDATE_SOT(table_data)
    dbg = table_data->so_dbg;
    table_data->so_magic_value = 0xdead;
    dwarf_dealloc(dbg,table_data, DW_DLA_STR_OFFSETS);
    return DW_DLV_OK;
}


int
dwarf_str_offsets_value_by_index(Dwarf_Str_Offsets_Table sot,
    Dwarf_Unsigned index,
    Dwarf_Unsigned *stroffset,
    Dwarf_Error *error)
{
    Dwarf_Small *entryptr = 0;
    Dwarf_Unsigned val = 0;

    VALIDATE_SOT(sot)
    if (index >= sot->so_array_entry_count) {
        _dwarf_error(sot->so_dbg,error, DW_DLE_STR_OFFSETS_ARRAY_INDEX_WRONG);
        return DW_DLV_ERROR;
    }
    entryptr = sot->so_array_ptr + (index * sot->so_array_entry_size);
    READ_UNALIGNED_CK(sot->so_dbg, val, Dwarf_Unsigned,
        entryptr, sot->so_array_entry_size,error,sot->so_end_cu_ptr);
    *stroffset = val;
    return DW_DLV_OK;
}

/*  We are assuming we can look for a str_offsets table
    header on 32bit boundaries.  Any non-zero value
    suggests we are at a table.  So we assume it is a table.
    Later we'll check for validity.
    This is just so that unused zeroed 32bit words, if any,
    do not stop our processing. */
static int
find_next_str_offsets_tab(Dwarf_Str_Offsets_Table sot,
    Dwarf_Unsigned starting_offset,
    Dwarf_Unsigned *table_offset_out,
    Dwarf_Error *error)
{
    Dwarf_Small *word_ptra = 0;
    Dwarf_Small *word_ptrb = 0;
    Dwarf_Unsigned offset  = starting_offset;

    word_ptra = word_ptrb = sot->so_section_start_ptr +offset;
    for ( ; ; word_ptrb += DWARF_32BIT_SIZE) {
        Dwarf_Unsigned one32bit = 0;
        if (word_ptrb >= sot->so_section_end_ptr) {
            sot->so_wasted_section_bytes += (word_ptrb - word_ptra);
            return DW_DLV_NO_ENTRY;
        }
        READ_UNALIGNED_CK(sot->so_dbg, one32bit, Dwarf_Unsigned,
            word_ptrb, DWARF_32BIT_SIZE,
            error,sot->so_section_end_ptr);
        if (one32bit) {
            /* Found a value */
            break;
        }
        offset += DWARF_32BIT_SIZE;
    }
    sot->so_wasted_section_bytes += (word_ptrb - word_ptra);
    *table_offset_out = offset;
    return DW_DLV_OK;
}

/* The minimum possible area .debug_str_offsets header . */
#define MIN_HEADER_LENGTH  8

/*  New April 2018.
    Beginning at starting_offset zero,
    returns data about the first table found.
    The value *next_table_offset is the value
    of the next table (if any), one byte past
    the end of the table whose data is returned..
    Returns DW_DLV_NO_ENTRY if the starting offset
    is past the end of valid data.

    There is no guarantee that there are no non-0 nonsense
    bytes in the section outside of useful tables,
    so this can fail and return nonsense or
    DW_DLV_ERROR  if such garbage exists.
*/

static int
is_all_zeroes(Dwarf_Small*start,
    Dwarf_Small*end)
{
    if (start >= end) {
        /*  We should not get here, this is just
            a defensive test. */
        return TRUE;
    }
    for( ; start < end; ++start) {
        if (!*start) {
            /* There is some garbage here. */
            return FALSE;
        }
    }
    /* All just zero bytes. */
    return TRUE;
}

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)
{

    Dwarf_Small *table_start_ptr = 0;
    Dwarf_Small *table_end_ptr = 0;
    Dwarf_Unsigned table_offset = 0;
    Dwarf_Unsigned local_length_size = 0;
    UNUSEDARG Dwarf_Unsigned local_extension_size = 0;
    Dwarf_Unsigned length = 0;
    Dwarf_Half version = 0;
    Dwarf_Half padding = 0;
    int res = 0;

    VALIDATE_SOT(sot)

    res = find_next_str_offsets_tab(sot,
        sot->so_next_table_offset,
        &table_offset,error);
    if (res != DW_DLV_OK) {
        /*  As a special case, if unused zero bytess at the end,
            count as wasted space. */
        if (res == DW_DLV_NO_ENTRY) {
            if (table_offset > sot->so_next_table_offset) {
                sot->so_wasted_section_bytes +=
                    (table_offset - sot->so_next_table_offset);
            }
        }
        return res;
    }
    if (table_offset > sot->so_next_table_offset) {
        sot->so_wasted_section_bytes +=
            (table_offset - sot->so_next_table_offset);
    }
    table_start_ptr = sot->so_section_start_ptr + table_offset;
    sot->so_header_ptr = table_start_ptr;
    if (table_start_ptr >= sot->so_section_end_ptr) {
        if (table_start_ptr == sot->so_section_end_ptr) {
            /* At end of section. Done. */
            return DW_DLV_NO_ENTRY;
        } else {
            /* bogus table offset. */
            ptrdiff_t len = table_start_ptr -
                sot->so_section_end_ptr;
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append_printf_i(&m,
                "DW_DLE_STR_OFFSETS_EXTRA_BYTES: "
                "Table Offset is %"   DW_PR_DSd
                " bytes past end of section",len);
            _dwarf_error_string(sot->so_dbg,error,
                DW_DLE_STR_OFFSETS_EXTRA_BYTES,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
        }
    }

    if ((table_start_ptr + MIN_HEADER_LENGTH) >
        sot->so_section_end_ptr) {

        /*  We have a too-short section it appears.
            Should we generate error? Or ignore?
            As of March 10 2020 we check for garbage
            bytes in-section. */
        ptrdiff_t len = 0;
        dwarfstring m;

        if (is_all_zeroes(table_start_ptr,sot->so_section_end_ptr)){
            return DW_DLV_NO_ENTRY;
        }
        len = sot->so_section_end_ptr -
            table_start_ptr;
        dwarfstring_constructor(&m);
        dwarfstring_append_printf_i(&m,
            "DW_DLE_STR_OFFSETS_EXTRA_BYTES: "
            "Table Offset plus a minimal header is %"
            DW_PR_DSd
            " bytes past end of section"
            " and some bytes in-section are non-zero",len);
        _dwarf_error_string(sot->so_dbg,error,
            DW_DLE_STR_OFFSETS_EXTRA_BYTES,
            dwarfstring_string(&m));
        dwarfstring_destructor(&m);
        return DW_DLV_ERROR;
    }
    READ_AREA_LENGTH_CK(sot->so_dbg,length,Dwarf_Unsigned,
        table_start_ptr,local_length_size,
        local_extension_size,error,
        sot->so_section_size,sot->so_section_end_ptr);

    /*  The 'length' part of any header is
        local_extension_size + local_length_size.
        Standard DWARF2 sums to 4.
        Standard DWARF3,4,5 sums to 4 or 12.
        Nonstandard SGI IRIX 64bit dwarf sums to 8 (SGI IRIX
        was all DWARF2 and could not have a .debug_str_offsets
        section).
        The header includes 2 bytes of version and two bytes
        of padding. */

    /*  table_start_ptr was incremented past the length data. */
    table_end_ptr = table_start_ptr + length;
    READ_UNALIGNED_CK(sot->so_dbg, version, Dwarf_Half,
        table_start_ptr, DWARF_HALF_SIZE,
        error,sot->so_section_end_ptr);
    table_start_ptr += DWARF_HALF_SIZE;
    READ_UNALIGNED_CK(sot->so_dbg, padding, Dwarf_Half,
        table_start_ptr, DWARF_HALF_SIZE,
        error,sot->so_section_end_ptr);
    table_start_ptr += DWARF_HALF_SIZE;

    if (version != DW_STR_OFFSETS_VERSION5) {
        _dwarf_error(sot->so_dbg,error,
            DW_DLE_STR_OFFSETS_VERSION_WRONG);
        return DW_DLV_ERROR;
    }

    /*  So now table_start_ptr points to a table of local_length_size
        entries.
        Each entry in this table is local_length_size bytes
        long: 4 or 8. */
    {
        Dwarf_Unsigned entrycount = 0;
        Dwarf_Unsigned entrybytes = 0;

        entrybytes = table_end_ptr - table_start_ptr;
        if (entrybytes % local_length_size) {
            _dwarf_error(sot->so_dbg,error,
                DW_DLE_STR_OFFSETS_ARRAY_SIZE);
            return DW_DLV_ERROR;
        }
        entrycount = entrybytes/local_length_size;
        sot->so_next_table_offset = table_end_ptr -
            sot->so_section_start_ptr;

        sot->so_end_cu_ptr =  table_end_ptr;
        sot->so_array_ptr = table_start_ptr;
        sot->so_table_start_offset = table_offset;
        sot->so_array_start_offset = table_start_ptr -
            sot->so_section_start_ptr;
        sot->so_array_entry_count = entrycount;
        sot->so_array_entry_size = local_length_size;
        sot->so_table_count += 1;

        /*  The data length  in bytes following the unit_length field
            of the header. */
        *unit_length_out = length;

        /*  Where the unit_length field starts in the section. */
        *unit_length_offset_out = sot->so_table_start_offset;

        /*  Where the table of offsets starts in the section. */
        *table_start_offset_out = sot->so_array_start_offset;

        /*   Entrysize: 4 or 8 */
        *entry_size_out  = local_length_size;

        /*   Version is 5. */
        *version_out  = version;

        /*   This is supposed to be zero. */
        *padding_out  = padding;

        /*  How many entry_size entries are in the array. */
        *table_value_count_out = entrycount;
        return DW_DLV_OK;
    }
}

/*  Meant to be called after all tables accessed using
    dwarf_next_str_offsets_table(). */
int
dwarf_str_offsets_statistics(Dwarf_Str_Offsets_Table table_data,
    Dwarf_Unsigned * wasted_byte_count,
    Dwarf_Unsigned * table_count,
    Dwarf_Error    * error)
{
    VALIDATE_SOT(table_data)
    if(wasted_byte_count) {
        *wasted_byte_count = table_data->so_wasted_section_bytes;
    }
    if(table_count) {
        *table_count = table_data->so_table_count;
    }
    return DW_DLV_OK;
}