llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.39k stars 12.15k forks source link

Incorrect Debug Information for Pointer Variables in Binaries Compiled with "-O3" and "-O2" #107443

Open edumoot opened 2 months ago

edumoot commented 2 months ago

Godblot link

There are two items needed to be considered:

  1. At line 25, the value that the pointer local_ptr is referencing differs from the value of g_values[7].
  2. Furthermore, at line 27, an error occurs when attempting to print local_ptr, displaying the message: "error: Couldn't materialize: couldn't get the value of variable local_ptr: variable not available error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression".

We can reproduce this issue in LLVM versions 18.1.8, 17.0.6, and 16.0.3. The behavior of the binary compiled with -O2 is identical to that of the -O3 binary, so we will use the -O3 binary for demonstration purposes.

clang -g -O3 -o 434_O3.out 434.c

(lldb) file 434_O3.out
(lldb) b main
(lldb) r
[...]
* thread #1, name = '434_O3.out', stop reason = breakpoint 2.1
    frame #0: 0x0000555555555141 434_O3.out`main [inlined] get_union_value at 434.c:25:29
   22   static const union U3 get_union_value(void) {
   23       long int *local_ptr = &g_values[7];
   24       
-> 25       *g_value_ptr = (249UL < g_flag); // Set value based on comparison
   26       *local_ptr = 0;                  // Reset the value at index 7
   27       *g_ptr_wrapper = &local_ptr;     // Update reference
(lldb) p g_values[7]
(int) -2139292940
(lldb) p *local_ptr
(int) 285576016

(lldb) s
(lldb) s
(lldb) p g_values[7]
(int) 0
(lldb) p *local_ptr
error: Couldn't materialize: couldn't get the value of variable local_ptr: variable not available
error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression
(lldb) p *g_ptr_wrapper
(int **) 0x0000555555558078
(lldb) p **g_ptr_wrapper
(int *) 0x000055555555804c
(lldb) p ***g_ptr_wrapper
(int) 0

cat 434.c

#include <stdio.h>

union U3 {
    volatile unsigned long f0;
    int f1;
    volatile signed char f2;
};

static volatile int g_flag = 0x6E54;
static long int g_values[10] = {0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4};

static long int *volatile g_value_ptr = &g_values[3];
static long int *g_ref_value = &g_values[7];
static long int **g_ref_ptr = &g_ref_value;
static long int ***volatile g_ptr_wrapper = &g_ref_ptr;

static const union U3 g_union_vals[7] = {{-1UL}, {-1UL}, {-1UL}, {-1UL}, {-1UL}, {-1UL}, {-1UL}};

static const union U3 get_union_value(void);
static void print_message(void);

static const union U3 get_union_value(void) {
    long int *local_ptr = &g_values[7];

    *g_value_ptr = (249UL < g_flag); // Set value based on comparison
    *local_ptr = 0;                  // Reset the value at index 7
    *g_ptr_wrapper = &local_ptr;     // Update reference

    return g_union_vals[6];          // Return the last union value
}

int main(void) {
    get_union_value();
    printf("Hello, world!\n");
    return 0;
}

GDB produces a similar issue.

$ gdb 434_O3.out
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
[...]
(gdb) b main
(gdb) r
(gdb) s
get_union_value () at 434.c:25
25      *g_value_ptr = (249UL < g_flag); // Set value based on comparison

(gdb) p *local_ptr
$1 = 285576016
(gdb) p g_values[7]
$2 = -2139292940

(gdb) s
26      *local_ptr = 0;                  // Reset the value at index 7
(gdb) s
27      *g_ptr_wrapper = &local_ptr;     // Update reference
(gdb) p *local_ptr
value has been optimised out
(gdb) p g_values[7]
$3 = 0
(gdb) p g_ptr_wrapper
$4 = (int *** volatile) 0x555555558070 <g_ref_ptr>
(gdb) p *g_ptr_wrapper
$5 = (int **) 0x555555558078 <g_ref_value>
(gdb) p **g_ptr_wrapper
$6 = (int *) 0x55555555804c <g_values+28>
(gdb) p ***g_ptr_wrapper
$7 = 0
llvmbot commented 2 months ago

@llvm/issue-subscribers-debuginfo

Author: Yachao Zhu (edumoot)

[Godblot link](https://godbolt.org/z/7bGbGcnr1) There are three items needed to be considered: 1. At line 25, the value that the pointer `local_ptr` is referencing differs from the value of `g_values[7]`. 2. Furthermore, at line 27, an error occurs when attempting to print `local_ptr`, displaying the message: "error: Couldn't materialize: couldn't get the value of variable local_ptr: variable not available error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression". 3. Wrong result of pointer assignment operation at line 27. Instead of setting g_values[7] to 0, the operation `*g_ptr_wrapper = &local_ptr` should set `long *` pointer to NULL. We can reproduce this issue in LLVM versions 18.1.8, 17.0.6, and 16.0.3. The behavior of the binary compiled with -O2 is identical to that of the -O3 binary, so we will use the -O3 binary for demonstration purposes. ``` clang -g -O3 -o 434_O3.out 434.c (lldb) file 434_O3.out (lldb) b main (lldb) r [...] * thread #1, name = '434_O3.out', stop reason = breakpoint 2.1 frame #0: 0x0000555555555141 434_O3.out`main [inlined] get_union_value at 434.c:25:29 22 static const union U3 get_union_value(void) { 23 long int *local_ptr = &g_values[7]; 24 -> 25 *g_value_ptr = (249UL < g_flag); // Set value based on comparison 26 *local_ptr = 0; // Reset the value at index 7 27 *g_ptr_wrapper = &local_ptr; // Update reference (lldb) p g_values[7] (int) -2139292940 (lldb) p *local_ptr (int) 285576016 (lldb) s (lldb) s (lldb) p g_values[7] (int) 0 (lldb) p *local_ptr error: Couldn't materialize: couldn't get the value of variable local_ptr: variable not available error: errored out in DoExecute, couldn't PrepareToExecuteJITExpression (lldb) p *g_ptr_wrapper (int **) 0x0000555555558078 (lldb) p **g_ptr_wrapper (int *) 0x000055555555804c (lldb) p ***g_ptr_wrapper (int) 0 ``` `cat 434.c` ``` #include <stdio.h> union U3 { volatile unsigned long f0; int f1; volatile signed char f2; }; static volatile int g_flag = 0x6E54; static long int g_values[10] = {0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4, 0x807CFAF4}; static long int *volatile g_value_ptr = &g_values[3]; static long int *g_ref_value = &g_values[7]; static long int **g_ref_ptr = &g_ref_value; static long int ***volatile g_ptr_wrapper = &g_ref_ptr; static const union U3 g_union_vals[7] = {{-1UL}, {-1UL}, {-1UL}, {-1UL}, {-1UL}, {-1UL}, {-1UL}}; static const union U3 get_union_value(void); static void print_message(void); static const union U3 get_union_value(void) { long int *local_ptr = &g_values[7]; *g_value_ptr = (249UL < g_flag); // Set value based on comparison *local_ptr = 0; // Reset the value at index 7 *g_ptr_wrapper = &local_ptr; // Update reference return g_union_vals[6]; // Return the last union value } int main(void) { get_union_value(); printf("Hello, world!\n"); return 0; } ``` GDB produces a similar issue. ``` $ gdb 420_O3.out GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git [...] (gdb) b main (gdb) r (gdb) s get_union_value () at 434.c:25 25 *g_value_ptr = (249UL < g_flag); // Set value based on comparison (gdb) p *local_ptr $1 = 285576016 (gdb) p g_values[7] $2 = -2139292940 (gdb) s 26 *local_ptr = 0; // Reset the value at index 7 (gdb) s 27 *g_ptr_wrapper = &local_ptr; // Update reference (gdb) p *local_ptr value has been optimised out (gdb) p g_values[7] $3 = 0 (gdb) p g_ptr_wrapper $4 = (int *** volatile) 0x555555558070 <g_ref_ptr> (gdb) p *g_ptr_wrapper $5 = (int **) 0x555555558078 <g_ref_value> (gdb) p **g_ptr_wrapper $6 = (int *) 0x55555555804c <g_values+28> (gdb) p ***g_ptr_wrapper $7 = 0 ```
edumoot commented 2 weeks ago

In LLVM19.1.0 context, for the -O3 binary, the issue seems to be fixed: local_ptr is optimized out at line 25. we get use of undeclared identifier 'local_ptr' when trying to print it at line 25.