summaryrefslogtreecommitdiff
path: root/plugins/dwarf/info.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/dwarf/info.c')
-rw-r--r--plugins/dwarf/info.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/plugins/dwarf/info.c b/plugins/dwarf/info.c
new file mode 100644
index 0000000..432280e
--- /dev/null
+++ b/plugins/dwarf/info.c
@@ -0,0 +1,853 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * info.c - lecture des informations principales du format DWARF
+ *
+ * Copyright (C) 2008-2017 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "info.h"
+
+
+#include "die.h"
+#include "dwarf-int.h"
+#include "../debuggable-int.h"
+
+
+
+
+
+static bool extract_dies_from_debug_information(GDwarfFormat *format, vmpa2t *pos, SourceEndian endian, const dw_compil_unit_header *header, dw_die *parent, dw_die **die);
+
+
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage DWARF à compléter. *
+* *
+* Description : Charge les informations depuis une section ".debug_info". *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool load_dwarf_debug_information(GDwarfFormat *format)
+{
+ bool result; /* Bilan à renvoyer */
+
+
+
+ mrange_t range; /* Couverture d'une section */
+ vmpa2t end;
+ vmpa2t iter; /* Tête de lecture mouvante */
+
+ dw_compil_unit_header header;
+ dw_die *die;
+
+
+
+ result = g_exe_format_get_section_range_by_name(G_DBG_FORMAT(format)->executable, ".debug_info", &range);
+ if (!result) goto lddi_exit;
+
+ copy_vmpa(&iter, get_mrange_addr(&range));
+
+
+ printf("[%d] Passage :: 0x%08llx 0x%08llx\n",
+ result,
+ (unsigned long long)range.addr.physical,
+ (unsigned long long)range.addr.virtual);
+
+ compute_mrange_end_addr(&range, &end);
+
+ while (result)
+ {
+ /* Si il n'y a plus rien à lire dans la section... */
+ if (cmp_vmpa(&iter, &end) >= 0) break;
+
+
+ printf("========================================================================\n");
+ printf("========================================================================\n");
+ printf("========================================================================\n");
+ printf("========================================================================\n");
+ printf("\n");
+ printf("HEADER START :: 0x%x\n", (unsigned int)iter.physical);
+
+
+ result = read_dwarf_compil_unit_header(G_BIN_FORMAT(format)->content, &iter,
+ SRE_LITTLE /* FIXME */, &header);
+ if (!result) break;
+
+ printf("[%d] header :: addr size=%hhu\n", result, header.address_size);
+
+
+ result = extract_dies_from_debug_information(format, &iter,
+ SRE_LITTLE /* FIXME */, &header,
+ NULL, &die);
+
+
+
+ }
+
+
+ format->info_die = die;
+
+
+ lddi_exit:
+
+ return result;
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage DWARF à compléter. *
+* pos = position de début de lecture. [OUT] *
+* endian = boutisme reconnu dans le format. *
+* header = en-tête de description de l'unité à traiter. *
+* parent = entrée parent de rattachement ou NULL si racine. *
+* die = emplacement de stockage de l'entrée. [OUT] *
+* *
+* Description : Procède à la lecture de l'en-tête d'un contenu binaire DWARF.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool extract_dies_from_debug_information(GDwarfFormat *format, vmpa2t *pos, SourceEndian endian, const dw_compil_unit_header *header, dw_die *parent, dw_die **die)
+{
+ bool result; /* Bilan à faire remonter */
+ dw_die *child; /* Entrée subordonnée à charger*/
+
+ printf("==================================\n");
+ printf("=== version : 0x%hx\n", header->version);
+ printf("=== offset abbrev : 0x%llx\n", (unsigned long long)header->debug_abbrev_offset);
+ printf("==================================\n");
+
+
+ phys_t start = 0x1039;
+
+ printf("start :: 0x%x\n", (unsigned int)(pos->physical - start));
+ printf("start :: 0x%x\n", (unsigned int)(0x0 + pos->physical));
+
+ result = build_dwarf_die(format, pos, SRE_LITTLE /* FIXME */, header, die);
+ if (*die == NULL) return result;
+
+ if (parent != NULL)
+ dw_die_append_child(parent, *die);
+
+ if (dw_die_has_children(*die))
+ {
+ printf("<<<< children >>>>\n");
+
+ printf("next :: 0x%x\n", (unsigned int)(pos->physical - start));
+
+ while (result)
+ {
+ result = extract_dies_from_debug_information(format, pos, endian, header, *die, &child);
+
+ if (!result)
+ delete_dwarf_die(*die);
+
+ /* Entrée avec un code nul -> fin */
+ if (child == NULL) break;
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+
+
+
+
+
+#if 0
+#include <malloc.h>
+#include <string.h>
+
+
+#include "abbrev.h"
+#include "dwarf-int.h"
+#include "utils.h"
+
+
+/* Informations utiles d'une unité de compilation */
+typedef struct _compil_unit
+{
+ off_t startpos; /* Position de début d'unité */
+ off_t endpos; /* Position d'unité suivante */
+
+ off_t offset; /* Position dans les abréviat° */
+ uint8_t ptrsize; /* Taille des adresses mémoire */
+
+} compil_unit;
+
+
+
+#define _(str) str
+
+
+/* Procède à la lecture d'une unité de compilation. */
+bool read_dwarf_compilation_unit(dwarf_format *, off_t *, compil_unit *);
+
+/* Récupère toutes les déclarations DWARF utiles trouvées. */
+bool parse_dwarf_compilation_unit(dwarf_format *, off_t *, const compil_unit *);
+
+/* Enregistre toutes les déclarations de fonction trouvées. */
+dw_dbg_function *look_for_dwarf_subprograms(dwarf_format *, const dw_abbrev *, off_t *, const compil_unit *);
+
+/* Obtient la description humaine d'un type. */
+char *resolve_dwarf_function_type(dwarf_format *, const dw_abbrev *, const off_t *, const compil_unit *);
+
+
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage à compléter. *
+* *
+* Description : Charge les informations trouvées dans un DWARF. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool load_dwarf_information(dwarf_format *format)
+{
+ bool result; /* Bilan à renvoyer */
+
+
+
+
+ off_t offset;
+ off_t start;
+ off_t size;
+
+ bool test;
+
+ int i;
+
+
+ compil_unit cu;
+
+
+
+ result = true;
+
+ test = find_exe_section(DBG_FORMAT(format)->e_format, ".debug_info", &start, &size, NULL);
+
+ offset = start;
+
+
+ printf(" -> offset=%d size=%d\n", offset, size);
+
+
+
+ for (i = 0; i < size; i++)
+ {
+ if (i % 25 == 0) printf("\n");
+ printf("0x%02hhx ", DBG_FORMAT(format)->content[offset + i]);
+ }
+
+ printf("\n");
+
+
+
+ while (offset < (start + size) && result)
+ {
+ printf("-------------\n");
+
+ result = read_dwarf_compilation_unit(format, &offset, &cu);
+
+ if (result)
+ parse_dwarf_compilation_unit(format, &offset, &cu);
+
+ }
+
+
+ printf("##############\nRegistered functions:\n");
+
+ for (i = 0; i < format->dbg_fc_count; i++)
+ printf(" > [0x%08llx] %s\n", format->dbg_functions[i]->low_pc, format->dbg_functions[i]->prototype);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage à effacer. *
+* *
+* Description : Décharge les informations trouvées dans un DWARF. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void unload_dwarf_information(dwarf_format *format)
+{
+
+
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage à compléter. *
+* pos = tête de lecture à mettre à jour. [OUT] *
+* cu = unité de compilation lue. [OUT] *
+* *
+* Description : Procède à la lecture d'une unité de compilation. *
+* *
+* Retour : true en cas de succès de la lecture, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool read_dwarf_compilation_unit(dwarf_format *format, off_t *pos, compil_unit *cu)
+{
+ off_t ulength; /* Taille de l'unité */
+ uint16_t version; /* Version du format DWARF */
+
+ cu->startpos = *pos;
+
+ if (!read_unit_length(format, pos, &ulength))
+ return false;
+
+ cu->endpos = *pos + ulength;
+
+ if (!read_uhalf(format, pos, &version))
+ return false;
+
+ if (version > 3) return false;
+
+ if (!read_abbrev_offset(format, pos, &cu->offset))
+ return false;
+
+ if (!read_address_size(format, pos, &cu->ptrsize))
+ return false;
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage à compléter. *
+* pos = tête de lecture à mettre à jour. [OUT] *
+* cu = unité de compilation courante. *
+* *
+* Description : Récupère toutes les déclarations DWARF utiles trouvées. *
+* *
+* Retour : true en cas de succès de la lecture, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool parse_dwarf_compilation_unit(dwarf_format *format, off_t *pos, const compil_unit *cu)
+{
+ bool result; /* Bilan à retourner */
+ const dw_abbrev *abbrev; /* Abréviation rencontrée */
+ dw_dbg_function *function; /* Nouvelle fonction lue */
+
+
+
+ result = true;
+
+ while (*pos < cu->endpos && result)
+ {
+
+
+ printf(" =+> Cur :: 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n",
+ DBG_FORMAT(format)->content[*pos],
+ DBG_FORMAT(format)->content[*pos + 1],
+ DBG_FORMAT(format)->content[*pos + 2],
+ DBG_FORMAT(format)->content[*pos + 3],
+ DBG_FORMAT(format)->content[*pos + 4]);
+
+
+
+ abbrev = find_dwarf_abbreviations(format, &cu->offset, pos);
+
+ if (abbrev == NULL)
+ break;
+
+
+
+ printf(" --> %p\n", abbrev);
+
+ printf(" == 0x%02x (matched ? %d)\n", abbrev->tag, abbrev->tag == DWT_SUBPROGRAM);
+
+
+
+ switch (abbrev->tag)
+ {
+ case DWT_SUBPROGRAM:
+ function = look_for_dwarf_subprograms(format, abbrev, pos, cu);
+
+ if (function != NULL)
+ {
+ format->dbg_functions = (dw_dbg_function **)realloc(format->dbg_functions, ++format->dbg_fc_count * sizeof(dw_dbg_function *));
+ format->dbg_functions[format->dbg_fc_count - 1] = function;
+ }
+ else result = false;
+
+ break;
+
+ default:
+ break;
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ if (!skip_dwarf_abbrev(format, pos, abbrev))
+ printf("error skipping :(\n");
+
+
+ }
+
+
+ printf(" =+> Next :: 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx 0x%02hhx\n",
+ DBG_FORMAT(format)->content[*pos],
+ DBG_FORMAT(format)->content[*pos + 1],
+ DBG_FORMAT(format)->content[*pos + 2],
+ DBG_FORMAT(format)->content[*pos + 3],
+ DBG_FORMAT(format)->content[*pos + 4]);
+
+
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage à compléter. *
+* abbrev = abréviation trouvée à traiter. *
+* pos = tête de lecture à mettre à jour. [OUT] *
+* cu = unité de compilation courante. *
+* *
+* Description : Enregistre toutes les déclarations de fonction trouvées. *
+* *
+* Retour : Fonction chargée en mémoire ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+dw_dbg_function *look_for_dwarf_subprograms(dwarf_format *format, const dw_abbrev *abbrev, off_t *pos, const compil_unit *cu)
+{
+ dw_dbg_function *result; /* Structure à retourner */
+ uint32_t type_pos; /* Décalage p/r au pt courant */
+ off_t subpos; /* Sous-position de lecture #1 */
+ const dw_abbrev *subabbrev; /* Abréviation fille à lire */
+ char *retstr; /* Elément du prototype */
+ char *prototype; /* Stockage temporaire */
+ size_t proto_len; /* Taille du prototype */
+ bool is_pointer; /* Mémorise le type 'pointeur' */
+ uint64_t index; /* Index de la fonction */
+ bool first_arg; /* Marque le 1er argument */
+ off_t subpos2; /* Sous-position de lecture #2 */
+ const dw_abbrev *subabbrev2; /* Abréviation fille à lire #2 */
+
+ result = (dw_dbg_function *)calloc(1, sizeof(dw_dbg_function));
+
+ /* Récupération des informations de base */
+
+ if (!read_dwarf_abbrev_attribute(format, pos, false, abbrev, DWA_NAME, &result->name))
+ goto lfds_error;
+
+ if (!read_dwarf_abbrev_attribute(format, pos, false, abbrev, DWA_LOW_PC, &result->low_pc))
+ goto lfds_error;
+
+ if (!read_dwarf_abbrev_attribute(format, pos, false, abbrev, DWA_HIGH_PC, &result->high_pc))
+ goto lfds_error;
+
+ /* Type de la fonction */
+
+ if (test_dwarf_abbrev_attribute(abbrev, DWA_TYPE))
+ {
+ if (!read_dwarf_abbrev_attribute(format, pos, false, abbrev, DWA_TYPE, &type_pos))
+ goto lfds_error;
+
+ subpos = cu->startpos + type_pos;
+
+ subabbrev = find_dwarf_abbreviations(format, &cu->offset, &subpos);
+
+ retstr = resolve_dwarf_function_type(format, subabbrev, &subpos, cu);
+
+ }
+ else retstr = strdup("void");
+
+ if (retstr == NULL)
+ {
+ proto_len = 3;
+ prototype = (char *)calloc(proto_len + 1, sizeof(char));
+ strcat(prototype, "???");
+
+ is_pointer = false;
+
+ }
+ else
+ {
+ proto_len = strlen(retstr);
+ prototype = (char *)calloc(proto_len + 1, sizeof(char));
+ strcat(prototype, retstr);
+
+ is_pointer = (retstr[strlen(retstr) - 1] == '*');
+
+ free(retstr);
+
+ }
+
+ /* On saute l'abréviation de la déclaration de fonction... */
+
+ subpos = *pos;
+
+ if (!read_uleb128(format, &subpos, &index, true))
+ goto lfds_error;
+
+ if (!skip_dwarf_abbrev(format, &subpos, abbrev))
+ goto lfds_error;
+
+ /* Lecture des différents arguments */
+
+ proto_len += (!is_pointer ? 1 : 0) + strlen(result->name) + 1;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ if (!is_pointer) strcat(prototype, " ");
+ strcat(prototype, result->name);
+ strcat(prototype, "(");
+
+ first_arg = true;
+
+ while (1)
+ {
+ subabbrev = find_dwarf_abbreviations(format, &cu->offset, &subpos);
+ if (subabbrev == NULL) goto exit_loop;
+
+ switch (subabbrev->tag)
+ {
+ case DWT_UNSPECIFIED_PARAMETERS:
+
+ /* Virgule de séparation */
+
+ if (first_arg) first_arg = false;
+ else
+ {
+ proto_len += 2;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ strcat(prototype, ", ");
+ }
+
+ /* Marque de l'absence de type */
+
+ proto_len += 3;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ strcat(prototype, "...");
+
+ break;
+
+ case DWT_FORMAL_PARAMETER:
+
+ if (!read_dwarf_abbrev_attribute(format, &subpos, false, subabbrev, DWA_TYPE, &type_pos))
+ goto lfds_error;
+
+ subpos2 = cu->startpos + type_pos;
+
+ subabbrev2 = find_dwarf_abbreviations(format, &cu->offset, &subpos2);
+
+ retstr = resolve_dwarf_function_type(format, subabbrev2, &subpos2, cu);
+
+ /* Virgule de séparation */
+
+ if (first_arg) first_arg = false;
+ else
+ {
+ proto_len += 2;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ strcat(prototype, ", ");
+ }
+
+ /* Type de l'argument */
+
+ if (retstr == NULL)
+ {
+ proto_len += 3;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ strcat(prototype, "???");
+
+ is_pointer = false;
+
+ }
+ else
+ {
+ proto_len += strlen(retstr);
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ strcat(prototype, retstr);
+
+ is_pointer = (retstr[strlen(retstr) - 1] == '*');
+
+ free(retstr);
+
+ }
+
+ /* Nom de l'argument */
+
+ if (test_dwarf_abbrev_attribute(abbrev, DWA_NAME))
+ {
+ if (!read_dwarf_abbrev_attribute(format, &subpos, false, subabbrev, DWA_NAME, &retstr))
+ goto lfds_error;
+ }
+ else retstr = strdup(_("[no name]"));
+
+ if (retstr != NULL)
+ {
+ proto_len += strlen(retstr) + (!is_pointer ? 1 : 0) + 1;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ if (!is_pointer) strcat(prototype, " ");
+ strcat(prototype, retstr);
+
+ free(retstr);
+
+ }
+
+ break;
+
+ default:
+ goto exit_loop;
+ break;
+
+ }
+
+ if (!skip_dwarf_abbrev(format, &subpos, subabbrev))
+ goto lfds_error;
+
+ }
+
+ exit_loop:
+
+ proto_len += 1;
+ prototype = (char *)realloc(prototype, (proto_len + 1) * sizeof(char));
+ strcat(prototype, ")");
+
+ result->prototype = prototype;
+
+ return result;
+
+ lfds_error:
+
+ if (result->name != NULL) free(result->name);
+ if (result->prototype != NULL) free(result->prototype);
+
+ free(result);
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = informations de débogage à compléter. *
+* abbrev = abréviation associée au type. *
+* pos = tête de lecture à avoir sous le coude. *
+* cu = unité de compilation courante. *
+* *
+* Description : Obtient la description humaine d'un type. *
+* *
+* Retour : Chaîne de caractères en cas de succès, NULL sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *resolve_dwarf_function_type(dwarf_format *format, const dw_abbrev *abbrev, const off_t *pos, const compil_unit *cu)
+{
+ char *result; /* Description à retourner */
+ off_t oldpos; /* Conservation de l'indice */
+ uint32_t type_pos; /* Sous-type détecté */
+ uint64_t index; /* Indice de l'abréviation... */
+ const dw_abbrev *subabbrev; /* ... et abréviation associée */
+ size_t len; /* Longeur d'un résultat */
+
+ result = NULL;
+ oldpos = *pos;
+
+ switch (abbrev->tag)
+ {
+ /* 0x04 */
+ case DWT_ENUMERATION_TYPE:
+
+ oldpos = *pos;
+ read_dwarf_abbrev_attribute(format, &oldpos, true /* ??? */, abbrev, DWA_NAME, &result);
+
+ if (result != NULL)
+ {
+ len = strlen(result);
+
+ result = (char *)realloc(result, (strlen("enum ") + len + 1) * sizeof(char));
+ memmove(&result[strlen("enum ")], result, len);
+ memcpy(result, "enum ", strlen("enum "));
+
+ }
+
+ break;
+
+ /* 0x0f */
+ case DWT_POINTER_TYPE:
+
+ if (test_dwarf_abbrev_attribute(abbrev, DWA_TYPE))
+ {
+ if (!read_dwarf_abbrev_attribute(format, &oldpos, true, abbrev, DWA_TYPE, &type_pos))
+ return NULL;
+
+ oldpos = cu->startpos + type_pos;
+
+ subabbrev = find_dwarf_abbreviations(format, &cu->offset, &oldpos);
+
+ result = resolve_dwarf_function_type(format, subabbrev, &oldpos, cu);
+
+ }
+ else result = strdup("void");
+
+ if (result != NULL)
+ {
+ len = strlen(result);
+
+ if (result[len - 1] == '*')
+ {
+ result = (char *)realloc(result, (len + 2) * sizeof(char));
+ result[len] = '*';
+ }
+ else
+ {
+ result = (char *)realloc(result, (len + 3) * sizeof(char));
+ strcat(result, " *");
+ }
+
+ }
+
+ break;
+
+ /* 0x13 */
+ case DWT_STRUCTURE_TYPE:
+
+ oldpos = *pos;
+ read_dwarf_abbrev_attribute(format, &oldpos, true /* ??? */, abbrev, DWA_NAME, &result);
+
+ if (result != NULL)
+ {
+ len = strlen(result);
+
+ result = (char *)realloc(result, (strlen("struct ") + len + 1) * sizeof(char));
+ memmove(&result[strlen("struct ")], result, len);
+ memcpy(result, "struct ", strlen("struct "));
+
+ }
+
+ break;
+
+ /* 0x16 */
+ case DWT_TYPEDEF:
+ oldpos = *pos;
+ read_dwarf_abbrev_attribute(format, &oldpos, true /* ??? */, abbrev, DWA_NAME, &result);
+ break;
+
+ /* 0x24 */
+ case DWT_BASE_TYPE:
+ oldpos = *pos;
+ read_dwarf_abbrev_attribute(format, &oldpos, true /* ??? */, abbrev, DWA_NAME, &result);
+ break;
+
+ /* 0x26 */
+ case DWT_CONST_TYPE:
+
+
+
+ if (read_dwarf_abbrev_attribute(format, &oldpos, true, abbrev, DWA_TYPE, &type_pos))
+ printf(" ## sub type :: 0x%08x\n", type_pos);
+ else printf(" error: no type\n");
+
+ oldpos = cu->startpos + type_pos;
+
+
+
+
+
+
+ subabbrev = find_dwarf_abbreviations(format, &cu->offset, &oldpos);
+ printf("subabbrev == %p\n", subabbrev);
+
+
+
+
+ result = resolve_dwarf_function_type(format, subabbrev, &oldpos, cu);
+
+ if (result != NULL)
+ {
+ len = strlen(result);
+
+ result = (char *)realloc(result, (strlen("const ") + len + 1) * sizeof(char));
+ memmove(&result[strlen("const ")], result, len);
+ memcpy(result, "const ", strlen("const "));
+
+ }
+
+ break;
+
+ default:
+ printf("### NOT HANDLED ### Tag :: 0x%02x\n", abbrev->tag);
+ break;
+
+ }
+
+ return result;
+
+}
+#endif