changeset 25259:2d1e73c83a13

12228 libctf could handle gcc dwarf4 Reviewed by: John Levon <john.levon@joyent.com> Approved by: Dan McDonald <danmcd@joyent.com>
author Robert Mustacchi <rm@fingolfin.org>
date Fri, 10 Jan 2020 22:00:08 +0000
parents 4076af232ed9
children 30063115dff3 91944138b45e
files usr/src/lib/libctf/common/ctf_dwarf.c
diffstat 1 files changed, 57 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/libctf/common/ctf_dwarf.c	Thu Jul 09 09:08:47 2020 -0700
+++ b/usr/src/lib/libctf/common/ctf_dwarf.c	Fri Jan 10 22:00:08 2020 +0000
@@ -209,6 +209,7 @@
 #include <errno.h>
 
 #define	DWARF_VERSION_TWO	2
+#define	DWARF_VERSION_FOUR	4
 #define	DWARF_VARARGS_NAME	"..."
 
 /*
@@ -262,6 +263,8 @@
 	Dwarf_Die	cu_cu;		/* libdwarf compilation unit */
 	Dwarf_Off	cu_cuoff;	/* cu's offset */
 	Dwarf_Off	cu_maxoff;	/* maximum offset */
+	Dwarf_Half	cu_vers;	/* Dwarf Version */
+	Dwarf_Half	cu_addrsz;	/* Dwarf Address Size */
 	ctf_file_t	*cu_ctfp;	/* output CTF file */
 	avl_tree_t	cu_map;		/* map die offsets to CTF types */
 	char		*cu_errbuf;	/* error message buffer */
@@ -576,6 +579,13 @@
 	return (ECTF_CONVBKERR);
 }
 
+/*
+ * The encoding of a DW_AT_data_member_location has changed between different
+ * revisions of the specification. It may be a general udata form or it may be
+ * location data information. In DWARF 2, it is only the latter. In later
+ * revisions of the spec, it may be either. To determine the form, we ask the
+ * class, which will be of type CONSTANT.
+ */
 static int
 ctf_dwarf_member_location(ctf_cu_t *cup, Dwarf_Die die, Dwarf_Unsigned *valp)
 {
@@ -584,11 +594,49 @@
 	Dwarf_Attribute attr;
 	Dwarf_Locdesc *loc;
 	Dwarf_Signed locnum;
+	Dwarf_Half form;
+	enum Dwarf_Form_Class class;
 
 	if ((ret = ctf_dwarf_attribute(cup, die, DW_AT_data_member_location,
 	    &attr)) != 0)
 		return (ret);
 
+	if (dwarf_whatform(attr, &form, &derr) != DW_DLV_OK) {
+		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+		    "failed to get dwarf attribute for for member location: %s",
+		    dwarf_errmsg(derr));
+		dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+		return (ECTF_CONVBKERR);
+	}
+
+	class = dwarf_get_form_class(cup->cu_vers, DW_AT_data_member_location,
+	    cup->cu_addrsz, form);
+	if (class == DW_FORM_CLASS_CONSTANT) {
+		Dwarf_Signed sign;
+
+		/*
+		 * We have a constant. We need to try to get both this as signed
+		 * and unsigned data, as unfortunately, DWARF doesn't define the
+		 * sign. Which is a joy. We try unsigned first. If neither
+		 * match, fall through to the normal path.
+		 */
+		if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) {
+			dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+			return (0);
+		}
+
+		if (dwarf_formsdata(attr, &sign, &derr) == DW_DLV_OK) {
+			dwarf_dealloc(cup->cu_dwarf, attr, DW_DLA_ATTR);
+			if (sign < 0) {
+				(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+				    "encountered negative member data "
+				    "location: %d", sign);
+			}
+			*valp = (Dwarf_Unsigned)sign;
+			return (0);
+		}
+	}
+
 	if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) {
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
 		    "failed to obtain location list for member offset: %s",
@@ -2986,7 +3034,11 @@
 			return (ECTF_CONVBKERR);
 		}
 
-		if (vers != DWARF_VERSION_TWO) {
+		switch (vers) {
+		case DWARF_VERSION_TWO:
+		case DWARF_VERSION_FOUR:
+			break;
+		default:
 			(void) snprintf(errbuf, errlen,
 			    "unsupported DWARF version: %d\n", vers);
 			return (ECTF_CONVBKERR);
@@ -3003,11 +3055,11 @@
 {
 	int ret;
 	Dwarf_Unsigned hdrlen, abboff, nexthdr;
-	Dwarf_Half addrsz;
+	Dwarf_Half addrsz, vers;
 	Dwarf_Unsigned offset = 0;
 	Dwarf_Error derr;
 
-	while ((ret = dwarf_next_cu_header(cup->cu_dwarf, &hdrlen, NULL,
+	while ((ret = dwarf_next_cu_header(cup->cu_dwarf, &hdrlen, &vers,
 	    &abboff, &addrsz, &nexthdr, &derr)) != DW_DLV_NO_ENTRY) {
 		char *name;
 		Dwarf_Die cu, child;
@@ -3028,6 +3080,8 @@
 		cup->cu_longtid = CTF_ERR;
 		cup->cu_elf = elf;
 		cup->cu_maxoff = nexthdr - 1;
+		cup->cu_vers = vers;
+		cup->cu_addrsz = addrsz;
 		cup->cu_ctfp = ctf_fdcreate(fd, &ret);
 		if (cup->cu_ctfp == NULL)
 			return (ret);