Bug 112708 - "gcc -fsanitize=address" produces wrong debug info for variables in function prologue
Summary: "gcc -fsanitize=address" produces wrong debug info for variables in function ...
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: sanitizer (show other bugs)
Version: 13.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-11-25 04:35 UTC by Bruno Haible
Modified: 2023-11-28 01:03 UTC (History)
6 users (show)

See Also:
Host: x86_64-linux-gnu
Target: x86_64-linux-gnu
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Bruno Haible 2023-11-25 04:35:49 UTC
As "gcc -fsanitize=address" finds several categories of memory related bugs,
I'm trying to use CC="gcc -fsanitize=address" everywhere. Unfortunately,
in the following case, a variable's value during a function prologue is
wrong when displayed by gdb. The value is displayed correctly when I don't
use the option -fsanitize=address. Which means that the culprit is gcc.

How to reproduce:
1. $ wget https://ftp.gnu.org/gnu/gettext/gettext-0.22.tar.xz
2. $ tar xf gettext-0.22.tar.xz
3. $ cd gettext-0.22
4. $ GCC13DIR=/some/directory/with/gcc-13.2.0
   $ PATH=$GCC13DIR/bin:$PATH
   Verify it:
   $ gcc --version
5. $ CC="gcc -fsanitize=address" CXX="g++ -fsanitize=address -Wl,-rpath,$GCC13DIR/lib64" CFLAGS=-ggdb ./configure --disable-shared
6. $ make
7. $ cd gettext-tools/src
8. $ cat > foo.vala <<\EOF
        primary_text.set_markup(
            "<span size=\"large\" weight=\"bold\">%s</span>".printf(_("Welcome to Shotwell!")));
EOF
9.
$ gdb xgettext
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from xgettext...
(gdb) break xg-message.c:383
Breakpoint 1 at 0x41cad1: file xg-message.c, line 383.
(gdb) run -o - foo.vala
Starting program: /tmp/gettext-0.22/gettext-tools/src/xgettext -o - foo.vala
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, remember_a_message (mlp=0x60e000000040, msgctxt=0x0, msgid=0x603000000a30 "Welcome to Shotwell!", is_utf8=true, pluralp=false, context=..., pos=0x6100000004c0, extracted_comment=0x0, comment=0x0, comment_is_utf8=false) at xg-message.c:383
383       set_format_flags_from_context (is_format, context, mp->msgid, pos, "msgid");
(gdb) print context
$1 = {is_format1 = 3, pass_format1 = 0, is_format2 = 0, pass_format2 = 0, is_format3 = 0, pass_format3 = 0, is_format4 = 0, pass_format4 = 0}
(gdb) step
set_format_flags_from_context (is_format=0x7fffffffc620, context=..., string=0x603000000a30 "Welcome to Shotwell!", pos=0x6100000004c0, pretty_msgstr=0x6f0d40 "msgid") at xg-message.c:50
50                                     flag_context_ty context, const char *string,
(gdb) print context
$2 = {is_format1 = 0, pass_format1 = 0, is_format2 = 2, pass_format2 = 0, is_format3 = 5, pass_format3 = 0, is_format4 = 7, pass_format4 = 0}
(gdb) next
55        if (context.is_format1 != undecided
(gdb) print context
$3 = {is_format1 = 3, pass_format1 = 0, is_format2 = 0, pass_format2 = 0, is_format3 = 0, pass_format3 = 0, is_format4 = 0, pass_format4 = 0}

The variable 'context' is passed from xg-message.c:383 to set_format_flags_from_context.
The value printed as $1 and $3 is correct.
The value printed as $2 is nonsense.
Comment 1 Andrew Pinski 2023-11-25 04:54:08 UTC
Is this with or without optimization?
Comment 2 Andrew Pinski 2023-11-25 04:54:31 UTC
>Which means that the culprit is gcc.

Not always ...
Comment 3 Andrew Pinski 2023-11-25 04:56:01 UTC
Also did you add -fvar-tracking-assignments ? (there are other reports asking to turn on -fvar-tracking-assignments for -O0 except it is a big compile time increase in many cases).
Comment 4 Bruno Haible 2023-11-25 05:50:46 UTC
(In reply to Andrew Pinski from comment #1)
> Is this with or without optimization?

Since in step 5, I specified CFLAGS=-ggdb, it is without optimization. (configure sets CFLAGS="-O2 -g" only if CFLAGS is not preset.)
Comment 5 Bruno Haible 2023-11-25 06:22:51 UTC
(In reply to Andrew Pinski from comment #3)
> Also did you add -fvar-tracking-assignments ?

No, I haven't. I have specified CFLAGS=-ggdb, indicating that
  - I don't care about the optimization level,
  - but I want the ability to debug with gdb. And that includes not being disturbed and alarmed by wrong values of variables. (I wouldn't mind if single-stepping would not stop at the function entry directly, only at the first statement of the function. Then I would not have the opportunity to do 'print context' at the wrong moment.)

Which passes and internal machinery GCC needs in order to fulfil these goals, should be GCC internal. In other words, I specify '-ggdb' and expect GCC to do the rest.

Additionally, Jakub Jelinek writes in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102523#c2 :
! sometimes -O0 -g is debuggable much better than -Og -g, sometimes the other way around.
Which is not really a recommendation to use this option on a general basis.
Comment 6 Bruno Haible 2023-11-26 02:08:06 UTC
For comparison, what clang 17 with -fsanitize=address does in this situation, is to not generate a stepping point at the function entry (xg-message.c:50). The gdb 'step' command brings me directly to the first statement in the function (xg-message.c:55). This may have some other drawbacks, but at least it prevents the possibility of displaying wrong values for function parameters.
Comment 7 Richard Biener 2023-11-27 08:04:19 UTC
It's -fvar-tracking, not -fvar-tracking-assignments.  At -O0 debug info during the prologue is unreliable without that.
Comment 8 Bruno Haible 2023-11-27 10:07:54 UTC
(In reply to Richard Biener from comment #7)
> It's -fvar-tracking, not -fvar-tracking-assignments.  At -O0 debug info
> during the prologue is unreliable without that.

Then how about enabling -fvar-tracking automatically when "-g" or "-ggdb" is requested and the debug format supports it?

The documentation https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Debugging-Options.html says:
"It is enabled by default when compiling with optimization (-Os, -O, -O2, …), debugging information (-g) and the debug info format supports it."

Why not also enable it when compiling _without_ optimization?

I'm not using "-Og" because the documentation https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Optimize-Options.html says "-Og enables all -O1 optimization flags except for those that may interfere with debugging", but what I want is optimal debugging and don't want to spend CPU cycles on optimization.
Comment 9 Jakub Jelinek 2023-11-27 10:29:10 UTC
(In reply to Bruno Haible from comment #8)
> (In reply to Richard Biener from comment #7)
> > It's -fvar-tracking, not -fvar-tracking-assignments.  At -O0 debug info
> > during the prologue is unreliable without that.
> 
> Then how about enabling -fvar-tracking automatically when "-g" or "-ggdb" is
> requested and the debug format supports it?

There are major problems with that.  var-tracking is very compile time intensive,
so it would significantly slow down -O0 compilation.
And, most of the time at -O0 variables live in their memory slots, so that var-tracking time would be also wasted.
The only things which need var-tracking at -O0 are:
1) register variables (or whatever else doesn't get an allocated stack slot)
2) variables at the start and end of their lifetime, where they are not yet in
   their stack slot or no longer in them (so, often function prologues and epilogues,
   or say for VLAs before everything is set up etc.)
So, as written in other PRs, for -O0 we'd likely want to invent some cheaper var-tracking variant which would only track the above 2 and not waste time on the rest.

> The documentation
> https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Debugging-Options.html says:
> "It is enabled by default when compiling with optimization (-Os, -O, -O2,
> …), debugging information (-g) and the debug info format supports it."
> 
> Why not also enable it when compiling _without_ optimization?
> 
> I'm not using "-Og" because the documentation
> https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Optimize-Options.html says
> "-Og enables all -O1 optimization flags except for those that may interfere
> with debugging", but what I want is optimal debugging and don't want to
> spend CPU cycles on optimization.

As for -Og, -Og doesn't perform too many optimizations and in function prologues code is usually better debuggable than with -O0.  But there is one major obstackle also recorded in various PRs, we don't artificially make all vars used at the end of their
scope, which means that if some var is unused after certain point in a function, it might no longer be available and e.g. DWARF5 DW_OP_entry_value used as the last resort; but that doesn't work always.

For the -fsanitize=address case, the thing is that the marking of end of prologue is
something we do during function prologue generation in pro_and_epilogue (or debugger does that from scanning the function IL).  While the asan "prologue" is something emitted during RTL expansion and so for pro_and_epilogue seen as part of function body.
Comment 10 Bruno Haible 2023-11-28 01:03:18 UTC
(In reply to Jakub Jelinek from comment #9)
> var-tracking is very compile time intensive,
> so it would significantly slow down -O0 compilation.

Indeed, these are the timings of "time make" that I observe when compiling
GNU gettext 0.22 (only the "user" time figure, in seconds):

                                            gcc 11.2    gcc 13.2

-ggdb                                         58.7        68.2
-ggdb -fvar-tracking                          62.5        71.8
-ggdb -fsanitize=address                      69.6        80.6
-ggdb -fsanitize=address -fvar-tracking       82.7        93.7

So, while -fvar-tracking without -fsanitize=address has a penalty of ca. 6%,
-fvar-tracking in the presence of -fsanitize=address has a penalty of ca. 17%.