diff --git a/libbacktrace/alloc.cpp b/libbacktrace/alloc.cpp index 79909cf9..a365a486 100644 --- a/libbacktrace/alloc.cpp +++ b/libbacktrace/alloc.cpp @@ -1,5 +1,5 @@ /* alloc.c -- Memory allocation without mmap. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/libbacktrace/backtrace.hpp b/libbacktrace/backtrace.hpp index f0c66a8f..d999803c 100644 --- a/libbacktrace/backtrace.hpp +++ b/libbacktrace/backtrace.hpp @@ -1,5 +1,5 @@ /* backtrace.h -- Public header file for stack backtrace library. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/libbacktrace/dwarf.cpp b/libbacktrace/dwarf.cpp index 4543c1fe..f76e03cf 100644 --- a/libbacktrace/dwarf.cpp +++ b/libbacktrace/dwarf.cpp @@ -1,5 +1,5 @@ /* dwarf.c -- Get file/line information from DWARF for backtraces. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -1389,7 +1389,7 @@ resolve_string (const struct dwarf_sections *dwarf_sections, int is_dwarf64, offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base; if (offset + (is_dwarf64 ? 8 : 4) - >= dwarf_sections->size[DEBUG_STR_OFFSETS]) + > dwarf_sections->size[DEBUG_STR_OFFSETS]) { error_callback (data, "DW_FORM_strx value out of range", 0); return 0; @@ -1433,7 +1433,7 @@ resolve_addr_index (const struct dwarf_sections *dwarf_sections, struct dwarf_buf addr_buf; offset = addr_index * addrsize + addr_base; - if (offset + addrsize >= dwarf_sections->size[DEBUG_ADDR]) + if (offset + addrsize > dwarf_sections->size[DEBUG_ADDR]) { error_callback (data, "DW_FORM_addrx value out of range", 0); return 0; @@ -1500,9 +1500,11 @@ function_addrs_compare (const void *v1, const void *v2) return strcmp (a1->function->name, a2->function->name); } -/* Compare a PC against a function_addrs for bsearch. Note that if - there are multiple ranges containing PC, which one will be returned - is unpredictable. We compensate for that in dwarf_fileline. */ +/* Compare a PC against a function_addrs for bsearch. We always + allocate an entra entry at the end of the vector, so that this + routine can safely look at the next entry. Note that if there are + multiple ranges containing PC, which one will be returned is + unpredictable. We compensate for that in dwarf_fileline. */ static int function_addrs_search (const void *vkey, const void *ventry) @@ -1514,7 +1516,7 @@ function_addrs_search (const void *vkey, const void *ventry) pc = *key; if (pc < entry->low) return -1; - else if (pc >= entry->high) + else if (pc > (entry + 1)->low) return 1; else return 0; @@ -1585,9 +1587,11 @@ unit_addrs_compare (const void *v1, const void *v2) return 0; } -/* Compare a PC against a unit_addrs for bsearch. Note that if there - are multiple ranges containing PC, which one will be returned is - unpredictable. We compensate for that in dwarf_fileline. */ +/* Compare a PC against a unit_addrs for bsearch. We always allocate + an entry entry at the end of the vector, so that this routine can + safely look at the next entry. Note that if there are multiple + ranges containing PC, which one will be returned is unpredictable. + We compensate for that in dwarf_fileline. */ static int unit_addrs_search (const void *vkey, const void *ventry) @@ -1599,7 +1603,7 @@ unit_addrs_search (const void *vkey, const void *ventry) pc = *key; if (pc < entry->low) return -1; - else if (pc >= entry->high) + else if (pc > (entry + 1)->low) return 1; else return 0; @@ -2427,6 +2431,7 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, size_t i; struct unit **pu; size_t unit_offset = 0; + struct unit_addrs *pa; memset (&addrs->vec, 0, sizeof addrs->vec); memset (&unit_vec->vec, 0, sizeof unit_vec->vec); @@ -2567,6 +2572,17 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, if (info.reported_underflow) goto fail; + /* Add a trailing addrs entry, but don't include it in addrs->count. */ + pa = ((struct unit_addrs *) + backtrace_vector_grow (state, sizeof (struct unit_addrs), + error_callback, data, &addrs->vec)); + if (pa == NULL) + goto fail; + pa->low = 0; + --pa->low; + pa->high = pa->low; + pa->u = NULL; + unit_vec->vec = units; unit_vec->count = units_count; return 1; @@ -2664,19 +2680,20 @@ read_v2_paths (struct backtrace_state *state, struct unit *u, ++hdr->dirs_count; } - hdr->dirs = NULL; - if (hdr->dirs_count != 0) - { - hdr->dirs = ((const char **) - backtrace_alloc (state, - hdr->dirs_count * sizeof (const char *), - hdr_buf->error_callback, - hdr_buf->data)); - if (hdr->dirs == NULL) - return 0; - } + /* The index of the first entry in the list of directories is 1. Index 0 is + used for the current directory of the compilation. To simplify index + handling, we set entry 0 to the compilation unit directory. */ + ++hdr->dirs_count; + hdr->dirs = ((const char **) + backtrace_alloc (state, + hdr->dirs_count * sizeof (const char *), + hdr_buf->error_callback, + hdr_buf->data)); + if (hdr->dirs == NULL) + return 0; - i = 0; + hdr->dirs[0] = u->comp_dir; + i = 1; while (*hdr_buf->buf != '\0') { if (hdr_buf->reported_underflow) @@ -2703,6 +2720,10 @@ read_v2_paths (struct backtrace_state *state, struct unit *u, ++hdr->filenames_count; } + /* The index of the first entry in the list of file names is 1. Index 0 is + used for the DW_AT_name of the compilation unit. To simplify index + handling, we set entry 0 to the compilation unit file name. */ + ++hdr->filenames_count; hdr->filenames = ((const char **) backtrace_alloc (state, hdr->filenames_count * sizeof (char *), @@ -2710,7 +2731,8 @@ read_v2_paths (struct backtrace_state *state, struct unit *u, hdr_buf->data)); if (hdr->filenames == NULL) return 0; - i = 0; + hdr->filenames[0] = u->filename; + i = 1; while (*hdr_buf->buf != '\0') { const char *filename; @@ -2724,7 +2746,7 @@ read_v2_paths (struct backtrace_state *state, struct unit *u, return 0; dir_index = read_uleb128 (hdr_buf); if (IS_ABSOLUTE_PATH (filename) - || (dir_index == 0 && u->comp_dir == NULL)) + || (dir_index < hdr->dirs_count && hdr->dirs[dir_index] == NULL)) hdr->filenames[i] = filename; else { @@ -2733,10 +2755,8 @@ read_v2_paths (struct backtrace_state *state, struct unit *u, size_t filename_len; char *s; - if (dir_index == 0) - dir = u->comp_dir; - else if (dir_index - 1 < hdr->dirs_count) - dir = hdr->dirs[dir_index - 1]; + if (dir_index < hdr->dirs_count) + dir = hdr->dirs[dir_index]; else { dwarf_buf_error (hdr_buf, @@ -3024,8 +3044,8 @@ read_line_header (struct backtrace_state *state, struct dwarf_data *ddata, static int read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, - struct unit *u, const struct line_header *hdr, - struct dwarf_buf *line_buf, struct line_vector *vec) + const struct line_header *hdr, struct dwarf_buf *line_buf, + struct line_vector *vec) { uint64_t address; unsigned int op_index; @@ -3035,8 +3055,8 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, address = 0; op_index = 0; - if (hdr->filenames_count > 0) - reset_filename = hdr->filenames[0]; + if (hdr->filenames_count > 1) + reset_filename = hdr->filenames[1]; else reset_filename = ""; filename = reset_filename; @@ -3101,10 +3121,8 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, size_t f_len; char *p; - if (dir_index == 0 && hdr->version < 5) - dir = u->comp_dir; - else if (dir_index - 1 < hdr->dirs_count) - dir = hdr->dirs[dir_index - 1]; + if (dir_index < hdr->dirs_count) + dir = hdr->dirs[dir_index]; else { dwarf_buf_error (line_buf, @@ -3171,14 +3189,14 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, filename = ""; else { - if (fileno - 1 >= hdr->filenames_count) + if (fileno >= hdr->filenames_count) { dwarf_buf_error (line_buf, ("invalid file number in " "line number program")); return 0; } - filename = hdr->filenames[fileno - 1]; + filename = hdr->filenames[fileno]; } } break; @@ -3268,7 +3286,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata, if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr)) goto fail; - if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec)) + if (!read_line_program (state, ddata, hdr, &line_buf, &vec)) goto fail; if (line_buf.reported_underflow) @@ -3609,7 +3627,7 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, function->caller_filename = ""; else { - if (val.u.uint - 1 >= lhdr->filenames_count) + if (val.u.uint >= lhdr->filenames_count) { dwarf_buf_error (unit_buf, ("invalid file number in " @@ -3617,7 +3635,7 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, return 0; } function->caller_filename = - lhdr->filenames[val.u.uint - 1]; + lhdr->filenames[val.u.uint]; } } break; @@ -3740,8 +3758,23 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, if (fvec.count > 0) { + struct function_addrs *p; struct function_addrs *faddrs; + /* Allocate a trailing entry, but don't include it + in fvec.count. */ + p = ((struct function_addrs *) + backtrace_vector_grow (state, + sizeof (struct function_addrs), + error_callback, data, + &fvec.vec)); + if (p == NULL) + return 0; + p->low = 0; + --p->low; + p->high = p->low; + p->function = NULL; + if (!backtrace_vector_release (state, &fvec.vec, error_callback, data)) return 0; @@ -3775,6 +3808,7 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata, struct function_vector lvec; struct function_vector *pfvec; struct dwarf_buf unit_buf; + struct function_addrs *p; struct function_addrs *addrs; size_t addrs_count; @@ -3806,6 +3840,18 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata, if (pfvec->count == 0) return; + /* Allocate a trailing entry, but don't include it in + pfvec->count. */ + p = ((struct function_addrs *) + backtrace_vector_grow (state, sizeof (struct function_addrs), + error_callback, data, &pfvec->vec)); + if (p == NULL) + return; + p->low = 0; + --p->low; + p->high = p->low; + p->function = NULL; + addrs_count = pfvec->count; if (fvec == NULL) @@ -3842,30 +3888,54 @@ report_inlined_functions (uintptr_t pc, struct function *function, backtrace_full_callback callback, void *data, const char **filename, int *lineno) { - struct function_addrs *function_addrs; + struct function_addrs *p; + struct function_addrs *match; struct function *inlined; int ret; if (function->function_addrs_count == 0) return 0; - function_addrs = ((struct function_addrs *) - bsearch (&pc, function->function_addrs, - function->function_addrs_count, - sizeof (struct function_addrs), - function_addrs_search)); - if (function_addrs == NULL) + /* Our search isn't safe if pc == -1, as that is the sentinel + value. */ + if (pc + 1 == 0) return 0; - while (((size_t) (function_addrs - function->function_addrs) + 1 - < function->function_addrs_count) - && pc >= (function_addrs + 1)->low - && pc < (function_addrs + 1)->high) - ++function_addrs; + p = ((struct function_addrs *) + bsearch (&pc, function->function_addrs, + function->function_addrs_count, + sizeof (struct function_addrs), + function_addrs_search)); + if (p == NULL) + return 0; + + /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are + sorted by low, so if pc > p->low we are at the end of a range of + function_addrs with the same low value. If pc == p->low walk + forward to the end of the range with that low value. Then walk + backward and use the first range that includes pc. */ + while (pc == (p + 1)->low) + ++p; + match = NULL; + while (1) + { + if (pc < p->high) + { + match = p; + break; + } + if (p == function->function_addrs) + break; + if ((p - 1)->low < p->low) + break; + --p; + } + if (match == NULL) + return 0; /* We found an inlined call. */ - inlined = function_addrs->function; + inlined = match->function; /* Report any calls inlined into this one. */ ret = report_inlined_functions (pc, inlined, callback, data, @@ -3874,7 +3944,7 @@ report_inlined_functions (uintptr_t pc, struct function *function, return ret; /* Report this inlined call. */ - ret = callback (data, pc, function_addrs->low, *filename, *lineno, inlined->name); + ret = callback (data, pc, match->low, *filename, *lineno, inlined->name); if (ret != 0) return ret; @@ -3898,11 +3968,13 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, int *found) { struct unit_addrs *entry; + int found_entry; struct unit *u; int new_data; struct line *lines; struct line *ln; - struct function_addrs *function_addrs; + struct function_addrs *p; + struct function_addrs *fmatch; struct function *function; const char *filename; int lineno; @@ -3910,8 +3982,10 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, *found = 1; - /* Find an address range that includes PC. */ - entry = (ddata->addrs_count == 0 + /* Find an address range that includes PC. Our search isn't safe if + PC == -1, as we use that as a sentinel value, so skip the search + in that case. */ + entry = (ddata->addrs_count == 0 || pc + 1 == 0 ? NULL : (struct unit_addrs*)bsearch (&pc, ddata->addrs, ddata->addrs_count, sizeof (struct unit_addrs), unit_addrs_search)); @@ -3922,14 +3996,32 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, return 0; } - /* If there are multiple ranges that contain PC, use the last one, - in order to produce predictable results. If we assume that all - ranges are properly nested, then the last range will be the - smallest one. */ - while ((size_t) (entry - ddata->addrs) + 1 < ddata->addrs_count - && pc >= (entry + 1)->low - && pc < (entry + 1)->high) + /* Here pc >= entry->low && pc < (entry + 1)->low. The unit_addrs + are sorted by low, so if pc > p->low we are at the end of a range + of unit_addrs with the same low value. If pc == p->low walk + forward to the end of the range with that low value. Then walk + backward and use the first range that includes pc. */ + while (pc == (entry + 1)->low) ++entry; + found_entry = 0; + while (1) + { + if (pc < entry->high) + { + found_entry = 1; + break; + } + if (entry == ddata->addrs) + break; + if ((entry - 1)->low < entry->low) + break; + --entry; + } + if (!found_entry) + { + *found = 0; + return 0; + } /* We need the lines, lines_count, function_addrs, function_addrs_count fields of u. If they are not set, we need @@ -3965,6 +4057,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, new_data = 0; if (lines == NULL) { + struct function_addrs *function_addrs; size_t function_addrs_count; struct line_header lhdr; size_t count; @@ -4081,24 +4174,39 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, if (entry->u->function_addrs_count == 0) return callback (data, pc, 0, ln->filename, ln->lineno, NULL); - function_addrs = ((struct function_addrs *) - bsearch (&pc, entry->u->function_addrs, - entry->u->function_addrs_count, - sizeof (struct function_addrs), - function_addrs_search)); - if (function_addrs == NULL) + p = ((struct function_addrs *) + bsearch (&pc, entry->u->function_addrs, + entry->u->function_addrs_count, + sizeof (struct function_addrs), + function_addrs_search)); + if (p == NULL) return callback (data, pc, 0, ln->filename, ln->lineno, NULL); - /* If there are multiple function ranges that contain PC, use the - last one, in order to produce predictable results. */ + /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are + sorted by low, so if pc > p->low we are at the end of a range of + function_addrs with the same low value. If pc == p->low walk + forward to the end of the range with that low value. Then walk + backward and use the first range that includes pc. */ + while (pc == (p + 1)->low) + ++p; + fmatch = NULL; + while (1) + { + if (pc < p->high) + { + fmatch = p; + break; + } + if (p == entry->u->function_addrs) + break; + if ((p - 1)->low < p->low) + break; + --p; + } + if (fmatch == NULL) + return callback (data, pc, 0, ln->filename, ln->lineno, NULL); - while (((size_t) (function_addrs - entry->u->function_addrs + 1) - < entry->u->function_addrs_count) - && pc >= (function_addrs + 1)->low - && pc < (function_addrs + 1)->high) - ++function_addrs; - - function = function_addrs->function; + function = fmatch->function; filename = ln->filename; lineno = ln->lineno; @@ -4108,7 +4216,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, if (ret != 0) return ret; - return callback (data, pc, function_addrs->low, filename, lineno, function->name); + return callback (data, pc, fmatch->low, filename, lineno, function->name); } diff --git a/libbacktrace/elf.cpp b/libbacktrace/elf.cpp index 459e13d7..50715bf9 100644 --- a/libbacktrace/elf.cpp +++ b/libbacktrace/elf.cpp @@ -1,5 +1,5 @@ /* elf.c -- Get debug data from an ELF file for backtraces. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -397,6 +397,14 @@ struct elf_syminfo_data size_t count; }; +/* A view that works for either a file or memory. */ + +struct elf_view +{ + struct backtrace_view view; + int release; /* If non-zero, must call backtrace_release_view. */ +}; + /* Information about PowerPC64 ELFv1 .opd section. */ struct elf_ppc64_opd_data @@ -408,9 +416,48 @@ struct elf_ppc64_opd_data /* Size of the .opd section. */ size_t size; /* Corresponding section view. */ - struct backtrace_view view; + struct elf_view view; }; +/* Create a view of SIZE bytes from DESCRIPTOR/MEMORY at OFFSET. */ + +static int +elf_get_view (struct backtrace_state *state, int descriptor, + const unsigned char *memory, size_t memory_size, off_t offset, + uint64_t size, backtrace_error_callback error_callback, + void *data, struct elf_view *view) +{ + if (memory == NULL) + { + view->release = 1; + return backtrace_get_view (state, descriptor, offset, size, + error_callback, data, &view->view); + } + else + { + if ((uint64_t) offset + size > (uint64_t) memory_size) + { + error_callback (data, "out of range for in-memory file", 0); + return 0; + } + view->view.data = (const void *) (memory + offset); + view->view.base = NULL; + view->view.len = size; + view->release = 0; + return 1; + } +} + +/* Release a view read by elf_get_view. */ + +static void +elf_release_view (struct backtrace_state *state, struct elf_view *view, + backtrace_error_callback error_callback, void *data) +{ + if (view->release) + backtrace_release_view (state, &view->view, error_callback, data); +} + /* Compute the CRC-32 of BUF/LEN. This uses the CRC used for .gnu_debuglink files. */ @@ -507,18 +554,6 @@ elf_crc32_file (struct backtrace_state *state, int descriptor, return ret; } -/* A dummy callback function used when we can't find any debug info. */ - -static int -elf_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, - uintptr_t pc ATTRIBUTE_UNUSED, - backtrace_full_callback callback ATTRIBUTE_UNUSED, - backtrace_error_callback error_callback, void *data) -{ - error_callback (data, "no debug info in ELF executable", -1); - return 0; -} - /* A dummy callback function used when we can't find a symbol table. */ @@ -531,6 +566,33 @@ elf_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, error_callback (data, "no symbol table in ELF executable", -1); } +/* A callback function used when we can't find any debug info. */ + +static int +elf_nodebug (struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) +{ + if (state->syminfo_fn != NULL && state->syminfo_fn != elf_nosyms) + { + struct backtrace_call_full bdata; + + /* Fetch symbol information so that we can least get the + function name. */ + + bdata.full_callback = callback; + bdata.full_error_callback = error_callback; + bdata.full_data = data; + bdata.ret = 0; + state->syminfo_fn (state, pc, backtrace_syminfo_to_full_callback, + backtrace_syminfo_to_full_error_callback, &bdata); + return bdata.ret; + } + + error_callback (data, "no debug info in ELF executable", -1); + return 0; +} + /* Compare struct elf_symbol for qsort. */ static int @@ -1042,7 +1104,7 @@ elf_open_debugfile_by_debuglink (struct backtrace_state *state, when this code is compiled with -g. */ static void -elf_zlib_failed(void) +elf_uncompress_failed(void) { } @@ -1069,7 +1131,7 @@ elf_zlib_fetch (const unsigned char **ppin, const unsigned char *pinend, if (unlikely (pinend - pin < 4)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1199,7 +1261,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, { if (unlikely (codes[i] >= 16)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1236,7 +1298,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely (jcnt > (1U << j))) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1256,7 +1318,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely ((val & ~HUFFMAN_VALUE_MASK) != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1272,7 +1334,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, { if (unlikely (table[ind] != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } table[ind] = tval; @@ -1360,7 +1422,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, } if (unlikely (jcnt != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } } @@ -1413,7 +1475,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely ((next_secondary & HUFFMAN_VALUE_MASK) != next_secondary)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1431,7 +1493,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely ((tprimary & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } secondary = tprimary & HUFFMAN_VALUE_MASK; @@ -1439,7 +1501,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, & HUFFMAN_BITS_MASK); if (unlikely (secondary_bits < j - 8)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } } @@ -1455,7 +1517,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, { if (unlikely (table[secondary + 0x100 + ind] != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } table[secondary + 0x100 + ind] = tval; @@ -1671,28 +1733,28 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely ((pin[0] & 0xf) != 8)) /* 8 is zlib encoding. */ { /* Unknown compression method. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((pin[0] >> 4) > 7)) { /* Window size too large. Other than this check, we don't care about the window size. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((pin[1] & 0x20) != 0)) { /* Stream expects a predefined dictionary, but we have no dictionary. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } val = (pin[0] << 8) | pin[1]; if (unlikely (val % 31 != 0)) { /* Header check failure. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } pin += 2; @@ -1729,7 +1791,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (type == 3)) { /* Invalid block type. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1752,7 +1814,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely ((pinend - pin) < 4)) { /* Missing length. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } len = pin[0] | (pin[1] << 8); @@ -1762,14 +1824,14 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (len != lenc)) { /* Corrupt data. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely (len > (unsigned int) (pinend - pin) || len > (unsigned int) (poutend - pout))) { /* Not enough space in buffers. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } memcpy (pout, pin, len); @@ -1819,7 +1881,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (nlit > 286 || ndist > 30)) { /* Values out of range. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1984,7 +2046,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, a secondary table is never necessary. */ if (unlikely ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2004,7 +2066,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (plen == plenbase)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2017,7 +2079,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, bits -= 2; if (unlikely ((unsigned int) (plenend - plen) < c)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2026,10 +2088,10 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, { case 6: *plen++ = prev; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 5: *plen++ = prev; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 4: *plen++ = prev; } @@ -2052,7 +2114,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, bits -= 3; if (unlikely ((unsigned int) (plenend - plen) < c)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2060,22 +2122,22 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, { case 10: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 9: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 8: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 7: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 6: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 5: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 4: *plen++ = 0; } @@ -2098,7 +2160,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, bits -= 7; if (unlikely ((unsigned int) (plenend - plen) < c)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2107,7 +2169,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, } else { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } } @@ -2117,7 +2179,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, plen = plenbase; if (unlikely (plen[256] == 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2169,7 +2231,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, { if (unlikely (pout == poutend)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2198,7 +2260,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, len = 258; else if (unlikely (lit > 285)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } else @@ -2251,13 +2313,13 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (pout == porigout)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((unsigned int) (poutend - pout) < len)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2266,7 +2328,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, } else if (unlikely (dist > 29)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } else @@ -2297,13 +2359,13 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely ((unsigned int) (pout - porigout) < dist)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((unsigned int) (poutend - pout) < len)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2333,7 +2395,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, /* We should have filled the output buffer. */ if (unlikely (pout != poutend)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2460,7 +2522,7 @@ elf_zlib_verify_checksum (const unsigned char *checkbytes, if (unlikely ((s2 << 16) + s1 != cksum)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2615,6 +2677,1311 @@ backtrace_uncompress_zdebug (struct backtrace_state *state, return ret; } +/* Number of LZMA states. */ +#define LZMA_STATES (12) + +/* Number of LZMA position states. The pb value of the property byte + is the number of bits to include in these states, and the maximum + value of pb is 4. */ +#define LZMA_POS_STATES (16) + +/* Number of LZMA distance states. These are used match distances + with a short match length: up to 4 bytes. */ +#define LZMA_DIST_STATES (4) + +/* Number of LZMA distance slots. LZMA uses six bits to encode larger + match lengths, so 1 << 6 possible probabilities. */ +#define LZMA_DIST_SLOTS (64) + +/* LZMA distances 0 to 3 are encoded directly, larger values use a + probability model. */ +#define LZMA_DIST_MODEL_START (4) + +/* The LZMA probability model ends at 14. */ +#define LZMA_DIST_MODEL_END (14) + +/* LZMA distance slots for distances less than 127. */ +#define LZMA_FULL_DISTANCES (128) + +/* LZMA uses four alignment bits. */ +#define LZMA_ALIGN_SIZE (16) + +/* LZMA match length is encoded with 4, 5, or 10 bits, some of which + are already known. */ +#define LZMA_LEN_LOW_SYMBOLS (8) +#define LZMA_LEN_MID_SYMBOLS (8) +#define LZMA_LEN_HIGH_SYMBOLS (256) + +/* LZMA literal encoding. */ +#define LZMA_LITERAL_CODERS_MAX (16) +#define LZMA_LITERAL_CODER_SIZE (0x300) + +/* LZMA is based on a large set of probabilities, each managed + independently. Each probability is an 11 bit number that we store + in a uint16_t. We use a single large array of probabilities. */ + +/* Lengths of entries in the LZMA probabilities array. The names used + here are copied from the Linux kernel implementation. */ + +#define LZMA_PROB_IS_MATCH_LEN (LZMA_STATES * LZMA_POS_STATES) +#define LZMA_PROB_IS_REP_LEN LZMA_STATES +#define LZMA_PROB_IS_REP0_LEN LZMA_STATES +#define LZMA_PROB_IS_REP1_LEN LZMA_STATES +#define LZMA_PROB_IS_REP2_LEN LZMA_STATES +#define LZMA_PROB_IS_REP0_LONG_LEN (LZMA_STATES * LZMA_POS_STATES) +#define LZMA_PROB_DIST_SLOT_LEN (LZMA_DIST_STATES * LZMA_DIST_SLOTS) +#define LZMA_PROB_DIST_SPECIAL_LEN (LZMA_FULL_DISTANCES - LZMA_DIST_MODEL_END) +#define LZMA_PROB_DIST_ALIGN_LEN LZMA_ALIGN_SIZE +#define LZMA_PROB_MATCH_LEN_CHOICE_LEN 1 +#define LZMA_PROB_MATCH_LEN_CHOICE2_LEN 1 +#define LZMA_PROB_MATCH_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS) +#define LZMA_PROB_MATCH_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS) +#define LZMA_PROB_MATCH_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS +#define LZMA_PROB_REP_LEN_CHOICE_LEN 1 +#define LZMA_PROB_REP_LEN_CHOICE2_LEN 1 +#define LZMA_PROB_REP_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS) +#define LZMA_PROB_REP_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS) +#define LZMA_PROB_REP_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS +#define LZMA_PROB_LITERAL_LEN \ + (LZMA_LITERAL_CODERS_MAX * LZMA_LITERAL_CODER_SIZE) + +/* Offsets into the LZMA probabilities array. This is mechanically + generated from the above lengths. */ + +#define LZMA_PROB_IS_MATCH_OFFSET 0 +#define LZMA_PROB_IS_REP_OFFSET \ + (LZMA_PROB_IS_MATCH_OFFSET + LZMA_PROB_IS_MATCH_LEN) +#define LZMA_PROB_IS_REP0_OFFSET \ + (LZMA_PROB_IS_REP_OFFSET + LZMA_PROB_IS_REP_LEN) +#define LZMA_PROB_IS_REP1_OFFSET \ + (LZMA_PROB_IS_REP0_OFFSET + LZMA_PROB_IS_REP0_LEN) +#define LZMA_PROB_IS_REP2_OFFSET \ + (LZMA_PROB_IS_REP1_OFFSET + LZMA_PROB_IS_REP1_LEN) +#define LZMA_PROB_IS_REP0_LONG_OFFSET \ + (LZMA_PROB_IS_REP2_OFFSET + LZMA_PROB_IS_REP2_LEN) +#define LZMA_PROB_DIST_SLOT_OFFSET \ + (LZMA_PROB_IS_REP0_LONG_OFFSET + LZMA_PROB_IS_REP0_LONG_LEN) +#define LZMA_PROB_DIST_SPECIAL_OFFSET \ + (LZMA_PROB_DIST_SLOT_OFFSET + LZMA_PROB_DIST_SLOT_LEN) +#define LZMA_PROB_DIST_ALIGN_OFFSET \ + (LZMA_PROB_DIST_SPECIAL_OFFSET + LZMA_PROB_DIST_SPECIAL_LEN) +#define LZMA_PROB_MATCH_LEN_CHOICE_OFFSET \ + (LZMA_PROB_DIST_ALIGN_OFFSET + LZMA_PROB_DIST_ALIGN_LEN) +#define LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET \ + (LZMA_PROB_MATCH_LEN_CHOICE_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE_LEN) +#define LZMA_PROB_MATCH_LEN_LOW_OFFSET \ + (LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE2_LEN) +#define LZMA_PROB_MATCH_LEN_MID_OFFSET \ + (LZMA_PROB_MATCH_LEN_LOW_OFFSET + LZMA_PROB_MATCH_LEN_LOW_LEN) +#define LZMA_PROB_MATCH_LEN_HIGH_OFFSET \ + (LZMA_PROB_MATCH_LEN_MID_OFFSET + LZMA_PROB_MATCH_LEN_MID_LEN) +#define LZMA_PROB_REP_LEN_CHOICE_OFFSET \ + (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + LZMA_PROB_MATCH_LEN_HIGH_LEN) +#define LZMA_PROB_REP_LEN_CHOICE2_OFFSET \ + (LZMA_PROB_REP_LEN_CHOICE_OFFSET + LZMA_PROB_REP_LEN_CHOICE_LEN) +#define LZMA_PROB_REP_LEN_LOW_OFFSET \ + (LZMA_PROB_REP_LEN_CHOICE2_OFFSET + LZMA_PROB_REP_LEN_CHOICE2_LEN) +#define LZMA_PROB_REP_LEN_MID_OFFSET \ + (LZMA_PROB_REP_LEN_LOW_OFFSET + LZMA_PROB_REP_LEN_LOW_LEN) +#define LZMA_PROB_REP_LEN_HIGH_OFFSET \ + (LZMA_PROB_REP_LEN_MID_OFFSET + LZMA_PROB_REP_LEN_MID_LEN) +#define LZMA_PROB_LITERAL_OFFSET \ + (LZMA_PROB_REP_LEN_HIGH_OFFSET + LZMA_PROB_REP_LEN_HIGH_LEN) + +#define LZMA_PROB_TOTAL_COUNT \ + (LZMA_PROB_LITERAL_OFFSET + LZMA_PROB_LITERAL_LEN) + +/* Check that the number of LZMA probabilities is the same as the + Linux kernel implementation. */ + +#if LZMA_PROB_TOTAL_COUNT != 1846 + (1 << 4) * 0x300 + #error Wrong number of LZMA probabilities +#endif + +/* Expressions for the offset in the LZMA probabilities array of a + specific probability. */ + +#define LZMA_IS_MATCH(state, pos) \ + (LZMA_PROB_IS_MATCH_OFFSET + (state) * LZMA_POS_STATES + (pos)) +#define LZMA_IS_REP(state) \ + (LZMA_PROB_IS_REP_OFFSET + (state)) +#define LZMA_IS_REP0(state) \ + (LZMA_PROB_IS_REP0_OFFSET + (state)) +#define LZMA_IS_REP1(state) \ + (LZMA_PROB_IS_REP1_OFFSET + (state)) +#define LZMA_IS_REP2(state) \ + (LZMA_PROB_IS_REP2_OFFSET + (state)) +#define LZMA_IS_REP0_LONG(state, pos) \ + (LZMA_PROB_IS_REP0_LONG_OFFSET + (state) * LZMA_POS_STATES + (pos)) +#define LZMA_DIST_SLOT(dist, slot) \ + (LZMA_PROB_DIST_SLOT_OFFSET + (dist) * LZMA_DIST_SLOTS + (slot)) +#define LZMA_DIST_SPECIAL(dist) \ + (LZMA_PROB_DIST_SPECIAL_OFFSET + (dist)) +#define LZMA_DIST_ALIGN(dist) \ + (LZMA_PROB_DIST_ALIGN_OFFSET + (dist)) +#define LZMA_MATCH_LEN_CHOICE \ + LZMA_PROB_MATCH_LEN_CHOICE_OFFSET +#define LZMA_MATCH_LEN_CHOICE2 \ + LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET +#define LZMA_MATCH_LEN_LOW(pos, sym) \ + (LZMA_PROB_MATCH_LEN_LOW_OFFSET + (pos) * LZMA_LEN_LOW_SYMBOLS + (sym)) +#define LZMA_MATCH_LEN_MID(pos, sym) \ + (LZMA_PROB_MATCH_LEN_MID_OFFSET + (pos) * LZMA_LEN_MID_SYMBOLS + (sym)) +#define LZMA_MATCH_LEN_HIGH(sym) \ + (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + (sym)) +#define LZMA_REP_LEN_CHOICE \ + LZMA_PROB_REP_LEN_CHOICE_OFFSET +#define LZMA_REP_LEN_CHOICE2 \ + LZMA_PROB_REP_LEN_CHOICE2_OFFSET +#define LZMA_REP_LEN_LOW(pos, sym) \ + (LZMA_PROB_REP_LEN_LOW_OFFSET + (pos) * LZMA_LEN_LOW_SYMBOLS + (sym)) +#define LZMA_REP_LEN_MID(pos, sym) \ + (LZMA_PROB_REP_LEN_MID_OFFSET + (pos) * LZMA_LEN_MID_SYMBOLS + (sym)) +#define LZMA_REP_LEN_HIGH(sym) \ + (LZMA_PROB_REP_LEN_HIGH_OFFSET + (sym)) +#define LZMA_LITERAL(code, size) \ + (LZMA_PROB_LITERAL_OFFSET + (code) * LZMA_LITERAL_CODER_SIZE + (size)) + +/* Read an LZMA varint from BUF, reading and updating *POFFSET, + setting *VAL. Returns 0 on error, 1 on success. */ + +static int +elf_lzma_varint (const unsigned char *compressed, size_t compressed_size, + size_t *poffset, uint64_t *val) +{ + size_t off; + int i; + uint64_t v; + unsigned char b; + + off = *poffset; + i = 0; + v = 0; + while (1) + { + if (unlikely (off >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + b = compressed[off]; + v |= (b & 0x7f) << (i * 7); + ++off; + if ((b & 0x80) == 0) + { + *poffset = off; + *val = v; + return 1; + } + ++i; + if (unlikely (i >= 9)) + { + elf_uncompress_failed (); + return 0; + } + } +} + +/* Normalize the LZMA range decoder, pulling in an extra input byte if + needed. */ + +static void +elf_lzma_range_normalize (const unsigned char *compressed, + size_t compressed_size, size_t *poffset, + uint32_t *prange, uint32_t *pcode) +{ + if (*prange < (1U << 24)) + { + if (unlikely (*poffset >= compressed_size)) + { + /* We assume this will be caught elsewhere. */ + elf_uncompress_failed (); + return; + } + *prange <<= 8; + *pcode <<= 8; + *pcode += compressed[*poffset]; + ++*poffset; + } +} + +/* Read and return a single bit from the LZMA stream, reading and + updating *PROB. Each bit comes from the range coder. */ + +static int +elf_lzma_bit (const unsigned char *compressed, size_t compressed_size, + uint16_t *prob, size_t *poffset, uint32_t *prange, + uint32_t *pcode) +{ + uint32_t bound; + + elf_lzma_range_normalize (compressed, compressed_size, poffset, + prange, pcode); + bound = (*prange >> 11) * (uint32_t) *prob; + if (*pcode < bound) + { + *prange = bound; + *prob += ((1U << 11) - *prob) >> 5; + return 0; + } + else + { + *prange -= bound; + *pcode -= bound; + *prob -= *prob >> 5; + return 1; + } +} + +/* Read an integer of size BITS from the LZMA stream, most significant + bit first. The bits are predicted using PROBS. */ + +static uint32_t +elf_lzma_integer (const unsigned char *compressed, size_t compressed_size, + uint16_t *probs, uint32_t bits, size_t *poffset, + uint32_t *prange, uint32_t *pcode) +{ + uint32_t sym; + uint32_t i; + + sym = 1; + for (i = 0; i < bits; i++) + { + int bit; + + bit = elf_lzma_bit (compressed, compressed_size, probs + sym, poffset, + prange, pcode); + sym <<= 1; + sym += bit; + } + return sym - (1 << bits); +} + +/* Read an integer of size BITS from the LZMA stream, least + significant bit first. The bits are predicted using PROBS. */ + +static uint32_t +elf_lzma_reverse_integer (const unsigned char *compressed, + size_t compressed_size, uint16_t *probs, + uint32_t bits, size_t *poffset, uint32_t *prange, + uint32_t *pcode) +{ + uint32_t sym; + uint32_t val; + uint32_t i; + + sym = 1; + val = 0; + for (i = 0; i < bits; i++) + { + int bit; + + bit = elf_lzma_bit (compressed, compressed_size, probs + sym, poffset, + prange, pcode); + sym <<= 1; + sym += bit; + val += bit << i; + } + return val; +} + +/* Read a length from the LZMA stream. IS_REP picks either LZMA_MATCH + or LZMA_REP probabilities. */ + +static uint32_t +elf_lzma_len (const unsigned char *compressed, size_t compressed_size, + uint16_t *probs, int is_rep, unsigned int pos_state, + size_t *poffset, uint32_t *prange, uint32_t *pcode) +{ + uint16_t *probs_choice; + uint16_t *probs_sym; + uint32_t bits; + uint32_t len; + + probs_choice = probs + (is_rep + ? LZMA_REP_LEN_CHOICE + : LZMA_MATCH_LEN_CHOICE); + if (elf_lzma_bit (compressed, compressed_size, probs_choice, poffset, + prange, pcode)) + { + probs_choice = probs + (is_rep + ? LZMA_REP_LEN_CHOICE2 + : LZMA_MATCH_LEN_CHOICE2); + if (elf_lzma_bit (compressed, compressed_size, probs_choice, + poffset, prange, pcode)) + { + probs_sym = probs + (is_rep + ? LZMA_REP_LEN_HIGH (0) + : LZMA_MATCH_LEN_HIGH (0)); + bits = 8; + len = 2 + 8 + 8; + } + else + { + probs_sym = probs + (is_rep + ? LZMA_REP_LEN_MID (pos_state, 0) + : LZMA_MATCH_LEN_MID (pos_state, 0)); + bits = 3; + len = 2 + 8; + } + } + else + { + probs_sym = probs + (is_rep + ? LZMA_REP_LEN_LOW (pos_state, 0) + : LZMA_MATCH_LEN_LOW (pos_state, 0)); + bits = 3; + len = 2; + } + + len += elf_lzma_integer (compressed, compressed_size, probs_sym, bits, + poffset, prange, pcode); + return len; +} + +/* Uncompress one LZMA block from a minidebug file. The compressed + data is at COMPRESSED + *POFFSET. Update *POFFSET. Store the data + into the memory at UNCOMPRESSED, size UNCOMPRESSED_SIZE. CHECK is + the stream flag from the xz header. Return 1 on successful + decompression. */ + +static int +elf_uncompress_lzma_block (const unsigned char *compressed, + size_t compressed_size, unsigned char check, + uint16_t *probs, unsigned char *uncompressed, + size_t uncompressed_size, size_t *poffset) +{ + size_t off; + size_t block_header_offset; + size_t block_header_size; + unsigned char block_flags; + uint64_t header_compressed_size; + uint64_t header_uncompressed_size; + unsigned char lzma2_properties; + uint32_t computed_crc; + uint32_t stream_crc; + size_t uncompressed_offset; + size_t dict_start_offset; + unsigned int lc; + unsigned int lp; + unsigned int pb; + uint32_t range; + uint32_t code; + uint32_t lstate; + uint32_t dist[4]; + + off = *poffset; + block_header_offset = off; + + /* Block header size is a single byte. */ + if (unlikely (off >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + block_header_size = (compressed[off] + 1) * 4; + if (unlikely (off + block_header_size > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* Block flags. */ + block_flags = compressed[off + 1]; + if (unlikely ((block_flags & 0x3c) != 0)) + { + elf_uncompress_failed (); + return 0; + } + + off += 2; + + /* Optional compressed size. */ + header_compressed_size = 0; + if ((block_flags & 0x40) != 0) + { + *poffset = off; + if (!elf_lzma_varint (compressed, compressed_size, poffset, + &header_compressed_size)) + return 0; + off = *poffset; + } + + /* Optional uncompressed size. */ + header_uncompressed_size = 0; + if ((block_flags & 0x80) != 0) + { + *poffset = off; + if (!elf_lzma_varint (compressed, compressed_size, poffset, + &header_uncompressed_size)) + return 0; + off = *poffset; + } + + /* The recipe for creating a minidebug file is to run the xz program + with no arguments, so we expect exactly one filter: lzma2. */ + + if (unlikely ((block_flags & 0x3) != 0)) + { + elf_uncompress_failed (); + return 0; + } + + if (unlikely (off + 2 >= block_header_offset + block_header_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* The filter ID for LZMA2 is 0x21. */ + if (unlikely (compressed[off] != 0x21)) + { + elf_uncompress_failed (); + return 0; + } + ++off; + + /* The size of the filter properties for LZMA2 is 1. */ + if (unlikely (compressed[off] != 1)) + { + elf_uncompress_failed (); + return 0; + } + ++off; + + lzma2_properties = compressed[off]; + ++off; + + if (unlikely (lzma2_properties > 40)) + { + elf_uncompress_failed (); + return 0; + } + + /* The properties describe the dictionary size, but we don't care + what that is. */ + + /* Block header padding. */ + if (unlikely (off + 4 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + off = (off + 3) &~ (size_t) 3; + + if (unlikely (off + 4 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* Block header CRC. */ + computed_crc = elf_crc32 (0, compressed + block_header_offset, + block_header_size - 4); + stream_crc = (compressed[off] + | (compressed[off + 1] << 8) + | (compressed[off + 2] << 16) + | (compressed[off + 3] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + off += 4; + + /* Read a sequence of LZMA2 packets. */ + + uncompressed_offset = 0; + dict_start_offset = 0; + lc = 0; + lp = 0; + pb = 0; + lstate = 0; + while (off < compressed_size) + { + unsigned char control; + + range = 0xffffffff; + code = 0; + + control = compressed[off]; + ++off; + if (unlikely (control == 0)) + { + /* End of packets. */ + break; + } + + if (control == 1 || control >= 0xe0) + { + /* Reset dictionary to empty. */ + dict_start_offset = uncompressed_offset; + } + + if (control < 0x80) + { + size_t chunk_size; + + /* The only valid values here are 1 or 2. A 1 means to + reset the dictionary (done above). Then we see an + uncompressed chunk. */ + + if (unlikely (control > 2)) + { + elf_uncompress_failed (); + return 0; + } + + /* An uncompressed chunk is a two byte size followed by + data. */ + + if (unlikely (off + 2 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + chunk_size = compressed[off] << 8; + chunk_size += compressed[off + 1]; + ++chunk_size; + + off += 2; + + if (unlikely (off + chunk_size > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + if (unlikely (uncompressed_offset + chunk_size > uncompressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + memcpy (uncompressed + uncompressed_offset, compressed + off, + chunk_size); + uncompressed_offset += chunk_size; + off += chunk_size; + } + else + { + size_t uncompressed_chunk_start; + size_t uncompressed_chunk_size; + size_t compressed_chunk_size; + size_t limit; + + /* An LZMA chunk. This starts with an uncompressed size and + a compressed size. */ + + if (unlikely (off + 4 >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + uncompressed_chunk_start = uncompressed_offset; + + uncompressed_chunk_size = (control & 0x1f) << 16; + uncompressed_chunk_size += compressed[off] << 8; + uncompressed_chunk_size += compressed[off + 1]; + ++uncompressed_chunk_size; + + compressed_chunk_size = compressed[off + 2] << 8; + compressed_chunk_size += compressed[off + 3]; + ++compressed_chunk_size; + + off += 4; + + /* Bit 7 (0x80) is set. + Bits 6 and 5 (0x40 and 0x20) are as follows: + 0: don't reset anything + 1: reset state + 2: reset state, read properties + 3: reset state, read properties, reset dictionary (done above) */ + + if (control >= 0xc0) + { + unsigned char props; + + /* Bit 6 is set, read properties. */ + + if (unlikely (off >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + props = compressed[off]; + ++off; + if (unlikely (props > (4 * 5 + 4) * 9 + 8)) + { + elf_uncompress_failed (); + return 0; + } + pb = 0; + while (props >= 9 * 5) + { + props -= 9 * 5; + ++pb; + } + lp = 0; + while (props > 9) + { + props -= 9; + ++lp; + } + lc = props; + if (unlikely (lc + lp > 4)) + { + elf_uncompress_failed (); + return 0; + } + } + + if (control >= 0xa0) + { + size_t i; + + /* Bit 5 or 6 is set, reset LZMA state. */ + + lstate = 0; + memset (&dist, 0, sizeof dist); + for (i = 0; i < LZMA_PROB_TOTAL_COUNT; i++) + probs[i] = 1 << 10; + range = 0xffffffff; + code = 0; + } + + /* Read the range code. */ + + if (unlikely (off + 5 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* The byte at compressed[off] is ignored for some + reason. */ + + code = ((compressed[off + 1] << 24) + + (compressed[off + 2] << 16) + + (compressed[off + 3] << 8) + + compressed[off + 4]); + off += 5; + + /* This is the main LZMA decode loop. */ + + limit = off + compressed_chunk_size; + *poffset = off; + while (*poffset < limit) + { + unsigned int pos_state; + + if (unlikely (uncompressed_offset + == (uncompressed_chunk_start + + uncompressed_chunk_size))) + { + /* We've decompressed all the expected bytes. */ + break; + } + + pos_state = ((uncompressed_offset - dict_start_offset) + & ((1 << pb) - 1)); + + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_MATCH (lstate, pos_state), + poffset, &range, &code)) + { + uint32_t len; + + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP (lstate), + poffset, &range, &code)) + { + int short_rep; + uint32_t next_dist; + + /* Repeated match. */ + + short_rep = 0; + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP0 (lstate), + poffset, &range, &code)) + { + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP1 (lstate), + poffset, &range, &code)) + { + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP2 (lstate), + poffset, &range, &code)) + { + next_dist = dist[3]; + dist[3] = dist[2]; + } + else + { + next_dist = dist[2]; + } + dist[2] = dist[1]; + } + else + { + next_dist = dist[1]; + } + + dist[1] = dist[0]; + dist[0] = next_dist; + } + else + { + if (!elf_lzma_bit (compressed, compressed_size, + (probs + + LZMA_IS_REP0_LONG (lstate, + pos_state)), + poffset, &range, &code)) + short_rep = 1; + } + + if (lstate < 7) + lstate = short_rep ? 9 : 8; + else + lstate = 11; + + if (short_rep) + len = 1; + else + len = elf_lzma_len (compressed, compressed_size, + probs, 1, pos_state, poffset, + &range, &code); + } + else + { + uint32_t dist_state; + uint32_t dist_slot; + uint16_t *probs_dist; + + /* Match. */ + + if (lstate < 7) + lstate = 7; + else + lstate = 10; + dist[3] = dist[2]; + dist[2] = dist[1]; + dist[1] = dist[0]; + len = elf_lzma_len (compressed, compressed_size, + probs, 0, pos_state, poffset, + &range, &code); + + if (len < 4 + 2) + dist_state = len - 2; + else + dist_state = 3; + probs_dist = probs + LZMA_DIST_SLOT (dist_state, 0); + dist_slot = elf_lzma_integer (compressed, + compressed_size, + probs_dist, 6, + poffset, &range, + &code); + if (dist_slot < LZMA_DIST_MODEL_START) + dist[0] = dist_slot; + else + { + uint32_t limit; + + limit = (dist_slot >> 1) - 1; + dist[0] = 2 + (dist_slot & 1); + if (dist_slot < LZMA_DIST_MODEL_END) + { + dist[0] <<= limit; + probs_dist = (probs + + LZMA_DIST_SPECIAL(dist[0] + - dist_slot + - 1)); + dist[0] += + elf_lzma_reverse_integer (compressed, + compressed_size, + probs_dist, + limit, poffset, + &range, &code); + } + else + { + uint32_t dist0; + uint32_t i; + + dist0 = dist[0]; + for (i = 0; i < limit - 4; i++) + { + uint32_t mask; + + elf_lzma_range_normalize (compressed, + compressed_size, + poffset, + &range, &code); + range >>= 1; + code -= range; + mask = -(code >> 31); + code += range & mask; + dist0 <<= 1; + dist0 += mask + 1; + } + dist0 <<= 4; + probs_dist = probs + LZMA_DIST_ALIGN (0); + dist0 += + elf_lzma_reverse_integer (compressed, + compressed_size, + probs_dist, 4, + poffset, + &range, &code); + dist[0] = dist0; + } + } + } + + if (unlikely (uncompressed_offset + - dict_start_offset < dist[0] + 1)) + { + elf_uncompress_failed (); + return 0; + } + if (unlikely (uncompressed_offset + len > uncompressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + if (dist[0] == 0) + { + /* A common case, meaning repeat the last + character LEN times. */ + memset (uncompressed + uncompressed_offset, + uncompressed[uncompressed_offset - 1], + len); + uncompressed_offset += len; + } + else if (dist[0] + 1 >= len) + { + memcpy (uncompressed + uncompressed_offset, + uncompressed + uncompressed_offset - dist[0] - 1, + len); + uncompressed_offset += len; + } + else + { + while (len > 0) + { + uint32_t copy; + + copy = len < dist[0] + 1 ? len : dist[0] + 1; + memcpy (uncompressed + uncompressed_offset, + (uncompressed + uncompressed_offset + - dist[0] - 1), + copy); + len -= copy; + uncompressed_offset += copy; + } + } + } + else + { + unsigned char prev; + unsigned char low; + size_t high; + uint16_t *lit_probs; + unsigned int sym; + + /* Literal value. */ + + if (uncompressed_offset > 0) + prev = uncompressed[uncompressed_offset - 1]; + else + prev = 0; + low = prev >> (8 - lc); + high = (((uncompressed_offset - dict_start_offset) + & ((1 << lp) - 1)) + << lc); + lit_probs = probs + LZMA_LITERAL (low + high, 0); + if (lstate < 7) + sym = elf_lzma_integer (compressed, compressed_size, + lit_probs, 8, poffset, &range, + &code); + else + { + unsigned int match; + unsigned int bit; + unsigned int match_bit; + unsigned int idx; + + sym = 1; + if (uncompressed_offset >= dist[0] + 1) + match = uncompressed[uncompressed_offset - dist[0] - 1]; + else + match = 0; + match <<= 1; + bit = 0x100; + do + { + match_bit = match & bit; + match <<= 1; + idx = bit + match_bit + sym; + sym <<= 1; + if (elf_lzma_bit (compressed, compressed_size, + lit_probs + idx, poffset, + &range, &code)) + { + ++sym; + bit &= match_bit; + } + else + { + bit &= ~ match_bit; + } + } + while (sym < 0x100); + } + + if (unlikely (uncompressed_offset >= uncompressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + uncompressed[uncompressed_offset] = (unsigned char) sym; + ++uncompressed_offset; + if (lstate <= 3) + lstate = 0; + else if (lstate <= 9) + lstate -= 3; + else + lstate -= 6; + } + } + + elf_lzma_range_normalize (compressed, compressed_size, poffset, + &range, &code); + + off = *poffset; + } + } + + /* We have reached the end of the block. Pad to four byte + boundary. */ + off = (off + 3) &~ (size_t) 3; + if (unlikely (off > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + switch (check) + { + case 0: + /* No check. */ + break; + + case 1: + /* CRC32 */ + if (unlikely (off + 4 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + computed_crc = elf_crc32 (0, uncompressed, uncompressed_offset); + stream_crc = (compressed[off] + | (compressed[off + 1] << 8) + | (compressed[off + 2] << 16) + | (compressed[off + 3] << 24)); + if (computed_crc != stream_crc) + { + elf_uncompress_failed (); + return 0; + } + off += 4; + break; + + case 4: + /* CRC64. We don't bother computing a CRC64 checksum. */ + if (unlikely (off + 8 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + off += 8; + break; + + case 10: + /* SHA. We don't bother computing a SHA checksum. */ + if (unlikely (off + 32 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + off += 32; + break; + + default: + elf_uncompress_failed (); + return 0; + } + + *poffset = off; + + return 1; +} + +/* Uncompress LZMA data found in a minidebug file. The minidebug + format is described at + https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html. + Returns 0 on error, 1 on successful decompression. For this + function we return 0 on failure to decompress, as the calling code + will carry on in that case. */ + +static int +elf_uncompress_lzma (struct backtrace_state *state, + const unsigned char *compressed, size_t compressed_size, + backtrace_error_callback error_callback, void *data, + unsigned char **uncompressed, size_t *uncompressed_size) +{ + size_t header_size; + size_t footer_size; + unsigned char check; + uint32_t computed_crc; + uint32_t stream_crc; + size_t offset; + size_t index_size; + size_t footer_offset; + size_t index_offset; + uint64_t index_compressed_size; + uint64_t index_uncompressed_size; + unsigned char *mem; + uint16_t *probs; + size_t compressed_block_size; + + /* The format starts with a stream header and ends with a stream + footer. */ + header_size = 12; + footer_size = 12; + if (unlikely (compressed_size < header_size + footer_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* The stream header starts with a magic string. */ + if (unlikely (memcmp (compressed, "\375" "7zXZ\0", 6) != 0)) + { + elf_uncompress_failed (); + return 0; + } + + /* Next come stream flags. The first byte is zero, the second byte + is the check. */ + if (unlikely (compressed[6] != 0)) + { + elf_uncompress_failed (); + return 0; + } + check = compressed[7]; + if (unlikely ((check & 0xf8) != 0)) + { + elf_uncompress_failed (); + return 0; + } + + /* Next comes a CRC of the stream flags. */ + computed_crc = elf_crc32 (0, compressed + 6, 2); + stream_crc = (compressed[8] + | (compressed[9] << 8) + | (compressed[10] << 16) + | (compressed[11] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + + /* Now that we've parsed the header, parse the footer, so that we + can get the uncompressed size. */ + + /* The footer ends with two magic bytes. */ + + offset = compressed_size; + if (unlikely (memcmp (compressed + offset - 2, "YZ", 2) != 0)) + { + elf_uncompress_failed (); + return 0; + } + offset -= 2; + + /* Before that are the stream flags, which should be the same as the + flags in the header. */ + if (unlikely (compressed[offset - 2] != 0 + || compressed[offset - 1] != check)) + { + elf_uncompress_failed (); + return 0; + } + offset -= 2; + + /* Before that is the size of the index field, which precedes the + footer. */ + index_size = (compressed[offset - 4] + | (compressed[offset - 3] << 8) + | (compressed[offset - 2] << 16) + | (compressed[offset - 1] << 24)); + index_size = (index_size + 1) * 4; + offset -= 4; + + /* Before that is a footer CRC. */ + computed_crc = elf_crc32 (0, compressed + offset, 6); + stream_crc = (compressed[offset - 4] + | (compressed[offset - 3] << 8) + | (compressed[offset - 2] << 16) + | (compressed[offset - 1] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + offset -= 4; + + /* The index comes just before the footer. */ + if (unlikely (offset < index_size + header_size)) + { + elf_uncompress_failed (); + return 0; + } + + footer_offset = offset; + offset -= index_size; + index_offset = offset; + + /* The index starts with a zero byte. */ + if (unlikely (compressed[offset] != 0)) + { + elf_uncompress_failed (); + return 0; + } + ++offset; + + /* Next is the number of blocks. We expect zero blocks for an empty + stream, and otherwise a single block. */ + if (unlikely (compressed[offset] == 0)) + { + *uncompressed = NULL; + *uncompressed_size = 0; + return 1; + } + if (unlikely (compressed[offset] != 1)) + { + elf_uncompress_failed (); + return 0; + } + ++offset; + + /* Next is the compressed size and the uncompressed size. */ + if (!elf_lzma_varint (compressed, compressed_size, &offset, + &index_compressed_size)) + return 0; + if (!elf_lzma_varint (compressed, compressed_size, &offset, + &index_uncompressed_size)) + return 0; + + /* Pad to a four byte boundary. */ + offset = (offset + 3) &~ (size_t) 3; + + /* Next is a CRC of the index. */ + computed_crc = elf_crc32 (0, compressed + index_offset, + offset - index_offset); + stream_crc = (compressed[offset] + | (compressed[offset + 1] << 8) + | (compressed[offset + 2] << 16) + | (compressed[offset + 3] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + offset += 4; + + /* We should now be back at the footer. */ + if (unlikely (offset != footer_offset)) + { + elf_uncompress_failed (); + return 0; + } + + /* Allocate space to hold the uncompressed data. If we succeed in + uncompressing the LZMA data, we never free this memory. */ + mem = (unsigned char *) backtrace_alloc (state, index_uncompressed_size, + error_callback, data); + if (unlikely (mem == NULL)) + return 0; + *uncompressed = mem; + *uncompressed_size = index_uncompressed_size; + + /* Allocate space for probabilities. */ + probs = ((uint16_t *) + backtrace_alloc (state, + LZMA_PROB_TOTAL_COUNT * sizeof (uint16_t), + error_callback, data)); + if (unlikely (probs == NULL)) + { + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + /* Uncompress the block, which follows the header. */ + offset = 12; + if (!elf_uncompress_lzma_block (compressed, compressed_size, check, probs, + mem, index_uncompressed_size, &offset)) + { + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + compressed_block_size = offset - 12; + if (unlikely (compressed_block_size + != ((index_compressed_size + 3) &~ (size_t) 3))) + { + elf_uncompress_failed (); + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + offset = (offset + 3) &~ (size_t) 3; + if (unlikely (offset != index_offset)) + { + elf_uncompress_failed (); + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + return 1; +} + +/* This function is a hook for testing the LZMA support. It is only + used by tests. */ + +int +backtrace_uncompress_lzma (struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) +{ + return elf_uncompress_lzma (state, compressed, compressed_size, + error_callback, data, uncompressed, + uncompressed_size); +} + /* Add the backtrace data for one ELF file. Returns 1 on success, 0 on failure (in both cases descriptor is closed) or -1 if exe is non-zero and the ELF file is ET_DYN, which tells the caller that @@ -2623,23 +3990,24 @@ backtrace_uncompress_zdebug (struct backtrace_state *state, static int elf_add (struct backtrace_state *state, const char *filename, int descriptor, + const unsigned char *memory, size_t memory_size, uintptr_t base_address, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, struct dwarf_data **fileline_entry, int exe, int debuginfo, const char *with_buildid_data, uint32_t with_buildid_size) { - struct backtrace_view ehdr_view; + struct elf_view ehdr_view; b_elf_ehdr ehdr; off_t shoff; unsigned int shnum; unsigned int shstrndx; - struct backtrace_view shdrs_view; + struct elf_view shdrs_view; int shdrs_view_valid; const b_elf_shdr *shdrs; const b_elf_shdr *shstrhdr; size_t shstr_size; off_t shstr_off; - struct backtrace_view names_view; + struct elf_view names_view; int names_view_valid; const char *names; unsigned int symtab_shndx; @@ -2647,31 +4015,36 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, unsigned int i; struct debug_section_info sections[DEBUG_MAX]; struct debug_section_info zsections[DEBUG_MAX]; - struct backtrace_view symtab_view; + struct elf_view symtab_view; int symtab_view_valid; - struct backtrace_view strtab_view; + struct elf_view strtab_view; int strtab_view_valid; - struct backtrace_view buildid_view; + struct elf_view buildid_view; int buildid_view_valid; const char *buildid_data; uint32_t buildid_size; - struct backtrace_view debuglink_view; + struct elf_view debuglink_view; int debuglink_view_valid; const char *debuglink_name; uint32_t debuglink_crc; - struct backtrace_view debugaltlink_view; + struct elf_view debugaltlink_view; int debugaltlink_view_valid; const char *debugaltlink_name; const char *debugaltlink_buildid_data; uint32_t debugaltlink_buildid_size; + struct elf_view gnu_debugdata_view; + int gnu_debugdata_view_valid; + size_t gnu_debugdata_size; + unsigned char *gnu_debugdata_uncompressed; + size_t gnu_debugdata_uncompressed_size; off_t min_offset; off_t max_offset; off_t debug_size; - struct backtrace_view debug_view; + struct elf_view debug_view; int debug_view_valid; unsigned int using_debug_view; uint16_t *zdebug_table; - struct backtrace_view split_debug_view[DEBUG_MAX]; + struct elf_view split_debug_view[DEBUG_MAX]; unsigned char split_debug_view_valid[DEBUG_MAX]; struct elf_ppc64_opd_data opd_data, *opd; struct dwarf_sections dwarf_sections; @@ -2697,17 +4070,19 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, debugaltlink_name = NULL; debugaltlink_buildid_data = NULL; debugaltlink_buildid_size = 0; + gnu_debugdata_view_valid = 0; + gnu_debugdata_size = 0; debug_view_valid = 0; memset (&split_debug_view_valid[0], 0, sizeof split_debug_view_valid); opd = NULL; - if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback, - data, &ehdr_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, 0, sizeof ehdr, + error_callback, data, &ehdr_view)) goto fail; - memcpy (&ehdr, ehdr_view.data, sizeof ehdr); + memcpy (&ehdr, ehdr_view.view.data, sizeof ehdr); - backtrace_release_view (state, &ehdr_view, error_callback, data); + elf_release_view (state, &ehdr_view, error_callback, data); if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 @@ -2755,14 +4130,14 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if ((shnum == 0 || shstrndx == SHN_XINDEX) && shoff != 0) { - struct backtrace_view shdr_view; + struct elf_view shdr_view; const b_elf_shdr *shdr; - if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr, - error_callback, data, &shdr_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, shoff, + sizeof shdr, error_callback, data, &shdr_view)) goto fail; - shdr = (const b_elf_shdr *) shdr_view.data; + shdr = (const b_elf_shdr *) shdr_view.view.data; if (shnum == 0) shnum = shdr->sh_size; @@ -2786,7 +4161,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, shstrndx -= 0x100; } - backtrace_release_view (state, &shdr_view, error_callback, data); + elf_release_view (state, &shdr_view, error_callback, data); } if (shnum == 0 || shstrndx == 0) @@ -2797,12 +4172,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, /* Read the section headers, skipping the first one. */ - if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr), - (shnum - 1) * sizeof (b_elf_shdr), - error_callback, data, &shdrs_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shoff + sizeof (b_elf_shdr), + (shnum - 1) * sizeof (b_elf_shdr), + error_callback, data, &shdrs_view)) goto fail; shdrs_view_valid = 1; - shdrs = (const b_elf_shdr *) shdrs_view.data; + shdrs = (const b_elf_shdr *) shdrs_view.view.data; /* Read the section names. */ @@ -2810,11 +4186,11 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, shstr_size = shstrhdr->sh_size; shstr_off = shstrhdr->sh_offset; - if (!backtrace_get_view (state, descriptor, shstr_off, shstrhdr->sh_size, - error_callback, data, &names_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, shstr_off, + shstrhdr->sh_size, error_callback, data, &names_view)) goto fail; names_view_valid = 1; - names = (const char *) names_view.data; + names = (const char *) names_view.view.data; symtab_shndx = 0; dynsym_shndx = 0; @@ -2879,13 +4255,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { const b_elf_note *note; - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &buildid_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &buildid_view)) goto fail; buildid_view_valid = 1; - note = (const b_elf_note *) buildid_view.data; + note = (const b_elf_note *) buildid_view.view.data; if (note->type == NT_GNU_BUILD_ID && note->namesz == 4 && strncmp (note->name, "GNU", 4) == 0 @@ -2913,13 +4289,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, const char *debuglink_data; size_t crc_offset; - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &debuglink_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &debuglink_view)) goto fail; debuglink_view_valid = 1; - debuglink_data = (const char *) debuglink_view.data; + debuglink_data = (const char *) debuglink_view.view.data; crc_offset = strnlen (debuglink_data, shdr->sh_size); crc_offset = (crc_offset + 3) & ~3; if (crc_offset + 4 <= shdr->sh_size) @@ -2935,13 +4311,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, const char *debugaltlink_data; size_t debugaltlink_name_len; - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &debugaltlink_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &debugaltlink_view)) goto fail; debugaltlink_view_valid = 1; - debugaltlink_data = (const char *) debugaltlink_view.data; + debugaltlink_data = (const char *) debugaltlink_view.view.data; debugaltlink_name = debugaltlink_data; debugaltlink_name_len = strnlen (debugaltlink_data, shdr->sh_size); if (debugaltlink_name_len < shdr->sh_size) @@ -2955,20 +4331,32 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } } + if (!gnu_debugdata_view_valid + && strcmp (name, ".gnu_debugdata") == 0) + { + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &gnu_debugdata_view)) + goto fail; + + gnu_debugdata_size = shdr->sh_size; + gnu_debugdata_view_valid = 1; + } + /* Read the .opd section on PowerPC64 ELFv1. */ if (ehdr.e_machine == EM_PPC64 && (ehdr.e_flags & EF_PPC64_ABI) < 2 && shdr->sh_type == SHT_PROGBITS && strcmp (name, ".opd") == 0) { - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &opd_data.view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &opd_data.view)) goto fail; opd = &opd_data; opd->addr = shdr->sh_addr; - opd->data = (const char *) opd_data.view.data; + opd->data = (const char *) opd_data.view.view.data; opd->size = shdr->sh_size; } } @@ -2992,15 +4380,15 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } strtab_shdr = &shdrs[strtab_shndx - 1]; - if (!backtrace_get_view (state, descriptor, symtab_shdr->sh_offset, - symtab_shdr->sh_size, error_callback, data, - &symtab_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + symtab_shdr->sh_offset, symtab_shdr->sh_size, + error_callback, data, &symtab_view)) goto fail; symtab_view_valid = 1; - if (!backtrace_get_view (state, descriptor, strtab_shdr->sh_offset, - strtab_shdr->sh_size, error_callback, data, - &strtab_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + strtab_shdr->sh_offset, strtab_shdr->sh_size, + error_callback, data, &strtab_view)) goto fail; strtab_view_valid = 1; @@ -3010,8 +4398,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, goto fail; if (!elf_initialize_syminfo (state, base_address, - (const unsigned char*)symtab_view.data, symtab_shdr->sh_size, - (const unsigned char*)strtab_view.data, strtab_shdr->sh_size, + (const unsigned char*)symtab_view.view.data, symtab_shdr->sh_size, + (const unsigned char*)strtab_view.view.data, strtab_shdr->sh_size, error_callback, data, sdata, opd)) { backtrace_free (state, sdata, sizeof *sdata, error_callback, data); @@ -3020,7 +4408,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, /* We no longer need the symbol table, but we hold on to the string table permanently. */ - backtrace_release_view (state, &symtab_view, error_callback, data); + elf_release_view (state, &symtab_view, error_callback, data); symtab_view_valid = 0; strtab_view_valid = 0; @@ -3029,9 +4417,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, elf_add_syminfo_data (state, sdata); } - backtrace_release_view (state, &shdrs_view, error_callback, data); + elf_release_view (state, &shdrs_view, error_callback, data); shdrs_view_valid = 0; - backtrace_release_view (state, &names_view, error_callback, data); + elf_release_view (state, &names_view, error_callback, data); names_view_valid = 0; /* If the debug info is in a separate file, read that one instead. */ @@ -3046,19 +4434,17 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { int ret; - backtrace_release_view (state, &buildid_view, error_callback, data); + elf_release_view (state, &buildid_view, error_callback, data); if (debuglink_view_valid) - backtrace_release_view (state, &debuglink_view, error_callback, - data); + elf_release_view (state, &debuglink_view, error_callback, data); if (debugaltlink_view_valid) - backtrace_release_view (state, &debugaltlink_view, error_callback, - data); - ret = elf_add (state, "", d, base_address, error_callback, data, - fileline_fn, found_sym, found_dwarf, NULL, 0, 1, NULL, - 0); + elf_release_view (state, &debugaltlink_view, error_callback, data); + ret = elf_add (state, "", d, NULL, 0, base_address, error_callback, + data, fileline_fn, found_sym, found_dwarf, NULL, 0, + 1, NULL, 0); if (ret < 0) backtrace_close (d, error_callback, data); - else + else if (descriptor >= 0) backtrace_close (descriptor, error_callback, data); return ret; } @@ -3066,13 +4452,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (buildid_view_valid) { - backtrace_release_view (state, &buildid_view, error_callback, data); + elf_release_view (state, &buildid_view, error_callback, data); buildid_view_valid = 0; } if (opd) { - backtrace_release_view (state, &opd->view, error_callback, data); + elf_release_view (state, &opd->view, error_callback, data); opd = NULL; } @@ -3087,17 +4473,15 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { int ret; - backtrace_release_view (state, &debuglink_view, error_callback, - data); + elf_release_view (state, &debuglink_view, error_callback, data); if (debugaltlink_view_valid) - backtrace_release_view (state, &debugaltlink_view, error_callback, - data); - ret = elf_add (state, "", d, base_address, error_callback, data, - fileline_fn, found_sym, found_dwarf, NULL, 0, 1, NULL, - 0); + elf_release_view (state, &debugaltlink_view, error_callback, data); + ret = elf_add (state, "", d, NULL, 0, base_address, error_callback, + data, fileline_fn, found_sym, found_dwarf, NULL, 0, + 1, NULL, 0); if (ret < 0) backtrace_close (d, error_callback, data); - else + else if (descriptor >= 0) backtrace_close(descriptor, error_callback, data); return ret; } @@ -3105,7 +4489,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (debuglink_view_valid) { - backtrace_release_view (state, &debuglink_view, error_callback, data); + elf_release_view (state, &debuglink_view, error_callback, data); debuglink_view_valid = 0; } @@ -3119,12 +4503,11 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { int ret; - ret = elf_add (state, filename, d, base_address, error_callback, data, - fileline_fn, found_sym, found_dwarf, &fileline_altlink, - 0, 1, debugaltlink_buildid_data, - debugaltlink_buildid_size); - backtrace_release_view (state, &debugaltlink_view, error_callback, - data); + ret = elf_add (state, filename, d, NULL, 0, base_address, + error_callback, data, fileline_fn, found_sym, + found_dwarf, &fileline_altlink, 0, 1, + debugaltlink_buildid_data, debugaltlink_buildid_size); + elf_release_view (state, &debugaltlink_view, error_callback, data); debugaltlink_view_valid = 0; if (ret < 0) { @@ -3136,10 +4519,36 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (debugaltlink_view_valid) { - backtrace_release_view (state, &debugaltlink_view, error_callback, data); + elf_release_view (state, &debugaltlink_view, error_callback, data); debugaltlink_view_valid = 0; } + if (gnu_debugdata_view_valid) + { + int ret; + + ret = elf_uncompress_lzma (state, + ((const unsigned char *) + gnu_debugdata_view.view.data), + gnu_debugdata_size, error_callback, data, + &gnu_debugdata_uncompressed, + &gnu_debugdata_uncompressed_size); + + elf_release_view (state, &gnu_debugdata_view, error_callback, data); + gnu_debugdata_view_valid = 0; + + if (ret) + { + ret = elf_add (state, filename, -1, gnu_debugdata_uncompressed, + gnu_debugdata_uncompressed_size, base_address, + error_callback, data, fileline_fn, found_sym, + found_dwarf, NULL, 0, 0, NULL, 0); + if (ret >= 0 && descriptor >= 0) + backtrace_close(descriptor, error_callback, data); + return ret; + } + } + /* Read all the debug sections in a single view, since they are probably adjacent in the file. If any of sections are uncompressed, we never release this view. */ @@ -3172,8 +4581,11 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } if (min_offset == 0 || max_offset == 0) { - if (!backtrace_close (descriptor, error_callback, data)) - goto fail; + if (descriptor >= 0) + { + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + } return 1; } @@ -3183,9 +4595,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (max_offset - min_offset < 0x20000000 || max_offset - min_offset < debug_size + 0x10000) { - if (!backtrace_get_view (state, descriptor, min_offset, - max_offset - min_offset, - error_callback, data, &debug_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, min_offset, + max_offset - min_offset, error_callback, data, + &debug_view)) goto fail; debug_view_valid = 1; } @@ -3203,24 +4615,28 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, else continue; - if (!backtrace_get_view (state, descriptor, dsec->offset, dsec->size, - error_callback, data, &split_debug_view[i])) + if (!elf_get_view (state, descriptor, memory, memory_size, + dsec->offset, dsec->size, error_callback, data, + &split_debug_view[i])) goto fail; split_debug_view_valid[i] = 1; if (sections[i].size != 0) sections[i].data = ((const unsigned char *) - split_debug_view[i].data); + split_debug_view[i].view.data); else zsections[i].data = ((const unsigned char *) - split_debug_view[i].data); + split_debug_view[i].view.data); } } /* We've read all we need from the executable. */ - if (!backtrace_close (descriptor, error_callback, data)) - goto fail; - descriptor = -1; + if (descriptor >= 0) + { + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + descriptor = -1; + } using_debug_view = 0; if (debug_view_valid) @@ -3231,7 +4647,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, sections[i].data = NULL; else { - sections[i].data = ((const unsigned char *) debug_view.data + sections[i].data = ((const unsigned char *) debug_view.view.data + (sections[i].offset - min_offset)); ++using_debug_view; } @@ -3239,7 +4655,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (zsections[i].size == 0) zsections[i].data = NULL; else - zsections[i].data = ((const unsigned char *) debug_view.data + zsections[i].data = ((const unsigned char *) debug_view.view.data + (zsections[i].offset - min_offset)); } } @@ -3276,8 +4692,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (split_debug_view_valid[i]) { - backtrace_release_view (state, &split_debug_view[i], - error_callback, data); + elf_release_view (state, &split_debug_view[i], + error_callback, data); split_debug_view_valid[i] = 0; } } @@ -3316,8 +4732,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, --using_debug_view; else if (split_debug_view_valid[i]) { - backtrace_release_view (state, &split_debug_view[i], - error_callback, data); + elf_release_view (state, &split_debug_view[i], error_callback, data); split_debug_view_valid[i] = 0; } } @@ -3328,7 +4743,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (debug_view_valid && using_debug_view == 0) { - backtrace_release_view (state, &debug_view, error_callback, data); + elf_release_view (state, &debug_view, error_callback, data); debug_view_valid = 0; } @@ -3351,30 +4766,31 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, fail: if (shdrs_view_valid) - backtrace_release_view (state, &shdrs_view, error_callback, data); + elf_release_view (state, &shdrs_view, error_callback, data); if (names_view_valid) - backtrace_release_view (state, &names_view, error_callback, data); + elf_release_view (state, &names_view, error_callback, data); if (symtab_view_valid) - backtrace_release_view (state, &symtab_view, error_callback, data); + elf_release_view (state, &symtab_view, error_callback, data); if (strtab_view_valid) - backtrace_release_view (state, &strtab_view, error_callback, data); + elf_release_view (state, &strtab_view, error_callback, data); if (debuglink_view_valid) - backtrace_release_view (state, &debuglink_view, error_callback, data); + elf_release_view (state, &debuglink_view, error_callback, data); if (debugaltlink_view_valid) - backtrace_release_view (state, &debugaltlink_view, error_callback, data); + elf_release_view (state, &debugaltlink_view, error_callback, data); + if (gnu_debugdata_view_valid) + elf_release_view (state, &gnu_debugdata_view, error_callback, data); if (buildid_view_valid) - backtrace_release_view (state, &buildid_view, error_callback, data); + elf_release_view (state, &buildid_view, error_callback, data); if (debug_view_valid) - backtrace_release_view (state, &debug_view, error_callback, data); + elf_release_view (state, &debug_view, error_callback, data); for (i = 0; i < (int) DEBUG_MAX; ++i) { if (split_debug_view_valid[i]) - backtrace_release_view (state, &split_debug_view[i], - error_callback, data); + elf_release_view (state, &split_debug_view[i], error_callback, data); } if (opd) - backtrace_release_view (state, &opd->view, error_callback, data); - if (descriptor != -1) + elf_release_view (state, &opd->view, error_callback, data); + if (descriptor >= 0) backtrace_close (descriptor, error_callback, data); return 0; } @@ -3436,7 +4852,7 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, return 0; } - if (elf_add (pd->state, filename, descriptor, info->dlpi_addr, + if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr, pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym, &found_dwarf, NULL, 0, 0, NULL, 0)) { @@ -3465,7 +4881,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, fileline elf_fileline_fn = elf_nodebug; struct phdr_data pd; - ret = elf_add (state, filename, descriptor, 0, error_callback, data, + ret = elf_add (state, filename, descriptor, NULL, 0, 0, error_callback, data, &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, NULL, 0); if (!ret) diff --git a/libbacktrace/fileline.cpp b/libbacktrace/fileline.cpp index 7b3dbfbf..8645d754 100644 --- a/libbacktrace/fileline.cpp +++ b/libbacktrace/fileline.cpp @@ -1,5 +1,5 @@ /* fileline.c -- Get file and line number information in a backtrace. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -43,28 +43,15 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #endif +#ifdef HAVE_MACH_O_DYLD_H +#include +#endif + #include "backtrace.hpp" #include "internal.hpp" #ifndef HAVE_GETEXECNAME -# ifdef __APPLE__ -# include -static const char* getexecname() -{ - static char execname[PATH_MAX+1]; - uint32_t size = sizeof( execname ); - if( _NSGetExecutablePath( execname, &size ) == 0 ) - { - return execname; - } - else - { - return nullptr; - } -} -# else #define getexecname() NULL -# endif #endif namespace tracy @@ -142,6 +129,35 @@ sysctl_exec_name2 (struct backtrace_state *state, #endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ +#ifdef HAVE_MACH_O_DYLD_H + +static char * +macho_get_executable_path (struct backtrace_state *state, + backtrace_error_callback error_callback, void *data) +{ + uint32_t len; + char *name; + + len = 0; + if (_NSGetExecutablePath (NULL, &len) == 0) + return NULL; + name = (char *) backtrace_alloc (state, len, error_callback, data); + if (name == NULL) + return NULL; + if (_NSGetExecutablePath (name, &len) != 0) + { + backtrace_free (state, name, len, error_callback, data); + return NULL; + } + return name; +} + +#else /* !defined (HAVE_MACH_O_DYLD_H) */ + +#define macho_get_executable_path(state, error_callback, data) NULL + +#endif /* !defined (HAVE_MACH_O_DYLD_H) */ + /* Initialize the fileline information from the executable. Returns 1 on success, 0 on failure. */ @@ -179,7 +195,7 @@ fileline_initialize (struct backtrace_state *state, descriptor = -1; called_error_callback = 0; - for (pass = 0; pass < 7; ++pass) + for (pass = 0; pass < 8; ++pass) { int does_not_exist; @@ -208,6 +224,9 @@ fileline_initialize (struct backtrace_state *state, case 6: filename = sysctl_exec_name2 (state, error_callback, data); break; + case 7: + filename = macho_get_executable_path (state, error_callback, data); + break; default: abort (); } @@ -302,4 +321,31 @@ backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, return 1; } +/* A backtrace_syminfo_callback that can call into a + backtrace_full_callback, used when we have a symbol table but no + debug info. */ + +void +backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, + const char *symname, + uintptr_t symval ATTRIBUTE_UNUSED, + uintptr_t symsize ATTRIBUTE_UNUSED) +{ + struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; + + bdata->ret = bdata->full_callback (bdata->full_data, pc, 0, NULL, 0, symname); +} + +/* An error callback that corresponds to + backtrace_syminfo_to_full_callback. */ + +void +backtrace_syminfo_to_full_error_callback (void *data, const char *msg, + int errnum) +{ + struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; + + bdata->full_error_callback (bdata->full_data, msg, errnum); +} + } diff --git a/libbacktrace/internal.hpp b/libbacktrace/internal.hpp index 45f11831..96c097e0 100644 --- a/libbacktrace/internal.hpp +++ b/libbacktrace/internal.hpp @@ -1,5 +1,5 @@ /* internal.h -- Internal header file for stack backtrace library. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -30,8 +30,8 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TRACY_BACKTRACE_INTERNAL_H -#define TRACY_BACKTRACE_INTERNAL_H +#ifndef BACKTRACE_INTERNAL_H +#define BACKTRACE_INTERNAL_H /* We assume that and "backtrace.h" have already been included. */ @@ -56,6 +56,14 @@ POSSIBILITY OF SUCH DAMAGE. */ # endif #endif +#ifndef ATTRIBUTE_FALLTHROUGH +# if (GCC_VERSION >= 7000) +# define ATTRIBUTE_FALLTHROUGH __attribute__ ((__fallthrough__)) +# else +# define ATTRIBUTE_FALLTHROUGH +# endif +#endif + #ifndef HAVE_SYNC_FUNCTIONS /* Define out the sync functions. These should never be called if @@ -329,6 +337,31 @@ extern int backtrace_dwarf_add (struct backtrace_state *state, void *data, fileline *fileline_fn, struct dwarf_data **fileline_entry); +/* A data structure to pass to backtrace_syminfo_to_full. */ + +struct backtrace_call_full +{ + backtrace_full_callback full_callback; + backtrace_error_callback full_error_callback; + void *full_data; + int ret; +}; + +/* A backtrace_syminfo_callback that can call into a + backtrace_full_callback, used when we have a symbol table but no + debug info. */ + +extern void backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, + const char *symname, + uintptr_t symval, + uintptr_t symsize); + +/* An error callback that corresponds to + backtrace_syminfo_to_full_callback. */ + +extern void backtrace_syminfo_to_full_error_callback (void *, const char *, + int); + /* A test-only hook for elf_uncompress_zdebug. */ extern int backtrace_uncompress_zdebug (struct backtrace_state *, @@ -338,6 +371,15 @@ extern int backtrace_uncompress_zdebug (struct backtrace_state *, unsigned char **uncompressed, size_t *uncompressed_size); +/* A test-only hook for elf_uncompress_lzma. */ + +extern int backtrace_uncompress_lzma (struct backtrace_state *, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback, void *data, + unsigned char **uncompressed, + size_t *uncompressed_size); + } #endif diff --git a/libbacktrace/macho.cpp b/libbacktrace/macho.cpp index 49f7b50a..cb50dc5f 100644 --- a/libbacktrace/macho.cpp +++ b/libbacktrace/macho.cpp @@ -1,5 +1,5 @@ /* elf.c -- Get debug data from a Mach-O file for backtraces. - Copyright (C) 2020 Free Software Foundation, Inc. + Copyright (C) 2020-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -78,7 +78,7 @@ struct macho_header_64 struct macho_header_fat { - uint32_t magic; /* Magic number (MACH_O_MH_MAGIC_FAT) */ + uint32_t magic; /* Magic number (MACH_O_MH_(MAGIC|CIGAM)_FAT(_64)?) */ uint32_t nfat_arch; /* Number of components */ }; @@ -88,6 +88,8 @@ struct macho_header_fat #define MACH_O_MH_MAGIC_64 0xfeedfacf #define MACH_O_MH_MAGIC_FAT 0xcafebabe #define MACH_O_MH_CIGAM_FAT 0xbebafeca +#define MACH_O_MH_MAGIC_FAT_64 0xcafebabf +#define MACH_O_MH_CIGAM_FAT_64 0xbfbafeca /* Value for the header filetype field. */ @@ -108,6 +110,20 @@ struct macho_fat_arch uint32_t align; /* Alignment of this entry */ }; +/* A component of a 64-bit fat file. This is used if the magic field + is MAGIC_FAT_64. This is only used when some file size or file + offset is too large to represent in the 32-bit format. */ + +struct macho_fat_arch_64 +{ + uint32_t cputype; /* CPU type */ + uint32_t cpusubtype; /* CPU subtype */ + uint64_t offset; /* File offset of this entry */ + uint64_t size; /* Size of this entry */ + uint32_t align; /* Alignment of this entry */ + uint32_t reserved; /* Reserved */ +}; + /* Values for the fat_arch cputype field (and the header cputype field). */ @@ -115,9 +131,11 @@ struct macho_fat_arch #define MACH_O_CPU_TYPE_X86 7 #define MACH_O_CPU_TYPE_ARM 12 +#define MACH_O_CPU_TYPE_PPC 18 #define MACH_O_CPU_TYPE_X86_64 (MACH_O_CPU_TYPE_X86 | MACH_O_CPU_ARCH_ABI64) #define MACH_O_CPU_TYPE_ARM64 (MACH_O_CPU_TYPE_ARM | MACH_O_CPU_ARCH_ABI64) +#define MACH_O_CPU_TYPE_PPC64 (MACH_O_CPU_TYPE_PPC | MACH_O_CPU_ARCH_ABI64) /* The header of a load command. */ @@ -743,14 +761,14 @@ static int macho_add_fat (struct backtrace_state *state, const char *filename, int descriptor, int swapped, off_t offset, const unsigned char *match_uuid, uintptr_t base_address, - int skip_symtab, uint32_t nfat_arch, + int skip_symtab, uint32_t nfat_arch, int is_64, backtrace_error_callback error_callback, void *data, fileline *fileline_fn, int *found_sym) { int arch_view_valid; unsigned int cputype; + size_t arch_size; struct backtrace_view arch_view; - size_t archoffset; unsigned int i; arch_view_valid = 0; @@ -763,46 +781,69 @@ macho_add_fat (struct backtrace_state *state, const char *filename, cputype = MACH_O_CPU_TYPE_ARM64; #elif defined (__arm__) cputype = MACH_O_CPU_TYPE_ARM; +#elif defined (__ppc__) + cputype = MACH_O_CPU_TYPE_PPC; +#elif defined (__ppc64__) + cputype = MACH_O_CPU_TYPE_PPC64; #else error_callback (data, "unknown Mach-O architecture", 0); goto fail; #endif + if (is_64) + arch_size = sizeof (struct macho_fat_arch_64); + else + arch_size = sizeof (struct macho_fat_arch); + if (!backtrace_get_view (state, descriptor, offset, - nfat_arch * sizeof (struct macho_fat_arch), + nfat_arch * arch_size, error_callback, data, &arch_view)) goto fail; - archoffset = 0; for (i = 0; i < nfat_arch; ++i) { - struct macho_fat_arch fat_arch; uint32_t fcputype; + uint64_t foffset; - memcpy (&fat_arch, - ((const char *) arch_view.data - + i * sizeof (struct macho_fat_arch)), - sizeof fat_arch); + if (is_64) + { + struct macho_fat_arch_64 fat_arch_64; - fcputype = fat_arch.cputype; - if (swapped) - fcputype = __builtin_bswap32 (fcputype); + memcpy (&fat_arch_64, + (const char *) arch_view.data + i * arch_size, + arch_size); + fcputype = fat_arch_64.cputype; + foffset = fat_arch_64.offset; + if (swapped) + { + fcputype = __builtin_bswap32 (fcputype); + foffset = __builtin_bswap64 (foffset); + } + } + else + { + struct macho_fat_arch fat_arch_32; + + memcpy (&fat_arch_32, + (const char *) arch_view.data + i * arch_size, + arch_size); + fcputype = fat_arch_32.cputype; + foffset = (uint64_t) fat_arch_32.offset; + if (swapped) + { + fcputype = __builtin_bswap32 (fcputype); + foffset = (uint64_t) __builtin_bswap32 ((uint32_t) foffset); + } + } if (fcputype == cputype) { - uint32_t foffset; - /* FIXME: What about cpusubtype? */ - foffset = fat_arch.offset; - if (swapped) - foffset = __builtin_bswap32 (foffset); backtrace_release_view (state, &arch_view, error_callback, data); return macho_add (state, filename, descriptor, foffset, match_uuid, base_address, skip_symtab, error_callback, data, fileline_fn, found_sym); } - - archoffset += sizeof (struct macho_fat_arch); } error_callback (data, "could not find executable in fat file", 0); @@ -871,6 +912,7 @@ macho_add_dsym (struct backtrace_state *state, const char *filename, dsymsuffixdirlen = strlen (dsymsuffixdir); dsymlen = (dirnamelen + + 1 + basenamelen + dsymsuffixdirlen + basenamelen @@ -893,7 +935,7 @@ macho_add_dsym (struct backtrace_state *state, const char *filename, if (diralc != NULL) { - backtrace_free (state, diralc, dirnamelen, error_callback, data); + backtrace_free (state, diralc, dirnamelen + 1, error_callback, data); diralc = NULL; } @@ -983,6 +1025,7 @@ macho_add (struct backtrace_state *state, const char *filename, int descriptor, hdroffset = offset + sizeof (struct macho_header_64); break; case MACH_O_MH_MAGIC_FAT: + case MACH_O_MH_MAGIC_FAT_64: { struct macho_header_fat fat_header; @@ -990,10 +1033,12 @@ macho_add (struct backtrace_state *state, const char *filename, int descriptor, memcpy (&fat_header, &header, sizeof fat_header); return macho_add_fat (state, filename, descriptor, 0, hdroffset, match_uuid, base_address, skip_symtab, - fat_header.nfat_arch, error_callback, data, - fileline_fn, found_sym); + fat_header.nfat_arch, + header.magic == MACH_O_MH_MAGIC_FAT_64, + error_callback, data, fileline_fn, found_sym); } case MACH_O_MH_CIGAM_FAT: + case MACH_O_MH_CIGAM_FAT_64: { struct macho_header_fat fat_header; uint32_t nfat_arch; @@ -1003,8 +1048,9 @@ macho_add (struct backtrace_state *state, const char *filename, int descriptor, nfat_arch = __builtin_bswap32 (fat_header.nfat_arch); return macho_add_fat (state, filename, descriptor, 1, hdroffset, match_uuid, base_address, skip_symtab, - nfat_arch, error_callback, data, - fileline_fn, found_sym); + nfat_arch, + header.magic == MACH_O_MH_CIGAM_FAT_64, + error_callback, data, fileline_fn, found_sym); } default: error_callback (data, "executable file is not in Mach-O format", 0); diff --git a/libbacktrace/mmapio.cpp b/libbacktrace/mmapio.cpp index b452d359..0e8f599b 100644 --- a/libbacktrace/mmapio.cpp +++ b/libbacktrace/mmapio.cpp @@ -1,5 +1,5 @@ /* mmapio.c -- File views using mmap. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -40,6 +40,10 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "backtrace.hpp" #include "internal.hpp" +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize (void); +#endif + #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif @@ -101,10 +105,10 @@ backtrace_release_view (struct backtrace_state *state ATTRIBUTE_UNUSED, union { const void *cv; void *v; - }; + } cc; - cv = view->base; - if (munmap (v, view->len) < 0) + cc.cv = view->base; + if (munmap (cc.v, view->len) < 0) error_callback (data, "munmap", errno); } diff --git a/libbacktrace/posix.cpp b/libbacktrace/posix.cpp index da6e56be..8233a8ea 100644 --- a/libbacktrace/posix.cpp +++ b/libbacktrace/posix.cpp @@ -1,5 +1,5 @@ /* posix.c -- POSIX file I/O routines for the backtrace library. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -71,9 +71,9 @@ backtrace_open (const char *filename, backtrace_error_callback error_callback, if (descriptor < 0) { /* If DOES_NOT_EXIST is not NULL, then don't call ERROR_CALLBACK - if the file does not exist. We treat lacking permission to - open the file as the file not existing; this case arises when - running the libgo syscall package tests as root. */ + if the file does not exist. We treat lacking permission to + open the file as the file not existing; this case arises when + running the libgo syscall package tests as root. */ if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES)) *does_not_exist = 1; else diff --git a/libbacktrace/sort.cpp b/libbacktrace/sort.cpp index 5f1bfab8..6daee0a6 100644 --- a/libbacktrace/sort.cpp +++ b/libbacktrace/sort.cpp @@ -1,5 +1,5 @@ /* sort.c -- Sort without allocating memory - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/libbacktrace/state.cpp b/libbacktrace/state.cpp index edc31e07..ea3c137c 100644 --- a/libbacktrace/state.cpp +++ b/libbacktrace/state.cpp @@ -1,5 +1,5 @@ /* state.c -- Create the backtrace state. - Copyright (C) 2012-2020 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without