diff options
author | Andrew Burgess <andrew.burgess@embecosm.com> | 2018-10-18 14:04:27 +0100 |
---|---|---|
committer | Andrew Burgess <andrew.burgess@embecosm.com> | 2018-10-31 13:15:59 +0000 |
commit | eb77c9df9f6d2f7aa644a170280fe31ce080f887 (patch) | |
tree | 20ef6036caba8f8f116a29da5cebfa66d757c7d8 /gdb/valprint.c | |
parent | 664306bcceb99fbe021ceb701a7192d62234f327 (diff) | |
download | gdb-eb77c9df9f6d2f7aa644a170280fe31ce080f887.zip gdb-eb77c9df9f6d2f7aa644a170280fe31ce080f887.tar.gz gdb-eb77c9df9f6d2f7aa644a170280fe31ce080f887.tar.bz2 |
gdb: Handle ICC's unexpected void return type
I encountered a binary compiled with Intel's C Compiler (ICC) version
14.0.5.212, which seemed to contain some non-standard DWARF.
The DWARF spec (V5 3.3.2) says:
Debugging information entries for C void functions should not have
an attribute for the return type.
However, what I observed in the DWARF from this ICC compiled binary
was this:
...
<0><857>: Abbrev Number: 1 (DW_TAG_compile_unit)
<858> DW_AT_comp_dir : (indirect string, offset: 0x48d): /tmp/
<85c> DW_AT_language : 1 (ANSI C)
<85d> DW_AT_name : (indirect string, offset: 0x77c): filename.c
<861> DW_AT_producer : (indirect string, offset: 0x520): Intel(R) C Intel(R) 64 Compiler ...
<865> DW_AT_low_pc : 0x4378d0
<86d> DW_AT_high_pc : 0x4378f0
<875> DW_AT_stmt_list : 0xa37
...
<1><7ea>: Abbrev Number: 2 (DW_TAG_base_type)
<7eb> DW_AT_byte_size : 0
<7ec> DW_AT_encoding : 5 (signed)
<7ed> DW_AT_name : (indirect string, offset: 0x58f): void
...
<1><7f1>: Abbrev Number: 3 (DW_TAG_subprogram)
<7f2> DW_AT_decl_line : 268
<7f4> DW_AT_decl_column : 30
<7f5> DW_AT_decl_file : 1
<7f6> DW_AT_type : <0x7ea>
<7fa> DW_AT_prototyped : 1
<7fb> DW_AT_name : (indirect string, offset: 0x761): function_foo
<7ff> DW_AT_MIPS_linkage_name: (indirect string, offset: 0x761): function_foo
<803> DW_AT_low_pc : 0x4378a0
<80b> DW_AT_high_pc : 0x4378d0
<813> DW_AT_external : 1
...
So function 'function_foo' has void return type, but still has a
DW_AT_type attribute for a 0 sized type called void.
What was found was that when the 'finish' command was used to leave
'function_foo', GDB would crash.
The problem is that in infcmd.c:print_return_value GDB tries to filter
out void return types, by looking for the TYPE_CODE_VOID, this fails
for the 'void' type as it has code TYPE_CODE_INT and GDB then tries to
print the 'void' type.
This eventually ends in a call to valprint.c:maybe_negate_by_bytes,
however, the len (length) of the value being negated is 0, which is
not detected or expected by this code, and invalid memory accesses
occur, some of which might cause GDB to crash.
The above DWARF was seen on version 14.0.5.212 of ICC.
I have also tested ICC versions 18.0.2.199 and 17.0.7.259, on both of
these versions, the DW_AT_type on the DW_TAG_subprogram has been
removed, bringing ICC inline with the DWARF standard, and with the
DWARF produced by GCC.
I only have limited access to these specific versions of ICC so I am
unable to get more specific details for when the generated DWARF
became non-standard or when it was changed to be more inline with the
DWARF standard.
Further testing revealed additional places where ICC produced 'void'
related DWARF that GDB struggles with. When I compiled code that
contained a function with this signature:
void funcx (void *arg);
on ICC 17/18, I got the following DWARF (notice the void return type
is now gone):
...
<1><32>: Abbrev Number: 2 (DW_TAG_subprogram)
<33> DW_AT_decl_line : 2
<34> DW_AT_decl_file : 1
<35> DW_AT_prototyped : 1
<36> DW_AT_name : (indirect string, offset: 0xc5): funcx
<3a> DW_AT_MIPS_linkage_name: (indirect string, offset: 0xc5): funcx
<3e> DW_AT_low_pc : 0x6dc
<46> DW_AT_high_pc : 0x703
<4e> DW_AT_external : 1
<2><4f>: Abbrev Number: 3 (DW_TAG_formal_parameter)
<50> DW_AT_decl_line : 2
<51> DW_AT_decl_file : 1
<52> DW_AT_type : <0x6a>
<56> DW_AT_name : arg
<5a> DW_AT_location : 2 byte block: 76 70 (DW_OP_breg6 (rbp): -16)
...
<1><6a>: Abbrev Number: 5 (DW_TAG_pointer_type)
<6b> DW_AT_type : <0x6f>
<1><6f>: Abbrev Number: 6 (DW_TAG_base_type)
<70> DW_AT_byte_size : 0
<71> DW_AT_encoding : 5 (signed)
<72> DW_AT_name : (indirect string, offset: 0xcb): void
...
However, the function argument 'arg' does still reference a 'void'
type. This case doesn't seem as obviously non-standard as the
previous one, but I think that the DWARF standard (V5 5.2) does
suggest that the above is not the recommended approach. If we compare
to the DWARF generated by GCC 7.3.1:
...
<1><68>: Abbrev Number: 5 (DW_TAG_subprogram)
<69> DW_AT_external : 1
<69> DW_AT_name : (indirect string, offset: 0x221): funcx
<6d> DW_AT_decl_file : 1
<6e> DW_AT_decl_line : 2
<6f> DW_AT_prototyped : 1
<6f> DW_AT_low_pc : 0x400487
<77> DW_AT_high_pc : 0x22
<7f> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<81> DW_AT_GNU_all_call_sites: 1
<81> DW_AT_sibling : <0xa0>
<2><85>: Abbrev Number: 6 (DW_TAG_formal_parameter)
<86> DW_AT_name : arg
<8a> DW_AT_decl_file : 1
<8b> DW_AT_decl_line : 2
<8c> DW_AT_type : <0xa0>
<90> DW_AT_location : 2 byte block: 91 58 (DW_OP_fbreg: -40)
...
<1><a0>: Abbrev Number: 7 (DW_TAG_pointer_type)
<a1> DW_AT_byte_size : 8
...
Here we see that the DW_TAG_pointer_type doesn't reference any further
type. This also seems out of line with the DWARF standard (which I
think recommends using a DW_TAG_unspecified_type entry), however GDB
does handle the GCC generated DWARF better.
If we look at how GDB handles the DWARF from GCC, then we see this:
(gdb) print *arg
Attempt to dereference a generic pointer.
While on the current HEAD of master dereferencing arg causes undefined
behaviour which will likely crash GDB (for the same reason as was
described above for the 'finish' case). On earlier versions of GDB
the ICC DWARF would cause this:
(gdb) print *arg
$1 = 0
In this patch both the return type, and general variable/parameter
type handling is fixed by transforming the synthetic void entries in
the DWARF, the ones that look like this:
<1><6f>: Abbrev Number: 6 (DW_TAG_base_type)
<70> DW_AT_byte_size : 0
<71> DW_AT_encoding : 5 (signed)
<72> DW_AT_name : (indirect string, offset: 0xcb): void
into GDB's builtin void type. My criteria for performing the fix are:
1. Binary produced by any version of ICC,
2. We're producing an integer type,
3. The size is 0, and
4. The name is "void".
I ignore the signed / unsigned nature of the integer.
Potentially we could drop the ICC detection too, this should be a
reasonably safe transformation to perform, however, I'm generally
pretty nervous when it comes to modifying how the DWARF is parsed so,
for now, I have restricted this to ICC only.
I also added an assertion to maybe_negate_by_bytes. This is nothing
to do with the actual fix, but should detect incorrect use of this
function in the future, without relying on undefined behaviour to
crash GDB.
I added a new test that makes use the of the testsuite's DWARF
generator. As it is tricky to create target independent tests that
pass function parameters using the DWARF generator (as specifying the
argument location is target specific) I have instead made use of a
global variable void*. This still shows the issue.
We already have a predicate in the DWARF parser to detect versions of
ICC prior to 14, however, this issue was spotted on a later version.
As a result I've added a new predicate that is true for any version of
ICC.
gdb/ChangeLog:
* dwarf2read.c (struct dwarf2_cu): Add producer_is_icc field.
(producer_is_icc): New function.
(check_producer): Set producer_is_icc field on dwarf2_cu.
(dwarf2_init_integer_type): New function.
(read_base_type): Call dwarf2_init_integer_type instead of
init_integer_type in all cases.
(dwarf2_cu::dwarf2_cu): Initialise producer_is_icc field.
* valprint.c (maybe_negate_by_bytes): Add an assertion that the
LEN is greater than 0.
gdb/testsuite/ChangeLog:
* gdb.dwarf2/void-type.c: New file.
* gdb.dwarf2/void-type.exp: New file.
Diffstat (limited to 'gdb/valprint.c')
-rw-r--r-- | gdb/valprint.c | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/gdb/valprint.c b/gdb/valprint.c index 2b63a91..b2236f8 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -1604,6 +1604,7 @@ maybe_negate_by_bytes (const gdb_byte *bytes, unsigned len, gdb::byte_vector *out_vec) { gdb_byte sign_byte; + gdb_assert (len > 0); if (byte_order == BFD_ENDIAN_BIG) sign_byte = bytes[0]; else |