Re: [RFC PATCH 0/3] Pretty-printing for errno

Zack Weinberg
2017-06-30T17:27:40Z

On Fri, Jun 30, 2017 at 12:37 PM, Pedro Alves <palves@redhat.com> wrote:
> On 06/30/2017 01:28 AM, Zack Weinberg wrote:
>> For the glibc patch to go through, this case also needs to work:
>>
>> typedef int error_t;
>> error_t var;
>> extern error_t *errloc(void);
>>
>> int main(void)
>> {
>> return *errloc();
>> }
>>
>> (compiled to .o file)
>>
>> (gdb) ptype errloc
>> No symbol "errloc" in current context.
>>
>> This might be a problem with inadequate debugging information
>> generated by the compiler
>
> Debug info is generated for function definitions, not declarations.
> I.e., that declaration for "errloc" alone produces no debug info
> at all. Try 'readelf -dw' on the .o file.
>
> But why is this a valid scenario? Even if you have no debug info,
> GDB should be able to find the function's address in the elf dynamic
> symbol table?
>
> I'm not sure what you mean by "glibc patch to go through".
> If you mean acceptance upstream, then I'd consider this a separate
> issue, because now you're talking about being able to print the
> "errno" variable in the first place, even as a plain int with no
> printer? That is related, but orthogonal from the error_t type printer?
> Otherwise, can you clarify?

The _primary_ goal of the glibc patches is to trigger a pretty-printer
when someone does

(gdb) p errno

with no further adornment. Since pretty-printers are keyed by type
(and since people do store errno codes in other places than errno),
this involves a type 'error_t' (and its implementation-namespace alias
'__error_t'), but if we can't get 'p errno' to work, we're probably
not going to bother.

In all currently-supported configurations, this is glibc's definition of errno:

extern int *__errno_location (void) __THROW __attribute__ ((__const__));
#define errno (*__errno_location ())

(__THROW is a macro expanding to 'throw ()' when compiling C++.)

The patches change that to

extern __error_t *__errno_location (void) __THROW __attribute__ ((__const__));

which should be sufficient to get the pretty-printing happening. But
obviously that's only going to work if GDB actually knows that the
return type of __errno_location is __error_t*, and it doesn't seem to,
*even when debug information for the definition is available*:

$ gdb /lib/x86_64-linux-gnu/libc.so.6
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
...

Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols
from /usr/lib/debug/.build-id/cc/80584889db7a969292959a46c718a2b1500702.debug...done.
done.
(gdb) ptype __errno_location
type = int ()
(gdb) p __errno_location
$1 = {<text variable, no debug info>} 0x20590 <__GI___errno_location>

... a suspicion occurs...

(gdb) ptype __GI___errno_location
type = int *(void)
(gdb) p __GI___errno_location
$2 = {int *(void)} 0x20590 <__GI___errno_location>

... so I guess this is a problem with the __GI_ indirection, which
*may* be a thing we can resolve on our end. I don't fully understand
that stuff.

Still, It Would Be Nice™ if you _didn't_ have to have the debug
symbols for libc.so installed for this to work. Probably a lot of
people think you only need those if you are debugging libc itself.

> The next problem is that without debug info for __errno_location,
> gdb has no clue of its prototype, only that its a function, and so
> it assumes that it has type "int()", i.e., that it returns int,
> while in reality it returns int * / __error_t *. (Falling back
> to assuming "int" is IMO not the best idea, but I don't know
> the history behind it.)

Probably because that's the pre-C89 legacy default function signature?

> One dirty way around it would be for the printer to
> re-define the errno macro (using to cast __errno_location to
> the correct type before calling it, I guess:
>
> (gdb) macro define errno *(*(__error_t *(*) (void)) __errno_location) ()
>
> That's make "errno" available when you compile with levels
> lower than -g3, too.

Hmm. How would one do that from inside Python?

zw