Date: Fri, 9 Dec 2022 11:19:17 -0800
Subject: [PATCH] EPPIC extension support for crach_8.X+gdb_10.X

The previous version of the interface between eppic and crash/gdb was
messy and not portable. This new version uses a clean interface with
execution through the standard gnu_request and gdb command funnel.

Signed-off-by: Luc Chouinard <lucchouina@gmail.com>
---
 defs.h              |  20 +++
 extensions/eppic.mk |  80 ++++++------
 gdb-10.2.patch      | 303 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 365 insertions(+), 38 deletions(-)

diff --git a/defs.h b/defs.h
index afdcf6c..681bf2e 100644
--- a/defs.h
+++ b/defs.h
@@ -4768,6 +4768,23 @@ static inline unsigned int __const_hweight8(unsigned long w)
 
 #endif /* !GDB_COMMON */
 
+typedef enum drill_ops_s {
+	EOP_MEMBER_SIZES,
+	EOP_MEMBER_NAME,
+	EOP_POINTER,
+	EOP_TYPEDEF,
+	EOP_INT,
+	EOP_VALUE,
+	EOP_ARRAY,
+	EOP_UNION,
+	EOP_ENUM,
+	EOP_ENUMVAL,
+	EOP_STRUCT,
+	EOP_FUNCTION,
+	EOP_DONE,
+	EOP_OOPS
+} drill_ops_t;
+
 /*
  *  Common request structure for BFD or GDB data or commands.
  */
@@ -4818,6 +4835,9 @@ struct gnu_request {
 	char *member_target_type_name;
 	char *member_target_type_tag_name;
 	char *type_tag_name;
+	/* callback function for 3rd party symbol and type (EPPIC for now) */
+	void *priv;
+	int (*tcb)(drill_ops_t, struct gnu_request *, const void *, const void *, const void *,  const void *);
 };
 
 /*
diff --git a/extensions/eppic.mk b/extensions/eppic.mk
index b9c046b..8c0d70e 100644
--- a/extensions/eppic.mk
+++ b/extensions/eppic.mk
@@ -11,64 +11,68 @@
 
 TARGET_FLAGS = -D$(TARGET)
 ifeq ($(TARGET), PPC64)
-        TARGET_FLAGS += -m64
+	TARGET_FLAGS += -m64
 endif
 ifeq ($(TARGET), ARM)
-        TARGET_FLAGS += -m32
+	TARGET_FLAGS += -m32
 endif
 ifeq ($(TARGET), MIPS)
-        TARGET_FLAGS += -m32
+	TARGET_FLAGS += -m32
 endif
 ifeq ($(TARGET), X86)
-        TARGET_FLAGS += -m32
+	TARGET_FLAGS += -m32
 endif
 
 APPFILE=eppic/applications/crash/eppic.c
 GIT := $(shell which git 2> /dev/null)
+# crash 8 with gdb 10 uses new third party callback (tcb) API
+EPPIC_BRANCH=v5.0
 
 all:
-	@if [ -f /usr/bin/flex ] && [ -f /usr/bin/bison ]; then \
-	  if [ -f ../$(GDB)/crash.target ]; \
-        then \
-        if  [ ! -f $(APPFILE) ]; \
-        then \
-          if [ -f "$(GIT)" ]; \
-          then \
-             if [ -n "$(EPPIC_GIT_URL)" ]; then \
-               git clone $(EPPIC_GIT_OPTIONS) $(EPPIC_GIT_URL) eppic; \
-             else \
-	          if ping -c 1 -W 5 github.com >/dev/null ; then \
-		    git clone $(EPPIC_GIT_OPTIONS) https://github.com/lucchouina/eppic.git eppic; \
-	          fi; \
-             fi; \
-          else \
-	     if [ ! -f "$(GIT)" ]; then \
-	         echo "eppic.so: git command is needed for pulling eppic extension code"; \
-	     fi; \
-          fi; \
-        fi; \
-        if  [ -f $(APPFILE) ]; \
-        then \
-	        make -f eppic.mk eppic.so; \
-        else \
-            echo "eppic.so: failed to pull eppic code from git repo"; \
-        fi; \
-      else \
-	    echo "eppic.so: build failed: requires the crash $(GDB) module"; \
-      fi ;\
+	@if [ -f /usr/bin/flex ] && [ -f /usr/bin/bison ]; \
+	then \
+		if [ -f ../$(GDB)/crash.target ]; \
+		then \
+			if  [ ! -f $(APPFILE) ]; \
+			then \
+				if [ -f "$(GIT)" ]; \
+				then \
+					if [ -n "$(EPPIC_GIT_URL)" ]; \
+					then \
+						git clone $(EPPIC_GIT_OPTIONS) $(EPPIC_GIT_URL) eppic; \
+					else \
+						if ping -c 1 -W 5 github.com >/dev/null ; then \
+							git clone -b $(EPPIC_BRANCH) $(EPPIC_GIT_OPTIONS) https://github.com/lucchouina/eppic.git eppic; \
+						fi; \
+					fi; \
+				else \
+					if [ ! -f "$(GIT)" ]; then \
+						echo "eppic.so: git command is needed for pulling eppic extension code"; \
+					fi; \
+				fi; \
+			fi; \
+			if  [ -f $(APPFILE) ]; \
+			then \
+				make -f eppic.mk eppic.so; \
+			else \
+				echo "eppic.so: failed to pull eppic code from git repo"; \
+			fi; \
+		else \
+			echo "eppic.so: build failed: requires the crash $(GDB) module"; \
+		fi ;\
 	else \
-	  echo "eppic.so: build failed: requires /usr/bin/flex and /usr/bin/bison"; \
-    fi
+		echo "eppic.so: build failed: requires /usr/bin/flex and /usr/bin/bison"; \
+	fi
 
 lib-eppic: 
 	cd eppic/libeppic && make
-            
+
 eppic.so: ../defs.h $(APPFILE) lib-eppic
-	gcc -g -Ieppic/libeppic -I../$(GDB)/gdb -I../$(GDB)/bfd -I../$(GDB)/include -I../$(GDB)/gdb/config -I../$(GDB)/gdb/common -I../$(GDB) -nostartfiles -shared -rdynamic -o eppic.so $(APPFILE) -fPIC $(TARGET_FLAGS) $(GDB_FLAGS) -Leppic/libeppic -leppic 
+	gcc -g -O0 -Ieppic/libeppic -I.. -nostartfiles -shared -rdynamic -o eppic.so $(APPFILE) -fPIC $(TARGET_FLAGS) $(GDB_FLAGS) -Leppic/libeppic -leppic 
 
 clean:
 	if  [ -d eppic/libeppic ]; \
 	then \
-	  cd eppic/libeppic && make -i clean; \
+		cd eppic/libeppic && make -i clean; \
 	fi
 	rm -f eppic.so
diff --git a/gdb-10.2.patch b/gdb-10.2.patch
index 91edfb3..1fb7079 100644
--- a/gdb-10.2.patch
+++ b/gdb-10.2.patch
@@ -1737,3 +1737,306 @@ exit 0
          struct field *nextfield;
          short nfields;
          struct type *typedef_type, *target_type;
+
+--- gdb-10.2/gdb/symtab.c.orig
++++ gdb-10.2/gdb/symtab.c
+@@ -6913,7 +6913,7 @@
+ #include "../../defs.h"
+ 
+ static void get_member_data(struct gnu_request *, struct type *, long, int);
+-static void dump_enum(struct type *, struct gnu_request *);
++static void walk_enum(struct type *, struct gnu_request *);
+ static void eval_enum(struct type *, struct gnu_request *);
+ static void gdb_get_line_number(struct gnu_request *);
+ static void gdb_get_datatype(struct gnu_request *);
+@@ -7122,6 +7122,79 @@
+ 
+ 
+ /*
++ * Follow the type linkage for full member and value type resolution, with callback 
++ */
++static void drillDownType(struct gnu_request *req, struct type *type)
++{
++        while (type)
++        {
++                /* check out for stub types and pull in the definition instead */
++                if (TYPE_STUB(type) && TYPE_TAG_NAME(type)) {
++                        struct symbol *sym;
++                        sym = lookup_symbol(TYPE_TAG_NAME(type), 0, STRUCT_DOMAIN, 0).symbol;
++                        if (sym)
++                                type = sym->type;
++                }
++                switch (TYPE_CODE(type)) {
++                        drill_ops_t op;
++                        long l1, l2;
++                        int typecode;
++
++                case TYPE_CODE_PTR:
++                        req->tcb(EOP_POINTER, req, 0, 0, 0, 0);
++                        break;
++
++                case TYPE_CODE_TYPEDEF:
++                        req->is_typedef = 1;
++                        req->typecode = TYPE_CODE(type);
++                        if(!req->tcb(EOP_TYPEDEF, req, TYPE_NAME(type), 0, 0, 0)) 
++                                return;
++                        break;
++
++                case TYPE_CODE_FUNC:
++                        req->tcb(EOP_FUNCTION, req, 0, 0, 0, 0);
++                        break;
++
++                case TYPE_CODE_ARRAY:
++                        l1 = TYPE_LENGTH (type);
++                        l2 = TYPE_LENGTH (check_typedef(TYPE_TARGET_TYPE (type)));
++                        req->tcb(EOP_ARRAY, req, &l1, &l2, 0, 0);
++                        break;
++
++                case TYPE_CODE_VOID:
++                case TYPE_CODE_INT:
++                case TYPE_CODE_BOOL:
++                        l1 = TYPE_LENGTH(type);
++                        req->tcb(EOP_INT, req, &l1, 0, 0, 0);
++                        break;
++
++                case TYPE_CODE_UNION:
++                        op = EOP_UNION;
++                        goto label;
++
++                case TYPE_CODE_ENUM:
++                        op = EOP_ENUM;
++                        goto label;
++
++                case TYPE_CODE_STRUCT:
++                        op = EOP_STRUCT;
++                        goto label;
++
++                default:
++                        typecode = TYPE_CODE(type);
++                        req->tcb(EOP_OOPS, req, &typecode, "Unknown typecode", 0, 0);
++                        return; // not reached
++
++                label:
++                        l1 = TYPE_LENGTH(type);
++                        req->tcb(op, req, &l1, type, TYPE_TAG_NAME(type), 0);
++                }
++                type = TYPE_TARGET_TYPE(type);
++        }
++        req->tcb(EOP_DONE, req, 0, 0, 0, 0);
++}
++
++/*
+  *  General purpose routine for determining datatypes.
+  */
+ 
+@@ -7149,10 +7222,8 @@
+                 if (req->member)
+                         get_member_data(req, sym->type, 0, 1);
+ 
+-                if (TYPE_CODE(sym->type) == TYPE_CODE_ENUM) {
+-                        if (req->flags & GNU_PRINT_ENUMERATORS)
+-                                dump_enum(sym->type, req);
+-                }
++                if (TYPE_CODE(sym->type) == TYPE_CODE_ENUM)
++                        walk_enum(sym->type, req);
+ 
+                 return;
+         }
+@@ -7172,17 +7243,25 @@
+                 if (gdb_CRASHDEBUG(2))
+                         console("expr->elts[0].opcode: OP_VAR_VALUE\n");
+                 type = expr.get()->elts[2].symbol->type;
+-                if (req->flags & GNU_VAR_LENGTH_TYPECODE) {
++                if (req->tcb) {
++                        long value = SYMBOL_VALUE(expr->elts[2].symbol);
++                        /* callback with symbol value */
+                         req->typecode = TYPE_CODE(type);
+-                        req->length = TYPE_LENGTH(type);
+-                }
+-                if (TYPE_CODE(type) == TYPE_CODE_ENUM) {
+-                        req->typecode = TYPE_CODE(type);
+-                        req->value = SYMBOL_VALUE(expr.get()->elts[2].symbol);
+-                        req->tagname = (char *)TYPE_TAG_NAME(type);
+-                        if (!req->tagname) {
+-                                val = evaluate_type(expr.get());
+-                                eval_enum(value_type(val), req);
++                        req->tcb(EOP_VALUE, req, &value, 0, 0, 0);
++                        drillDownType(req, type);
++                } else {
++                        if (req->flags & GNU_VAR_LENGTH_TYPECODE) {
++                                req->typecode = TYPE_CODE(type);
++                                req->length = TYPE_LENGTH(type);
++                        }
++                        if (TYPE_CODE(type) == TYPE_CODE_ENUM) {
++                                req->typecode = TYPE_CODE(type);
++                                req->value = SYMBOL_VALUE(expr->elts[2].symbol);
++                                req->tagname = (char *)TYPE_TAG_NAME(type);
++                                if (!req->tagname) {
++                                        val = evaluate_type(expr.get());
++                                        eval_enum(value_type(val), req);
++                                }
+                         }
+                 }
+                 break;
+@@ -7192,26 +7271,21 @@
+                         console("expr->elts[0].opcode: OP_TYPE\n");
+                     type = expr.get()->elts[1].type;
+ 
+-                req->typecode = TYPE_CODE(type);
+-                req->length = TYPE_LENGTH(type);
+-
+-                if (TYPE_CODE(type) == TYPE_CODE_TYPEDEF) {
+-                        req->is_typedef = TYPE_CODE_TYPEDEF;
+-                        if ((typedef_type = check_typedef(type))) {
+-                                req->typecode = TYPE_CODE(typedef_type);
+-                                req->length = TYPE_LENGTH(typedef_type);
+-                                type = typedef_type;
+-                        }
+-                }
+-
+-                if (TYPE_CODE(type) == TYPE_CODE_ENUM) {
+-                        if (req->is_typedef)
+-                        if (req->flags & GNU_PRINT_ENUMERATORS) {
+-                                if (req->is_typedef)
+-                                        fprintf_filtered(gdb_stdout,
+-                                                "typedef ");
+-                                dump_enum(type, req);
++                if (req->tcb) {
++                        drillDownType(req, type);
++                } else {
++                        req->typecode = TYPE_CODE(type);
++                        req->length = TYPE_LENGTH(type);
++                        if (TYPE_CODE(type) == TYPE_CODE_TYPEDEF) {
++                                req->is_typedef = TYPE_CODE_TYPEDEF;
++                                if ((typedef_type = check_typedef(type))) {
++                                        req->typecode = TYPE_CODE(typedef_type);
++                                        req->length = TYPE_LENGTH(typedef_type);
++                                        type = typedef_type;
++                                }
+                         }
++                        if (TYPE_CODE(type) == TYPE_CODE_ENUM)
++                                walk_enum(type, req);
+                 }
+ 
+                 if (req->member)
+@@ -7233,36 +7307,38 @@
+  *  identifier, each on its own line.
+  */
+ static void
+-dump_enum(struct type *type, struct gnu_request *req)
++walk_enum(struct type *type, struct gnu_request *req)
+ {
+         int i;
+-        int len;
++        int len, print = (req->flags & GNU_PRINT_ENUMERATORS);
+         long long lastval;
+ 
+-        len = TYPE_NFIELDS (type);
+-        lastval = 0;
+-        if (TYPE_TAG_NAME(type))
+-                fprintf_filtered(gdb_stdout,
+-                        "enum %s {\n", TYPE_TAG_NAME (type));
+-        else
+-                fprintf_filtered(gdb_stdout, "enum {\n");
++        if (print) {
++                if (req->is_typedef)
++                        fprintf_filtered(gdb_stdout, "typedef ");
++                if (TYPE_TAG_NAME(type))
++                        fprintf_filtered(gdb_stdout, "enum %s {\n", TYPE_TAG_NAME (type));
++                else
++                        fprintf_filtered(gdb_stdout, "enum {\n");
++        }
+ 
++        len = TYPE_NFIELDS (type);
+         for (i = 0; i < len; i++) {
+-                fprintf_filtered(gdb_stdout, "  %s",
+-                        TYPE_FIELD_NAME (type, i));
+-                if (lastval != TYPE_FIELD_ENUMVAL (type, i)) {
+-                        fprintf_filtered (gdb_stdout, " = %s",
+-                                plongest(TYPE_FIELD_ENUMVAL (type, i)));
+-                        lastval = TYPE_FIELD_ENUMVAL (type, i);
+-                } else
++                if (print)
++                        fprintf_filtered(gdb_stdout, "  %s", TYPE_FIELD_NAME (type, i));
++                lastval = TYPE_FIELD_ENUMVAL (type, i);
++                if (print) {
+                         fprintf_filtered(gdb_stdout, " = %s", plongest(lastval));
+-                fprintf_filtered(gdb_stdout, "\n");
+-                lastval++;
++                        fprintf_filtered(gdb_stdout, "\n");
++                } else if (req->tcb)
++                        req->tcb(EOP_ENUMVAL, req, TYPE_FIELD_NAME (type, i), &lastval, 0, 0);
++        }
++        if (print) {
++                if (TYPE_TAG_NAME(type))
++                        fprintf_filtered(gdb_stdout, "};\n");
++                else
++                        fprintf_filtered(gdb_stdout, "} %s;\n", req->name);
+         }
+-        if (TYPE_TAG_NAME(type))
+-                fprintf_filtered(gdb_stdout, "};\n");
+-        else
+-                fprintf_filtered(gdb_stdout, "} %s;\n", req->name);
+ }
+ 
+ /*
+@@ -7320,26 +7396,43 @@
+         }
+ 
+         for (i = 0; i < nfields; i++) {
+-                if (STREQ(req->member, nextfield->name)) {
+-                        req->member_offset = offset + nextfield->loc.bitpos;
+-                        req->member_length = TYPE_LENGTH(nextfield->type());
+-                        req->member_typecode = TYPE_CODE(nextfield->type());
+-                        req->member_main_type_name = (char *)TYPE_NAME(nextfield->type());
+-                        req->member_main_type_tag_name = (char *)TYPE_TAG_NAME(nextfield->type());
+-                        target_type = TYPE_TARGET_TYPE(nextfield->type());
+-                        if (target_type) {
+-                                req->member_target_type_name = (char *)TYPE_NAME(target_type);
+-                                req->member_target_type_tag_name = (char *)TYPE_TAG_NAME(target_type);
+-                        }
+-                        if ((req->member_typecode == TYPE_CODE_TYPEDEF) &&
+-                            (typedef_type = check_typedef(nextfield->type())))
+-                                req->member_length = TYPE_LENGTH(typedef_type);
+-                        return;
+-                } else if (*nextfield->name == 0) { /* Anonymous struct/union */
++                if (*nextfield->name == 0) { /* Anonymous struct/union */
+                         get_member_data(req, nextfield->type(),
+                             offset + nextfield->loc.bitpos, 0);
+                         if (req->member_offset != -1)
+                                 return;
++                } else {
++                        /* callback may be just looking for a specific member name */
++                        if (req->tcb) {
++                                if (req->tcb(EOP_MEMBER_NAME, req, nextfield->name, 0, 0, 0)) {
++                                        long bitpos = FIELD_BITPOS(*nextfield);
++                                        long bitsize = FIELD_BITSIZE(*nextfield);
++                                        long len = TYPE_LENGTH(nextfield->type());
++                                        long byteOffset;
++                                        offset += nextfield->loc.bitpos;
++                                        byteOffset = offset/8;
++                                        console("EOP_MEMBER_SIZES\n");
++                                        req->tcb(EOP_MEMBER_SIZES, req, &byteOffset, &len, &bitpos, &bitsize);
++                                        /* callback with full type info */
++                                        drillDownType(req, nextfield->type());
++                                }
++                        } else if (STREQ(req->member, nextfield->name)) {
++                                req->member_offset = offset + nextfield->loc.bitpos;
++                                req->member_length = TYPE_LENGTH(nextfield->type());
++                                req->member_typecode = TYPE_CODE(nextfield->type());
++                                req->member_main_type_name = (char *)TYPE_NAME(nextfield->type());
++                                req->member_main_type_tag_name = (char *)TYPE_TAG_NAME(nextfield->type());
++                                target_type = TYPE_TARGET_TYPE(nextfield->type());
++                                if (target_type) {
++                                        req->member_target_type_name = (char *)TYPE_NAME(target_type);
++                                        req->member_target_type_tag_name = (char *)TYPE_TAG_NAME(target_type);
++                                }
++                                if ((req->member_typecode == TYPE_CODE_TYPEDEF) &&
++                                    (typedef_type = check_typedef(nextfield->type()))) {
++                                        req->member_length = TYPE_LENGTH(typedef_type);
++                                }
++                                return;
++                        }
+                 }
+                 nextfield++;
+         }
-- 
2.25.1

