RFC on how to implement something between solib-irix and procfs

Joel Brobecker
2004-07-04T19:25:20Z

Hello,

One of our customers noticed that breakpoints inserted in functions
called from a shared library -init code were inneffective (the "-init
code" is the some special code in the shared library that is executed
by the loader before it runs the program).

Here is what happens at the moment:
- GDB sets up a procfs trap on exec system calls, and then fork.
The child GDB then does an exec, which causes it to stop on that trap.
- The parent GDB then sets up a breakpoint at the program entry point,
and runs until it is reached. Only then does it load the symbols
for all used shared libraries, and insert the shared libs breakpoints.

Unfortunately, this is already too late, as by this time, the code has
already been executed by the loader. Here is the sequence of events as
documented in the DSO(5) man page:

> What Happens at Runtime?
> The runtime events are as follows:
>
> 1. exec(2) loads the main program and then loads one of the following
> interpreters as specified in the main program:
>
> * /usr/lib/libc.so.1 is loaded for programs compiled with the -32
> compiler option.
>
> * /usr/lib32/libc.so.1 is loaded for programs compiled with the
> -n32 compiler option.
>
> * /usr/lib64/libc.so.1 is loaded for programs compiled with the -64
> compiler option.
>
> 2. The interpreter loads rld(5), the runtime linking loader, which
> finishes the exec(2) operation. Starting with the main program's
> liblist, rld(5) loads each DSO on the list that is not marked to be
> delay-loaded. rld(5) reads that object's liblist and repeats the
> operation until all DSOs have been loaded, in a breadth-first
> manner. The breadth first loading process, which ignores objects
> marked to be delay-loaded, results in defining a sequence of
> objects.
>
> 3. rld(5) allocates storage for COMMON block symbols and fixes up
> symbolic references in each loaded object. This is necessary
> because the location at which the object will be loaded is not
> known until runtime. To look up a given symbol in the process of
> fixing up symbolic references, rld(5) examines each object's
> dynamic symbol table. If rld(5) finds a strong symbol that
> satisfies the reference (that is, it has the name of the given
> symbol and is an external definition) it stops with that symbol.
> If it does not find a strong definition with that name, then the
> first weak symbol found is accepted as the definition.
>
> 4. Each object's -init code is executed. This is code specified as an
> argument to the -init option on the ld(1) command. For information
> on -init code, see ld(1).
>
> 5. Control transfers to __start in the main program

Thanks to David Anderson of SGI, I have been told how to have a more
proper startup sequence. The idea relies on the fact that the runtime
loader (rld) calls a dummy internal-to-rld function __dbx_link() once
the DSOs have been mapped in memory, but before the -init code has been
executed.

The trick is therefore to break on that function instead of breaking
of at the program entry point. However, this is not that easy, as:

- rld hasn't been loaded yet at the time when the inferior stops on
the exec-exit event. So we can't insert the breakpoint just yet.

- Which rld has been used depends on a number of factors, so we have
to find which one in order to find the address of __dbx_link().

So, first of all, we need to find a way of waiting until rld has been
loaded. For that, we need to setup another system-call-exit trap on
syssgi() this time. And keep waiting until we find rld. Finding rld
is done by iterating over each TEXT memory regions, getting its
associated fd, and using that fd to scan the symbol table (ELF) until
we find "__dbx_link".

My question is: Where should I put the code: I need to do some procfs
operations from solib-irix. I was thinking of having all the code doing
the procfs stuff inside procfs, and call that code from solib-irix.c
until I get the __dbx_lin() address. But at the same, it seems to me
that solib-irix.c is a target-dependent file, while procfs.c is
host-dependent. So it is not impossible that a cross-debugger be built,
making my procfs operations unavoidable (assuming I can link, which is
not guarantied either).

How do you think I should solve this issue?

Thanks,
--
Joel