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
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
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
Proposed fix: https://reviews.llvm.org/D70894