From: Ian Lance Taylor Date: Mon, 6 Jul 2026 17:55:52 +0000 (-0700) Subject: libbacktrace: support returning discriminator field X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=49a5c52438b6a85b49884e596ae53e33150c296e;p=thirdparty%2Fgcc.git libbacktrace: support returning discriminator field Based on patch from Andi Kleen. * backtrace.h (backtrace_create_state): Rewrite comment to change threaded parameter to flags. (backtrace_full_callback, backtrace_syminfo_callback): Document behavior change if moredata flag is set. (struct backtrace_moredata): Define. * backtrace-supported.h.in (BACKTRACE_SUPPORTS_MOREDATA): Define. * internal.h (struct backtrace_state): Add moredata field. (BACKTRACE_MOREDATA_VERSION): Define. * state.c (backtrace_create_state): Change threaded parameter to flags. Set state moredata field based on flags. * dwarf.c (struct line): Add disc field. (struct function): Add caller_disc field. (call_callback): New static function. (add_line): Add disc parameter. Change all callers. Store disc in new line value. (read_line_program): Track discriminator value. (read_line_info): Initialize disc field in final line entry. (read_function_entry): Handle DW_AT_GNU_discriminator. (report_inlined_functions): Add disc parameter. Change all callers. Call call_callback rather than calling callback directly. (dwarf_lookup_pc): Handle inlined discriminators. Call call_callback rather than calling callback directly. (dwarf_fileline): Call call_callback rather than calling callback directly. * elf.c (elf_syminfo): Pass moredata if requested. * macho.c (macho_syminfo): Likewise. * pecoff.c (coff_syminfo): Likewise. * xcoff.c (xcoff_syminfo): Likewise. (xcoff_lookup_pc): Likewise. * backtrace.c: #include . (unwind): Pass moredata if requested. * unknown.c: #include . (unknown_fileline): Pass moredata if requested. * fileline.c (backtrace_syminfo_to_full_callback): Add comment about moredata. * mdtest.c: New test file. * Makefile.am (mdtest_SOURCES): Define. (mdtest_CFLAGS, mdtest_LDFLAGS, mdtest_LDADD): Define. (BUILDTESTS): Add mdtest. * Makefile.in: Regenerate. --- diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index 3a58461cfc0..c7bdd516016 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -345,6 +345,13 @@ endif HAVE_OBJCOPY_DEBUGLINK endif HAVE_DWZ +mdtest_SOURCES = mdtest.c testlib.c +mdtest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -O +mdtest_LDFLAGS = $(libbacktrace_testing_ldflags) +mdtest_LDADD = libbacktrace.la + +BUILDTESTS += mdtest + stest_SOURCES = stest.c stest_CFLAGS = $(libbacktrace_TEST_CFLAGS) stest_LDFLAGS = $(libbacktrace_testing_ldflags) diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index 50fe4be9686..7530c1d4daa 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -138,7 +138,7 @@ TESTS = $(am__append_4) $(MAKETESTS) $(am__EXEEXT_17) @HAVE_BUILDID_TRUE@@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_8 = b3test @HAVE_BUILDID_TRUE@@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_9 = b3test_dwz_buildid b3test_dwz_buildidfull @HAVE_ELF_TRUE@@NATIVE_TRUE@am__append_10 = btest_lto -@NATIVE_TRUE@am__append_11 = btest_alloc stest stest_alloc +@NATIVE_TRUE@am__append_11 = btest_alloc mdtest stest stest_alloc @HAVE_DWZ_TRUE@@NATIVE_TRUE@am__append_12 = btest_dwz @HAVE_DWZ_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_13 = btest_dwz_gnudebuglink @HAVE_ELF_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_14 = -lz @@ -235,8 +235,8 @@ libbacktrace_noformat_la_OBJECTS = \ @NATIVE_TRUE@ test_unknown$(EXEEXT) unittest$(EXEEXT) \ @NATIVE_TRUE@ unittest_alloc$(EXEEXT) btest$(EXEEXT) @HAVE_ELF_TRUE@@NATIVE_TRUE@am__EXEEXT_6 = btest_lto$(EXEEXT) -@NATIVE_TRUE@am__EXEEXT_7 = btest_alloc$(EXEEXT) stest$(EXEEXT) \ -@NATIVE_TRUE@ stest_alloc$(EXEEXT) +@NATIVE_TRUE@am__EXEEXT_7 = btest_alloc$(EXEEXT) mdtest$(EXEEXT) \ +@NATIVE_TRUE@ stest$(EXEEXT) stest_alloc$(EXEEXT) @HAVE_ELF_TRUE@@NATIVE_TRUE@am__EXEEXT_8 = ztest$(EXEEXT) \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ ztest_alloc$(EXEEXT) \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ zstdtest$(EXEEXT) \ @@ -396,6 +396,13 @@ m2test_OBJECTS = $(am_m2test_OBJECTS) m2test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(m2test_CFLAGS) $(CFLAGS) \ $(m2test_LDFLAGS) $(LDFLAGS) -o $@ +@NATIVE_TRUE@am_mdtest_OBJECTS = mdtest-mdtest.$(OBJEXT) \ +@NATIVE_TRUE@ mdtest-testlib.$(OBJEXT) +mdtest_OBJECTS = $(am_mdtest_OBJECTS) +@NATIVE_TRUE@mdtest_DEPENDENCIES = libbacktrace.la +mdtest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mdtest_CFLAGS) $(CFLAGS) \ + $(mdtest_LDFLAGS) $(LDFLAGS) -o $@ @NATIVE_TRUE@am_mtest_OBJECTS = mtest-mtest.$(OBJEXT) \ @NATIVE_TRUE@ mtest-testlib.$(OBJEXT) mtest_OBJECTS = $(am_mtest_OBJECTS) @@ -620,12 +627,13 @@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ $(ctestg_alloc_SOURCES) $(ctestzstd_SOURCES) \ $(ctestzstd_alloc_SOURCES) $(dwarf5_SOURCES) \ $(dwarf5_alloc_SOURCES) $(edtest_SOURCES) \ - $(edtest_alloc_SOURCES) $(m2test_SOURCES) $(mtest_SOURCES) \ - $(stest_SOURCES) $(stest_alloc_SOURCES) $(test_elf_32_SOURCES) \ - $(test_elf_64_SOURCES) $(test_macho_SOURCES) \ - $(test_pecoff_SOURCES) $(test_unknown_SOURCES) \ - $(test_xcoff_32_SOURCES) $(test_xcoff_64_SOURCES) \ - $(ttest_SOURCES) $(ttest_alloc_SOURCES) $(unittest_SOURCES) \ + $(edtest_alloc_SOURCES) $(m2test_SOURCES) $(mdtest_SOURCES) \ + $(mtest_SOURCES) $(stest_SOURCES) $(stest_alloc_SOURCES) \ + $(test_elf_32_SOURCES) $(test_elf_64_SOURCES) \ + $(test_macho_SOURCES) $(test_pecoff_SOURCES) \ + $(test_unknown_SOURCES) $(test_xcoff_32_SOURCES) \ + $(test_xcoff_64_SOURCES) $(ttest_SOURCES) \ + $(ttest_alloc_SOURCES) $(unittest_SOURCES) \ $(unittest_alloc_SOURCES) $(xztest_SOURCES) \ $(xztest_alloc_SOURCES) $(zstdtest_SOURCES) \ $(zstdtest_alloc_SOURCES) $(ztest_SOURCES) \ @@ -1148,6 +1156,10 @@ libbacktrace_testing_ldflags = -no-install @NATIVE_TRUE@btest_alloc_CFLAGS = $(libbacktrace_TEST_CFLAGS) @NATIVE_TRUE@btest_alloc_LDFLAGS = $(libbacktrace_testing_ldflags) @NATIVE_TRUE@btest_alloc_LDADD = libbacktrace_alloc.la +@NATIVE_TRUE@mdtest_SOURCES = mdtest.c testlib.c +@NATIVE_TRUE@mdtest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -O +@NATIVE_TRUE@mdtest_LDFLAGS = $(libbacktrace_testing_ldflags) +@NATIVE_TRUE@mdtest_LDADD = libbacktrace.la @NATIVE_TRUE@stest_SOURCES = stest.c @NATIVE_TRUE@stest_CFLAGS = $(libbacktrace_TEST_CFLAGS) @NATIVE_TRUE@stest_LDFLAGS = $(libbacktrace_testing_ldflags) @@ -1445,6 +1457,10 @@ m2test$(EXEEXT): $(m2test_OBJECTS) $(m2test_DEPENDENCIES) $(EXTRA_m2test_DEPENDE @rm -f m2test$(EXEEXT) $(AM_V_CCLD)$(m2test_LINK) $(m2test_OBJECTS) $(m2test_LDADD) $(LIBS) +mdtest$(EXEEXT): $(mdtest_OBJECTS) $(mdtest_DEPENDENCIES) $(EXTRA_mdtest_DEPENDENCIES) + @rm -f mdtest$(EXEEXT) + $(AM_V_CCLD)$(mdtest_LINK) $(mdtest_OBJECTS) $(mdtest_LDADD) $(LIBS) + mtest$(EXEEXT): $(mtest_OBJECTS) $(mtest_DEPENDENCIES) $(EXTRA_mtest_DEPENDENCIES) @rm -f mtest$(EXEEXT) $(AM_V_CCLD)$(mtest_LINK) $(mtest_OBJECTS) $(mtest_LDADD) $(LIBS) @@ -1756,6 +1772,18 @@ m2test-testlib.o: testlib.c m2test-testlib.obj: testlib.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(m2test_CFLAGS) $(CFLAGS) -c -o m2test-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` +mdtest-mdtest.o: mdtest.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-mdtest.o `test -f 'mdtest.c' || echo '$(srcdir)/'`mdtest.c + +mdtest-mdtest.obj: mdtest.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-mdtest.obj `if test -f 'mdtest.c'; then $(CYGPATH_W) 'mdtest.c'; else $(CYGPATH_W) '$(srcdir)/mdtest.c'; fi` + +mdtest-testlib.o: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +mdtest-testlib.obj: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mdtest_CFLAGS) $(CFLAGS) -c -o mdtest-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + mtest-mtest.o: mtest.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mtest_CFLAGS) $(CFLAGS) -c -o mtest-mtest.o `test -f 'mtest.c' || echo '$(srcdir)/'`mtest.c @@ -2354,6 +2382,13 @@ btest_alloc.log: btest_alloc$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +mdtest.log: mdtest$(EXEEXT) + @p='mdtest$(EXEEXT)'; \ + b='mdtest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) stest.log: stest$(EXEEXT) @p='stest$(EXEEXT)'; \ b='stest'; \ diff --git a/libbacktrace/backtrace-supported.h.in b/libbacktrace/backtrace-supported.h.in index 456c329f85d..e5ecf87f46a 100644 --- a/libbacktrace/backtrace-supported.h.in +++ b/libbacktrace/backtrace-supported.h.in @@ -35,7 +35,6 @@ POSSIBILITY OF SUCH DAMAGE. */ be #include'd to see whether the backtrace library will be able to get a backtrace and produce symbolic information. */ - /* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library should work, 0 if it will not. Libraries may #include this to make other arrangements. */ @@ -60,7 +59,12 @@ POSSIBILITY OF SUCH DAMAGE. */ #define BACKTRACE_SUPPORTS_THREADS @BACKTRACE_SUPPORTS_THREADS@ -/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo +/* BACKTRACE_SUPPORTS_DATA will be #define'd as 1 if the backtrace_syminfo will work for variables. It will always work for functions. */ #define BACKTRACE_SUPPORTS_DATA @BACKTRACE_SUPPORTS_DATA@ + +/* BACKTRACE_SUPPORTS_MOREDATA will be #define'd as 1 if + backtrace_create_state supports setting the MOREDATA flag. */ + +#define BACKTRACE_SUPPORTS_MOREDATA 1 diff --git a/libbacktrace/backtrace.c b/libbacktrace/backtrace.c index 3935a3ed968..b4261c438c4 100644 --- a/libbacktrace/backtrace.c +++ b/libbacktrace/backtrace.c @@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" +#include #include #include "unwind.h" @@ -85,11 +86,24 @@ unwind (struct _Unwind_Context *context, void *vdata) if (!ip_before_insn) --pc; - if (!bdata->can_alloc) - bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL); - else + if (bdata->can_alloc) bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback, bdata->error_callback, bdata->data); + else + { + if (!bdata->state->moredata) + bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL); + else + { + struct backtrace_moredata md; + + memset (&md, 0, sizeof md); + md.backtrace_version = BACKTRACE_MOREDATA_VERSION; + md.backtrace_data = bdata->data; + bdata->ret = bdata->callback (&md, pc, NULL, 0, NULL); + } + } + if (bdata->ret != 0) return _URC_END_OF_STACK; diff --git a/libbacktrace/backtrace.h b/libbacktrace/backtrace.h index fd0c659decc..9ca12730a7c 100644 --- a/libbacktrace/backtrace.h +++ b/libbacktrace/backtrace.h @@ -85,15 +85,41 @@ typedef void (*backtrace_error_callback) (void *data, const char *msg, /* Create state information for the backtrace routines. This must be called before any of the other routines, and its return value must - be passed to all of the other routines. FILENAME is the path name - of the executable file; if it is NULL the library will try - system-specific path names. If not NULL, FILENAME must point to a - permanent buffer. If THREADED is non-zero the state may be - accessed by multiple threads simultaneously, and the library will - use appropriate atomic operations. If THREADED is zero the state - may only be accessed by one thread at a time. This returns a state - pointer on success, NULL on error. If an error occurs, this will - call the ERROR_CALLBACK routine. + be passed to all of the other routines. + + FILENAME is the path name of the executable file; if it is NULL the + library will try system-specific path names. If not NULL, FILENAME + must point to a permanent buffer. + + FLAGS passes flags as bits in an int value: + 1: THREADED + 2: MOREDATA + + If (FLAGS & 1) != 0 the THREADED flag is set. If this flag is set, + the state may be accessed by multiple threads simultaneously, and the + library will use appropriate atomic operations. If THREADED is not + set the state may only be accessed by one thread at a time. + + If (FLAGS & 2) != 0 the MOREDATA flag is set. If this flag is set, + then backtrace_full_callback and backtrace_symbol_callback will not + pass the DATA argument as the user-specified DATA value, but will + instead pass it as a pointer to a backtrace_moredata struct. This is + a backward compatible approach to getting more data from the various + backtrace functions. + + Historical note: in previous versions (before July, 2026) the FLAGS + argument was named THREADED, and passing non-zero for THREADED was + documented as doing what setting the THREADED flag does today. In + practice all callers passed either 0 or 1, so the new semantics of + the flag do not affect users of old versions of the library. + However, code that passes the MOREDATA flag must ensure that it is + using a new version of the library. The backtrace-supported.h file + will #define BACKTRACE_SUPPORTS_MOREDATA as 1 for versions of + libbacktrace that support the MOREDATA flag. + + The backtrace_create_state function returns a state pointer on + success, NULL on error. If an error occurs, it will call the + ERROR_CALLBACK routine before returning. Calling this function allocates resources that cannot be freed. There is no backtrace_free_state function. The state is used to @@ -106,13 +132,15 @@ extern struct backtrace_state *backtrace_create_state ( backtrace_error_callback error_callback, void *data); /* The type of the callback argument to the backtrace_full function. - DATA is the argument passed to backtrace_full. PC is the program - counter. FILENAME is the name of the file containing PC, or NULL - if not available. LINENO is the line number in FILENAME containing - PC, or 0 if not available. FUNCTION is the name of the function - containing PC, or NULL if not available. This should return 0 to - continuing tracing. The FILENAME and FUNCTION buffers may become - invalid after this function returns. */ + DATA is either the argument passed to backtrace_full (if the MOREDATA + flag was not set when calling backtrace_create_state) or a pointer to + a backtrace_moredata struct. PC is the program counter. FILENAME is + the name of the file containing PC, or NULL if not available. LINENO + is the line number in FILENAME containing PC, or 0 if not available. + FUNCTION is the name of the function containing PC, or NULL if not + available. This should return 0 to continuing tracing. The FILENAME + and FUNCTION buffers may become invalid after this function + returns. */ typedef int (*backtrace_full_callback) (void *data, uintptr_t pc, const char *filename, int lineno, @@ -173,11 +201,14 @@ extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, backtrace_error_callback error_callback, void *data); -/* The type of the callback argument to backtrace_syminfo. DATA and - PC are the arguments passed to backtrace_syminfo. SYMNAME is the - name of the symbol for the corresponding code. SYMVAL is the - value and SYMSIZE is the size of the symbol. SYMNAME will be NULL - if no error occurred but the symbol could not be found. */ +/* The type of the callback argument to backtrace_syminfo. DATA is + either the argument passed to backtrace_full (if the MOREDATA flag + was not set when calling backtrace_create_state) or a pointer to a + backtrace_moredata struct. PC is the the argument passed to + backtrace_syminfo. SYMNAME is the name of the symbol for the + corresponding code. SYMVAL is the value and SYMSIZE is the size of + the symbol. SYMNAME will be NULL if no error occurred but the symbol + could not be found. */ typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc, const char *symname, @@ -199,6 +230,31 @@ extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr, backtrace_error_callback error_callback, void *data); +/* The type of the value that the DATA argument passed to + backtrace_full_callback or backtrace_syminfo_callback points to if + the MOREDATA flag is set in the call to backtrace_create_state. + + The backtrace_moredata value will only be valid for the lifetime of + the callback; the callback may copy the data out but must not save + the pointer it receives. */ + +struct backtrace_moredata +{ + /* The version of this struct. The current expectation is that the + version number will be the number of fields in the struct. It's + possible that future versions of libbacktrace will add new fields + and increment the version number accordingly. There is no plan to + remove fields from this struct. Thus the current value of the + version field will be 3. */ + int backtrace_version; + /* The DATA value passed to whatever function is calling the callback + (backtrace_full, backtrace_pcinfo, or backtrace_syminfo). */ + void *backtrace_data; + /* The DWARF discriminator. This is zero if there is none. See + https://wiki.dwarfstd.org/Path_Discriminators.md. */ + unsigned int backtrace_discriminator; +}; + #ifdef __cplusplus } /* End extern "C". */ #endif diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c index ecf32f7688b..0d29443d7aa 100644 --- a/libbacktrace/dwarf.c +++ b/libbacktrace/dwarf.c @@ -234,6 +234,8 @@ struct line const char *filename; /* Line number. */ int lineno; + /* Discriminator. */ + int disc; /* Index of the object in the original array read from the DWARF section, before it has been sorted. The index makes it possible to use Quicksort and maintain stability. */ @@ -263,6 +265,9 @@ struct function /* If this is an inlined function, the line number of the call site. */ int caller_lineno; + /* If this is an inlined function, the discriminator of the call + site. */ + unsigned int caller_disc; /* Map PC ranges to inlined functions. */ struct function_addrs *function_addrs; size_t function_addrs_count; @@ -726,6 +731,28 @@ read_initial_length (struct dwarf_buf *buf, int *is_dwarf64) return len; } +/* Call the callback function, which differs based on the moredata state + flag. */ + +static int +call_callback (struct backtrace_state *state, backtrace_full_callback callback, + void *data, uintptr_t pc, const char *filename, int lineno, + const char *function, unsigned int disc) +{ + if (!state->moredata) + return callback (data, pc, filename, lineno, function); + else + { + struct backtrace_moredata md; + + memset (&md, 0, sizeof md); + md.backtrace_version = BACKTRACE_MOREDATA_VERSION; + md.backtrace_data = data; + md.backtrace_discriminator = disc; + return callback ((void *) &md, pc, filename, lineno, function); + } +} + /* Free an abbreviations structure. */ static void @@ -2484,7 +2511,7 @@ build_address_map (struct backtrace_state *state, static int add_line (struct backtrace_state *state, struct dwarf_data *ddata, - uintptr_t pc, const char *filename, int lineno, + uintptr_t pc, const char *filename, int lineno, int disc, backtrace_error_callback error_callback, void *data, struct line_vector *vec) { @@ -2496,7 +2523,13 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata, { ln = (struct line *) vec->vec.base + (vec->count - 1); if (pc == ln->pc && filename == ln->filename && lineno == ln->lineno) - return 1; + { + /* We only care about the discriminator if moredata is true. */ + if (!state->moredata) + return 1; + if (disc == ln->disc) + return 1; + } } ln = ((struct line *) @@ -2511,6 +2544,7 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata, ln->filename = filename; ln->lineno = lineno; + ln->disc = disc; ln->idx = vec->count; ++vec->count; @@ -2929,6 +2963,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, const char *reset_filename; const char *filename; int lineno; + unsigned int disc; address = 0; op_index = 0; @@ -2938,6 +2973,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, reset_filename = ""; filename = reset_filename; lineno = 1; + disc = 0; while (line_buf->left > 0) { unsigned int op; @@ -2954,8 +2990,9 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, / hdr->max_ops_per_insn); op_index = (op_index + advance) % hdr->max_ops_per_insn; lineno += hdr->line_base + (int) (op % hdr->line_range); - add_line (state, ddata, address, filename, lineno, + add_line (state, ddata, address, filename, lineno, disc, line_buf->error_callback, line_buf->data, vec); + disc = 0; } else if (op == DW_LNS_extended_op) { @@ -2973,6 +3010,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, op_index = 0; filename = reset_filename; lineno = 1; + disc = 0; break; case DW_LNE_set_address: address = read_address (line_buf, hdr->addrsize); @@ -3028,8 +3066,7 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, } break; case DW_LNE_set_discriminator: - /* We don't care about discriminators. */ - read_uleb128 (line_buf); + disc = read_uleb128 (line_buf); break; default: if (!advance (line_buf, len - 1)) @@ -3042,8 +3079,9 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, switch (op) { case DW_LNS_copy: - add_line (state, ddata, address, filename, lineno, + add_line (state, ddata, address, filename, lineno, disc, line_buf->error_callback, line_buf->data, vec); + disc = 0; break; case DW_LNS_advance_pc: { @@ -3183,6 +3221,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata, ln->pc = (uintptr_t) -1; ln->filename = NULL; ln->lineno = 0; + ln->disc = 0; ln->idx = 0; if (!backtrace_vector_release (state, &vec.vec, error_callback, data)) @@ -3512,6 +3551,11 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, } break; + case DW_AT_GNU_discriminator: + if (val.encoding == ATTR_VAL_UINT) + function->caller_disc = val.u.uint; + break; + case DW_AT_call_line: if (val.encoding == ATTR_VAL_UINT) function->caller_lineno = val.u.uint; @@ -3752,13 +3796,15 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata, } /* See if PC is inlined in FUNCTION. If it is, print out the inlined - information, and update FILENAME and LINENO for the caller. + information, and update FILENAME, LINENO, and DISC for the caller. Returns whatever CALLBACK returns, or 0 to keep going. */ static int -report_inlined_functions (uintptr_t pc, struct function *function, +report_inlined_functions (struct backtrace_state *state, uintptr_t pc, + struct function *function, backtrace_full_callback callback, void *data, - const char **filename, int *lineno) + const char **filename, int *lineno, + unsigned int *disc) { struct function_addrs *p; struct function_addrs *match; @@ -3810,20 +3856,22 @@ report_inlined_functions (uintptr_t pc, struct function *function, inlined = match->function; /* Report any calls inlined into this one. */ - ret = report_inlined_functions (pc, inlined, callback, data, - filename, lineno); + ret = report_inlined_functions (state, pc, inlined, callback, data, + filename, lineno, disc); if (ret != 0) return ret; /* Report this inlined call. */ - ret = callback (data, pc, *filename, *lineno, inlined->name); + ret = call_callback (state, callback, data, pc, *filename, *lineno, + inlined->name, *disc); if (ret != 0) return ret; /* Our caller will report the caller of the inlined function; tell - it the appropriate filename and line number. */ + it the appropriate filename, line number, and discriminator. */ *filename = inlined->caller_filename; *lineno = inlined->caller_lineno; + *disc = inlined->caller_disc; return 0; } @@ -3850,6 +3898,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, struct function *function; const char *filename; int lineno; + unsigned int disc; int ret; *found = 1; @@ -3991,7 +4040,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, if (new_data) return dwarf_lookup_pc (state, ddata, pc, callback, error_callback, data, found); - return callback (data, pc, NULL, 0, NULL); + return call_callback (state, callback, data, pc, NULL, 0, NULL, 0); } /* Search for PC within this unit. */ @@ -4038,13 +4087,15 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, entry->u->abs_filename = filename; } - return callback (data, pc, entry->u->abs_filename, 0, NULL); + return call_callback (state, callback, data, pc, entry->u->abs_filename, + 0, NULL, 0); } /* Search for function name within this unit. */ if (entry->u->function_addrs_count == 0) - return callback (data, pc, ln->filename, ln->lineno, NULL); + return call_callback (state, callback, data, pc, ln->filename, ln->lineno, + NULL, ln->disc); p = ((struct function_addrs *) bsearch (&pc, entry->u->function_addrs, @@ -4052,7 +4103,8 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, sizeof (struct function_addrs), function_addrs_search)); if (p == NULL) - return callback (data, pc, ln->filename, ln->lineno, NULL); + return call_callback (state, callback, data, pc, ln->filename, ln->lineno, + NULL, ln->disc); /* 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 @@ -4076,19 +4128,22 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, --p; } if (fmatch == NULL) - return callback (data, pc, ln->filename, ln->lineno, NULL); + return call_callback (state, callback, data, pc, ln->filename, ln->lineno, + NULL, ln->disc); function = fmatch->function; filename = ln->filename; lineno = ln->lineno; + disc = ln->disc; - ret = report_inlined_functions (pc, function, callback, data, - &filename, &lineno); + ret = report_inlined_functions (state, pc, function, callback, data, + &filename, &lineno, &disc); if (ret != 0) return ret; - return callback (data, pc, filename, lineno, function->name); + return call_callback (state, callback, data, pc, filename, lineno, + function->name, disc); } @@ -4138,7 +4193,7 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, /* FIXME: See if any libraries have been dlopen'ed. */ - return callback (data, pc, NULL, 0, NULL); + return call_callback (state, callback, data, pc, NULL, 0, NULL, 0); } /* Initialize our data structures from the DWARF debug info for a diff --git a/libbacktrace/elf.c b/libbacktrace/elf.c index e1c34584578..60489327024 100644 --- a/libbacktrace/elf.c +++ b/libbacktrace/elf.c @@ -767,6 +767,8 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr, { struct elf_syminfo_data *edata; struct elf_symbol *sym = NULL; + void *mdata; + struct backtrace_moredata md; if (!state->threaded) { @@ -802,10 +804,20 @@ elf_syminfo (struct backtrace_state *state, uintptr_t addr, } } + if (!state->moredata) + mdata = data; + else + { + memset (&md, 0, sizeof md); + md.backtrace_version = BACKTRACE_MOREDATA_VERSION; + md.backtrace_data = data; + mdata = (void *) &md; + } + if (sym == NULL) - callback (data, addr, NULL, 0, 0); + callback (mdata, addr, NULL, 0, 0); else - callback (data, addr, sym->name, sym->address, sym->size); + callback (mdata, addr, sym->name, sym->address, sym->size); } /* Return whether FILENAME is a symlink. */ diff --git a/libbacktrace/fileline.c b/libbacktrace/fileline.c index 82535e3f6cf..18bd87d65b3 100644 --- a/libbacktrace/fileline.c +++ b/libbacktrace/fileline.c @@ -425,6 +425,9 @@ backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, { struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; + /* If STATE->MOREDATA is set, then data will point to a + backtrace_moredata struct, which is what full_callback expects. */ + bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname); } diff --git a/libbacktrace/internal.h b/libbacktrace/internal.h index 09a2147fdef..355d2a353fe 100644 --- a/libbacktrace/internal.h +++ b/libbacktrace/internal.h @@ -143,6 +143,8 @@ struct backtrace_state const char *filename; /* Non-zero if threaded. */ int threaded; + /* Non-zero if passing additional data. */ + int moredata; /* The master lock for fileline_fn, fileline_data, syminfo_fn, syminfo_data, fileline_initialization_failed and everything the data pointers point to. */ @@ -425,4 +427,7 @@ extern int backtrace_uncompress_lzma (struct backtrace_state *, unsigned char **uncompressed, size_t *uncompressed_size); +/* Current expected backtrace_moredata version. */ +#define BACKTRACE_MOREDATA_VERSION (3) + #endif diff --git a/libbacktrace/macho.c b/libbacktrace/macho.c index 5eab4a6c7f8..5a951aadf42 100644 --- a/libbacktrace/macho.c +++ b/libbacktrace/macho.c @@ -711,6 +711,8 @@ macho_syminfo (struct backtrace_state *state, uintptr_t addr, { struct macho_syminfo_data *sdata; struct macho_symbol *sym; + void *mdata; + struct backtrace_moredata md; sym = NULL; if (!state->threaded) @@ -747,10 +749,20 @@ macho_syminfo (struct backtrace_state *state, uintptr_t addr, } } + if (!state->moredata) + mdata = data; + else + { + memset (&md, 0, sizeof md); + md.backtrace_version = BACKTRACE_MOREDATA_VERSION; + md.backtrace_data = data; + mdata = (void *) &md; + } + if (sym == NULL) - callback (data, addr, NULL, 0, 0); + callback (mdata, addr, NULL, 0, 0); else - callback (data, addr, sym->name, sym->address, 0); + callback (mdata, addr, sym->name, sym->address, 0); } /* Look through a fat file to find the relevant executable. Returns 1 diff --git a/libbacktrace/mdtest.c b/libbacktrace/mdtest.c new file mode 100644 index 00000000000..5f6665f1e2b --- /dev/null +++ b/libbacktrace/mdtest.c @@ -0,0 +1,290 @@ +/* mdtest.c -- Test for libbacktrace library with moredata flag + Copyright (C) 2026 Free Software Foundation, Inc. + Written by Ian Lance Taylor. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + (1) Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + (2) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +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. */ + +/* This program tests the externally visible interfaces of the + libbacktrace library. */ + +#include +#include +#include +#include +#include +#include + +#include "filenames.h" + +#include "backtrace.h" +#include "backtrace-supported.h" +#include "internal.h" + +#include "testlib.h" + +/* backtrace_full_callback with moredata flag. */ + +static int +md_callback_one (void *vdata, uintptr_t pc, const char *filename, int lineno, + const char *function) +{ + struct backtrace_moredata *md = (struct backtrace_moredata *) vdata; + struct bdata *data = (struct bdata *) md->backtrace_data; + + if (md->backtrace_version != BACKTRACE_MOREDATA_VERSION) + { + fprintf (stderr, "md_callback_one: wrong moredata version: got %u, want %u\n", md->backtrace_version, BACKTRACE_MOREDATA_VERSION); + data->failed = 1; + return 1; + } + + return callback_one ((void *) data, pc, filename, lineno, function); +} + +/* backtrace_syminfo_callback with moredata flag. */ + +static void +md_callback_three (void *vdata, uintptr_t pc, const char *symname, + uintptr_t symval, uintptr_t symsize) +{ + struct backtrace_moredata *md = (struct backtrace_moredata *) vdata; + struct symdata *data = (struct symdata *) md->backtrace_data; + + if (md->backtrace_version != BACKTRACE_MOREDATA_VERSION) + { + fprintf (stderr, "md_callback_one: wrong moredata version: got %u, want %u\n", md->backtrace_version, BACKTRACE_MOREDATA_VERSION); + return; + } + + callback_three ((void *) data, pc, symname, symval, symsize); +} + +/* Test the backtrace function with non-inlined functions. */ + +static int test1 (void) __attribute__ ((noinline, noclone, optnone, unused)); +static int f2 (int) __attribute__ ((noinline, noclone)); +static int f3 (int, int) __attribute__ ((noinline, noclone)); + +static int +test1 (void) +{ + /* Returning a value here and elsewhere avoids a tailcall which + would mess up the backtrace. */ + return f2 (__LINE__) + 1; +} + +static int +f2 (int f1line) +{ + return f3 (f1line, __LINE__) + 2; +} + +static int +f3 (int f1line, int f2line) +{ + struct info all[20]; + struct bdata data; + int f3line; + int i; + + data.all = &all[0]; + data.index = 0; + data.max = 20; + data.failed = 0; + + f3line = __LINE__ + 1; + i = backtrace_full (state, 0, md_callback_one, error_callback_one, &data); + + if (i != 0) + { + fprintf (stderr, "test1: unexpected return value %d\n", i); + data.failed = 1; + } + + if (data.index < 3) + { + fprintf (stderr, + "test1: not enough frames; got %zu, expected at least 3\n", + data.index); + data.failed = 1; + } + + check ("test1", 0, all, f3line, "f3", "mdtest.c", &data.failed); + check ("test1", 1, all, f2line, "f2", "mdtest.c", &data.failed); + check ("test1", 2, all, f1line, "test1", "mdtest.c", &data.failed); + + printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS"); + + if (data.failed) + ++failures; + + return failures; +} + +/* Test the backtrace function with inlined functions. */ + +static inline int test2 (void) __attribute__ ((always_inline, unused)); +static inline int f12 (int) __attribute__ ((always_inline)); +static inline int f13 (int, int) __attribute__ ((always_inline)); + +static inline int +test2 (void) +{ + return f12 (__LINE__) + 1; +} + +static inline int +f12 (int f1line) +{ + return f13 (f1line, __LINE__) + 2; +} + +static inline int +f13 (int f1line, int f2line) +{ + struct info all[20]; + struct bdata data; + int f3line; + int i; + + data.all = &all[0]; + data.index = 0; + data.max = 20; + data.failed = 0; + + f3line = __LINE__ + 1; + i = backtrace_full (state, 0, md_callback_one, error_callback_one, &data); + + if (i != 0) + { + fprintf (stderr, "test2: unexpected return value %d\n", i); + data.failed = 1; + } + + check ("test2", 0, all, f3line, "f13", "mdtest.c", &data.failed); + check ("test2", 1, all, f2line, "f12", "mdtest.c", &data.failed); + check ("test2", 2, all, f1line, "test2", "mdtest.c", &data.failed); + + printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS"); + + if (data.failed) + ++failures; + + return failures; +} + +static int test5 (void) __attribute__ ((unused)); + +int global = 1; + +static int +test5 (void) +{ + struct symdata symdata; + int i; + uintptr_t addr = (uintptr_t) &global; + + if (sizeof (global) > 1) + addr += 1; + + symdata.name = NULL; + symdata.val = 0; + symdata.size = 0; + symdata.failed = 0; + + i = backtrace_syminfo (state, addr, md_callback_three, + error_callback_three, &symdata); + if (i == 0) + { + fprintf (stderr, + "test5: unexpected return value from backtrace_syminfo %d\n", + i); + symdata.failed = 1; + } + + if (!symdata.failed) + { + if (symdata.name == NULL) + { + fprintf (stderr, "test5: NULL syminfo name\n"); + symdata.failed = 1; + } + else if (!(strncmp (symdata.name, "global", 6) == 0 + && (symdata.name[6] == '\0'|| symdata.name[6] == '.'))) + { + fprintf (stderr, + "test5: unexpected syminfo name got %s expected %s\n", + symdata.name, "global"); + symdata.failed = 1; + } + else if (symdata.val != (uintptr_t) &global) + { + fprintf (stderr, + "test5: unexpected syminfo value got %lx expected %lx\n", + (unsigned long) symdata.val, + (unsigned long) (uintptr_t) &global); + symdata.failed = 1; + } + else if (symdata.size != sizeof (global) && symdata.size != 0) + { + fprintf (stderr, + "test5: unexpected syminfo size got %lx expected %lx\n", + (unsigned long) symdata.size, + (unsigned long) sizeof (global)); + symdata.failed = 1; + } + } + + printf ("%s: backtrace_syminfo variable\n", + symdata.failed ? "FAIL" : "PASS"); + + if (symdata.failed) + ++failures; + + return failures; +} + +/* Run all the tests. */ + +int +main (int argc ATTRIBUTE_UNUSED, char **argv) +{ + state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS | 2, + error_callback_create, NULL); + +#if BACKTRACE_SUPPORTED + test1 (); + test2 (); +#if BACKTRACE_SUPPORTS_DATA + test5 (); +#endif +#endif + + exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/libbacktrace/pecoff.c b/libbacktrace/pecoff.c index f2b1aa5023a..08ec4c2ba13 100644 --- a/libbacktrace/pecoff.c +++ b/libbacktrace/pecoff.c @@ -587,6 +587,8 @@ coff_syminfo (struct backtrace_state *state, uintptr_t addr, { struct coff_syminfo_data *sdata; struct coff_symbol *sym = NULL; + void *mdata; + struct backtrace_moredata md; if (!state->threaded) { @@ -622,10 +624,20 @@ coff_syminfo (struct backtrace_state *state, uintptr_t addr, } } + if (!state->moredata) + mdata = data; + else + { + memset (&md, 0, sizeof md); + md.backtrace_version = 3; + md.backtrace_data = data; + mdata = (void *) &md; + } + if (sym == NULL) - callback (data, addr, NULL, 0, 0); + callback (mdata, addr, NULL, 0, 0); else - callback (data, addr, sym->name, sym->address, 0); + callback (mdata, addr, sym->name, sym->address, 0); } /* Add the backtrace data for one PE/COFF file. Returns 1 on success, diff --git a/libbacktrace/state.c b/libbacktrace/state.c index 8c201624006..491a14dc156 100644 --- a/libbacktrace/state.c +++ b/libbacktrace/state.c @@ -43,13 +43,23 @@ POSSIBILITY OF SUCH DAMAGE. */ other routines. */ struct backtrace_state * -backtrace_create_state (const char *filename, int threaded, +backtrace_create_state (const char *filename, int flags, backtrace_error_callback error_callback, void *data) { + int threaded; + int moredata; struct backtrace_state init_state; struct backtrace_state *state; + threaded = (flags & 1) != 0; + moredata = (flags & 2) != 0; + if ((flags & ~3) != 0) + { + error_callback (data, "backtrace_create_state: unsupported flag", 0); + return NULL; + } + #ifndef HAVE_SYNC_FUNCTIONS if (threaded) { @@ -61,6 +71,7 @@ backtrace_create_state (const char *filename, int threaded, memset (&init_state, 0, sizeof init_state); init_state.filename = filename; init_state.threaded = threaded; + init_state.moredata = moredata; state = ((struct backtrace_state *) backtrace_alloc (&init_state, sizeof *state, error_callback, data)); diff --git a/libbacktrace/unknown.c b/libbacktrace/unknown.c index 4834ec92c18..80dc19d640d 100644 --- a/libbacktrace/unknown.c +++ b/libbacktrace/unknown.c @@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" +#include #include #include "backtrace.h" @@ -40,13 +41,23 @@ POSSIBILITY OF SUCH DAMAGE. */ /* A trivial routine that always fails to find fileline data. */ static int -unknown_fileline (struct backtrace_state *state ATTRIBUTE_UNUSED, - uintptr_t pc, backtrace_full_callback callback, +unknown_fileline (struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) { - return callback (data, pc, NULL, 0, NULL); + if (!state->moredata) + return callback (data, pc, NULL, 0, NULL); + else + { + struct backtrace_moredata md; + + memset (&md, 0, sizeof md); + md.backtrace_version = 3; + md.backtrace_data = data; + return callback ((void *) &md, pc, NULL, 0, NULL); + } } /* Initialize the backtrace data when we don't know how to read the diff --git a/libbacktrace/xcoff.c b/libbacktrace/xcoff.c index 874642ab354..43f78398762 100644 --- a/libbacktrace/xcoff.c +++ b/libbacktrace/xcoff.c @@ -512,6 +512,8 @@ xcoff_syminfo (struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr, struct xcoff_syminfo_data *edata; struct xcoff_symbol *sym = NULL; const char *name; + void *mdata; + struct backtrace_moredata md; if (!state->threaded) { @@ -547,15 +549,25 @@ xcoff_syminfo (struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr, } } + if (!state->moredata) + mdata = data; + else + { + memset (&md, 0, sizeof md); + md.backtrace_version = 3; + md.backtrace_data = data; + mdata = (void *) &md; + } + if (sym == NULL) - callback (data, addr, NULL, 0, 0); + callback (mdata, addr, NULL, 0, 0); else { name = sym->name; /* AIX prepends a '.' to function entry points, remove it. */ if (name && *name == '.') ++name; - callback (data, addr, name, sym->address, sym->size); + callback (mdata, addr, name, sym->address, sym->size); } } @@ -723,7 +735,7 @@ xcoff_incl_search (const void *vkey, const void *ventry) 0 if not. */ static int -xcoff_lookup_pc (struct backtrace_state *state ATTRIBUTE_UNUSED, +xcoff_lookup_pc (struct backtrace_state *state, struct xcoff_fileline_data *fdata, uintptr_t pc, backtrace_full_callback callback, backtrace_error_callback error_callback ATTRIBUTE_UNUSED, @@ -805,7 +817,18 @@ xcoff_lookup_pc (struct backtrace_state *state ATTRIBUTE_UNUSED, /* AIX prepends a '.' to function entry points, remove it. */ if (function != NULL && *function == '.') ++function; - return callback (data, pc, filename, lnno, function); + + if (!state->moredata) + return callback (data, pc, filename, lnno, function); + else + { + struct backtrace_moredata md; + + memset (&md, 0, sizeof md); + md.backtrace_version = 3; + md.backtrace_data = data; + return callback ((void *) &md, pc, filename, lnno, function); + } } /* Return the file/line information for a PC using the XCOFF lineno @@ -855,7 +878,17 @@ xcoff_fileline (struct backtrace_state *state, uintptr_t pc, /* FIXME: See if any libraries have been dlopen'ed. */ - return callback (data, pc, NULL, 0, NULL); + if (!state->moredata) + return callback (data, pc, NULL, 0, NULL); + else + { + struct backtrace_moredata md; + + memset (&md, 0, sizeof md); + md.backtrace_version = 3; + md.backtrace_data = data; + return callback ((void *) data, pc, NULL, 0, NULL); + } } /* Initialize the function vector info for xcoff_fileline. */