/*******************************************************************************
 * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution, and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * This module implements low-level functions for reading DWARF debug information.
 *
 * Functions in this module use exceptions to report errors, see exceptions.h
 */
#include "config.h"
#if SERVICE_LineNumbers

#include <assert.h>
#include <string.h>
#include "dwarfio.h"
#include "dwarf.h"
#include "myalloc.h"
#include "exceptions.h"

#define INP_BUF_SIZE            0x1000
#define ABBREV_TABLE_SIZE       1021

struct DIO_Abbreviation {
    U2_T mTag;
    U1_T mChildren;
    U4_T mAttrLen;
    U2_T mAttrs[2];
};

typedef struct DIO_Abbreviation DIO_Abbreviation;

struct DIO_AbbrevSet {
    struct DIO_AbbrevSet * mNext;
    ELF_File * mFile;
    U8_T mOffset;
    DIO_Abbreviation ** mTable;
    U4_T mSize;
};

typedef struct DIO_AbbrevSet DIO_AbbrevSet;

struct DIO_Cache {
    U1_T * mStringTable;
    U4_T mStringTableSize;
    DIO_AbbrevSet ** mAbbrevTable;
};

typedef struct DIO_Cache DIO_Cache;

U4_T dio_gVersion = 0;
U1_T dio_g64bit = 0;
U1_T dio_gAddressSize = 4;
U8_T dio_gUnitPos = 0;
U4_T dio_gUnitSize = 0;
U8_T dio_gEntryPos = 0;

U8_T dio_gFormRef;
U8_T dio_gFormData = 0;
U1_T dio_gFormDataSize = 0;
U4_T dio_gFormBlockSize = 0;
U1_T * dio_gFormBlockBuf = NULL;

static ELF_Section * sSection;
static U1_T sBigEndian;
static DIO_Abbreviation ** sAbbrevTable = NULL;
static U4_T sAbbrevTableSize = 0;
static U1_T * sInpBuf = NULL;
static U4_T sBufPos;
static U4_T sBufLen;
static U8_T sDataPos;

static void dio_CloseELF(ELF_File * File) {
    U4_T n, m;
    DIO_Cache * Cache = (DIO_Cache *)File->dwarf_cache;

    if (Cache == NULL) return;
    for (n = 0; n < ABBREV_TABLE_SIZE; n++) {
        DIO_AbbrevSet * Set = Cache->mAbbrevTable[n];
        while (Set != NULL) {
            DIO_AbbrevSet * Next = Set->mNext;
            for (m = 0; m < Set->mSize; m++) {
                loc_free(Set->mTable[m]);
            }
            loc_free(Set->mTable);
            loc_free(Set);
            Set = Next;
        }
    }
    loc_free(Cache->mAbbrevTable);
    File->dwarf_cache = NULL;
}

static DIO_Cache * dio_GetCache(ELF_File * File) {
    static int Inited = 0;
    DIO_Cache * Cache = (DIO_Cache *)File->dwarf_cache;

    if (!Inited) {
        elf_add_close_listener(dio_CloseELF);
        Inited = 1;
    }
    if (Cache == NULL) {
        Cache = (DIO_Cache *)(File->dwarf_cache = loc_alloc_zero(sizeof(DIO_Cache)));
    }
    return Cache;
}

void dio_EnterSection(ELF_Section * Section, U8_T Offset) {
    sSection = Section;
    sDataPos = Offset;
    sBufPos = 0;
    sBufLen = 0;
    sBigEndian = Section->file->big_endian;

    if (sInpBuf == NULL) {
        sInpBuf = (U1_T *)loc_alloc(INP_BUF_SIZE);
    }
}

void dio_ExitSection() {
    sSection = NULL;
    sDataPos = 0;
    sBufPos = 0;
    sBufLen = 0;
}

U8_T dio_GetPos() {
    return sDataPos;
}

void dio_Skip(U8_T Bytes) {
    sDataPos += Bytes;
    if (sBufPos + Bytes >= sBufLen) {
        sBufPos = 0;
        sBufLen = 0;
    }
    else {
        sBufPos += (U4_T)Bytes;
    }
}

void dio_Read(U1_T * Buf, U4_T Size) {
    assert(sSection->size >= sDataPos + Size);
    if (sBufPos < sBufLen) {
        U4_T FromBuf = Size < sBufLen - sBufPos ? Size : sBufLen - sBufPos;
        memcpy(Buf, sInpBuf + sBufPos, FromBuf);
        sBufPos += FromBuf;
        Buf += FromBuf;
        Size -= FromBuf;
        sDataPos += FromBuf;
    }
    if (Size > 0) {
        U4_T Rd = 0;
        if (elf_read(sSection, sDataPos, Buf, Size, &Rd) < 0) exception(errno);
        if (Rd < Size) exception(ERR_EOF);
        assert(sBufPos >= sBufLen);
        sDataPos += Size;
    }
}

static U1_T dio_ReadU1F(void) {
    U1_T c;
    if (sDataPos >= sSection->size) exception(ERR_EOF);
    if (sBufPos < sBufLen) {
        c = sInpBuf[sBufPos++];
    }
    else if (sBufPos >= sBufLen) {
        U4_T Rd = 0;
        U8_T Size = sSection->size - sDataPos;
        if (Size > INP_BUF_SIZE) Size = INP_BUF_SIZE;
        if (elf_read(sSection, sDataPos, sInpBuf, (U4_T)Size, &Rd) < 0) exception(errno);
        if (Rd == 0) exception(ERR_EOF);
        sBufLen = Rd;
        c = sInpBuf[0];
        sBufPos = 1;
    }
    sDataPos++;
    return c;
}

U1_T dio_ReadU1(void) {
    return sBufPos >= sBufLen ? dio_ReadU1F() : (sDataPos++, sInpBuf[sBufPos++]);
}

#define dio_ReadU1() (sBufPos >= sBufLen ? dio_ReadU1F() : (sDataPos++, sInpBuf[sBufPos++]))

U2_T dio_ReadU2(void) {
    U1_T x0 = dio_ReadU1();
    U1_T x1 = dio_ReadU1();
    return sBigEndian ? (x0 << 8) | x1 : x0 | (x1 << 8);
}
                
U4_T dio_ReadU4(void) {
    U2_T x0 = dio_ReadU2();
    U2_T x1 = dio_ReadU2();
    return sBigEndian ? (x0 << 16) | x1 : x0 | (x1 << 16);
}

U8_T dio_ReadU8(void) {
    U8_T x0 = dio_ReadU4();
    U8_T x1 = dio_ReadU4();
    return sBigEndian ? (x0 << 32) | x1 : x0 | (x1 << 32);
}

U4_T dio_ReadLEB128(void) {
    U4_T res = 0;
    int i = 0;
    for (;; i += 7) {
        U1_T n = dio_ReadU1();
        res |= (n & 0x7Fu) << i;
        if ((n & 0x80) == 0) break;
    }
    return res;
}

U8_T dio_ReadU8LEB128(void) {
    U8_T res = 0;
    int i = 0;
    for (;; i += 7) {
        U1_T n = dio_ReadU1();
        res |= (n & 0x7Fu) << i;
        if ((n & 0x80) == 0) break;
    }
    return res;
}

I8_T dio_ReadI8LEB128(void) {
    U8_T res = 0;
    int i = 0;
    for (;; i += 7) {
        U1_T n = dio_ReadU1();
        res |= (n & 0x7Fu) << i;
        if ((n & 0x80) == 0) {
            res |= -(n & 0x40) << i;
            break;
        }
    }
    return (I8_T)res;
}

U8_T dio_ReadUX(int Size) {
    switch (Size) {
    case 2:
        return dio_ReadU2();
    case 4:
        return dio_ReadU4();
    case 8:
        return dio_ReadU8();
    default:
        assert(0);
        return 0;
    }
}

U8_T dio_ReadAddress(void) {
    switch (dio_gAddressSize) {
    case 2:
        return dio_ReadU2();
    case 4:
        return dio_ReadU4();
    case 8:
        return dio_ReadU8();
    default:
        assert(0);
        return 0;
    }
}

static void dio_CheckBlockBufCapacity(U4_T Size) {
    static U4_T BufSize = 0;
    if (BufSize < Size) { 
        BufSize = BufSize == 0 ? 0x100 : BufSize * 2;
        dio_gFormBlockBuf = (U1_T *)loc_realloc(dio_gFormBlockBuf, BufSize);
    }
}

char * dio_ReadString(void) {
    char * Res = NULL;
    U4_T Length = 0;
    for (;;) {
        U1_T Char = dio_ReadU1();
        dio_CheckBlockBufCapacity(Length + 1);
        dio_gFormBlockBuf[Length++] = Char;
        if (Char == 0) break;
    }
    if (Length == 1) return NULL;
    Res = (char *)loc_alloc(Length);
    strcpy(Res, (char *)dio_gFormBlockBuf);
    return Res;
}

U8_T dio_ReadAddrBuf(U1_T * Buf) {
    int i;
    U8_T Addr = 0;
    for (i = 0; i < dio_gAddressSize; i++) {
        U8_T Byte = (U8_T)Buf[i];
        if (sBigEndian) {
            Addr |= Byte << ((dio_gAddressSize - i - 1) * 8);
        }
        else {
            Addr |= Byte << (i * 8);
        }
    }
    return Addr;
}

static U1_T * dio_LoadStringTable(U4_T * StringTableSize) {
    ELF_File * File = sSection->file;
    DIO_Cache * Cache = dio_GetCache(File);

    if (Cache->mStringTable == NULL) {
        U4_T ID;
        ELF_Section * Section = NULL;

        for (ID = 1; ID < File->section_cnt; ID++) {
            if (strcmp(File->sections[ID]->name, ".debug_str") == 0) {
                if (Section != NULL) {
                    str_exception(ERR_DWARF, "more then one .debug_str section in a file");
                }
                Section = File->sections[ID];
                assert(Section->file == File);
            }
        }

        if (Section == NULL) {
            str_exception(ERR_DWARF, "section .debug_str not found");
        }

        Cache->mStringTableSize = (size_t)Section->size;
        if (elf_load(Section, &Cache->mStringTable) < 0) {
            str_exception(ERR_DWARF, "invalid .debug_str section");
        }
    }

    *StringTableSize = Cache->mStringTableSize;
    return Cache->mStringTable;
}

static void dio_ReadFormAddr(void) {
    dio_gFormRef = dio_ReadAddress();
}

static void dio_ReadFormBlock(U2_T Attr, U4_T Size) {
    U1_T * Buf;
    dio_gFormBlockSize = Size;
    dio_CheckBlockBufCapacity(Size);
    Buf = dio_gFormBlockBuf;
    while (Size > 0) {
        *Buf++ = dio_ReadU1();
        Size--;
    }
}

static void dio_ReadFormData(U2_T Attr, U1_T Size, U8_T Data) {
    dio_gFormData = Data;
    dio_gFormDataSize = Size;
}

static void dio_ReadFormFlag(void) {
    dio_gFormData = dio_ReadU1();
    dio_gFormDataSize = 1;
}

static void dio_ReadFormRef(void) {
    dio_gFormRef = dio_ReadU4();
}

static void dio_ReadFormRelRef(U8_T Offset) {
    if (dio_gUnitSize > 0 && Offset >= dio_gUnitSize) {
        str_exception(ERR_DWARF, "invalid REF attribute value");
    }
    dio_gFormRef = dio_gUnitPos + Offset;
}

static void dio_ReadFormRefAddr(void) {
    U4_T Size = dio_gAddressSize;
    if (dio_gVersion >= 3) Size = dio_g64bit ? 8 : 4;
    dio_gFormRef = dio_ReadUX(Size);
}

static void dio_ReadFormString(void) {
    dio_gFormBlockSize = 0;
    for (;;) {
        U1_T Char = dio_ReadU1();
        dio_CheckBlockBufCapacity(dio_gFormBlockSize + 1);
        dio_gFormBlockBuf[dio_gFormBlockSize++] = Char;
        if (Char == 0) break;
    }
}

static void dio_ReadFormStringRef(void) {
    U8_T Offset = dio_ReadUX(dio_g64bit ? 8 : 4);
    U4_T StringTableSize = 0;
    U1_T * StringTable = dio_LoadStringTable(&StringTableSize);
    dio_gFormBlockSize = 0;
    for (;;) {
        U1_T Char;
        if (Offset >= StringTableSize) {
            str_exception(ERR_DWARF, "invalid FORM_STRP attribute");
        }
        dio_CheckBlockBufCapacity(dio_gFormBlockSize + 1);
        Char = StringTable[Offset++];
        dio_gFormBlockBuf[dio_gFormBlockSize++] = Char;
        if (Char == 0) break;
    }
}

static void dio_ReadAttribute(U2_T Attr, U2_T Form) {
    switch (Form) {
    case FORM_ADDR      : dio_ReadFormAddr(); break;
    case FORM_REF       : dio_ReadFormRef(); break;
    case FORM_BLOCK1    : dio_ReadFormBlock(Attr, dio_ReadU1()); break;
    case FORM_BLOCK2    : dio_ReadFormBlock(Attr, dio_ReadU2()); break;
    case FORM_BLOCK4    : dio_ReadFormBlock(Attr, dio_ReadU4()); break;
    case FORM_BLOCK     : dio_ReadFormBlock(Attr, dio_ReadLEB128()); break;
    case FORM_DATA1     : dio_ReadFormData(Attr, 1, dio_ReadU1()); break;
    case FORM_DATA2     : dio_ReadFormData(Attr, 2, dio_ReadU2()); break;
    case FORM_DATA4     : dio_ReadFormData(Attr, 4, dio_ReadU4()); break;
    case FORM_DATA8     : dio_ReadFormData(Attr, 8, dio_ReadU8()); break;
    case FORM_SDATA     : dio_ReadFormData(Attr, 8, dio_ReadI8LEB128()); break;
    case FORM_UDATA     : dio_ReadFormData(Attr, 8, dio_ReadU8LEB128()); break;
    case FORM_FLAG      : dio_ReadFormFlag(); break;
    case FORM_STRING    : dio_ReadFormString(); break;
    case FORM_STRP      : dio_ReadFormStringRef(); break;
    case FORM_REF_ADDR  : dio_ReadFormRefAddr(); break;
    case FORM_REF1      : dio_ReadFormRelRef(dio_ReadU1()); break;
    case FORM_REF2      : dio_ReadFormRelRef(dio_ReadU2()); break;
    case FORM_REF4      : dio_ReadFormRelRef(dio_ReadU4()); break;
    case FORM_REF8      : dio_ReadFormRelRef(dio_ReadU8()); break;
    case FORM_REF_UDATA : dio_ReadFormRelRef(dio_ReadLEB128()); break;
    default: str_exception(ERR_DWARF, "invalid FORM");
    }
}

static void dio_ReadEntry(DIO_EntryCallBack CallBack) {
    DIO_Abbreviation * Abbr = NULL;
    U2_T Tag = 0;
    U4_T AttrPos = 0;
    U4_T EntrySize = 0;
    int Init = 1;
    dio_gEntryPos = dio_GetPos();
    if (dio_gVersion >= 2) {
        U4_T AbbrCode = dio_ReadLEB128();
        if (AbbrCode == 0) return;
        if (AbbrCode >= sAbbrevTableSize || sAbbrevTable[AbbrCode] == NULL) {
            str_exception(ERR_DWARF, "invalid abbreviation table");
        }
        Abbr =  sAbbrevTable[AbbrCode];
        Tag = Abbr->mTag;
    }
    else {
        EntrySize = dio_ReadU4();
        if (EntrySize < 8) {
            while (EntrySize > 4) {
                dio_ReadU1();
                EntrySize--;
            }
            return;
        }
        Tag = dio_ReadU2();
    }
    for (;;) {
        U2_T Attr = 0;
        U2_T Form = 0;
        if (Init) {
            Form = 1;
            Init = 0;
        }
        else if (Abbr != NULL) {
            if (AttrPos < Abbr->mAttrLen) {
                Attr = Abbr->mAttrs[AttrPos++];
                Form = Abbr->mAttrs[AttrPos++];
                if (Form == FORM_INDIRECT) Form = (U2_T)dio_ReadLEB128();
            }
        }
        else {
            if (dio_GetPos() < dio_gEntryPos + EntrySize) {
                Attr = dio_ReadU2();
                Form = Attr & 0xF;
                Attr = (Attr & 0xfff0) >> 4;
            }
        }
        if (Attr != 0 && Form != 0) dio_ReadAttribute(Attr, Form);
        if (Tag == TAG_compile_unit) {
            if (Attr == AT_sibling && dio_gUnitSize == 0) {
                dio_ChkRef(Form);
                assert(dio_gVersion == 1);
                dio_gUnitSize = (U4_T)(dio_gFormRef - dio_gUnitPos);
                assert(dio_gUnitPos < dio_GetPos());
                assert(dio_gUnitPos + dio_gUnitSize >= dio_GetPos());
            }
            else if (Attr == 0 && Form == 0) {
                if (dio_gUnitSize == 0) str_exception(ERR_DWARF, "missing compilation unit sibling attribute");
            }
        }
        CallBack(Tag, Attr, Form);
        if (Attr == 0 && Form == 0) break;
    }
}

static void dio_FindAbbrevTable(U4_T Offset, DIO_Abbreviation *** AbbrevTable, U4_T * AbbrevTableSize);

void dio_ReadUnit(DIO_EntryCallBack CallBack) {
    dio_gUnitPos = dio_GetPos();
    dio_g64bit = 0;
    if (dio_gVersion >= 2) {
        dio_gUnitSize = dio_ReadU4();
        if (dio_gUnitSize == 0xffffffffu) {
            dio_g64bit = 1;
            str_exception(ERR_DWARF, "64-bit DWARF is not supported yet");
        }
        else {
            dio_gUnitSize += 4;
        }
        dio_gVersion = dio_ReadU2();
        dio_FindAbbrevTable(dio_ReadU4(), &sAbbrevTable, &sAbbrevTableSize);
        dio_gAddressSize = dio_ReadU1();
    }
    else {
        dio_gUnitSize = 0;
        dio_gVersion = 1;
        dio_gAddressSize = 4;
        sAbbrevTable = NULL;
        sAbbrevTableSize = 0;
    }
    while (dio_gUnitSize == 0 || dio_GetPos() < dio_gUnitPos + dio_gUnitSize) {
        dio_ReadEntry(CallBack);
    }
}

#define dio_AbbrevTableHash(File, Offset) (((int)(File) + (int)(Offset)) / 4 % ABBREV_TABLE_SIZE)

static int dio_IsAbbrevSectionLoaded(ELF_File * File) {
    DIO_Cache * Cache = dio_GetCache(File);
    
    if (Cache->mAbbrevTable != NULL) {
        U4_T Hash = dio_AbbrevTableHash(File, 0);
        DIO_AbbrevSet * AbbrevSet = Cache->mAbbrevTable[Hash];
        while (AbbrevSet != NULL) {
            if (AbbrevSet->mFile == File) return 1;
            AbbrevSet = AbbrevSet->mNext;
        }
    }
    return 0;
}

void dio_LoadAbbrevTable(ELF_File * File) {
    U4_T ID;
    U8_T TableOffset = 0;
    ELF_Section * Section = NULL;
    static U2_T * AttrBuf = NULL;
    static U4_T AttrBufSize = 0;
    DIO_Abbreviation ** AbbrevTable = NULL;
    U4_T AbbrevTableSize = 0;
    DIO_Cache * Cache = dio_GetCache(File);

    if (dio_IsAbbrevSectionLoaded(File)) return;

    assert(sSection == NULL);
    for (ID = 1; ID < File->section_cnt; ID++) {
        if (strcmp(File->sections[ID]->name, ".debug_abbrev") == 0) {
            if (Section != NULL) {
                str_exception(ERR_DWARF, "more then one .debug_abbrev section in a file");
            }
            Section = File->sections[ID];
        }
    }
    if (Section == NULL) return;
    dio_EnterSection(Section, 0);
    for (;;) {
        U4_T AttrPos = 0;
        U2_T Tag = 0;
        U1_T Children = 0;
        U4_T ID = dio_ReadLEB128();
        if (ID == 0) {
            /* End of compilation unit */
            U4_T Hash = dio_AbbrevTableHash(File, TableOffset);
            DIO_AbbrevSet * AbbrevSet = (DIO_AbbrevSet *)loc_alloc_zero(sizeof(DIO_AbbrevSet));
            AbbrevSet->mFile = File;
            AbbrevSet->mOffset = TableOffset;
            AbbrevSet->mTable = AbbrevTable;
            AbbrevSet->mSize = AbbrevTableSize;
            if (Cache->mAbbrevTable == NULL) {
                Cache->mAbbrevTable = (DIO_AbbrevSet **)loc_alloc_zero(sizeof(DIO_AbbrevSet *) * ABBREV_TABLE_SIZE);
            }
            AbbrevSet->mNext = Cache->mAbbrevTable[Hash];
            Cache->mAbbrevTable[Hash] = AbbrevSet;
            AbbrevTable = NULL;
            AbbrevTableSize = 0;
            if (dio_GetPos() >= Section->size) break;
            TableOffset = dio_GetPos();
            continue;
        }
        if (ID >= 0x1000000) str_exception(ERR_DWARF, "invalid abbreviation table");
        if (ID >= AbbrevTableSize) {
            U4_T Size = AbbrevTableSize;
            AbbrevTableSize = ID + 1024u;
            AbbrevTable = (DIO_Abbreviation **)loc_realloc(AbbrevTable, sizeof(DIO_Abbreviation *) * AbbrevTableSize);
            memset(AbbrevTable + Size, 0, sizeof(DIO_Abbreviation *) * (AbbrevTableSize - Size));
        }
        Tag = (U2_T)dio_ReadLEB128();
        Children = (U2_T)dio_ReadU1() != 0;
        for (;;) {
            U4_T Attr = dio_ReadLEB128();
            U4_T Form = dio_ReadLEB128();
            if (Attr >= 0x10000 || Form >= 0x10000) str_exception(ERR_DWARF, "invalid abbreviation table");
            if (Attr == 0 && Form == 0) {
                DIO_Abbreviation * Abbr = AbbrevTable[ID];
                assert(Abbr == NULL);
                Abbr = (DIO_Abbreviation *)loc_alloc_zero(sizeof(DIO_Abbreviation) + sizeof(U2_T) * (AttrPos - 2));
                Abbr->mTag = Tag;
                Abbr->mChildren = Children;
                Abbr->mAttrLen = AttrPos;
                memcpy(Abbr->mAttrs, AttrBuf, sizeof(U2_T) * AttrPos);
                AbbrevTable[ID] = Abbr;
                break;
            }
            if (AttrBufSize < AttrPos + 2) {
                AttrBufSize = AttrPos + 256;
                AttrBuf = (U2_T *)loc_realloc(AttrBuf, sizeof(U2_T) * AttrBufSize);
            }
            AttrBuf[AttrPos++] = (U2_T)Attr;
            AttrBuf[AttrPos++] = (U2_T)Form;
        }
    }
    assert(AbbrevTable == NULL);
    assert(AbbrevTableSize == 0);
    dio_ExitSection();
}

static void dio_FindAbbrevTable(U4_T Offset, DIO_Abbreviation *** AbbrevTable, U4_T * AbbrevTableSize) {
    DIO_Cache * Cache = dio_GetCache(sSection->file);
    if (Cache->mAbbrevTable != NULL) {
        U4_T Hash = dio_AbbrevTableHash(sSection->file, Offset);
        DIO_AbbrevSet * AbbrevSet = Cache->mAbbrevTable[Hash];
        while (AbbrevSet != NULL) {
            if (AbbrevSet->mFile == sSection->file && AbbrevSet->mOffset == Offset) {
                *AbbrevTable = AbbrevSet->mTable;
                *AbbrevTableSize = AbbrevSet->mSize;
                return;
            }
            AbbrevSet = AbbrevSet->mNext;
        }
    }
    str_exception(ERR_DWARF, "invalid abbreviation table offset");
    *AbbrevTable = NULL;
    *AbbrevTableSize = 0;
}

void dio_ChkFlag(U2_T Form) {
    switch (Form) {
    case FORM_FLAG      :
        return;
    }
    str_exception(ERR_DWARF, "FORM_FLAG expected");
}

void dio_ChkRef(U2_T Form) {
    switch (Form) {
    case FORM_REF       :
    case FORM_REF_ADDR  :
    case FORM_REF1      :
    case FORM_REF2      :
    case FORM_REF4      :
    case FORM_REF8      :
    case FORM_REF_UDATA :
        return;
    }
    str_exception(ERR_DWARF, "FORM_REF* expected");
}

void dio_ChkAddr(U2_T Form) {
    switch (Form) {
    case FORM_ADDR      :
        return;
    }
    str_exception(ERR_DWARF, "FORM_ADDR expected");
}

void dio_ChkData(U2_T Form) {
    switch (Form) {
    case FORM_DATA1     :
    case FORM_DATA2     :
    case FORM_DATA4     :
    case FORM_DATA8     :
    case FORM_SDATA     :
    case FORM_UDATA     :
        return;
    }
    str_exception(ERR_DWARF, "FORM_DATA* expected");
}

void dio_ChkBlock(U2_T Form, U1_T ** Buf, U4_T * Size) {
    switch (Form) {
    case FORM_BLOCK1    :
    case FORM_BLOCK2    :
    case FORM_BLOCK4    :
    case FORM_BLOCK     :
        *Size = dio_gFormBlockSize;
        *Buf = dio_gFormBlockBuf;
        break;
    case FORM_DATA1     :
    case FORM_DATA2     :
    case FORM_DATA4     :
    case FORM_DATA8     :
    case FORM_SDATA     :
    case FORM_UDATA     :
        *Size = dio_gFormDataSize;
        *Buf = (U1_T *)&dio_gFormData;
        break;
    default:
        str_exception(ERR_DWARF, "FORM_BLOCK expected");
    }
}

void dio_ChkString(U2_T Form) {
    if (Form == FORM_STRING) return;
    if (Form == FORM_STRP) return;
    str_exception(ERR_DWARF, "FORM_STRING expected");
}

#endif
