Bug 28060 - [libabigail] Invalid offset for bitfields
Summary: [libabigail] Invalid offset for bitfields
Status: RESOLVED FIXED
Alias: None
Product: libabigail
Classification: Unclassified
Component: default (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Dodji Seketeli
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-07-07 09:27 UTC by David Marchand
Modified: 2021-07-19 12:01 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description David Marchand 2021-07-07 09:27:56 UTC
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>
Comment 1 David Marchand 2021-07-07 09:31:31 UTC
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 */
};
Comment 2 gprocida 2021-07-08 09:34:20 UTC
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'/>
Comment 3 David Marchand 2021-07-08 09:42:49 UTC
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.
Comment 4 gprocida 2021-07-08 13:41:22 UTC
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; }
Comment 5 Dodji Seketeli 2021-07-19 12:01:09 UTC
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!