ld: Handle extended-length data structures in PDB types
A few fixes to minor issues I've discovered in my PDB patches. * If sizes or offsets are greater than 0x8000, they get encoded as extended values in the same way as for enum values - e.g. a LF_ULONG .short followed by a .long. * I've managed to coax MSVC to produce another type, LF_VFTABLE, which is seen when dealing with COM. I don't think LLVM emits this. Note that we can't just implement everything in Microsoft's header files, as most of it is obsolete. * Fixes a stupid bug in the test program, where I was adding an index to a size. The index was hard-coded to 0, so this didn't cause any actual issues.
This commit is contained in:
committed by
Alan Modra
parent
826eed8027
commit
fdf591c4c6
@@ -2476,6 +2476,7 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
case LF_MEMBER:
|
||||
{
|
||||
struct lf_member *mem = (struct lf_member *) ptr;
|
||||
uint16_t offset;
|
||||
size_t name_len, subtype_len;
|
||||
|
||||
if (left < offsetof (struct lf_member, name))
|
||||
@@ -2488,9 +2489,34 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
if (!remap_type (&mem->type, map, type_num, num_types))
|
||||
return false;
|
||||
|
||||
subtype_len = offsetof (struct lf_member, name);
|
||||
|
||||
offset = bfd_getl16 (&mem->offset);
|
||||
|
||||
/* If offset >= 0x8000, actual value follows. */
|
||||
if (offset >= 0x8000)
|
||||
{
|
||||
unsigned int param_len = extended_value_len (offset);
|
||||
|
||||
if (param_len == 0)
|
||||
{
|
||||
einfo (_("%P: warning: unhandled type %v within"
|
||||
" LF_MEMBER\n"), offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
subtype_len += param_len;
|
||||
|
||||
if (left < subtype_len)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_MEMBER\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
name_len =
|
||||
strnlen (mem->name,
|
||||
left - offsetof (struct lf_member, name));
|
||||
strnlen ((char *) mem + subtype_len, left - subtype_len);
|
||||
|
||||
if (name_len == left - offsetof (struct lf_member, name))
|
||||
{
|
||||
@@ -2501,7 +2527,7 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
|
||||
name_len++;
|
||||
|
||||
subtype_len = offsetof (struct lf_member, name) + name_len;
|
||||
subtype_len += name_len;
|
||||
|
||||
if (subtype_len % 4 != 0)
|
||||
subtype_len += 4 - (subtype_len % 4);
|
||||
@@ -2706,6 +2732,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
case LF_BCLASS:
|
||||
{
|
||||
struct lf_bclass *bc = (struct lf_bclass *) ptr;
|
||||
size_t subtype_len;
|
||||
uint16_t offset;
|
||||
|
||||
if (left < sizeof (struct lf_bclass))
|
||||
{
|
||||
@@ -2718,8 +2746,44 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
num_types))
|
||||
return false;
|
||||
|
||||
ptr += sizeof (struct lf_bclass);
|
||||
left -= sizeof (struct lf_bclass);
|
||||
subtype_len = sizeof (struct lf_bclass);
|
||||
|
||||
offset = bfd_getl16 (&bc->offset);
|
||||
|
||||
/* If offset >= 0x8000, actual value follows. */
|
||||
if (offset >= 0x8000)
|
||||
{
|
||||
unsigned int param_len = extended_value_len (offset);
|
||||
|
||||
if (param_len == 0)
|
||||
{
|
||||
einfo (_("%P: warning: unhandled type %v within"
|
||||
" LF_BCLASS\n"), offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
subtype_len += param_len;
|
||||
|
||||
if (left < subtype_len)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_BCLASS\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (subtype_len % 4 != 0)
|
||||
subtype_len += 4 - (subtype_len % 4);
|
||||
|
||||
if (left < subtype_len)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_BCLASS\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr += subtype_len;
|
||||
left -= subtype_len;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2748,6 +2812,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
case LF_IVBCLASS:
|
||||
{
|
||||
struct lf_vbclass *vbc = (struct lf_vbclass *) ptr;
|
||||
size_t subtype_len;
|
||||
uint16_t offset;
|
||||
|
||||
if (left < sizeof (struct lf_vbclass))
|
||||
{
|
||||
@@ -2764,8 +2830,70 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
type_num, num_types))
|
||||
return false;
|
||||
|
||||
ptr += sizeof (struct lf_vbclass);
|
||||
left -= sizeof (struct lf_vbclass);
|
||||
subtype_len = offsetof (struct lf_vbclass,
|
||||
virtual_base_vbtable_offset);
|
||||
|
||||
offset = bfd_getl16 (&vbc->virtual_base_pointer_offset);
|
||||
|
||||
/* If offset >= 0x8000, actual value follows. */
|
||||
if (offset >= 0x8000)
|
||||
{
|
||||
unsigned int param_len = extended_value_len (offset);
|
||||
|
||||
if (param_len == 0)
|
||||
{
|
||||
einfo (_("%P: warning: unhandled type %v within"
|
||||
" LF_VBCLASS/LF_IVBCLASS\n"), offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
subtype_len += param_len;
|
||||
|
||||
if (left < subtype_len)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_VBCLASS/LF_IVBCLASS\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
offset = bfd_getl16 ((char *)vbc + subtype_len);
|
||||
subtype_len += sizeof (uint16_t);
|
||||
|
||||
/* If offset >= 0x8000, actual value follows. */
|
||||
if (offset >= 0x8000)
|
||||
{
|
||||
unsigned int param_len = extended_value_len (offset);
|
||||
|
||||
if (param_len == 0)
|
||||
{
|
||||
einfo (_("%P: warning: unhandled type %v within"
|
||||
" LF_VBCLASS/LF_IVBCLASS\n"), offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
subtype_len += param_len;
|
||||
|
||||
if (left < subtype_len)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_VBCLASS/LF_IVBCLASS\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (subtype_len % 4 != 0)
|
||||
subtype_len += 4 - (subtype_len % 4);
|
||||
|
||||
if (left < subtype_len)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_VBCLASS/LF_IVBCLASS\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr += subtype_len;
|
||||
left -= subtype_len;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2950,8 +3078,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
case LF_STRUCTURE:
|
||||
{
|
||||
struct lf_class *cl = (struct lf_class *) data;
|
||||
uint16_t prop;
|
||||
size_t name_len;
|
||||
uint16_t prop, num_bytes;
|
||||
size_t name_len, name_off;
|
||||
|
||||
if (size < offsetof (struct lf_class, name))
|
||||
{
|
||||
@@ -2969,9 +3097,35 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
if (!remap_type (&cl->vshape, map, type_num, num_types))
|
||||
return false;
|
||||
|
||||
name_len = strnlen (cl->name, size - offsetof (struct lf_class, name));
|
||||
name_off = offsetof (struct lf_class, name);
|
||||
|
||||
if (name_len == size - offsetof (struct lf_class, name))
|
||||
num_bytes = bfd_getl16 (&cl->length);
|
||||
|
||||
/* If num_bytes >= 0x8000, actual value follows. */
|
||||
if (num_bytes >= 0x8000)
|
||||
{
|
||||
unsigned int param_len = extended_value_len (num_bytes);
|
||||
|
||||
if (param_len == 0)
|
||||
{
|
||||
einfo (_("%P: warning: unhandled type %v within"
|
||||
" LF_CLASS/LF_STRUCTURE\n"), num_bytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
name_off += param_len;
|
||||
|
||||
if (size < name_off)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_CLASS/LF_STRUCTURE\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
name_len = strnlen ((char *) cl + name_off, size - name_off);
|
||||
|
||||
if (name_len == size - name_off)
|
||||
{
|
||||
einfo (_("%P: warning: name for LF_CLASS/LF_STRUCTURE has no"
|
||||
" terminating zero\n"));
|
||||
@@ -2984,10 +3138,11 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
{
|
||||
/* Structure has another name following first one. */
|
||||
|
||||
size_t len = offsetof (struct lf_class, name) + name_len + 1;
|
||||
size_t len = name_off + name_len + 1;
|
||||
size_t unique_name_len;
|
||||
|
||||
unique_name_len = strnlen (cl->name + name_len + 1, size - len);
|
||||
unique_name_len = strnlen ((char *) cl + name_off + name_len + 1,
|
||||
size - len);
|
||||
|
||||
if (unique_name_len == size - len)
|
||||
{
|
||||
@@ -2998,10 +3153,10 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
}
|
||||
|
||||
if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED))
|
||||
&& !is_name_anonymous (cl->name, name_len))
|
||||
&& !is_name_anonymous ((char *) cl + name_off, name_len))
|
||||
{
|
||||
other_hash = true;
|
||||
cv_hash = crc32 ((uint8_t *) cl->name, name_len);
|
||||
cv_hash = crc32 ((uint8_t *) cl + name_off, name_len);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -3010,8 +3165,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
case LF_UNION:
|
||||
{
|
||||
struct lf_union *un = (struct lf_union *) data;
|
||||
uint16_t prop;
|
||||
size_t name_len;
|
||||
uint16_t prop, num_bytes;
|
||||
size_t name_len, name_off;
|
||||
|
||||
if (size < offsetof (struct lf_union, name))
|
||||
{
|
||||
@@ -3023,9 +3178,35 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
if (!remap_type (&un->field_list, map, type_num, num_types))
|
||||
return false;
|
||||
|
||||
name_len = strnlen (un->name, size - offsetof (struct lf_union, name));
|
||||
name_off = offsetof (struct lf_union, name);
|
||||
|
||||
if (name_len == size - offsetof (struct lf_union, name))
|
||||
num_bytes = bfd_getl16 (&un->length);
|
||||
|
||||
/* If num_bytes >= 0x8000, actual value follows. */
|
||||
if (num_bytes >= 0x8000)
|
||||
{
|
||||
unsigned int param_len = extended_value_len (num_bytes);
|
||||
|
||||
if (param_len == 0)
|
||||
{
|
||||
einfo (_("%P: warning: unhandled type %v within"
|
||||
" LF_UNION\n"), num_bytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
name_off += param_len;
|
||||
|
||||
if (size < name_off)
|
||||
{
|
||||
einfo (_("%P: warning: truncated CodeView type record"
|
||||
" LF_UNION\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
name_len = strnlen ((char *) un + name_off, size - name_off);
|
||||
|
||||
if (name_len == size - name_off)
|
||||
{
|
||||
einfo (_("%P: warning: name for LF_UNION has no"
|
||||
" terminating zero\n"));
|
||||
@@ -3038,10 +3219,11 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
{
|
||||
/* Structure has another name following first one. */
|
||||
|
||||
size_t len = offsetof (struct lf_union, name) + name_len + 1;
|
||||
size_t len = name_off + name_len + 1;
|
||||
size_t unique_name_len;
|
||||
|
||||
unique_name_len = strnlen (un->name + name_len + 1, size - len);
|
||||
unique_name_len = strnlen ((char *) un + name_off + name_len + 1,
|
||||
size - len);
|
||||
|
||||
if (unique_name_len == size - len)
|
||||
{
|
||||
@@ -3052,10 +3234,10 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
|
||||
}
|
||||
|
||||
if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED))
|
||||
&& !is_name_anonymous (un->name, name_len))
|
||||
&& !is_name_anonymous ((char *) un + name_off, name_len))
|
||||
{
|
||||
other_hash = true;
|
||||
cv_hash = crc32 ((uint8_t *) un->name, name_len);
|
||||
cv_hash = crc32 ((uint8_t *) un + name_off, name_len);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -480,7 +480,6 @@ struct lf_bclass
|
||||
uint16_t attributes;
|
||||
uint32_t base_class_type;
|
||||
uint16_t offset;
|
||||
uint16_t padding;
|
||||
} ATTRIBUTE_PACKED;
|
||||
|
||||
/* lfVFuncTab in cvinfo.h */
|
||||
|
||||
@@ -10,4 +10,6 @@ Contents of section .data:
|
||||
0050 ffd80200 b0260100 7c060200 e3240200 *
|
||||
0060 63ff0100 fb6b0300 0ad90100 523c0200 *
|
||||
0070 4d5e0200 8a940200 4b710300 6aa90300 *
|
||||
0080 0a2c0300 67e10300 4a3d0300 *
|
||||
0080 0a2c0300 67e10300 4a3d0300 fa460300 *
|
||||
0090 db020200 ec4e0100 131e0300 fb120300 *
|
||||
00a0 aece0200 1db70100 *
|
||||
@@ -57,4 +57,18 @@ Contents of section .data:
|
||||
0340 7200f2f1 10150000 20100000 6e657374 r....... ...nest
|
||||
0350 65645f65 6e756d00 1a000515 01000000 ed_enum.........
|
||||
0360 21100000 00000000 00000000 04007175 !.............qu
|
||||
0370 757800f1 ux..
|
||||
0370 757800f1 12000315 10000000 74000000 ux..........t...
|
||||
0380 028060ea 00f3f2f1 2e000312 0d150300 ..`.............
|
||||
0390 23100000 00006100 0d150300 23100000 #.....a.....#...
|
||||
03a0 028060ea 6200f2f1 0d150300 23100000 ..`.b.......#...
|
||||
03b0 0480c0d4 01006300 26000515 03000000 ......c.&.......
|
||||
03c0 24100000 00000000 00000000 048020bf $............. .
|
||||
03d0 02006c6f 6e677374 72756374 00f3f2f1 ..longstruct....
|
||||
03e0 1a000312 0d150300 23100000 00006100 ........#.....a.
|
||||
03f0 0d150300 23100000 00006200 1a000615 ....#.....b.....
|
||||
0400 02000000 26100000 028060ea 6c6f6e67 ....&.....`.long
|
||||
0410 756e696f 6e00f2f1 1e000312 00140000 union...........
|
||||
0420 25100000 0480c0d4 0100f2f1 0d150300 %...............
|
||||
0430 23100000 00006400 26000312 01140000 #.....d.&.......
|
||||
0440 25100000 00000000 028060ea 0480c0d4 %.........`.....
|
||||
0450 0100f2f1 0d150300 23100000 00006400 ........#.....d.
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
.equ LF_USHORT, 0x8002
|
||||
.equ LF_LONG, 0x8003
|
||||
.equ LF_ULONG, 0x8004
|
||||
.equ LF_UQUADWORD, 0x800a
|
||||
|
||||
.equ CV_PTR_NEAR32, 0xa
|
||||
@@ -447,7 +448,7 @@
|
||||
|
||||
# Type 1021, struct quux, field list 1020
|
||||
.struct4:
|
||||
.short .types_end - .struct4 - 2
|
||||
.short .arr2 - .struct4 - 2
|
||||
.short LF_STRUCTURE
|
||||
.short 1 # no. members
|
||||
.short 0 # property
|
||||
@@ -458,4 +459,122 @@
|
||||
.asciz "quux" # name
|
||||
.byte 0xf1 # padding
|
||||
|
||||
# Type 1022, array[60000] of char
|
||||
.arr2:
|
||||
.short .fieldlist8 - .arr2 - 2
|
||||
.short LF_ARRAY
|
||||
.long T_CHAR # element type
|
||||
.long T_INT4 # index type
|
||||
.short LF_USHORT
|
||||
.short 60000 # size in bytes
|
||||
.byte 0 # name
|
||||
.byte 0xf3 # padding
|
||||
.byte 0xf2 # padding
|
||||
.byte 0xf1 # padding
|
||||
|
||||
# Type 1023, field list for struct longstruct
|
||||
.fieldlist8:
|
||||
.short .struct5 - .fieldlist8 - 2
|
||||
.short LF_FIELDLIST
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short 0 # offset
|
||||
.asciz "a"
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short LF_USHORT
|
||||
.short 60000 # offset
|
||||
.asciz "b"
|
||||
.byte 0xf2 # padding
|
||||
.byte 0xf1 # padding
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short LF_ULONG
|
||||
.long 120000 # offset
|
||||
.asciz "c"
|
||||
|
||||
# Type 1024, struct longstruct
|
||||
.struct5:
|
||||
.short .fieldlist9 - .struct5 - 2
|
||||
.short LF_STRUCTURE
|
||||
.short 3 # no. members
|
||||
.short 0 # property
|
||||
.long 0x1023 # field list
|
||||
.long 0 # type derived from
|
||||
.long 0 # type of vshape table
|
||||
.short LF_ULONG
|
||||
.long 180000 # size
|
||||
.asciz "longstruct" # name
|
||||
.byte 0xf3 # padding
|
||||
.byte 0xf2 # padding
|
||||
.byte 0xf1 # padding
|
||||
|
||||
# Type 1025, field list for union longunion
|
||||
.fieldlist9:
|
||||
.short .union4 - .fieldlist9 - 2
|
||||
.short LF_FIELDLIST
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short 0 # offset
|
||||
.asciz "a"
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short 0 # offset
|
||||
.asciz "b"
|
||||
|
||||
# Type 1026, union longunion (field list 1025)
|
||||
.union4:
|
||||
.short .fieldlist10 - .union4 - 2
|
||||
.short LF_UNION
|
||||
.short 2 # no. members
|
||||
.short 0 # property
|
||||
.long 0x1025 # field list
|
||||
.short LF_USHORT
|
||||
.short 60000 # size
|
||||
.asciz "longunion"
|
||||
.byte 0xf2 # padding
|
||||
.byte 0xf1 # padding
|
||||
|
||||
# Type 1027, field list with base class longstruct
|
||||
.fieldlist10:
|
||||
.short .fieldlist11 - .fieldlist10 - 2
|
||||
.short LF_FIELDLIST
|
||||
.short LF_BCLASS
|
||||
.short 0 # attributes
|
||||
.long 0x1024 # base class
|
||||
.short LF_ULONG
|
||||
.long 120000 # offset within class
|
||||
.byte 0xf2 # padding
|
||||
.byte 0xf1 # padding
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short 0 # offset
|
||||
.asciz "d"
|
||||
|
||||
# Type 1028, field list with virtual base class longstruct
|
||||
.fieldlist11:
|
||||
.short .types_end - .fieldlist11 - 2
|
||||
.short LF_FIELDLIST
|
||||
.short LF_VBCLASS
|
||||
.short 0 # attributes
|
||||
.long 0x1024 # type index of direct virtual base class
|
||||
.long 0 # type index of virtual base pointer
|
||||
.short LF_USHORT
|
||||
.short 60000 # virtual base pointer offset
|
||||
.short LF_ULONG
|
||||
.long 120000 # virtual base offset from vbtable
|
||||
.byte 0xf2 # padding
|
||||
.byte 0xf1 # padding
|
||||
.short LF_MEMBER
|
||||
.short 3 # public
|
||||
.long 0x1022
|
||||
.short 0 # offset
|
||||
.asciz "d"
|
||||
|
||||
.types_end:
|
||||
|
||||
@@ -1029,7 +1029,7 @@ proc test5 { } {
|
||||
binary scan $data i end_type
|
||||
|
||||
# end_type is one greater than the last type in the stream
|
||||
if { $end_type != 0x1023 } {
|
||||
if { $end_type != 0x102a } {
|
||||
fail "Incorrect end type value in TPI stream."
|
||||
} else {
|
||||
pass "Correct end type value in TPI stream."
|
||||
|
||||
Reference in New Issue
Block a user