libdrgn: determine default size_t and ptrdiff_t more intelligently

Currently, size_t and ptrdiff_t default to typedefs of the default
unsigned long and long, respectively, regardless of what the program
actually defines unsigned long or long as. Instead, make them refer the
whatever integer type (long, long long, or int) is the same size as the
word size.
This commit is contained in:
Omar Sandoval 2019-05-10 15:10:04 -07:00
parent 83285d3f29
commit ab58a5bff0
3 changed files with 113 additions and 62 deletions

View File

@ -171,15 +171,15 @@ void drgn_type_index_remove_finder(struct drgn_type_index *tindex)
tindex->finders = finder;
}
/* Default long, unsigned long, size_t, and ptrdiff_t are 64 bits. */
/* Default long and unsigned long are 64 bits. */
static struct drgn_type default_primitive_types[DRGN_PRIMITIVE_TYPE_NUM];
/* 32-bit version of long, unsigned long, size_t, and ptrdiff_t. */
static struct drgn_type default_primitive_types_32bit[4];
/* 32-bit versions of long and unsigned long. */
static struct drgn_type default_long_32bit;
static struct drgn_type default_unsigned_long_32bit;
__attribute__((constructor(200)))
static void default_primitive_types_init(void)
{
struct drgn_qualified_type qualified_type;
size_t i;
drgn_int_type_init(&default_primitive_types[DRGN_C_TYPE_CHAR],
@ -227,43 +227,24 @@ static void default_primitive_types_init(void)
drgn_float_type_init(&default_primitive_types[DRGN_C_TYPE_LONG_DOUBLE],
drgn_primitive_type_spellings[DRGN_C_TYPE_LONG_DOUBLE][0],
16);
qualified_type.type = &default_primitive_types[DRGN_C_TYPE_UNSIGNED_LONG];
qualified_type.qualifiers = 0;
drgn_typedef_type_init(&default_primitive_types[DRGN_C_TYPE_SIZE_T],
drgn_primitive_type_spellings[DRGN_C_TYPE_SIZE_T][0],
qualified_type);
qualified_type.type = &default_primitive_types[DRGN_C_TYPE_LONG];
drgn_typedef_type_init(&default_primitive_types[DRGN_C_TYPE_PTRDIFF_T],
drgn_primitive_type_spellings[DRGN_C_TYPE_PTRDIFF_T][0],
qualified_type);
for (i = 0; i < ARRAY_SIZE(default_primitive_types); i++) {
if (drgn_primitive_type_kind[i] == DRGN_TYPE_VOID)
if (drgn_primitive_type_kind[i] == DRGN_TYPE_VOID ||
i == DRGN_C_TYPE_SIZE_T || i == DRGN_C_TYPE_PTRDIFF_T)
continue;
assert(drgn_type_primitive(&default_primitive_types[i]) == i);
}
drgn_int_type_init(&default_primitive_types_32bit[0],
drgn_int_type_init(&default_long_32bit,
drgn_primitive_type_spellings[DRGN_C_TYPE_LONG][0],
4, true);
drgn_int_type_init(&default_primitive_types_32bit[1],
assert(drgn_type_primitive(&default_long_32bit) ==
DRGN_C_TYPE_LONG);
drgn_int_type_init(&default_unsigned_long_32bit,
drgn_primitive_type_spellings[DRGN_C_TYPE_UNSIGNED_LONG][0],
4, false);
qualified_type.type = &default_primitive_types_32bit[1];
drgn_typedef_type_init(&default_primitive_types_32bit[2],
drgn_primitive_type_spellings[DRGN_C_TYPE_SIZE_T][0],
qualified_type);
qualified_type.type = &default_primitive_types_32bit[0];
drgn_typedef_type_init(&default_primitive_types_32bit[3],
drgn_primitive_type_spellings[DRGN_C_TYPE_PTRDIFF_T][0],
qualified_type);
assert(drgn_type_primitive(&default_primitive_types_32bit[0]) ==
DRGN_C_TYPE_LONG);
assert(drgn_type_primitive(&default_primitive_types_32bit[1]) ==
assert(drgn_type_primitive(&default_unsigned_long_32bit) ==
DRGN_C_TYPE_UNSIGNED_LONG);
assert(drgn_type_primitive(&default_primitive_types_32bit[2]) ==
DRGN_C_TYPE_SIZE_T);
assert(drgn_type_primitive(&default_primitive_types_32bit[3]) ==
DRGN_C_TYPE_PTRDIFF_T);
}
/*
@ -337,33 +318,67 @@ drgn_type_index_find_primitive(struct drgn_type_index *tindex,
}
}
switch (type) {
case DRGN_C_TYPE_LONG:
i = 0;
break;
case DRGN_C_TYPE_UNSIGNED_LONG:
i = 1;
break;
case DRGN_C_TYPE_SIZE_T:
i = 2;
break;
case DRGN_C_TYPE_PTRDIFF_T:
i = 3;
break;
default:
*ret = &default_primitive_types[type];
goto out;
/* long and unsigned long default to the word size. */
if (type == DRGN_C_TYPE_LONG || type == DRGN_C_TYPE_UNSIGNED_LONG) {
if (!tindex->word_size) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"word size has not been set");
}
if (tindex->word_size == 4) {
*ret = (type == DRGN_C_TYPE_LONG ?
&default_long_32bit :
&default_unsigned_long_32bit);
goto out;
}
}
/*
* size_t and ptrdiff_t default to typedefs of whatever integer type
* matches the word size.
*/
if (type == DRGN_C_TYPE_SIZE_T || type == DRGN_C_TYPE_PTRDIFF_T) {
static enum drgn_primitive_type integer_types[2][3] = {
{
DRGN_C_TYPE_UNSIGNED_LONG,
DRGN_C_TYPE_UNSIGNED_LONG_LONG,
DRGN_C_TYPE_UNSIGNED_INT,
},
{
DRGN_C_TYPE_LONG,
DRGN_C_TYPE_LONG_LONG,
DRGN_C_TYPE_INT,
},
};
if (!tindex->word_size) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"word size has not been set");
}
for (i = 0; i < 3; i++) {
enum drgn_primitive_type integer_type;
integer_type = integer_types[type == DRGN_C_TYPE_PTRDIFF_T][i];
err = drgn_type_index_find_primitive(tindex,
integer_type,
&qualified_type.type);
if (err)
return err;
if (drgn_type_size(qualified_type.type) ==
tindex->word_size) {
qualified_type.qualifiers = 0;
*ret = (type == DRGN_C_TYPE_SIZE_T ?
&tindex->default_size_t :
&tindex->default_ptrdiff_t);
drgn_typedef_type_init(*ret, spellings[0],
qualified_type);
goto out;
}
}
return drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
"no suitable integer type for %s",
spellings[0]);
}
if (!tindex->word_size) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
"word size has not been set");
}
if (tindex->word_size == 4)
*ret = &default_primitive_types_32bit[i];
else
*ret = &default_primitive_types[type];
*ret = &default_primitive_types[type];
out:
tindex->primitive_types[type] = *ret;

View File

@ -94,6 +94,8 @@ struct drgn_type_index {
struct drgn_type_finder *finders;
/** Cache of primitive types. */
struct drgn_type *primitive_types[DRGN_PRIMITIVE_TYPE_NUM];
struct drgn_type default_size_t;
struct drgn_type default_ptrdiff_t;
/** Cache of created pointer types. */
struct drgn_pointer_type_set pointer_types;
/** Cache of created array types. */

View File

@ -30,6 +30,7 @@ from tests.elf import ET, PT
from tests.elfwriter import ElfSection, create_elf_file
MOCK_32BIT_ARCH = Architecture.IS_LITTLE_ENDIAN
MOCK_ARCH = Architecture.IS_64_BIT | Architecture.IS_LITTLE_ENDIAN
@ -43,7 +44,7 @@ def mock_memory_read(data, address, count, physical, offset):
return data[offset:offset + count]
def mock_program(*, arch=MOCK_ARCH, segments=None, types=None, symbols=None):
def mock_program(arch=MOCK_ARCH, *, segments=None, types=None, symbols=None):
def mock_find_type(kind, name, filename):
if filename:
return None
@ -227,11 +228,7 @@ class TestTypes(unittest.TestCase):
yield ' '.join(perm)
for word_size in [8, 4]:
if word_size == 8:
arch = MOCK_ARCH | Architecture.IS_64_BIT
else:
arch = MOCK_ARCH & ~Architecture.IS_64_BIT
prog = mock_program(arch=arch)
prog = mock_program(MOCK_ARCH if word_size == 8 else MOCK_32BIT_ARCH)
self.assertEqual(prog.type('_Bool'), bool_type('_Bool', 1))
self.assertEqual(prog.type('char'), int_type('char', 1, True))
for spelling in spellings(['signed', 'char']):
@ -289,6 +286,43 @@ class TestTypes(unittest.TestCase):
self.assertEqual(prog.type('unsigned long'),
int_type('unsigned long', 8, False))
def test_size_t_and_ptrdiff_t(self):
# 64-bit architecture with 4-byte long/unsigned long.
prog = mock_program(types=[
int_type('long', 4, True),
int_type('unsigned long', 4, False),
])
self.assertEqual(prog.type('size_t'),
typedef_type('size_t', prog.type('unsigned long long')))
self.assertEqual(prog.type('ptrdiff_t'),
typedef_type('ptrdiff_t', prog.type('long long')))
# 32-bit architecture with 8-byte long/unsigned long.
prog = mock_program(MOCK_32BIT_ARCH, types=[
int_type('long', 8, True),
int_type('unsigned long', 8, False),
])
self.assertEqual(prog.type('size_t'),
typedef_type('size_t', prog.type('unsigned int')))
self.assertEqual(prog.type('ptrdiff_t'),
typedef_type('ptrdiff_t', prog.type('int')))
# Nonsense sizes.
prog = mock_program(types=[
int_type('int', 1, True),
int_type('unsigned int', 1, False),
int_type('long', 1, True),
int_type('unsigned long', 1, False),
int_type('long long', 2, True),
int_type('unsigned long long', 2, False),
])
self.assertRaisesRegex(ValueError,
'no suitable integer type for size_t',
prog.type, 'size_t')
self.assertRaisesRegex(ValueError,
'no suitable integer type for ptrdiff_t',
prog.type, 'ptrdiff_t')
def test_tagged_type(self):
prog = mock_program(types=[point_type, option_type, color_type])
self.assertEqual(prog.type('struct point'), point_type)