This had been caught by a patchset submitted in the dpdk project. Here is a reproducer with libabigail 1.8.2: $ cat plop.c #include <inttypes.h> struct bigstruct { char name[128]; #ifdef BEFORE uint8_t bitfield0:1; #else uint8_t bitfield0:1, bitfield1:1; #endif }; void access_bigstruct(struct bigstruct *st) { #ifndef BEFORE st->bitfield1 = 1; #endif } $ cat Makefile CC ?= gcc CFLAGS ?= -Wall -Werror -g ABIDIFF = abidiff before_%.o: %.c $(CC) $(CFLAGS) -DBEFORE -o $@ -c $< pahole $@ after_%.o: %.c $(CC) $(CFLAGS) -o $@ -c $< pahole $@ dump: before_plop.o after_plop.o $(ABIDIFF) $^ abidiff before_plop.o after_plop.o $ make Functions changes summary: 0 Removed, 1 Changed, 0 Added function Variables changes summary: 0 Removed, 0 Changed, 0 Added variable 1 function with some indirect sub-type change: [C] 'function void access_bigstruct(bigstruct*)' at plop.c:13:1 has some indirect sub-type changes: parameter 1 of type 'bigstruct*' has sub-type changes: in pointed to type 'struct bigstruct' at plop.c:3:1: type size hasn't changed 1 data member insertion: 'uint8_t bigstruct::bitfield1', at offset 6 (in bits) at plop.c:9:1 make: *** [dump] Error 4 Afaiu, this offset 6 should instead be 1026. Looking into debug info: $ readelf --debug-dump=info after_plop.o |grep -C 10 bitfield1 <2><88>: Abbrev Number: 7 (DW_TAG_member) <89> DW_AT_name : (indirect string, offset: 0x0): bitfield0 <8d> DW_AT_decl_file : 1 <8e> DW_AT_decl_line : 8 <8f> DW_AT_type : <0x49> <93> DW_AT_byte_size : 1 <94> DW_AT_bit_size : 1 <95> DW_AT_bit_offset : 7 <96> DW_AT_data_member_location: 128 <2><97>: Abbrev Number: 7 (DW_TAG_member) <98> DW_AT_name : (indirect string, offset: 0xa): bitfield1 <9c> DW_AT_decl_file : 1 <9d> DW_AT_decl_line : 9 <9e> DW_AT_type : <0x49> <a2> DW_AT_byte_size : 1 <a3> DW_AT_bit_size : 1 <a4> DW_AT_bit_offset : 6 <a5> DW_AT_data_member_location: 128 <2><a6>: Abbrev Number: 0 <1><a7>: Abbrev Number: 8 (DW_TAG_array_type) <a8> DW_AT_type : <0xbe>
pahole before_plop.o struct bigstruct { char name[128]; /* 0 128 */ /* --- cacheline 2 boundary (128 bytes) --- */ uint8_t bitfield0:1; /* 128: 7 1 */ /* size: 129, cachelines: 3, members: 2 */ /* bit_padding: 7 bits */ /* last cacheline: 1 bytes */ }; pahole after_plop.o struct bigstruct { char name[128]; /* 0 128 */ /* --- cacheline 2 boundary (128 bytes) --- */ uint8_t bitfield0:1; /* 128: 7 1 */ uint8_t bitfield1:1; /* 128: 6 1 */ /* size: 129, cachelines: 3, members: 3 */ /* bit_padding: 6 bits */ /* last cacheline: 1 bytes */ };
The problem is in abidw, not abidiff. I'm guessing that both DW_AT_data_member_location and DW_AT_data_bit_offset need to be taken into consideration, not just one of them. The problem exists with -gdwarf-3 and -gdwarf-4, but not -gdwarf-5. $ head test.{0,1}.c ==> test.0.c <== #include <inttypes.h> struct bigstruct { char name[128]; uint8_t bitfield0:1; }; uint8_t foo(const struct bigstruct * big) { return big->bitfield0; } ==> test.1.c <== #include <inttypes.h> struct bigstruct { char name[128]; uint8_t bitfield0:1; uint8_t bitfield1:1; }; uint8_t foo(const struct bigstruct * big) { return big->bitfield0; } $ for i in 0 1; do gcc -Wall -Wextra -g -c test.$i.c; abidw --no-show-locs test.$i.o > test.$i.xml; done $ diff -u test.{0,1}.xml --- test.0.xml 2021-07-08 10:21:30.460919575 +0100 +++ test.1.xml 2021-07-08 10:21:30.516919874 +0100 @@ -1,8 +1,8 @@ -<abi-corpus version='2.0' path='test.0.o' architecture='elf-amd-x86_64'> +<abi-corpus version='2.0' path='test.1.o' architecture='elf-amd-x86_64'> <elf-function-symbols> <elf-symbol name='foo' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> </elf-function-symbols> - <abi-instr address-size='64' path='test.0.c' comp-dir-path='/usr/local/google/home/gprocida/dev/libabigail/bitfields2' language='LANG_C99'> + <abi-instr address-size='64' path='test.1.c' comp-dir-path='/usr/local/google/home/gprocida/dev/libabigail/bitfields2' language='LANG_C99'> <type-decl name='char' size-in-bits='8' id='type-id-1'/> <array-type-def dimensions='1' type-id='type-id-1' size-in-bits='1024' id='type-id-2'> <subrange length='128' type-id='type-id-3' id='type-id-4'/> @@ -18,6 +18,9 @@ <data-member access='public' layout-offset-in-bits='0'> <var-decl name='bitfield0' type-id='type-id-7' visibility='default'/> </data-member> + <data-member access='public' layout-offset-in-bits='1'> + <var-decl name='bitfield1' type-id='type-id-7' visibility='default'/> + </data-member> </class-decl> <qualified-type-def type-id='type-id-8' const='yes' id='type-id-9'/> <pointer-type-def type-id='type-id-9' size-in-bits='64' id='type-id-10'/>
Thanks for looking at this issue. (In reply to gprocida from comment #2) > The problem is in abidw, not abidiff. > > I'm guessing that both DW_AT_data_member_location and DW_AT_data_bit_offset > need to be taken into consideration, not just one of them. That was my understanding too.
And, for completeness, and example where the ABI has changed incompatibly but the ABI XML remains the same. ==> test2.0.c <== #include <inttypes.h> struct S { char name[5]; uint8_t bit:1; uint32_t :0; }; uint8_t foo(const struct S * s) { return s->bit; } ==> test2.1.c <== #include <inttypes.h> struct S { char name[5]; uint16_t :0; uint8_t bit:1; uint32_t :0; }; uint8_t foo(const struct S * s) { return s->bit; }
This should be fixed by commit https://sourceware.org/git/?p=libabigail.git;a=commit;h=cfd81dec10b26759b08c60c59b0d32f905d9913f in the master branch. It should be available in the 2.0 release. Thanks and sorry for the inconvenience!