Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong debugging information with -fsanitize=address (and memset?) #27047

Closed
Lekensteyn opened this issue Feb 20, 2016 · 4 comments
Closed

Wrong debugging information with -fsanitize=address (and memset?) #27047

Lekensteyn opened this issue Feb 20, 2016 · 4 comments
Assignees
Labels
bugzilla Issues migrated from bugzilla clang Clang issues not falling into any other category

Comments

@Lekensteyn
Copy link
Contributor

Bugzilla Link 26673
Resolution FIXED
Resolved on Dec 03, 2019 02:42
Version 3.7
OS Linux
Attachments Minimal test case (with commands and output in comments)
CC @krobelus
Fixed by commit(s) 09667bc

Extended Description

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.outmain(argc=4289312, argv=0x00000000004d8e6d) + 23 at test.c:35, name = 'a.out', stop reason = breakpoint 1.1 frame #&#8203;0: 0x00000000004d8ce7 a.outmain(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

@Lekensteyn
Copy link
Contributor Author

assigned to @krobelus

@Lekensteyn
Copy link
Contributor Author

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

@krobelus
Copy link
Member

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

@krobelus
Copy link
Member

krobelus commented Dec 2, 2019

Proposed fix: https://reviews.llvm.org/D70894

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla clang Clang issues not falling into any other category
Projects
None yet
Development

No branches or pull requests

2 participants