From 89307c532a3b1b8e637829d1a867fd336b0f6788 Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Fri, 6 Dec 2019 11:17:43 -0800 Subject: [PATCH] libdrgn: add DRGN_FORMAT_OBJECT_CHAR --- docs/api_reference.rst | 4 + libdrgn/drgn.h.in | 3 +- libdrgn/language.h | 3 +- libdrgn/language_c.c | 206 +++++++++++++++++++++++----------------- libdrgn/python/object.c | 3 +- tests/test_object.py | 16 ++++ 6 files changed, 145 insertions(+), 90 deletions(-) diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 60fbe5b8..f4e2241f 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -797,6 +797,10 @@ Objects For C, this applies to pointers to and arrays of ``char``, ``signed char``, and ``unsigned char``. Defaults to ``True``. :type string: bool or None + :param char: Format objects with character type as character literals. + For C, this applies to ``char``, ``signed char``, and ``unsigned + char``. Defaults to ``False``. + :type char: bool or None :rtype: str .. function:: NULL(prog, type) diff --git a/libdrgn/drgn.h.in b/libdrgn/drgn.h.in index 73cf8736..6fd8f77e 100644 --- a/libdrgn/drgn.h.in +++ b/libdrgn/drgn.h.in @@ -2005,11 +2005,12 @@ enum drgn_format_object_flags { DRGN_FORMAT_OBJECT_DEREFERENCE = 1 << 0, DRGN_FORMAT_OBJECT_SYMBOLIZE = 1 << 1, DRGN_FORMAT_OBJECT_STRING = 1 << 2, + DRGN_FORMAT_OBJECT_CHAR = 1 << 3, /** Default "pretty" flags. */ DRGN_FORMAT_OBJECT_PRETTY = (DRGN_FORMAT_OBJECT_DEREFERENCE | DRGN_FORMAT_OBJECT_SYMBOLIZE | DRGN_FORMAT_OBJECT_STRING), - DRGN_FORMAT_OBJECT_VALID_FLAGS = (1 << 3) - 1, + DRGN_FORMAT_OBJECT_VALID_FLAGS = (1 << 4) - 1, }; /** diff --git a/libdrgn/language.h b/libdrgn/language.h index 22ebc819..45801bd8 100644 --- a/libdrgn/language.h +++ b/libdrgn/language.h @@ -127,7 +127,8 @@ static inline enum drgn_format_object_flags drgn_passthrough_format_object_flags(enum drgn_format_object_flags flags) { return (flags & (DRGN_FORMAT_OBJECT_SYMBOLIZE | - DRGN_FORMAT_OBJECT_STRING)); + DRGN_FORMAT_OBJECT_STRING | + DRGN_FORMAT_OBJECT_CHAR)); } /** @} */ diff --git a/libdrgn/language_c.c b/libdrgn/language_c.c index b510b7dd..d73b7ad1 100644 --- a/libdrgn/language_c.c +++ b/libdrgn/language_c.c @@ -584,11 +584,126 @@ c_format_object_impl(const struct drgn_object *obj, bool cast, enum drgn_format_object_flags flags, struct string_builder *sb); +static bool is_character_type(struct drgn_type *type) +{ + switch (drgn_type_primitive(type)) { + case DRGN_C_TYPE_CHAR: + case DRGN_C_TYPE_SIGNED_CHAR: + case DRGN_C_TYPE_UNSIGNED_CHAR: + return true; + default: + return false; + } +} + static struct drgn_error * -c_format_int_object(const struct drgn_object *obj, struct string_builder *sb) +c_format_character(unsigned char c, bool escape_single_quote, + bool escape_double_quote, struct string_builder *sb) +{ + bool ret; + + switch (c) { + case '\0': + ret = string_builder_append(sb, "\\0"); + break; + case '\a': + ret = string_builder_append(sb, "\\a"); + break; + case '\b': + ret = string_builder_append(sb, "\\b"); + break; + case '\t': + ret = string_builder_append(sb, "\\t"); + break; + case '\n': + ret = string_builder_append(sb, "\\n"); + break; + case '\v': + ret = string_builder_append(sb, "\\v"); + break; + case '\f': + ret = string_builder_append(sb, "\\f"); + break; + case '\r': + ret = string_builder_append(sb, "\\r"); + break; + case '"': + if (!escape_double_quote) + goto no_escape; + ret = string_builder_append(sb, "\\\""); + break; + case '\'': + if (!escape_single_quote) + goto no_escape; + ret = string_builder_append(sb, "\\'"); + break; + case '\\': + ret = string_builder_append(sb, "\\\\"); + break; + default: + if (c <= '\x1f' || c >= '\x7f') { + ret = string_builder_appendf(sb, "\\x%02x", c); + } else { +no_escape: + ret = string_builder_appendc(sb, c); + } + break; + } + return ret ? NULL : &drgn_enomem; +} + +static struct drgn_error * +c_format_string(struct drgn_memory_reader *reader, uint64_t address, + uint64_t length, struct string_builder *sb) { struct drgn_error *err; + if (!string_builder_appendc(sb, '"')) + return &drgn_enomem; + while (length) { + unsigned char c; + + err = drgn_memory_reader_read(reader, &c, address++, 1, false); + if (err) + return err; + + if (c == '\0') { + break; + } else { + err = c_format_character(c, false, true, sb); + if (err) + return err; + } + length--; + } + if (!string_builder_appendc(sb, '"')) + return &drgn_enomem; + return NULL; +} + +static struct drgn_error * +c_format_int_object(const struct drgn_object *obj, + enum drgn_format_object_flags flags, + struct string_builder *sb) +{ + struct drgn_error *err; + + if ((flags & DRGN_FORMAT_OBJECT_CHAR) && is_character_type(obj->type)) { + union drgn_value value; + + if (!string_builder_appendc(sb, '\'')) + return &drgn_enomem; + err = drgn_object_read_integer(obj, &value); + if (err) + return err; + err = c_format_character(value.uvalue, true, false, sb); + if (err) + return err; + if (!string_builder_appendc(sb, '\'')) + return &drgn_enomem; + return NULL; + } + switch (obj->kind) { case DRGN_OBJECT_SIGNED: { int64_t svalue; @@ -813,90 +928,6 @@ c_format_enum_object(const struct drgn_object *obj, } } -static bool is_character_type(struct drgn_type *type) -{ - switch (drgn_type_primitive(type)) { - case DRGN_C_TYPE_CHAR: - case DRGN_C_TYPE_SIGNED_CHAR: - case DRGN_C_TYPE_UNSIGNED_CHAR: - return true; - default: - return false; - } -} - -static struct drgn_error * -c_format_character(unsigned char c, struct string_builder *sb) -{ - bool ret; - - switch (c) { - case '\a': - ret = string_builder_append(sb, "\\a"); - break; - case '\b': - ret = string_builder_append(sb, "\\b"); - break; - case '\t': - ret = string_builder_append(sb, "\\t"); - break; - case '\n': - ret = string_builder_append(sb, "\\n"); - break; - case '\v': - ret = string_builder_append(sb, "\\v"); - break; - case '\f': - ret = string_builder_append(sb, "\\f"); - break; - case '\r': - ret = string_builder_append(sb, "\\r"); - break; - case '"': - ret = string_builder_append(sb, "\\\""); - break; - case '\\': - ret = string_builder_append(sb, "\\\\"); - break; - default: - if (c <= '\x1f' || c >= '\x7f') - ret = string_builder_appendf(sb, "\\x%02x", c); - else - ret = string_builder_appendc(sb, c); - break; - } - return ret ? NULL : &drgn_enomem; -} - -static struct drgn_error * -c_format_string(struct drgn_memory_reader *reader, uint64_t address, - uint64_t length, struct string_builder *sb) -{ - struct drgn_error *err; - - if (!string_builder_appendc(sb, '"')) - return &drgn_enomem; - while (length) { - unsigned char c; - - err = drgn_memory_reader_read(reader, &c, address++, 1, false); - if (err) - return err; - - if (c == '\0') { - break; - } else { - err = c_format_character(c, sb); - if (err) - return err; - } - length--; - } - if (!string_builder_appendc(sb, '"')) - return &drgn_enomem; - return NULL; -} - static struct drgn_error * c_format_pointer_object(const struct drgn_object *obj, struct drgn_type *underlying_type, bool cast, @@ -1042,7 +1073,8 @@ c_format_array_object(const struct drgn_object *obj, for (i = 0; i < size; i++) { if (buf[i] == '\0') break; - err = c_format_character(buf[i], sb); + err = c_format_character(buf[i], false, true, + sb); if (err) return err; } @@ -1279,7 +1311,7 @@ c_format_object_impl(const struct drgn_object *obj, bool cast, size_t indent, "cannot format void object"); case DRGN_TYPE_INT: case DRGN_TYPE_BOOL: - return c_format_int_object(obj, sb); + return c_format_int_object(obj, flags, sb); case DRGN_TYPE_FLOAT: return c_format_float_object(obj, sb); case DRGN_TYPE_COMPLEX: diff --git a/libdrgn/python/object.c b/libdrgn/python/object.c index 79777c6c..bd93439d 100644 --- a/libdrgn/python/object.c +++ b/libdrgn/python/object.c @@ -972,7 +972,8 @@ static PyObject *DrgnObject_format(DrgnObject *self, PyObject *args, #define FLAGS \ X(dereference, DRGN_FORMAT_OBJECT_DEREFERENCE) \ X(symbolize, DRGN_FORMAT_OBJECT_SYMBOLIZE) \ - X(string, DRGN_FORMAT_OBJECT_STRING) + X(string, DRGN_FORMAT_OBJECT_STRING) \ + X(char, DRGN_FORMAT_OBJECT_CHAR) static char *keywords[] = { #define X(name, value) #name, diff --git a/tests/test_object.py b/tests/test_object.py index 969d2205..ef0dd447 100644 --- a/tests/test_object.py +++ b/tests/test_object.py @@ -1228,6 +1228,22 @@ class TestCPretty(ObjectTestCase): self.assertEqual(str(Object(self.prog, 'const int', value=-99)), '(const int)-99') + def test_char(self): + obj = Object(self.prog, 'char', value=65) + self.assertEqual(str(obj), '(char)65') + self.assertEqual(obj.format_(char=True), "(char)'A'") + self.assertEqual( + Object(self.prog, 'signed char', value=65).format_(char=True), + "(signed char)'A'") + self.assertEqual( + Object(self.prog, 'unsigned char', value=65).format_(char=True), + "(unsigned char)'A'") + self.assertEqual( + Object(self.prog, + typedef_type('uint8_t', self.prog.type('unsigned char')), + value=65).format_(char=True), + "(uint8_t)65") + def test_bool(self): self.assertEqual(str(Object(self.prog, '_Bool', value=False)), '(_Bool)0')