Bug 27715 - Fails to detect GObjectClass ABI changes
Summary: Fails to detect GObjectClass ABI changes
Status: WAITING
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-04-09 17:05 UTC by Marc-André Lureau
Modified: 2021-04-29 15:13 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2021-04-09 00:00:00


Attachments
Old gobject.so (291.26 KB, application/x-sharedlib)
2021-04-12 13:55 UTC, Marc-André Lureau
Details
New gobject.so (291.18 KB, application/x-sharedlib)
2021-04-12 13:55 UTC, Marc-André Lureau
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Marc-André Lureau 2021-04-09 17:05:50 UTC
GObject (and other similar C object-oriented code) have class structures that should be checked for ABI breakage. However, abidiff fails to report the change:


abidiff --headers-dir1 /tmp/old/usr/include/spice-client-glib-2.0 --headers-dir2 /tmp/new/usr/include/spice-client-glib-2.0  --fail-no-debug-info --no-added-syms                 /tmp/old/usr/lib64/libspice-client-glib-2.0.so.8.7.0 /tmp/new/usr/lib64/libspice-client-glib-2.0.so.8.7.0

diff -u /tmp/old/usr/include/spice-client-glib-2.0/spice-channel.h /tmp/new/usr/include/spice-client-glib-2.0/spice-channel.h
--- /tmp/old/usr/include/spice-client-glib-2.0/spice-channel.h	2021-04-09 20:40:30.686290157 +0400
+++ /tmp/new/usr/include/spice-client-glib-2.0/spice-channel.h	2021-04-09 20:42:19.934229443 +0400
@@ -106,7 +106,6 @@
 
     /*< private >*/
     /* virtual method, any context */
-    gpointer deprecated;
     void (*channel_reset)(SpiceChannel *channel, gboolean migrating);
     gpointer deprecated2;
 

Supposedly more structures/types in the headers should be checked by default (even if nothing explicitely depend on it).
Comment 1 Matthias Maennich 2021-04-09 17:54:43 UTC
Thanks for reporting. Can you please provide a reproducing example? E.g. the binaries before/after the change or the abi.xml files extracted with abidw from before/after? Ideal would be some source code that demonstrates the missing analysis. (you got hold off the relevant object files, maybe?)

In particular, it would be interesting to see how "gpointer deprecated" participates in the ABI surface of the library.
Comment 2 Marc-André Lureau 2021-04-12 08:21:56 UTC
Reproducer:

https://gitlab.gnome.org/GNOME/glib/

meson setup build && ninja -C build
DESTDIR=/tmp/old ninja -C build install

Modify a base class:

diff --git a/gobject/gtypemodule.h b/gobject/gtypemodule.h
index 5c4025063..f15f18a32 100644
--- a/gobject/gtypemodule.h
+++ b/gobject/gtypemodule.h
@@ -80,7 +80,6 @@ struct _GTypeModuleClass
   void (*reserved1) (void);
   void (*reserved2) (void);
   void (*reserved3) (void);
-  void (*reserved4) (void);
 };
 

ninja -C build
DESTDIR=/tmp/new ninja -C build install

abidiff --headers-dir1 /tmp/old/usr/include/glib-2.0/gobject --headers-dir2 /tmp/new/usr/include/glib-2.0/gobject/  --fail-no-debug-info --no-added-syms                 /tmp/old/usr/lib64/libgobject-2.0.so.0.6900.0 /tmp/new/usr/lib64/libgobject-2.0.so.0.6900.0

The starting point is that GTypeModuleClass isn't being used explicitely by functions. Yet it is the base class / vfunc / private of objects and should have ABI stability.
Comment 3 Matthias Maennich 2021-04-12 09:49:06 UTC
I am not familiar with glib and/or meson. Does this build produce debug symbols in the final binary?

Can you try whether this is reported in the harmless mode (--harmless)?

It would be helpful if you would upload binaries (before/after the change) to this tracking bug.
Comment 4 Marc-André Lureau 2021-04-12 13:54:29 UTC
> Can you try whether this is reported in the harmless mode (--harmless)?

Yes, no change reported either.

> It would be helpful if you would upload binaries (before/after the change) to this tracking bug.

ok
Comment 5 Marc-André Lureau 2021-04-12 13:55:22 UTC
Created attachment 13363 [details]
Old gobject.so
Comment 6 Marc-André Lureau 2021-04-12 13:55:53 UTC
Created attachment 13364 [details]
New gobject.so
Comment 7 Matthias Maennich 2021-04-12 14:05:37 UTC
Thanks for that.

I can see that:
 - _GTypeModuleClass increases in size from old->new in DWARF
 - old and new have debug information sufficient for ABI analysis
 - _GTypeModuleClass is not attached directly or indirectly as a type to any ELF symbol

Can you please tell me which symbol you would expect to become incompatible based on a change to this class? How does it relate to any of the exported symbols?
Comment 8 Marc-André Lureau 2021-04-12 19:01:26 UTC
(In reply to Matthias Maennich from comment #7)
> Thanks for that.
> 
> I can see that:
>  - _GTypeModuleClass increases in size from old->new in DWARF
>  - old and new have debug information sufficient for ABI analysis
>  - _GTypeModuleClass is not attached directly or indirectly as a type to any
> ELF symbol
> 
> Can you please tell me which symbol you would expect to become incompatible
> based on a change to this class? How does it relate to any of the exported
> symbols?

FooClass/vtable (and other object-oriented structures) are often passed around as void * (or parent types) on the public functions etc. However, the implementation then cast the void/parent pointer to some concrete type, and expect the structure to keep the same layout.

So any change in those OO structures is an ABI break, it would be nice to catch them.
Comment 9 Matthias Maennich 2021-04-14 10:53:45 UTC
Anything passed to the API via opaque pointers is essentially hidden from ABI analysis as no type information (other than void*) is attached to it. From an ABI point of view the ABI of the symbol that passes void* is unchanged. Though further down this of course can become incompatible.

There are cases that you intentionally want to pass void* to be ABI compatible even though the type changes. E.g. if your library has such an interface:

void* get_handle(){
  // allocates the resources and returns just a handle to it.
  ...
  return (void*) some_object;
}

void do_something(void* handle) {
  // uses the handle as a reference
}

The ABI is stable even though the type could have changed. The client code might not even know the concrete type and is also not supposed to cast to it.

So, as much as I would also like to sometimes inspect underlying types of void*, flagging changes to those types is probably wrong. If you want this type to be stable, you need to let it participate as a type in the interface.

One way of achieving this is to define a symbol just for the sake of ABI analysis:

void (struct _GTypeModuleClass obj) { }

This will get picked up by libabigail and the type will participate in the analysis then.
Comment 10 Marc-André Lureau 2021-04-20 11:55:40 UTC
Isn't the a way to annotate a type in a header for abi check?

That would be useful in other cases, like shared header structures for protocols and shared structures (common in virtualized hardware for instance)
Comment 11 Marc-André Lureau 2021-04-29 15:13:05 UTC
(In reply to Matthias Maennich from comment #9)
> [..] 
> So, as much as I would also like to sometimes inspect underlying types of
> void*, flagging changes to those types is probably wrong. If you want this
> type to be stable, you need to let it participate as a type in the interface.

This bug is about structures that are exposed in the public header. So they may be taken as argument, or returned, by void* or parent* pointers, but it would be nice to annotate the actual type

> One way of achieving this is to define a symbol just for the sake of ABI
> analysis:
> 
> void (struct _GTypeModuleClass obj) { }
> 
> This will get picked up by libabigail and the type will participate in the
> analysis then.

Ok, but I am looking for a more friendly way to express that *all* my types in my headers must be taken into account without having to define such extra symbols. Imho it should be an option to the tool somehow.