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