summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason King <jason.king@joyent.com>2020-06-24 15:43:51 -0500
committerJason King <jason.king@joyent.com>2020-06-29 20:00:59 -0500
commit5f9772673df89384a3fa8f1cbfcccd8d331f83ff (patch)
treec8312f5b74c8f53c1a23fb84c43e39ded404d532
parent3f764e121447070c490c9637dd5791f8c8823ee4 (diff)
downloadillumos-joyent-5f9772673df89384a3fa8f1cbfcccd8d331f83ff.tar.gz
10854 empty struct array confuses CTF
Reviewed by: Andy Fiddaman <omnios@citrus-it.co.uk> Reviewed by: Robert Mustacchi <rm@fingolfin.org> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/lib/libctf/common/ctf_dwarf.c117
-rw-r--r--usr/src/test/util-tests/tests/ctf/check-array.c10
-rw-r--r--usr/src/test/util-tests/tests/ctf/test-array.c4
3 files changed, 104 insertions, 27 deletions
diff --git a/usr/src/lib/libctf/common/ctf_dwarf.c b/usr/src/lib/libctf/common/ctf_dwarf.c
index 2f501a2334..3079831715 100644
--- a/usr/src/lib/libctf/common/ctf_dwarf.c
+++ b/usr/src/lib/libctf/common/ctf_dwarf.c
@@ -1456,13 +1456,102 @@ ctf_dwarf_create_sou(ctf_cu_t *cup, Dwarf_Die die, ctf_id_t *idp,
}
static int
+ctf_dwarf_array_upper_bound(ctf_cu_t *cup, Dwarf_Die range, ctf_arinfo_t *ar)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Unsigned uval;
+ Dwarf_Signed sval;
+ Dwarf_Half form;
+ Dwarf_Error derr;
+ const char *formstr = NULL;
+ int ret = 0;
+
+ ctf_dprintf("setting array upper bound\n");
+
+ ar->ctr_nelems = 0;
+
+ ret = ctf_dwarf_attribute(cup, range, DW_AT_upper_bound, &attr);
+ /*
+ * Treat the lack of an upper bound attribute as a zero element array
+ * and return success, otherwise return the error.
+ */
+ if (ret != 0) {
+ if (ret == ENOENT)
+ return (0);
+ return (ret);
+ }
+
+ if (dwarf_whatform(attr, &form, &derr) != DW_DLV_OK) {
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get DW_AT_upper_bound attribute form: %s\n",
+ dwarf_errmsg(derr));
+ ret = ECTF_CONVBKERR;
+ goto done;
+ }
+
+ /*
+ * Compilers can indicate array bounds using signed or unsigned values.
+ * Additionally, some compilers may also store the array bounds
+ * using as DW_FORM_data{1,2,4,8} (which DWARF treats as raw data and
+ * expects the caller to understand how to interpret the value).
+ *
+ * GCC 4.4.4 appears to always use unsigned values to encode the
+ * array size (using '(unsigned)-1' to represent a zero-length or
+ * unknown length array). Later versions of GCC use a signed value of
+ * -1 for zero/unknown length arrays, and unsigned values to encode
+ * known array sizes.
+ *
+ * Both dwarf_formsdata() and dwarf_formudata() will retrieve values
+ * as their respective signed/unsigned forms, but both will also
+ * retreive DW_FORM_data{1,2,4,8} values and treat them as signed or
+ * unsigned integers (i.e. dwarf_formsdata() treats DW_FORM_dataXX
+ * as signed integers and dwarf_formudata() treats DW_FORM_dataXX as
+ * unsigned integers). Both will return an error if the form is not
+ * their respective signed/unsigned form, or DW_FORM_dataXX.
+ *
+ * To obtain the upper bound, we use the appropriate
+ * dwarf_form[su]data() function based on the form of DW_AT_upper_bound.
+ * Additionally, we let dwarf_formudata() handle the DW_FORM_dataXX
+ * forms (via the default option in the switch). If the value is in an
+ * unexpected form (i.e. not DW_FORM_udata or DW_FORM_dataXX),
+ * dwarf_formudata() will return failure (i.e. not DW_DLV_OK) and set
+ * derr with the specific error value.
+ */
+ switch (form) {
+ case DW_FORM_sdata:
+ if (dwarf_formsdata(attr, &sval, &derr) == DW_DLV_OK) {
+ ar->ctr_nelems = sval + 1;
+ goto done;
+ }
+ break;
+ case DW_FORM_udata:
+ default:
+ if (dwarf_formudata(attr, &uval, &derr) == DW_DLV_OK) {
+ ar->ctr_nelems = uval + 1;
+ goto done;
+ }
+ break;
+ }
+
+ if (dwarf_get_FORM_name(form, &formstr) != DW_DLV_OK)
+ formstr = "unknown DWARF form";
+
+ (void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+ "failed to get %s (%hu) value for DW_AT_upper_bound: %s\n",
+ formstr, form, dwarf_errmsg(derr));
+ ret = ECTF_CONVBKERR;
+
+done:
+ dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+ return (ret);
+}
+
+static int
ctf_dwarf_create_array_range(ctf_cu_t *cup, Dwarf_Die range, ctf_id_t *idp,
ctf_id_t base, int isroot)
{
int ret;
Dwarf_Die sib;
- Dwarf_Unsigned val;
- Dwarf_Signed sval;
ctf_arinfo_t ar;
ctf_dprintf("creating array range\n");
@@ -1482,30 +1571,8 @@ ctf_dwarf_create_array_range(ctf_cu_t *cup, Dwarf_Die range, ctf_id_t *idp,
if ((ar.ctr_index = ctf_dwarf_long(cup)) == CTF_ERR)
return (ctf_errno(cup->cu_ctfp));
- /*
- * Array bounds can be signed or unsigned, but there are several kinds
- * of signless forms (data1, data2, etc) that take their sign from the
- * routine that is trying to interpret them. That is, data1 can be
- * either signed or unsigned, depending on whether you use the signed or
- * unsigned accessor function. GCC will use the signless forms to store
- * unsigned values which have their high bit set, so we need to try to
- * read them first as unsigned to get positive values. We could also
- * try signed first, falling back to unsigned if we got a negative
- * value.
- */
- if ((ret = ctf_dwarf_unsigned(cup, range, DW_AT_upper_bound,
- &val)) == 0) {
- ar.ctr_nelems = val + 1;
- } else if (ret != ENOENT) {
- return (ret);
- } else if ((ret = ctf_dwarf_signed(cup, range, DW_AT_upper_bound,
- &sval)) == 0) {
- ar.ctr_nelems = sval + 1;
- } else if (ret != ENOENT) {
+ if ((ret = ctf_dwarf_array_upper_bound(cup, range, &ar)) != 0)
return (ret);
- } else {
- ar.ctr_nelems = 0;
- }
if ((*idp = ctf_add_array(cup->cu_ctfp, isroot, &ar)) == CTF_ERR)
return (ctf_errno(cup->cu_ctfp));
diff --git a/usr/src/test/util-tests/tests/ctf/check-array.c b/usr/src/test/util-tests/tests/ctf/check-array.c
index be7a192480..46be6332cb 100644
--- a/usr/src/test/util-tests/tests/ctf/check-array.c
+++ b/usr/src/test/util-tests/tests/ctf/check-array.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2019, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
/*
@@ -36,6 +36,7 @@ static check_symbol_t check_syms[] = {
{ "g", "int [4][5][6][7][8]" },
{ "h", "int [4][5][6][7][8][9]" },
{ "i", "int [4][5][6][7][8][9][10]" },
+ { "empty", "int [0]" },
{ NULL }
};
@@ -72,11 +73,18 @@ static check_descent_t check_array_i[] = {
{ NULL },
};
+static check_descent_t check_array_empty[] = {
+ { "int [0]", CTF_K_ARRAY, "int", 0 },
+ { "int", CTF_K_INTEGER },
+ { NULL }
+};
+
static check_descent_test_t descents[] = {
{ "a", check_array_a },
{ "b", check_array_b },
{ "c", check_array_c },
{ "i", check_array_i },
+ { "empty", check_array_empty },
{ NULL }
};
diff --git a/usr/src/test/util-tests/tests/ctf/test-array.c b/usr/src/test/util-tests/tests/ctf/test-array.c
index 9c15771caf..14fc21d5dd 100644
--- a/usr/src/test/util-tests/tests/ctf/test-array.c
+++ b/usr/src/test/util-tests/tests/ctf/test-array.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
*/
/*
@@ -27,3 +27,5 @@ int f[4][5][6][7];
int g[4][5][6][7][8];
int h[4][5][6][7][8][9];
int i[4][5][6][7][8][9][10];
+
+int empty[] = { };