/*
  Copyright (C) 2015-2016 David Anderson. All Rights Reserved.

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2.1 of the GNU Lesser General Public License
  as published by the Free Software Foundation.

  This program is distributed in the hope that it would be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  Further, this software is distributed without any warranty that it is
  free of the rightful claim of any third person regarding infringement
  or the like.  Any license provided herein, whether implied or
  otherwise, applies only to this software file.  Patent licenses, if
  any, provided herein do not apply to combinations of this program with
  other software, or any other product whatsoever.

  You should have received a copy of the GNU Lesser General Public
  License along with this program; if not, write the Free Software
  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301,
  USA.

*/

#include "config.h"
#include "dwarf_incl.h"
#include <stdio.h>
#include <limits.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */
#include "dwarf_macro5.h"

#define TRUE 1
#define FALSE 0

/*  Section 6.3: Macro Information:
    Each macro unit ends with an entry
    containing an opcode of 0. */

static const Dwarf_Small dwarf_udata_string_form[]  = {DW_FORM_udata,DW_FORM_string};
static const Dwarf_Small dwarf_udata_udata_form[]   = {DW_FORM_udata,DW_FORM_udata};
static const Dwarf_Small dwarf_udata_strp_form[]    = {DW_FORM_udata,DW_FORM_strp};
static const Dwarf_Small dwarf_udata_strp_sup_form[] = {DW_FORM_udata,DW_FORM_strp_sup};
static const Dwarf_Small dwarf_secoffset_form[]     = {DW_FORM_sec_offset};
static const Dwarf_Small dwarf_udata_strx_form[]    = {DW_FORM_udata,DW_FORM_strx};

struct Dwarf_Macro_Forms_s dw5formsarray[] = {
    {0,0,0},
    {DW_MACRO_define,2,dwarf_udata_string_form},
    {DW_MACRO_undef,2,dwarf_udata_string_form},
    {DW_MACRO_start_file,2,dwarf_udata_udata_form},
    {DW_MACRO_end_file,0,0},

    {DW_MACRO_define_strp,2,dwarf_udata_strp_form},
    {DW_MACRO_undef_strp,2,dwarf_udata_strp_form},
    {DW_MACRO_import,1,dwarf_secoffset_form},

    {DW_MACRO_define_sup,2,dwarf_udata_strp_sup_form},
    {DW_MACRO_undef_sup,2,dwarf_udata_strp_sup_form},
    {DW_MACRO_import_sup,1,dwarf_secoffset_form},

    {DW_MACRO_define_strx,2,dwarf_udata_strx_form},
    {DW_MACRO_undef_strx,2,dwarf_udata_strx_form},
};



/* Represents DWARF 5 macro info */
/* .debug_macro predefined, in order by value  */
static const struct Dwarf_Macro_OperationsList_s dwarf_default_macro_opslist = {
13, dw5formsarray
};


static int _dwarf_internal_macro_context_by_offset(Dwarf_Debug dbg,
    Dwarf_Unsigned offset,
    Dwarf_Unsigned  * version_out,
    Dwarf_Macro_Context * macro_context_out,
    Dwarf_Unsigned *macro_ops_count_out,
    Dwarf_Unsigned *macro_ops_data_length,
    char **srcfiles,
    Dwarf_Signed srcfilescount,
    const char *comp_dir,
    const char *comp_name,
    Dwarf_CU_Context cu_context,
    Dwarf_Error * error);

static int _dwarf_internal_macro_context(Dwarf_Die die,
    Dwarf_Bool offset_specified,
    Dwarf_Unsigned offset,
    Dwarf_Unsigned  * version_out,
    Dwarf_Macro_Context * macro_context_out,
    Dwarf_Unsigned *macro_unit_offset_out,
    Dwarf_Unsigned *macro_ops_count_out,
    Dwarf_Unsigned *macro_ops_data_length,
    Dwarf_Error * error);

static int
is_std_moperator(Dwarf_Small op)
{
    if (op >= 1 && op <= DW_MACRO_undef_strx) {
        return TRUE;
    }
    return FALSE;
}

static int
_dwarf_skim_forms(Dwarf_Debug dbg,
    Dwarf_Macro_Context mcontext,
    Dwarf_Small *mdata_start,
    unsigned formcount,
    const Dwarf_Small *forms,
    Dwarf_Small *section_end,
    Dwarf_Unsigned *forms_length,
    Dwarf_Error *error)
{
    unsigned i = 0;
    Dwarf_Small curform = 0 ;
    Dwarf_Unsigned totallen = 0;
    Dwarf_Unsigned v = 0;
    Dwarf_Unsigned ret_value = 0;
    Dwarf_Unsigned length;
    Dwarf_Small *mdata = mdata_start;
    Dwarf_Word leb128_length = 0;

    for( ; i < formcount; ++i) {
        curform = forms[i];
        if (mdata >= section_end) {
            _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
            return DW_DLV_ERROR;
        }
        switch(curform) {
        default:
            _dwarf_error(dbg,error,DW_DLE_DEBUG_FORM_HANDLING_INCOMPLETE);
            return DW_DLV_ERROR;
        case DW_FORM_block1:
            v =  *(Dwarf_Small *) mdata;
            totallen += v+1;
            mdata += v+1;
            break;
        case DW_FORM_block2:
            READ_UNALIGNED_CK(dbg, ret_value, Dwarf_Unsigned,
                mdata, sizeof(Dwarf_Half),
                error,section_end);
            v = ret_value + sizeof(Dwarf_Half);
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_block4:
            READ_UNALIGNED_CK(dbg, ret_value, Dwarf_Unsigned,
                mdata, sizeof(Dwarf_ufixed),
                error,section_end);
            v = ret_value + sizeof(Dwarf_ufixed);
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_data1:
            v = 1;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_data2:
            v = 2;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_data4:
            v = 4;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_data8:
            v = 8;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_data16:
            v = 8;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_string: {
            int res = _dwarf_check_string_valid(dbg,
                mdata,mdata, section_end,
                DW_DLE_MACRO_STRING_BAD,error);
            if(res != DW_DLV_OK) {
                return res;
            }
            v = strlen((char *) mdata) + 1;
            totallen += v;
            mdata += v;
            }
            break;
        case DW_FORM_block:
            DECODE_LEB128_UWORD_LEN_CK(mdata,length,leb128_length,
                dbg, error,section_end);
            v = length + leb128_length;
            totallen += v;
            break;
        case DW_FORM_flag:
            v = 1;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_sec_offset:
            /* If 32bit dwarf, is 4. Else is 64bit dwarf and is 8. */
            v = mcontext->mc_offset_size;
            totallen += v;
            mdata += v;
            break;
        case DW_FORM_sdata:
            /*  Discard the decoded value, we just want the length
                of the value. */
            DECODE_LEB128_UWORD_LEN_CK(mdata,length,leb128_length,
                dbg, error,section_end);
            totallen += v;
            break;
        case DW_FORM_strx:
            DECODE_LEB128_UWORD_LEN_CK(mdata,length,leb128_length,
                dbg, error,section_end);
            totallen += leb128_length;;
            break;
        case DW_FORM_strp:
            v = mcontext->mc_offset_size;
            mdata += v;
            totallen += v;
            break;
        case DW_FORM_udata:
            /*  Discard the decoded value, we just want the length
                of the value. */
            DECODE_LEB128_UWORD_LEN_CK(mdata,length,leb128_length,
                dbg, error,section_end);
            totallen += leb128_length;
            break;
        }
    }
    if (mdata > section_end) {
        _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
        return DW_DLV_ERROR;
    }
    *forms_length = totallen;
    return DW_DLV_OK;
}

#if 0 /* FOR DEBUGGING */
static void
dump_bytes(Dwarf_Small * start, long len)
{
    Dwarf_Small *end = start + len;
    Dwarf_Small *cur = start;
    unsigned pos = 0;

    printf("dump %ld bytes, start at 0x%lx\n",len,(unsigned long)start);
    printf("0x");
    for (; cur < end;pos++, cur++) {
        if (!(pos %4)) {
            printf(" ");
        }
        printf("%02x",*cur);
    }
    printf("\n");
}
Dwarf_Bool
is_defundef(unsigned op)
{
    switch(op){
    case DW_MACRO_define:
    case DW_MACRO_undef:
    case DW_MACRO_define_strp:
    case DW_MACRO_undef_strp:
    case DW_MACRO_define_strx:
    case DW_MACRO_undef_strx:
    case DW_MACRO_define_sup:
    case DW_MACRO_undef_sup:
        return TRUE;
    }
    return FALSE;
}
#endif /* FOR DEBUGGING */


/*  On first call (for this macro_context),
    build_ops_array is FALSE. On second,
    it is TRUE and we know the count so we allocate and fill in
    the ops array. */
static int
_dwarf_get_macro_ops_count_internal(Dwarf_Macro_Context macro_context,
    Dwarf_Bool build_ops_array,
    Dwarf_Error *error)
{
    Dwarf_Debug dbg = 0;
    Dwarf_Small *mdata = 0;
    Dwarf_Small *section_end = 0;
    Dwarf_Small *section_base = 0;
    Dwarf_Unsigned opcount = 0;
    Dwarf_Unsigned known_ops_count = 0;
    struct Dwarf_Macro_Operator_s *opsarray = 0;
    struct Dwarf_Macro_Operator_s *curopsentry = 0;
    int res = 0;

    dbg = macro_context->mc_dbg;
    if (build_ops_array) {
        known_ops_count = macro_context->mc_macro_ops_count;
        opsarray = (struct Dwarf_Macro_Operator_s *)
            calloc(known_ops_count,sizeof(struct Dwarf_Macro_Operator_s));
        if(!opsarray) {
            _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
            return DW_DLV_ERROR;
        }
        curopsentry = opsarray;
        macro_context->mc_ops = opsarray;
    }
    section_base = dbg->de_debug_macro.dss_data;
    section_end = section_base + dbg->de_debug_macro.dss_size;
    mdata = macro_context->mc_macro_ops;

    while (mdata < section_end) {
        Dwarf_Small op = 0;

        if (mdata >= section_end)  {
            _dwarf_error(dbg, error, DW_DLE_MACRO_PAST_END);
            return DW_DLV_ERROR;
        }
        op = *mdata;
        ++opcount;
        ++mdata;
        /*  Here so we would set it for the zero op,
            though that is kind of redundant since
            the curopsentry starts out zero-d.
            if (build_ops_array)
                curopsentry->mo_opcode = op;  */
        if (!op) {
            Dwarf_Unsigned opslen = 0;
            /*  End of ops, this is terminator, count the ending 0
                as an operator so dwarfdump can print it.  */
            opslen = mdata - macro_context->mc_macro_ops;
            macro_context->mc_macro_ops_count = opcount;
            macro_context->mc_ops_data_length = opslen;
            macro_context->mc_total_length = opslen +
                macro_context->mc_macro_header_length;
            return DW_DLV_OK;
        }
        if (is_std_moperator(op)) {
            struct Dwarf_Macro_Forms_s * ourform =
                dw5formsarray + op;
            /* ASSERT: op == ourform->mf_code */
            unsigned formcount = ourform->mf_formcount;
            const Dwarf_Small *forms = ourform->mf_formbytes;
            Dwarf_Unsigned forms_length = 0;

            res = _dwarf_skim_forms(dbg,macro_context,mdata,
                formcount,forms,
                section_end,
                &forms_length,error);
            if ( res != DW_DLV_OK) {
                return res;
            }
            if(build_ops_array) {
                curopsentry->mo_opcode = op;
                curopsentry->mo_form = ourform;
                curopsentry->mo_data = mdata;
            }
            mdata += forms_length;
        } else {
            /* FIXME Add support for user defined ops. */
            _dwarf_error(dbg, error, DW_DLE_MACRO_OP_UNHANDLED);
            return DW_DLV_ERROR;
        }
        if (mdata > section_end)  {
            _dwarf_error(dbg, error, DW_DLE_MACRO_PAST_END);
            return DW_DLV_ERROR;
        }
        curopsentry++;
    }
    _dwarf_error(dbg, error, DW_DLE_MACRO_PAST_END);
    return DW_DLV_ERROR;
}

int
dwarf_get_macro_op(Dwarf_Macro_Context macro_context,
    Dwarf_Unsigned op_number,
    Dwarf_Unsigned * op_start_section_offset,
    Dwarf_Half    * macro_operator,
    Dwarf_Half    * forms_count,
    const Dwarf_Small **   formcode_array,
    Dwarf_Error *error)
{
    struct Dwarf_Macro_Operator_s *curop = 0;
    Dwarf_Debug dbg = 0;
    if (!macro_context || macro_context->mc_sentinel != 0xada) {
        if(macro_context) {
            dbg = macro_context->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }
    dbg = macro_context->mc_dbg;
    if (op_number >= macro_context->mc_macro_ops_count) {
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_INDEX);
        return DW_DLV_ERROR;
    }
    curop = macro_context->mc_ops + op_number;

    /*  ASSERT: *op_start_section_offset ==
        (curop->mo_data -1) - dbg->de_debug_macro.dss_data  */
    *op_start_section_offset =
        ((curop->mo_data -1) - macro_context->mc_macro_header) +
        macro_context->mc_section_offset;
    *macro_operator = curop->mo_opcode;
    if (curop->mo_form) {
        *forms_count  = curop->mo_form->mf_formcount;
        *formcode_array = curop->mo_form->mf_formbytes;
    } else {
        /* ASSERT: macro_operator == 0 */
        *forms_count  = 0;
        *formcode_array = 0;
    }
    return DW_DLV_OK;
}


/*  Here a DW_DLV_NO_ENTRY return means the macro operator
    is not a def/undef operator. */
int
dwarf_get_macro_defundef(Dwarf_Macro_Context macro_context,
    Dwarf_Unsigned op_number,
    Dwarf_Unsigned * line_number,
    Dwarf_Unsigned * index,
    Dwarf_Unsigned * offset,
    Dwarf_Half     * forms_count,
    const char     ** macro_string,
    Dwarf_Error *error)
{
    Dwarf_Debug dbg = 0;
    Dwarf_Small *mdata = 0;
    int res = 0;
    Dwarf_Small *startptr = 0;
    Dwarf_Small *endptr = 0;
    Dwarf_Half lformscount = 0;
    struct Dwarf_Macro_Operator_s *curop = 0;
    unsigned macop = 0;

    if (!macro_context || macro_context->mc_sentinel != 0xada) {
        if(macro_context) {
            dbg = macro_context->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }
    dbg = macro_context->mc_dbg;
    if (op_number >= macro_context->mc_macro_ops_count) {
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_INDEX);
        return DW_DLV_ERROR;
    }
    curop = macro_context->mc_ops + op_number;
    macop = curop->mo_opcode;
    startptr = macro_context->mc_macro_header;
    endptr = startptr + macro_context->mc_total_length;
    mdata = curop->mo_data;
    lformscount = curop->mo_form->mf_formcount;
    if (lformscount != 2) {
        /*_dwarf_error(dbg, error,DW_DLE_MACRO_OPCODE_FORM_BAD);*/
        return DW_DLV_NO_ENTRY;
    }
    switch(macop){
    case DW_MACRO_define:
    case DW_MACRO_undef: {
        Dwarf_Unsigned linenum = 0;
        const char * content = 0;

        DECODE_LEB128_UWORD_CK(mdata,linenum,
            dbg, error,endptr);
        content = (const char *)mdata;
        res = _dwarf_check_string_valid(dbg,
            startptr,mdata, endptr,
            DW_DLE_MACRO_STRING_BAD,error);
        if(res != DW_DLV_OK) {
            return res;
        }
        *line_number = linenum;
        *index = 0;
        *offset = 0;
        *forms_count = lformscount;
        *macro_string = content;
        }
        return DW_DLV_OK;
    case DW_MACRO_define_strp:
    case DW_MACRO_undef_strp: {
        Dwarf_Unsigned linenum = 0;
        Dwarf_Unsigned stringoffset = 0;
        Dwarf_Small form1 =  curop->mo_form->mf_formbytes[1];
        char * localstr = 0;


        DECODE_LEB128_UWORD_CK(mdata,linenum,
            dbg, error,endptr);
        READ_UNALIGNED_CK(dbg,stringoffset,Dwarf_Unsigned,
            mdata,macro_context->mc_offset_size,
            error,endptr);
        mdata += macro_context->mc_offset_size;
        res = _dwarf_extract_local_debug_str_string_given_offset(dbg,
            form1,
            stringoffset,
            &localstr,
            error);
        *index = 0;
        *line_number = linenum;
        *offset = stringoffset;
        *forms_count = lformscount;
        if (res == DW_DLV_ERROR) {
            return res;
            *macro_string = "<Error: getting local .debug_str>";
        } else if (res == DW_DLV_NO_ENTRY) {
            *macro_string = "<Error: NO_ENTRY on .debug_string (strp)>";
        } else {
            *macro_string = (const char *)localstr;
        }
        }
        return DW_DLV_OK;
    case DW_MACRO_define_strx:
    case DW_MACRO_undef_strx: {
        Dwarf_Unsigned linenum = 0;
        Dwarf_Unsigned stringindex = 0;
        Dwarf_Unsigned offsettostr= 0;
        int ress = 0;
        Dwarf_Byte_Ptr mdata_copy = 0;
        Dwarf_Small form1 =  curop->mo_form->mf_formbytes[1];

        DECODE_LEB128_UWORD_CK(mdata,linenum, dbg, error,endptr);
        *line_number = linenum;
        mdata_copy = mdata;
        DECODE_LEB128_UWORD_CK(mdata_copy,stringindex, dbg, error,endptr);
        /* mdata_copy is for call below */


        *index = stringindex;
        *forms_count = lformscount;

        /* Redoes the index-getting. Gets offset. */
        ress = _dwarf_extract_string_offset_via_str_offsets(dbg,
            mdata_copy,
            endptr,
            DW_AT_macros, /*arbitrary, unused by called routine. */
            form1,
            macro_context->mc_cu_context,
            &offsettostr,
            error);
        if (ress  == DW_DLV_ERROR) {
            return ress;
        }
        if (ress == DW_DLV_OK) {
            char *localstr = 0;

            *index = stringindex;
            *offset = offsettostr;
            ress = _dwarf_extract_local_debug_str_string_given_offset(dbg,
                form1,
                offsettostr,
                &localstr,
                error);
            if(ress == DW_DLV_ERROR) {
                return ress;
            } else if (ress == DW_DLV_NO_ENTRY){
                *macro_string = "<:No string available>";
            } else {
                *macro_string = (const char *)localstr;
                /* All is ok. */
            }
        } else {
            *index = stringindex;
            *offset = 0;
            *macro_string = "<.debug_str_offsets not available>";
        }
        }
        return DW_DLV_OK;
    case DW_MACRO_define_sup:
    case DW_MACRO_undef_sup: {
        Dwarf_Unsigned linenum = 0;
        Dwarf_Unsigned supoffset = 0;
        char *localstring = 0;
        int resup = 0;
        Dwarf_Error lerr = 0;

        DECODE_LEB128_UWORD_CK(mdata,linenum,
            dbg, error,endptr);
        READ_UNALIGNED_CK(dbg,supoffset,Dwarf_Unsigned,
            mdata,macro_context->mc_offset_size,
            error,endptr);
        mdata += macro_context->mc_offset_size;
        *line_number = linenum;
        *index = 0;
        *offset = supoffset;
        *forms_count = lformscount;
        resup = _dwarf_get_string_from_tied(dbg, supoffset,
            &localstring, &lerr);
        if (resup != DW_DLV_OK) {
            if (resup == DW_DLV_ERROR) {
                int myerrno = dwarf_errno(lerr);
                if(myerrno == DW_DLE_NO_TIED_FILE_AVAILABLE) {
                    *macro_string =
                        (char *)"<DW_FORM_str_sup-no-tied_file>";
                } else {
                    _dwarf_error(dbg,error,myerrno);
                    *macro_string =
                        (char *)"<Error: DW_FORM_str_sup-got-error>";
                }
                dwarf_dealloc(dbg,lerr,DW_DLA_ERROR);
            } else {
                *macro_string = "<DW_FORM_str_sup-no-entry>";
            }
            return resup;
        }
        *macro_string = (const char *)localstring;
        /*  If NO ENTRY available, return DW_DLV_NO_ENTRY.
            We suspect this is better than DW_DLV_OK.  */
        return resup;
        }
    default:
        _dwarf_error(dbg,error,DW_DLE_MACRO_OP_UNHANDLED);
        return DW_DLV_ERROR;
    }
    return DW_DLV_NO_ENTRY;
}

/*  ASSERT: we elsewhere guarantee room to copy into.
    If trimtarg ==1, trim trailing slash in targ.
    Caller should not pass in 'src'
    with leading /  */
static void
specialcat(char *targ,char *src,int trimtarg)
{
    char *last = 0;

    while( *targ) {
        last = targ;
        targ++;
    }
    /* TARG now points at terminating NUL */
    /* LAST points at final character in targ. */
    if (trimtarg ) {
        if(last && *last == '/') {
            /* Truncate. */
            *last = 0;
            targ = last;
            /* TARG again points at terminating NUL */
        }
    }
    while (*src) {
        *targ = *src;
        targ++;
        src++;
    }
    *targ = 0;
}

/* If returns NULL caller must handle it. */
static const char *
construct_from_dir_and_name(const char *dir,
   const char *name)
{
    int truelen = 0;
    char *final = 0;

    /* Allow for NUL char and added /  */
    truelen = strlen(dir) + strlen(name) + 1 +1;
    final = (char *)malloc(truelen);
    if(!final) {
        return NULL;
    }
    final[0] = 0;
    specialcat(final,(char *)dir,1);
    strcat(final,"/");
    specialcat(final,(char *)name,0);
    return final;
}

/* If returns NULL caller must handle it. */
static const char *
construct_at_path_from_parts(Dwarf_Macro_Context mc)
{
    if (mc->mc_file_path) {
        return mc->mc_file_path;
    }
    if(!mc->mc_at_comp_dir || !mc->mc_at_comp_dir[0]) {
        return mc->mc_at_name;
    }
    if (!mc->mc_at_name || !mc->mc_at_name[0]) {
        return NULL;
    }
    if(_dwarf_file_name_is_full_path((Dwarf_Small *)mc->mc_at_name)) {
        return mc->mc_at_name;
    }
    /* Dwarf_Macro_Context destructor will free this. */
    mc->mc_file_path = construct_from_dir_and_name(
        mc->mc_at_comp_dir,mc->mc_at_name);
    return mc->mc_file_path;
}


int
dwarf_get_macro_startend_file(Dwarf_Macro_Context macro_context,
    Dwarf_Unsigned op_number,
    Dwarf_Unsigned * line_number,
    Dwarf_Unsigned * name_index_to_line_tab,
    const char     ** src_file_name,
    Dwarf_Error *error)
{
    Dwarf_Debug dbg = 0;
    Dwarf_Small *mdata = 0;
    unsigned macop = 0;
    struct Dwarf_Macro_Operator_s *curop = 0;
    Dwarf_Byte_Ptr startptr =  0;
    Dwarf_Byte_Ptr endptr =  0;

    if (!macro_context || macro_context->mc_sentinel != 0xada) {
        if(macro_context) {
            dbg = macro_context->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }
    dbg = macro_context->mc_dbg;
    if (op_number >= macro_context->mc_macro_ops_count) {
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_INDEX);
        return DW_DLV_ERROR;
    }
    startptr = macro_context->mc_macro_header;
    endptr = startptr + macro_context->mc_total_length;

    curop = macro_context->mc_ops + op_number;
    macop = curop->mo_opcode;
    mdata = curop->mo_data;
    if (macop != DW_MACRO_start_file && macop != DW_MACRO_end_file) {
        return DW_DLV_NO_ENTRY;
    }
    if (macop == DW_MACRO_start_file) {
        Dwarf_Unsigned linenum = 0;
        Dwarf_Unsigned srcindex = 0;
        Dwarf_Signed trueindex = 0;

        DECODE_LEB128_UWORD_CK(mdata,linenum, dbg, error,endptr);
        DECODE_LEB128_UWORD_CK(mdata,srcindex, dbg, error,endptr);
        *line_number = linenum;
        *name_index_to_line_tab = srcindex;
        /*  For DWARF 2,3,4, decrement by 1.
            FOR DWARF 5 do not decrement. */
        if(macro_context->mc_version_number >= 5) {
            trueindex = srcindex;
            if (trueindex < 0) {
                *src_file_name = "<source-file-index-low-no-name-available>";
                return DW_DLV_OK;
            }
            if (trueindex < macro_context->mc_srcfiles_count) {
                *src_file_name = macro_context->mc_srcfiles[trueindex];
                return DW_DLV_OK;
            } else {
                *src_file_name =
                    "<src-index-high-no-source-file-name-available>";
                return DW_DLV_OK;
            }
        } else {
            /* Unsigned to signed here. */
            trueindex = srcindex;
            /* Protects against crazy big srcindex, overflow territory. */
            if (trueindex < 0 ) {
                /* Something insane here. */
                *src_file_name = "<source-file-index-low-no-name-available>";
                return DW_DLV_OK;
            }
            /* Protects against crazy big srcindex, overflow territory. */
            if (trueindex > (macro_context->mc_srcfiles_count+1)) {
                /* Something insane here. */
                *src_file_name =
                    "<source-file-index-high-no-name-available>";
                return DW_DLV_OK;
            }
            --trueindex;
            if (trueindex > macro_context->mc_srcfiles_count) {
                *src_file_name =
                    "<adjusted-source-file-index-high-no-name-available>";
            }
            if (srcindex > 0 &&
                trueindex < macro_context->mc_srcfiles_count) {
                *src_file_name = macro_context->mc_srcfiles[trueindex];
            } else {
                const char *mcatcomp = construct_at_path_from_parts(
                    macro_context);
                if(mcatcomp) {
                    *src_file_name = mcatcomp;
                } else {
                    *src_file_name = "<no-source-file-name-available>";
                }
            }
        }
    } else {
        /* DW_MACRO_end_file. No operands. */
    }
    return DW_DLV_OK;
}

/*  Target_offset is the offset in a .debug_macro section,
    of a macro unit header.
    Returns DW_DLV_NO_ENTRY if the macro operator is not
    one of the import operators.  */
int
dwarf_get_macro_import(Dwarf_Macro_Context macro_context,
    Dwarf_Unsigned   op_number,
    Dwarf_Unsigned * target_offset,
    Dwarf_Error *error)
{
    Dwarf_Unsigned supoffset = 0;
    Dwarf_Debug dbg = 0;
    unsigned macop = 0;
    struct Dwarf_Macro_Operator_s *curop = 0;
    Dwarf_Small *mdata = 0;
    Dwarf_Byte_Ptr startptr =  0;
    Dwarf_Byte_Ptr endptr =  0;

    if (!macro_context || macro_context->mc_sentinel != 0xada) {
        if(macro_context) {
            dbg = macro_context->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }
    startptr = macro_context->mc_macro_header;
    endptr = startptr + macro_context->mc_total_length;
    dbg = macro_context->mc_dbg;
    if (op_number >= macro_context->mc_macro_ops_count) {
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_INDEX);
        return DW_DLV_ERROR;
    }
    curop = macro_context->mc_ops + op_number;
    macop = curop->mo_opcode;
    mdata = curop->mo_data;
    if (macop != DW_MACRO_import && macop != DW_MACRO_import_sup) {
        return DW_DLV_NO_ENTRY;
    }
    READ_UNALIGNED_CK(dbg,supoffset,Dwarf_Unsigned,
        mdata,macro_context->mc_offset_size,
        error,endptr);
    *target_offset = supoffset;
    return DW_DLV_OK;
}

/* */
static int
valid_macro_form(Dwarf_Half form)
{
    switch(form) {
    case DW_FORM_block:
    case DW_FORM_block1:
    case DW_FORM_block2:
    case DW_FORM_block4:
    case DW_FORM_data1:
    case DW_FORM_data2:
    case DW_FORM_data4:
    case DW_FORM_data8:
    case DW_FORM_data16:
    case DW_FORM_sdata:
    case DW_FORM_udata:
    case DW_FORM_flag:
    case DW_FORM_sec_offset:
    case DW_FORM_string:
    case DW_FORM_strp:
    case DW_FORM_strx:
        return TRUE;
    }
    return FALSE;
}

static int
validate_opcode(Dwarf_Debug dbg,
   struct Dwarf_Macro_Forms_s *curform,
   Dwarf_Error * error)
{
    unsigned i = 0;
    struct Dwarf_Macro_Forms_s *stdfptr = 0;
    if (curform->mf_code >= DW_MACRO_lo_user) {
        /* Nothing to check. user level. */
        return DW_DLV_OK;
    }
    if (curform->mf_code > DW_MACRO_undef_strx) {
        _dwarf_error(dbg, error, DW_DLE_MACRO_OPCODE_BAD);
        return (DW_DLV_ERROR);
    }
    if (!curform->mf_code){
        _dwarf_error(dbg, error, DW_DLE_MACRO_OPCODE_BAD);
        return (DW_DLV_ERROR);
    }
    stdfptr = &dwarf_default_macro_opslist.mol_data[curform->mf_code];

    if (curform->mf_formcount != stdfptr->mf_formcount) {
        _dwarf_error(dbg, error, DW_DLE_MACRO_OPCODE_FORM_BAD);
        return (DW_DLV_ERROR);
    }
    for(i = 0; i < curform->mf_formcount; ++i) {
        if (curform->mf_formbytes[i] != stdfptr->mf_formbytes[1]) {
            _dwarf_error(dbg, error, DW_DLE_MACRO_OPCODE_FORM_BAD);
            return (DW_DLV_ERROR);
        }
    }
    return DW_DLV_OK;
}

static int
read_operands_table(Dwarf_Macro_Context macro_context,
    Dwarf_Small * macro_header,
    Dwarf_Small * macro_data,
    Dwarf_Small * section_base,
    Dwarf_Unsigned section_size,
    Dwarf_Unsigned *table_size_out,
    Dwarf_Error *error)
{
    Dwarf_Small* table_data_start = macro_data;
    Dwarf_Unsigned local_size = 0;
    Dwarf_Unsigned cur_offset = 0;
    Dwarf_Small operand_table_count = 0;
    unsigned i = 0;
    struct Dwarf_Macro_Forms_s *curformentry = 0;
    Dwarf_Debug dbg = 0;
    Dwarf_Byte_Ptr startptr = 0;
    Dwarf_Byte_Ptr endptr = 0;

    if (!macro_context || macro_context->mc_sentinel != 0xada) {
        if(macro_context) {
            dbg = macro_context->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }

    dbg = macro_context->mc_dbg;
    cur_offset = (1+ macro_data) - macro_header;
    if (cur_offset >= section_size) {
        _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
        return (DW_DLV_ERROR);
    }

    startptr = macro_context->mc_macro_header;
    endptr = startptr + macro_context->mc_total_length;
    READ_UNALIGNED_CK(dbg,operand_table_count,Dwarf_Small,
        macro_data,sizeof(Dwarf_Small),error,endptr);
    macro_data += sizeof(Dwarf_Small);
    /* Estimating minimum size */
    local_size = operand_table_count * 4;

    cur_offset = (local_size+ macro_data) - section_base;
    if (cur_offset >= section_size) {
        _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
        return (DW_DLV_ERROR);
    }
    /* first, get size of table. */
    table_data_start = macro_data;
    for (i = 0; i < operand_table_count; ++i) {
        /*  Compiler warning about unused opcode_number
            variable should be ignored. */
        UNUSEDARG Dwarf_Small opcode_number = 0;
        Dwarf_Unsigned formcount = 0;
        READ_UNALIGNED_CK(dbg,opcode_number,Dwarf_Small,
            macro_data,sizeof(Dwarf_Small),error,endptr);
        macro_data += sizeof(Dwarf_Small);

        DECODE_LEB128_UWORD_CK(macro_data,formcount,
            dbg, error, endptr);
        cur_offset = (formcount+ macro_data) - section_base;
        if (cur_offset >= section_size) {
            _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
            return (DW_DLV_ERROR);
        }
        /* The 1 ubyte forms follow. Step past them. */
        macro_data += formcount;
    }
    /* reset for reread. */
    macro_data = table_data_start;
    /* allocate table */
    macro_context->mc_opcode_forms =  (struct Dwarf_Macro_Forms_s *)
        calloc(operand_table_count,
            sizeof(struct Dwarf_Macro_Forms_s));
    macro_context->mc_opcode_count = operand_table_count;
    if(!macro_context->mc_opcode_forms) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }

    curformentry = macro_context->mc_opcode_forms;
    for (i = 0; i < operand_table_count; ++i,++curformentry) {
        Dwarf_Small opcode_number = 0;
        Dwarf_Unsigned formcount = 0;
        int res = 0;

        cur_offset = (2 + macro_data) - section_base;
        if (cur_offset >= section_size) {
            _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
            return (DW_DLV_ERROR);
        }
        READ_UNALIGNED_CK(dbg,opcode_number,Dwarf_Small,
            macro_data,sizeof(Dwarf_Small),
            error,endptr);
        macro_data += sizeof(Dwarf_Small);
        DECODE_LEB128_UWORD_CK(macro_data,formcount,
            dbg, error, endptr);

        curformentry->mf_code = opcode_number;
        curformentry->mf_formcount = formcount;

        cur_offset = (formcount+ macro_data) - section_base;
        if (cur_offset >= section_size) {
            _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
            return (DW_DLV_ERROR);
        }
        curformentry->mf_formbytes = macro_data;
        macro_data += formcount;
        if (opcode_number  > DW_MACRO_undef_strx ) {
            Dwarf_Half k = 0;
            for(k = 0; k < formcount; ++k) {
                if (!valid_macro_form(curformentry->mf_formbytes[k])) {
                    _dwarf_error(dbg, error, DW_DLE_MACRO_OP_UNHANDLED);
                    return (DW_DLV_ERROR);
                }
            }
        }
        res = validate_opcode(macro_context->mc_dbg,curformentry, error);
        if(res != DW_DLV_OK) {
            return res;
        }
    }
    *table_size_out = macro_data - table_data_start;
    return DW_DLV_OK;
}

/*  This is not the normal srcfiles from dwarf_srcfiles.
    See translate translate_srcfiles_to_srcfiles2().
    It is a list, but the contents were directly malloc,
    not _dwarf_get_alloc.
*/
static void
dealloc_macro_srcfiles(char ** srcfiles,
    Dwarf_Signed srcfiles_count)
{
    Dwarf_Signed i = 0;
    if (!srcfiles || !srcfiles_count) {
        return;
    }
    for (i = 0; i < srcfiles_count; ++i) {
        if (srcfiles[i]) {
            free(srcfiles[i]);
            srcfiles[i] = 0;
        }
    }
    free(srcfiles);
}

/*  This makes the macro context safe from
    duplicate frees in case of error. */
static int
translate_srcfiles_to_srcfiles2(char **srcfiles,
   Dwarf_Signed srcfiles_count,
   char **srcfiles2)
{
    Dwarf_Signed i = 0;

    for(i = 0; i < srcfiles_count; ++i) {
        char * ostr = 0;
        char * newstr = 0;
        size_t slen = 0;

        ostr = srcfiles[i];
        slen = strlen(ostr);
        newstr =  calloc(1,slen+1);
        if (!newstr) {
            return DW_DLV_ERROR;
        }
        strcpy(newstr,ostr);
        srcfiles[i] = 0;
        srcfiles2[i] = newstr;
    }
    return DW_DLV_OK;
}

static int
_dwarf_internal_macro_context(Dwarf_Die die,
    Dwarf_Bool        offset_specified,
    Dwarf_Unsigned    offset_in,
    Dwarf_Unsigned  * version_out,
    Dwarf_Macro_Context * macro_context_out,
    Dwarf_Unsigned      * macro_unit_offset_out,
    Dwarf_Unsigned      * macro_ops_count_out,
    Dwarf_Unsigned      * macro_ops_data_length,
    Dwarf_Error * error)
{
    Dwarf_CU_Context   cu_context = 0;

    /*  The Dwarf_Debug this die belongs to. */
    Dwarf_Debug dbg = 0;
    int resattr = DW_DLV_ERROR;
    int lres = DW_DLV_ERROR;
    int res = DW_DLV_ERROR;
    Dwarf_Unsigned macro_offset = 0;
    Dwarf_Attribute macro_attr = 0;
    Dwarf_Signed srcfiles_count = 0;
    char ** srcfiles = 0;

    /*  srcfiles uses dwarf_get_alloc for strings
        so dealloc_macro_srcfiles() here will result in double-dealloc
        when dwarf_finish() happens to see the string deallocs
        before the macro context dealloc (the context dealloc
        will call dealloc_macro_srcfiles() !).

        Also see the comment at _dwarf_macro_destructor() here.
    */
    char ** srcfiles2 = 0;

    const char *comp_dir = 0;
    const char *comp_name = 0;

    /*  ***** BEGIN CODE ***** */
    if (error != NULL) {
        *error = NULL;
    }

    CHECK_DIE(die, DW_DLV_ERROR);
    cu_context = die->di_cu_context;
    dbg = cu_context->cc_dbg;

    /*  Doing the load here results in duplication of the
        section-load call  (in the by_offset
        interface below) but detects the missing section
        quickly. */
    res = _dwarf_load_section(dbg, &dbg->de_debug_macro,error);
    if (res != DW_DLV_OK) {
        return res;
    }
    if (!dbg->de_debug_macro.dss_size) {
        return (DW_DLV_NO_ENTRY);
    }
    resattr = dwarf_attr(die, DW_AT_macros, &macro_attr, error);
    if (resattr == DW_DLV_NO_ENTRY) {
        resattr = dwarf_attr(die, DW_AT_GNU_macros, &macro_attr, error);
    }
    if (resattr != DW_DLV_OK) {
        return resattr;
    }
    if (!offset_specified) {
        lres = dwarf_global_formref(macro_attr, &macro_offset, error);
        if (lres != DW_DLV_OK) {
            return lres;
        }
    } else {
        macro_offset = offset_in;
    }
    lres = dwarf_srcfiles(die,&srcfiles,&srcfiles_count, error);
    if (lres == DW_DLV_ERROR) {
        return lres;
    }
    lres = _dwarf_internal_get_die_comp_dir(die, &comp_dir,
        &comp_name,error);
    if (resattr == DW_DLV_ERROR) {
        Dwarf_Signed i = 0;
        for (i = 0; i < srcfiles_count; ++i) {
            if(srcfiles[i]) {
                dwarf_dealloc(dbg, srcfiles[i], DW_DLA_STRING);
            }
        }
        dwarf_dealloc(dbg, srcfiles, DW_DLA_LIST);
        srcfiles = 0;
        return lres;
    }
    *macro_unit_offset_out = macro_offset;
    /*  We cannot use space allocated by
        _dwarf_get_alloc() in the macro_context
        we will allocate shortly.
        So copy from what we have to a similar data set
        but malloc space directly. */

    if (srcfiles_count > 0) {
        srcfiles2 = (char **) calloc(srcfiles_count, sizeof(char *));
        if (!srcfiles2) {
            _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
            return (DW_DLV_ERROR);
        }
        lres  = translate_srcfiles_to_srcfiles2(srcfiles,
            srcfiles_count,srcfiles2);
        {
            Dwarf_Signed i = 0;
            for (i = 0; i < srcfiles_count; ++i) {
                if(srcfiles[i]) {
                    dwarf_dealloc(dbg, srcfiles[i], DW_DLA_STRING);
                    srcfiles[i] = 0;
                }
            }
            dwarf_dealloc(dbg, srcfiles, DW_DLA_LIST);
            srcfiles = 0;
        }
        if (lres == DW_DLV_OK) {
            srcfiles = 0;
        } else {
            dealloc_macro_srcfiles(srcfiles2, srcfiles_count);
            _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
            return lres;
        }
    }

    /*  NO ENTRY or OK we accept, though NO ENTRY means there
        are no source files available. */
    lres = _dwarf_internal_macro_context_by_offset(dbg,
        macro_offset,version_out,macro_context_out,
        macro_ops_count_out,
        macro_ops_data_length,
        srcfiles2,srcfiles_count,
        comp_dir,
        comp_name,
        cu_context,
        error);
    /*  In case of ERROR or NO_ENTRY srcfiles2 is already freed. */
    return lres;
}

static int
_dwarf_internal_macro_context_by_offset(Dwarf_Debug dbg,
    Dwarf_Unsigned offset,
    Dwarf_Unsigned  * version_out,
    Dwarf_Macro_Context * macro_context_out,
    Dwarf_Unsigned      * macro_ops_count_out,
    Dwarf_Unsigned      * macro_ops_data_length,
    char **srcfiles,
    Dwarf_Signed srcfilescount,
    const char *comp_dir,
    const char *comp_name,
    Dwarf_CU_Context cu_context,
    Dwarf_Error * error)
{
    Dwarf_Unsigned line_table_offset = 0;
    Dwarf_Small * macro_header = 0;
    Dwarf_Small * macro_data = 0;
    Dwarf_Half version = 0;
    Dwarf_Small flags = 0;
    Dwarf_Small offset_size = 4;
    Dwarf_Unsigned cur_offset = 0;
    Dwarf_Unsigned section_size = 0;
    Dwarf_Small *section_base = 0;
    Dwarf_Small *section_end = 0;
    Dwarf_Unsigned optablesize = 0;
    Dwarf_Unsigned macro_offset = offset;
    int res = 0;
    Dwarf_Macro_Context macro_context = 0;
    Dwarf_Bool build_ops_array = FALSE;

    res = _dwarf_load_section(dbg, &dbg->de_debug_macro,error);
    if (res != DW_DLV_OK) {
        dealloc_macro_srcfiles(srcfiles,srcfilescount);
        return res;
    }
    if (!dbg->de_debug_macro.dss_size) {
        dealloc_macro_srcfiles(srcfiles,srcfilescount);
        return (DW_DLV_NO_ENTRY);
    }

    section_base = dbg->de_debug_macro.dss_data;
    section_size = dbg->de_debug_macro.dss_size;
    /*  The '3'  ensures the header initial bytes present too. */
    if ((3+macro_offset) >= section_size) {
        dealloc_macro_srcfiles(srcfiles,srcfilescount);
        _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
        return (DW_DLV_ERROR);
    }
    macro_header = macro_offset + section_base;
    macro_data = macro_header;
    section_end = section_base +section_size;


    macro_context = (Dwarf_Macro_Context)
        _dwarf_get_alloc(dbg,DW_DLA_MACRO_CONTEXT,1);
    if (!macro_context) {
        dealloc_macro_srcfiles(srcfiles,srcfilescount);
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }

    if ((section_base + sizeof(Dwarf_Half) + sizeof(Dwarf_Small)) >                     section_end ) {
        dealloc_macro_srcfiles(srcfiles,srcfilescount);
        _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
        return DW_DLV_ERROR;
    }
    /* Note here so if error return we get these freed eventually. */
    macro_context->mc_srcfiles = srcfiles;
    macro_context->mc_srcfiles_count = srcfilescount;
    macro_context->mc_cu_context =  cu_context;

    READ_UNALIGNED_CK(dbg,version, Dwarf_Half,
        macro_data,sizeof(Dwarf_Half),error,section_end);
    macro_data += sizeof(Dwarf_Half);
    READ_UNALIGNED_CK(dbg,flags, Dwarf_Small,
        macro_data,sizeof(Dwarf_Small),error,section_end);
    macro_data += sizeof(Dwarf_Small);

    macro_context->mc_at_comp_dir = comp_dir;
    macro_context->mc_at_name = comp_name;
    macro_context->mc_macro_header = macro_header;
    macro_context->mc_section_offset = macro_offset;
    macro_context->mc_version_number = version;
    macro_context->mc_flags = flags;
    macro_context->mc_dbg = dbg;
    macro_context->mc_offset_size_flag =
        flags& MACRO_OFFSET_SIZE_FLAG?TRUE:FALSE;
    macro_context->mc_debug_line_offset_flag =
        flags& MACRO_LINE_OFFSET_FLAG?TRUE:FALSE;
    macro_context->mc_operands_table_flag =
        flags& MACRO_OP_TABLE_FLAG?TRUE:FALSE;
    offset_size = macro_context->mc_offset_size_flag?8:4;
    macro_context->mc_offset_size = offset_size;
    if (macro_context->mc_debug_line_offset_flag) {
        cur_offset = (offset_size+ macro_data) - section_base;
        if (cur_offset >= section_size) {
            dwarf_dealloc_macro_context(macro_context);
            _dwarf_error(dbg, error, DW_DLE_MACRO_OFFSET_BAD);
            return (DW_DLV_ERROR);
        }
        READ_UNALIGNED_CK(dbg,line_table_offset,Dwarf_Unsigned,
            macro_data,offset_size,error,section_end);
        macro_data += offset_size;
        macro_context->mc_debug_line_offset = line_table_offset;
    }
    if (macro_context->mc_operands_table_flag) {
        res = read_operands_table(macro_context,
            macro_header,
            macro_data,
            section_base,
            section_size,
            &optablesize,
            error);
        if (res != DW_DLV_OK) {
            dwarf_dealloc_macro_context(macro_context);
            return res;
        }
    }

    macro_data += optablesize;
    macro_context->mc_macro_ops = macro_data;
    macro_context->mc_macro_header_length =macro_data - macro_header;

    build_ops_array = FALSE;
    res = _dwarf_get_macro_ops_count_internal(macro_context,
        build_ops_array,
        error);
    if (res != DW_DLV_OK) {
        dwarf_dealloc_macro_context(macro_context);
        return res;
    }
    build_ops_array = TRUE;
    res = _dwarf_get_macro_ops_count_internal(macro_context,
        build_ops_array,
        error);
    if (res != DW_DLV_OK) {
        dwarf_dealloc_macro_context(macro_context);
        return res;
    }
    *macro_ops_count_out = macro_context->mc_macro_ops_count;
    *macro_ops_data_length = macro_context->mc_ops_data_length;
    *version_out = version;
    *macro_context_out = macro_context;
    return DW_DLV_OK;
}

int dwarf_macro_context_head(Dwarf_Macro_Context head,
    Dwarf_Half     * version,
    Dwarf_Unsigned * mac_offset,
    Dwarf_Unsigned * mac_len,
    Dwarf_Unsigned * mac_header_len,
    unsigned *       flags,
    Dwarf_Bool *     has_line_offset,
    Dwarf_Unsigned * line_offset,
    Dwarf_Bool *     has_offset_size_64,
    Dwarf_Bool *     has_operands_table,
    Dwarf_Half *     opcode_count,
    Dwarf_Error *error)
{
    if (!head || head->mc_sentinel != 0xada) {
        Dwarf_Debug dbg = 0;
        if(head) {
            dbg = head->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }
    *version = head->mc_version_number;
    *mac_offset = head->mc_section_offset;
    *mac_len    = head->mc_total_length;
    *mac_header_len = head->mc_macro_header_length;
    *flags = head->mc_flags;
    *line_offset = head->mc_debug_line_offset;
    *has_line_offset = head->mc_debug_line_offset_flag;
    *has_offset_size_64 = head->mc_offset_size_flag;
    *has_operands_table = head->mc_operands_table_flag;
    *opcode_count = head->mc_opcode_count;
    return DW_DLV_OK;
}
int dwarf_macro_operands_table(Dwarf_Macro_Context head,
    Dwarf_Half  index, /* 0 to opcode_count -1 */
    Dwarf_Half  *opcode_number,
    Dwarf_Half  *operand_count,
    const Dwarf_Small **operand_array,
    Dwarf_Error *error)
{
    struct Dwarf_Macro_Forms_s * ops = 0;
    Dwarf_Debug dbg = 0;
    if (!head || head->mc_sentinel != 0xada) {
        if(head) {
            dbg = head->mc_dbg;
        }
        _dwarf_error(dbg, error,DW_DLE_BAD_MACRO_HEADER_POINTER);
        return DW_DLV_ERROR;
    }
    dbg = head->mc_dbg;
    if (index >= head->mc_opcode_count) {
        _dwarf_error(dbg, error, DW_DLE_BAD_MACRO_INDEX);
        return DW_DLV_ERROR;
    }
    ops = head->mc_opcode_forms + index;
    *opcode_number = ops->mf_code;
    *operand_count = ops->mf_formcount;
    *operand_array = ops->mf_formbytes;
    return DW_DLV_OK;
}

/*  The base interface to the .debug_macro section data
    for a specific CU.

    The version number passed back by *version_out
    may be 4 (a gnu extension of DWARF)  or 5. */
int
dwarf_get_macro_context(Dwarf_Die cu_die,
    Dwarf_Unsigned      * version_out,
    Dwarf_Macro_Context * macro_context,
    Dwarf_Unsigned      * macro_unit_offset_out,
    Dwarf_Unsigned      * macro_ops_count_out,
    Dwarf_Unsigned      * macro_ops_data_length,
    Dwarf_Error * error)
{
    int res = 0;
    Dwarf_Bool offset_specified = FALSE;
    Dwarf_Unsigned offset = 0;

    res =  _dwarf_internal_macro_context(cu_die,
        offset_specified,
        offset,
        version_out,
        macro_context,
        macro_unit_offset_out,
        macro_ops_count_out,
        macro_ops_data_length,
        error);
    return res;
}

/*  Like  dwarf_get_macro_context but
    here we use a specfied offset  instead  of
    the offset in the cu_die. */
int
dwarf_get_macro_context_by_offset(Dwarf_Die cu_die,
    Dwarf_Unsigned offset,
    Dwarf_Unsigned      * version_out,
    Dwarf_Macro_Context * macro_context,
    Dwarf_Unsigned      * macro_ops_count_out,
    Dwarf_Unsigned      * macro_ops_data_length,
    Dwarf_Error * error)
{
    int res = 0;
    Dwarf_Bool offset_specified = TRUE;
    Dwarf_Unsigned macro_unit_offset_out = 0;

    res = _dwarf_internal_macro_context(cu_die,
        offset_specified,
        offset,
        version_out,
        macro_context,
        &macro_unit_offset_out,
        macro_ops_count_out,
        macro_ops_data_length,
        error);
    return res;
}

int dwarf_get_macro_section_name(Dwarf_Debug dbg,
   const char **sec_name_out,
   UNUSEDARG Dwarf_Error *error)
{
    struct Dwarf_Section_s *sec = 0;

    sec = &dbg->de_debug_macro;
    if (sec->dss_size == 0) {
        /* We don't have such a  section at all. */
        return DW_DLV_NO_ENTRY;
    }
    *sec_name_out = sec->dss_name;
    return DW_DLV_OK;
}

void
dwarf_dealloc_macro_context(Dwarf_Macro_Context mc)
{
    Dwarf_Debug dbg = mc->mc_dbg;
    dwarf_dealloc(dbg,mc,DW_DLA_MACRO_CONTEXT);
}

int
_dwarf_macro_constructor(Dwarf_Debug dbg, void *m)
{
    /* Nothing to do, the space is zeroed out */
    Dwarf_Macro_Context mc= (Dwarf_Macro_Context)m;
    /* Arbitrary sentinel. For debugging. */
    mc->mc_sentinel = 0xada;
    mc->mc_dbg = dbg;
    return DW_DLV_OK;
}

/*  Here we free various fields of Dwarf_Macro_Context.
    The fields do not get dealloc'd.
    If we had a separate destructor for hand-calling
    (meaning when an error is detected during creation
    of a  Dwarf_Macro_Context)
    and one for calling by dwarf_dealloc() then
    we could have the hand-calling dwarf_dealloc the fields
    and the one called on the dealloc of a  Dwarf_Macro_Context
    could leave the _dwarf_get_alloc() fields for for
    normal dwarf_finish() cleanup.

    But for now we share this destructor for both purposes
    so no fields are _dwarf_get_alloc() and all are free-d
    here..
*/
void
_dwarf_macro_destructor(void *m)
{
    Dwarf_Macro_Context mc= (Dwarf_Macro_Context)m;

    dealloc_macro_srcfiles(mc->mc_srcfiles, mc->mc_srcfiles_count);
    mc->mc_srcfiles = 0;
    mc->mc_srcfiles_count = 0;
    free((void *)mc->mc_file_path);
    mc->mc_file_path = 0;
    free(mc->mc_ops);
    mc->mc_ops = 0;
    free(mc->mc_opcode_forms);
    mc->mc_opcode_forms = 0;
    memset(mc,0,sizeof(*mc));
    /* Just a recognizable sentinel. For debugging.  No real meaning. */
    mc->mc_sentinel = 0xdeadbeef;
}
