summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdwarf/common/dwarf_str_offsets.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libdwarf/common/dwarf_str_offsets.c')
-rw-r--r--usr/src/lib/libdwarf/common/dwarf_str_offsets.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/usr/src/lib/libdwarf/common/dwarf_str_offsets.c b/usr/src/lib/libdwarf/common/dwarf_str_offsets.c
new file mode 100644
index 0000000000..aef7d000a7
--- /dev/null
+++ b/usr/src/lib/libdwarf/common/dwarf_str_offsets.c
@@ -0,0 +1,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;
+}