LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 26673 - Wrong debugging information with -fsanitize=address (and memset?)
Summary: Wrong debugging information with -fsanitize=address (and memset?)
Status: RESOLVED FIXED
Alias: None
Product: clang
Classification: Unclassified
Component: -New Bugs (show other bugs)
Version: 3.7
Hardware: PC Linux
: P normal
Assignee: Johannes Altmanninger
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-02-19 16:58 PST by Peter Wu
Modified: 2019-12-03 02:42 PST (History)
2 users (show)

See Also:
Fixed By Commit(s): 09667bc1920463107684a566c3f2c3cef9b156e7


Attachments
Minimal test case (with commands and output in comments) (1.37 KB, text/x-c)
2016-02-19 16:58 PST, Peter Wu
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Peter Wu 2016-02-19 16:58:33 PST
Created attachment 15921 [details]
Minimal test case (with commands and output in comments)

Since a while (months), Clang with ASAN on Arch Linux generates objects which have wrong debugging information. The addresses seems incorrect and will result in wrong debugging output.

I have reduced it to a function containing a memset (printf does not trigger the issue for example). See the following output (argc is expected to be 1, argv an address like 0x7fffffffe418).

$ clang -fsanitize=address -g test.c && gdb -ex start -batch ./a.out
Temporary breakpoint 1 at 0x4d8ce7: file test.c, line 15.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Temporary breakpoint 1, main (argc=4289312, argv=0x4d8e6d <__libc_csu_init+77>) at test.c:35
35      int main(int argc, char *argv[]) {

$ lldb ./a.out
(lldb) target create "./a.out"
Current executable set to './a.out' (x86_64).
(lldb) breakpoint set -name main
Breakpoint 1: where = a.out`main + 23 at test.c:35, address = 0x00000000004d8ce7
(lldb) r
AddressSanitizer debugger support is active. Memory error breakpoint has been installed and you can now use the 'memory history' command.
Process 4799 launched: './a.out' (x86_64)
Process 4799 stopped
* thread #1: tid = 4919, 0x00000000004d8ce7 a.out`main(argc=4289312, argv=0x00000000004d8e6d) + 23 at test.c:35, name = 'a.out', stop reason = breakpoint 1.1
    frame #0: 0x00000000004d8ce7 a.out`main(argc=4289312, argv=0x00000000004d8e6d) + 23 at test.c:35
   32   */
   33   extern void *memset(void *, int, unsigned long);
   34  
-> 35   int main(int argc, char *argv[]) {
   36       int action;
   37       memset(&action, 0, sizeof(action));
   38       return 0;

$ pacman -Q clang lldb gdb
clang 3.7.1-1
lldb 3.7.1-1
gdb 7.10.1-1

$ clang --version
clang version 3.7.1 (tags/RELEASE_371/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Comment 1 Peter Wu 2019-07-18 20:11:00 PDT
This is still reproducible with both GDB and LLDB. It is only reproducible if an object is compiled with -fsanitize=address. Tested with:
- clang version 9.0.0 (trunk 366159) (llvm/trunk 366164)
- clang version 8.0.0 (tags/RELEASE_800/final)
- GCC 9.1.0
- GDB 8.3
- LLDB 8.0.0
- binutils 2.32

Reproducers, in either case varying the local array size still reproduces the problem:
# Reproducer 1 which I mostly used in debugging (see also below)
cat > func.c <<EOF
#include <stdio.h>
void f(int argc, const char **argv)
{
    char x[10];
    printf("%s %p\n", argv[0], x);
}
EOF
cat > main.c <<EOF
extern void f(int argc, const char **argv);
int main(int argc, const char *argv[])
{
    f(argc, argv);
}
EOF
# build files separately such that a smaller func.o can be dumped.
clang -fsanitize=address -g func.c -c
clang -fsanitize=address -g func.o main.c

# Reproducer 2
cat > x.c <<EOF
#include <stdio.h>
int main(int argc, const char *argv[])
{
    char x[100];
    printf("%s %p\n", argv[0], x);
}
EOF

Again, the expected result is that the correct parameters are reported. In practice, garbage is displayed in both "bt" (backtrace) and "frame".


When a breakpoint is set at "f", GDB stops here (at "B>"):

	.loc	1 3 0 prologue_end      # func.c:3:0
B>	cmpl	$0, (%rdx)
	movl	%edi, 100(%rbx)         # 4-byte Spill
	movq	%rsi, 88(%rbx)          # 8-byte Spill
	movq	%rcx, 80(%rbx)          # 8-byte Spill
...
	movl	100(%rbx), %r8d         # 4-byte Reload
	movl	%r8d, 124(%rbx)
	movq	88(%rbx), %rdi          # 8-byte Reload
	movq	%rdi, 112(%rbx)
	.loc	1 4 5 is_stmt 1         # func.c:4:5
	movw	$512, 2147450884(%rdx)  # imm = 0x200

After running "stepi" a couple of times, it turns out that after the instruction before ".loc 1 4 5", the correct information is displayed. With GCC however, the ".loc 1 3" marker (file 1, line 3) is correctly placed after the prologue and does contain the correct information.


Example output of "objdump -sg a.out", use the first offset between brackets to find the corresponding output in the "Contents of the .debug_info section" (e.g. find "2a" in the line starting with " 0020 "):
 <1><2a>: Abbrev Number: 2 (DW_TAG_subprogram)
    <2b>   DW_AT_low_pc      : 0x0
    <33>   DW_AT_high_pc     : 0x1a4
    <37>   DW_AT_frame_base  : 1 byte block: 56 	(DW_OP_reg6 (rbp))
    <39>   DW_AT_name        : (indirect string, offset: 0x3e): f
    <3d>   DW_AT_decl_file   : 1
    <3e>   DW_AT_decl_line   : 3
    <3f>   DW_AT_prototyped  : 1
    <3f>   DW_AT_external    : 1

With GCC, there is this line instead:
    <2f8>   DW_AT_frame_base  : 1 byte block: 9c 	(DW_OP_call_frame_cfa)

but trying to patch the Clang-generated binary (0156 -> 019c) to use this did not improve the situation.


Debugging tools:
- Dump assembly: objdump -Mintel -d a.out
- Dump full debug info and its sections: objdump -sg a.out
- Compile assembly only: clang -fsanitize=address -g -S func.c
- Dump just debug info and abbreviation:
  objdump -s -j.debug_info -j.debug_abbrev --dwarf=info,abbrev a.out
- Hex editor: "vim -b a.out", convert with ":%!xxd", reverse with ":%!xxd -r"
- Start GDB: gdb -q -ex "b f" -ex start ./a.out
- Useful GDB commands:
  start          # set temporary breakpoint at main
  b f            # set breakpoint on function f
  bt             # display backtrace
  frame          # display last frame info (like "bt 1")
  info frame     # show current Call Frame Information (CFI)
  record full    # start recording instructions to enable reverse stepping
  stepi          # step forward
  reverse-stepi  # step backwards
  layout asm     # https://sourceware.org/gdb/onlinedocs/gdb/TUI.html
  # use "ctrl-x, o" to toggle focus between the the views (for arrow keys)
  # use "ctrl-x, a" to switch between normal GDB and TUI

Spec references:
http://www.dwarfstd.org/doc/DWARF5.pdf
http://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
Comment 2 Johannes Altmanninger 2019-08-13 05:49:39 PDT
I found a similar test case that triggers the same symptoms in both gcc and Clang. Reported to gcc in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91439
Comment 3 Johannes Altmanninger 2019-12-02 04:54:22 PST
Proposed fix: https://reviews.llvm.org/D70894