https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101378
Pedro Alves <alves.ped at gmail dot com> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |alves.ped at gmail dot com --- Comment #2 from Pedro Alves <alves.ped at gmail dot com> --- It's indeed about [[no_unique_address]], and empty objects. Here's a smaller reproducer: $ cat test.cpp struct Empty {}; struct S { [[no_unique_address]] Empty empty; } s; $ g++ test.cpp -g -c -o test.o $ readelf --debug-dump test.o | grep "DW_AT_data_member_location:" <3d> DW_AT_data_member_location: -1 If you replace Empty with anything that isn't empty, you no longer get the -1. Notice this too: ~~~ $ cat test1.cpp #include <stdio.h> #include <stddef.h> struct Empty {}; struct S { [[no_unique_address]] Empty empty1; [[no_unique_address]] Empty empty2; [[no_unique_address]] Empty empty3; } s; int main () { printf ("&s.empty1 = %p\n", &s.empty1); printf ("&s.empty2 = %p\n", &s.empty2); printf ("&s.empty3 = %p\n", &s.empty3); printf ("s = %p\n", &s); printf ("offset empty1 = %ld\n", offsetof (struct S, empty1)); printf ("offset empty2 = %ld\n", offsetof (struct S, empty2)); printf ("offset empty3 = %ld\n", offsetof (struct S, empty3)); return 0; } ~~~ The debug info shows: <2><a3>: Abbrev Number: 10 (DW_TAG_member) <a4> DW_AT_name : (indirect string, offset: 0x186f): empty1 ... <af> DW_AT_data_member_location: -1 <2><b0>: Abbrev Number: 2 (DW_TAG_member) <b1> DW_AT_name : (indirect string, offset: 0x1876): empty2 ... <ba> DW_AT_data_member_location: 0 <2><bb>: Abbrev Number: 2 (DW_TAG_member) <bc> DW_AT_name : (indirect string, offset: 0x187d): empty3 ... <c5> DW_AT_data_member_location: 1 While at run time, we get: $ ./test1 &s.empty1 = 0x5627442ab011 &s.empty2 = 0x5627442ab012 &s.empty3 = 0x5627442ab013 s = 0x5627442ab011 offset empty1 = 0 offset empty2 = 1 offset empty3 = 2 So (0, 1, 2) at runtime, but (-1, 0, 1) in the DWARF. And then note this, with a non-empty field added before the empty ones: ~~~~ $ cat test2.cpp #include <stdio.h> #include <stddef.h> struct Empty {}; struct S { int i; [[no_unique_address]] Empty empty1; [[no_unique_address]] Empty empty2; } s; int main () { printf ("&s.i = %p\n", &s.i); printf ("&s.empty1 = %p\n", &s.empty1); printf ("&s.empty2 = %p\n", &s.empty2); printf ("s = %p\n", &s); printf ("offset i = %ld\n", offsetof (struct S, i)); printf ("offset empty1 = %ld\n", offsetof (struct S, empty1)); printf ("offset empty2 = %ld\n", offsetof (struct S, empty2)); return 0; } we see: <2><a3>: Abbrev Number: 9 (DW_TAG_member) <a4> DW_AT_name : i ... <ad> DW_AT_data_member_location: 0 <2><ae>: Abbrev Number: 10 (DW_TAG_member) <af> DW_AT_name : (indirect string, offset: 0x1872): empty1 ... <ba> DW_AT_data_member_location: -1 <2><bb>: Abbrev Number: 11 (DW_TAG_member) <bc> DW_AT_name : (indirect string, offset: 0x1879): empty2 ... <c7> DW_AT_data_member_location: 3 while at run time: $ ./test2 &s.i = 0x561fac538038 &s.empty1 = 0x561fac538038 &s.empty2 = 0x561fac53803c s = 0x561fac538038 offset i = 0 offset empty1 = 0 offset empty2 = 4 So it was (0, -1, 3) in the DWARF and (0, 0, 4) at run time. This all seems to suggest an off by one for the empty fields in the DWARF.