Saturday, 6 August 2011

Debugging with Mac OS X shared libraries

Writing Ada means you don't need the debugger that often. But when you do need it, you really need it. And often the reason will be an exception, which you'll want to catch and investigate.

When you tell GDB to, for example, catch exception constraint_error, it actually places a catchpoint on the hook __gnat_debug_raise_exception. This is one of several related hooks within the Ada runtime, in the unit System.Exceptions (s-except).

Normally, when you build an executable with -g, gnatmake arranges to call in the static Ada runtime, and things work. otool -L shows no dependency on Ada shared libraries:

$ otool -L ews-server-test
ews-server-test:
 /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1105.0.0)
 /usr/local/gnat/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.0.0)

However, if you call in an Ada shared library by mistake (perhaps because you missed an external variable setting in a .gpr), the linker will find the dynamic Ada runtime because it's referenced from the Ada shared library:

$ otool -L ews-server-test
ews-server-test:
 @rpath/libbc.dylib (compatibility version 0.0.0, current version 0.0.0)
 @rpath/libgnarl-2011.dylib (compatibility version 0.0.0, current version 0.0.0)
 @rpath/libgnat-2011.dylib (compatibility version 0.0.0, current version 0.0.0)
 /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1105.0.0)
 /usr/local/gnat/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.0.0)

In this case, libbc.dylib caused libgnarl-2011.dylib and libgnat-2011.dylib to be linked.

There are two basic kinds of debug information that UNIX-based systems can use: DWARF and stabs. In Mac OS X, object files contain DWARF information, while executables (main and shared) don't (they do contain stabs; I don't understand this). The stabs information in the Ada runtime isn't enough for the gdb supplied with GNAT GPL 2011 to use, so it can't catch exceptions:

$ gdb ews-server-test
GNU gdb (GDB) 7.2 for GNAT GPL 2011 (20110419) [rev=gdb-7.2-ref-110-g8cd875d]
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
See your support agreement for details of warranty and support.
If you do not have a current support agreement, then there is absolutely
no warranty for this version of GDB.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin10.2.0".
For bug reporting instructions, please see:
...
unable to read unknown load command 0x24
unable to read unknown load command 0x26
unable to read unknown load command 0x24
unable to read unknown load command 0x26
Reading symbols from /Users/simon/sf/embed-web-srvr/src/ews-server-test...done.
(gdb) catch exception
Unable to insert catchpoint. Try to start the program first.
(gdb) start
Temporary breakpoint 1 at 0x100008e81: file /Users/simon/sf/embed-web-srvr/src/ews-server-test.adb, line 82.
Starting program: /Users/simon/sf/embed-web-srvr/src/ews-server-test 
unable to read unknown load command 0x24
unable to read unknown load command 0x26

Temporary breakpoint 1, ews.server.test () at /Users/simon/sf/embed-web-srvr/src/ews-server-test.adb:82
82    Current_Date_Format : Date_Format := ISO;
(gdb) catch exception
Cannot insert catchpoints in this configuration.
(gdb) break __gnat_debug_raise_exception
Function "__gnat_debug_raise_exception" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n
(gdb) 

The red section says that a shared library may not have been loaded yet. After the start, the shared libraries should have been loaded, but the green section says (in a confusing sort of way) that things still aren't going to work. The blue section says the same thing in a different way.

The system gdb (from Xcode) doesn't understand Ada, even at version 4.1 (for Lion), so you can't say catch exception. You can get part way by setting a break on the hook noted above:

$ /usr/bin/gdb ews-server-test
GNU gdb 6.3.50-20050815 (Apple version gdb-1705) (Fri Jul  1 10:50:06 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin"...Reading symbols for shared libraries ... done

(gdb) break __gnat_debug_raise_exception
Function "__gnat_debug_raise_exception" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (__gnat_debug_raise_exception) pending.
(gdb) start
Breakpoint 2 at 0x100008de1: file /Users/simon/sf/embed-web-srvr/src/ews-server-test.adb, line 26.
Starting program: /Users/simon/sf/embed-web-srvr/src/ews-server-test 
Reading symbols for shared libraries ...+........................ done
Breakpoint 1 at 0x10021488a: file s-except.adb, line 40.
Pending breakpoint 1 - "__gnat_debug_raise_exception" resolved

Breakpoint 2, 0x0000000100008de1 in ews.server.test () at /Users/simon/sf/embed-web-srvr/src/ews-server-test.adb:26
26 procedure EWS.Server.Test is
(gdb) 

The red section again says that a shared library may not have been loaded. The blue section shows that the pending breakpoint is now set.

The reason that the system gdb works is that it understands Apple's "Lazy" DWARF Scheme. The DWARF information for a shared library can be packed into a dSym bundle using the tool dsymutil, and indeed the GCC build process does just that:

$ cd /opt/gnat-gpl-2011/lib/gcc/x86_64-apple-darwin10.2.0/4.5.3/adalib/
$ ls -ld libgnat*
-rwxr-xr-x@ 1 simon  simon  3886280 19 Apr 18:25 libgnat-2011.dylib
drwxr-xr-x@ 3 simon  simon      102 19 Apr 18:25 libgnat-2011.dylib.dSYM
-rw-r--r--@ 1 simon  simon  5321016 19 Apr 18:25 libgnat.a
lrwxr-xr-x@ 1 simon  simon       18 19 Apr 18:25 libgnat.dylib -> libgnat-2011.dylib
drwxr-xr-x  3 simon  simon      102  6 Aug 06:46 libgnat.dylib.dSYM

What's needed is to merge Apple's dSym handling with the Ada-awareness added by AdaCore. More to come!

2 comments:

  1. Thanks for review, it was excellent and very informative.
    thank you :)

    ReplyDelete
    Replies
    1. Thanks for the comment. Things seem to have moved on a bit since this post, I should post some more. See https://sourceware.org/bugzilla/show_bug.cgi?id=11385 .

      Delete