/*
  Copyright (C) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
  Portions Copyright (C) 2007-2018 David Anderson. All Rights Reserved.
  Portions Copyright (C) 2010-2012 SN Systems Ltd. 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 <string.h> /* memset() */

#if defined(_WIN32) && defined(HAVE_STDAFX_H)
#include "stdafx.h"
#endif /* HAVE_STDAFX_H */

#include "dwarf.h"
#include "libdwarf.h"
#include "libdwarf_private.h"
#include "dwarf_base_types.h"
#include "dwarf_opaque.h"
#include "dwarf_alloc.h"
#include "dwarf_error.h"
#include "dwarf_util.h"
#include "dwarf_loc.h"
#include "dwarf_string.h"

static int _dwarf_read_loc_section_dwo(Dwarf_Debug dbg,
    Dwarf_Block_c * return_block,
    Dwarf_Addr * lowpc,
    Dwarf_Addr * highpc,
    Dwarf_Bool * at_end,
    Dwarf_Half * lle_op,
    Dwarf_Off    sec_offset,
    Dwarf_Half   address_size,
    Dwarf_Half   lkind,
    Dwarf_Error *error);

/*  Used to enable sanity checking of these data
    items before we return to caller. */
int
_dwarf_locdesc_c_constructor(Dwarf_Debug dbg, void *locd)
{
    Dwarf_Locdesc_c  ldp = (Dwarf_Locdesc_c)locd;

    if (!dbg) {
        return DW_DLV_ERROR;
    }
    ldp->ld_lle_value = DW_LLE_VALUE_BOGUS;
    ldp->ld_kind = DW_LKIND_unknown;
    return DW_DLV_OK;
}

static void
_dwarf_lkind_name(unsigned lkind, dwarfstring *m)
{
    switch(lkind) {
    case DW_LKIND_expression:
        dwarfstring_append(m,"DW_LKIND_expression");
        return;
    case DW_LKIND_loclist:
        dwarfstring_append(m,"DW_LKIND_loclist");
        return;
    case DW_LKIND_GNU_exp_list:
        dwarfstring_append(m,"DW_LKIND_GNU_exp_list");
        return;
    case DW_LKIND_loclists:
        dwarfstring_append(m,"DW_LKIND_loclists");
        return;
    case DW_LKIND_unknown:
        dwarfstring_append(m,"DW_LKIND_unknown");
        return;
    default: break;
    }
    dwarfstring_append_printf_u(m,
        "<DW_LKIND location kind is unknown and has value %u>.",
        lkind);
}

static int
determine_location_lkind(unsigned int version,
    unsigned int form,
    Dwarf_Bool is_dwo)
{
    switch(form) {
    case DW_FORM_exprloc: /* only defined for
        DW_CFA_def_cfa_expression */
    case DW_FORM_block:
    case DW_FORM_block1:
    case DW_FORM_block2:
    case DW_FORM_block4:
        return DW_LKIND_expression;
        break;
    case DW_FORM_data4:
    case DW_FORM_data8:
        if (version > 1 && version < 4) {
            return DW_LKIND_loclist;
        }
        break;
    case DW_FORM_sec_offset:
        if (version == 5 ) {
            return DW_LKIND_loclists;
        }
        if (version == 4 &&  is_dwo  ) {
            return DW_LKIND_GNU_exp_list;
        }
        return DW_LKIND_loclist;
        break;
    case DW_FORM_loclistx:
        if (version == 5 ) {
            return DW_LKIND_loclists;
        }
        break;
    default:
        break;
    }
    return DW_LKIND_unknown;
}

static void
_dwarf_free_op_chain(Dwarf_Debug dbg,
    Dwarf_Loc_Chain headloc)
{
    Dwarf_Loc_Chain cur = headloc;

    while (cur) {
        Dwarf_Loc_Chain next = cur->lc_next;
        dwarf_dealloc(dbg, cur, DW_DLA_LOC_CHAIN);
        cur = next;
    }
}

/*  Using a loclist offset to get the in-memory
    address of .debug_loc data to read, returns the loclist
    'header' info in return_block.
*/

#define MAX_ADDR \
    ((address_size == 8)?0xffffffffffffffffULL:0xffffffff)

static int
_dwarf_read_loc_section(Dwarf_Debug dbg,
    Dwarf_Block_c * return_block,
    Dwarf_Addr    * lowpc,
    Dwarf_Addr    * hipc,
    Dwarf_Half    * lle_val,
    Dwarf_Off       sec_offset,
    Dwarf_Half      address_size,
    Dwarf_Error   * error)
{
    Dwarf_Small *beg = dbg->de_debug_loc.dss_data + sec_offset;
    Dwarf_Small *loc_section_end =
        dbg->de_debug_loc.dss_data + dbg->de_debug_loc.dss_size;

    /*  start_addr and end_addr are actually offsets
        of the applicable base address of the CU.
        They are address-size. */
    Dwarf_Addr start_addr = 0;
    Dwarf_Addr end_addr = 0;
    Dwarf_Half exprblock_size = 0;
    Dwarf_Unsigned exprblock_off =
        2 * address_size + DWARF_HALF_SIZE;

    if (sec_offset >= dbg->de_debug_loc.dss_size) {
        /* We're at the end. No more present. */
        return DW_DLV_NO_ENTRY;
    }

    /* If it goes past end, error */
    if (exprblock_off > dbg->de_debug_loc.dss_size) {
        _dwarf_error(dbg, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
        return DW_DLV_ERROR;
    }

    READ_UNALIGNED_CK(dbg, start_addr, Dwarf_Addr, beg, address_size,
        error,loc_section_end);
    READ_UNALIGNED_CK(dbg, end_addr, Dwarf_Addr,
        beg + address_size, address_size,
        error,loc_section_end);
    if (start_addr == 0 && end_addr == 0) {
        /*  If start_addr and end_addr are 0, it's the end and no
            exprblock_size field follows. */
        exprblock_size = 0;
        exprblock_off -= DWARF_HALF_SIZE;
        *lle_val = DW_LLE_end_of_list;
    } else if (start_addr == MAX_ADDR) {
        /*  End address is a base address,
            no exprblock_size field here either */
        exprblock_size = 0;
        exprblock_off -=  DWARF_HALF_SIZE;
        *lle_val = DW_LLE_base_address;
    } else {
        /*  Here we note the address and length of the
            expression operators, DW_OP_reg0 etc */
        READ_UNALIGNED_CK(dbg, exprblock_size, Dwarf_Half,
            beg + 2 * address_size, DWARF_HALF_SIZE,
            error,loc_section_end);
        /* exprblock_size can be zero, means no expression */
        if ( exprblock_size >= dbg->de_debug_loc.dss_size) {
            _dwarf_error(dbg, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
            return DW_DLV_ERROR;
        }
        if ((sec_offset +exprblock_off + exprblock_size) >
            dbg->de_debug_loc.dss_size) {
            _dwarf_error(dbg, error, DW_DLE_DEBUG_LOC_SECTION_SHORT);
            return DW_DLV_ERROR;
        }
        *lle_val = DW_LLE_start_end;
    }
    *lowpc = start_addr;
    *hipc = end_addr;

    return_block->bl_len = exprblock_size;
    return_block->bl_kind = DW_LKIND_loclist;
    return_block->bl_data = beg + exprblock_off;
    return_block->bl_section_offset =
        ((Dwarf_Small *) return_block->bl_data) -
        dbg->de_debug_loc.dss_data;
    return DW_DLV_OK;
}

static int
_dwarf_get_loclist_lle_count_dwo(Dwarf_Debug dbg,
    Dwarf_Off loclist_offset,
    Dwarf_Half address_size,
    Dwarf_Half lkind,
    int *loclist_count,
    Dwarf_Error * error)
{
    int count = 0;
    Dwarf_Off offset = loclist_offset;

    for (;;) {
        Dwarf_Block_c b;
        Dwarf_Bool at_end = FALSE;
        Dwarf_Addr lowpc = 0;
        Dwarf_Addr highpc = 0;
        Dwarf_Half lle_op = 0;
        int res = _dwarf_read_loc_section_dwo(dbg, &b,
            &lowpc,
            &highpc,
            &at_end,
            &lle_op,
            offset,
            address_size,
            lkind,
            error);
        if (res != DW_DLV_OK) {
            return res;
        }
        if (at_end) {
            count++;
            break;
        }
        offset = b.bl_len + b.bl_section_offset;
        count++;
    }
    *loclist_count = count;
    return DW_DLV_OK;
}

static int
_dwarf_get_loclist_lle_count(Dwarf_Debug dbg,
    Dwarf_Off loclist_offset,
    Dwarf_Half address_size,
    int *loclist_count,
    Dwarf_Error * error)
{
    int count = 0;
    Dwarf_Off offset = loclist_offset;

    for (;;) {
        Dwarf_Block_c b;
        Dwarf_Addr lowpc = 0;
        Dwarf_Addr highpc = 0;
        Dwarf_Half lle_val = DW_LLE_VALUE_BOGUS;

        int res = _dwarf_read_loc_section(dbg, &b,
            &lowpc, &highpc,
            &lle_val,
            offset, address_size,error);
        if (res != DW_DLV_OK) {
            return res;
        }
        offset = b.bl_len + b.bl_section_offset;
        if (lowpc == 0 && highpc == 0) {
            break;
        }
        count++;
    }
    *loclist_count = count;
    return DW_DLV_OK;
}

/* Helper routine to avoid code duplication.
*/
static int
_dwarf_setup_loc(Dwarf_Attribute attr,
    Dwarf_Debug *     dbg_ret,
    Dwarf_CU_Context *cucontext_ret,
    Dwarf_Half       *form_ret,
    Dwarf_Error      *error)
{
    Dwarf_Debug dbg = 0;
    Dwarf_Half form = 0;
    int blkres = DW_DLV_ERROR;

    /*  Creating an error with NULL dbg is not a good thing.
        These won't be freed if we later call dealloc
        with a non-NULL dbg.
    */
    if (!attr) {
        _dwarf_error_string(NULL, error, DW_DLE_ATTR_NULL,
            "DW_DLE_ATTR_NULL: the attribute passed to "
            "dwarf_get_loclist_c() is a NULL pointer");
        return DW_DLV_ERROR;
    }
    if (attr->ar_cu_context == NULL) {
        _dwarf_error(NULL, error, DW_DLE_ATTR_NO_CU_CONTEXT);
        return DW_DLV_ERROR;
    }
    *cucontext_ret = attr->ar_cu_context;

    dbg = attr->ar_cu_context->cc_dbg;
    if (!dbg || dbg->de_magic != DBG_IS_VALID) {
        _dwarf_error_string(NULL, error, DW_DLE_ATTR_DBG_NULL,
            "DW_DLE_ATTR_DBG_NULL The Attribute passed to "
            "dwarf_get_loclist_c() "
            "points to an invalid Dwarf_Debug");
        return DW_DLV_ERROR;
    }
    *dbg_ret = dbg;
    blkres = dwarf_whatform(attr, &form, error);
    if (blkres != DW_DLV_OK) {
        return blkres;
    }
    *form_ret = form;
    return DW_DLV_OK;
}

/* Helper routine  to avoid code duplication.
*/
static int
_dwarf_get_loclist_header_start(Dwarf_Debug dbg,
    Dwarf_Attribute attr,
    Dwarf_Unsigned * loclist_offset_out,
    Dwarf_Error * error)
{
    Dwarf_Unsigned loc_sec_size = 0;
    Dwarf_Unsigned loclist_offset = 0;

    int blkres = dwarf_global_formref(attr, &loclist_offset, error);
    if (blkres != DW_DLV_OK) {
        return blkres;
    }
    if (!dbg->de_debug_loc.dss_data) {
        int secload = _dwarf_load_section(dbg,
            &dbg->de_debug_loc,error);
        if (secload != DW_DLV_OK) {
            return secload;
        }
        if (!dbg->de_debug_loc.dss_size) {
            return DW_DLV_NO_ENTRY;
        }
    }
    loc_sec_size = dbg->de_debug_loc.dss_size;
    if (loclist_offset >= loc_sec_size) {
        _dwarf_error(dbg, error, DW_DLE_LOCLIST_OFFSET_BAD);
        return DW_DLV_ERROR;
    }

    {
        int fisres = 0;
        Dwarf_Unsigned fissoff = 0;
        Dwarf_Unsigned size = 0;
        fisres = _dwarf_get_fission_addition_die(attr->ar_die,
            DW_SECT_LOCLISTS,
            &fissoff, &size,error);
        if (fisres != DW_DLV_OK) {
            return fisres;
        }
        if (fissoff >= loc_sec_size) {
            _dwarf_error(dbg, error, DW_DLE_LOCLIST_OFFSET_BAD);
            return DW_DLV_ERROR;
        }
        loclist_offset += fissoff;
        if (loclist_offset >= loc_sec_size) {
            _dwarf_error(dbg, error, DW_DLE_LOCLIST_OFFSET_BAD);
            return DW_DLV_ERROR;
        }
    }
    *loclist_offset_out = loclist_offset;
    return DW_DLV_OK;
}

static int
context_is_cu_not_tu(Dwarf_CU_Context context,
    Dwarf_Bool *r)
{
    int ut = context->cc_unit_type;

    if (ut == DW_UT_type || ut == DW_UT_split_type ) {
        *r =FALSE;
        return DW_DLV_OK;
    }
    *r = TRUE;
    return DW_DLV_OK;
}

/*  Handles only a location expression.
    It returns the location expression as a loclist with
    a single entry.

    Usable to access dwarf expressions from any source, but
    specifically from
        DW_CFA_def_cfa_expression
        DW_CFA_expression
        DW_CFA_val_expression
    expression_in must point to a valid dwarf expression
*/

/* ============== the October 2015 interfaces. */
int
_dwarf_loc_block_sanity_check(Dwarf_Debug dbg,
    Dwarf_Block_c *loc_block,Dwarf_Error* error)
{
    unsigned lkind = loc_block->bl_kind;
    if (lkind == DW_LKIND_loclist) {
        Dwarf_Small *loc_ptr = 0;
        Dwarf_Unsigned loc_len = 0;
        Dwarf_Small *end_ptr = 0;

        loc_ptr = loc_block->bl_data;
        loc_len = loc_block->bl_len;
        end_ptr =  dbg->de_debug_loc.dss_size +
            dbg->de_debug_loc.dss_data;
        if ((loc_ptr +loc_len) > end_ptr) {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append(&m,
                "DW_DLE_DEBUG_LOC_SECTION_SHORT kind: ");
            _dwarf_lkind_name(lkind, &m);
            _dwarf_error_string(dbg,error,
                DW_DLE_DEBUG_LOC_SECTION_SHORT,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
        }
        return DW_DLV_OK;
    }
    if (lkind == DW_LKIND_loclists) {
        Dwarf_Small *loc_ptr = 0;
        Dwarf_Unsigned loc_len = 0;
        Dwarf_Small *end_ptr = 0;

        loc_ptr = loc_block->bl_data;
        loc_len = loc_block->bl_len;
        end_ptr =  dbg->de_debug_loclists.dss_size +
            dbg->de_debug_loclists.dss_data;
        if ((loc_ptr +loc_len) > end_ptr) {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append(&m,
                "DW_DLE_DEBUG_LOC_SECTION_SHORT "
                "(the .debug_loclists section is short), kind: ");
            _dwarf_lkind_name(lkind, &m);
            _dwarf_error_string(dbg,error,
                DW_DLE_DEBUG_LOC_SECTION_SHORT,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
        }
    }
    return DW_DLV_OK;
}

/*  ld_kind was checked before calling this, so we
    know its value is an intended value.  */
static const char *kindset[] = {
"DW_LKIND_expression",
"DW_LKIND_loclist",
"DW_LKIND_GNU_exp_list",
"DW_LKIND_unknown3",
"DW_LKIND_unknown4",
"DW_LKIND_loclists"
};
static const char *
get_loc_kind_str(Dwarf_Small lkind)
{
    if (lkind <= DW_LKIND_loclists) {
        return kindset[lkind];
    }
    if (lkind == DW_LKIND_unknown) {
        return "DW_LKIND_unknown";
    }
    return "UNKNOWN DW_LKIND!";
}
static int
validate_lle_value(Dwarf_Debug dbg,
    Dwarf_Locdesc_c locdesc,
    Dwarf_Error *error)
{
    dwarfstring m;

    if (locdesc->ld_kind != DW_LKIND_GNU_exp_list) {
        switch(locdesc->ld_lle_value) {
        case DW_LLE_end_of_list:
        case DW_LLE_base_addressx:
        case DW_LLE_startx_endx:
        case DW_LLE_startx_length:
        case DW_LLE_offset_pair:
        case DW_LLE_default_location:
        case DW_LLE_base_address:
        case DW_LLE_start_end:
        case DW_LLE_start_length:
            return DW_DLV_OK;
        default: break;
        }
        dwarfstring_constructor(&m);

        dwarfstring_append_printf_s(&m,
            "DW_DLE_LOCATION_ERROR: For location kind %s (",
            (char *)get_loc_kind_str(locdesc->ld_kind));
        dwarfstring_append_printf_u(&m,"%u) the DW_LLE value is "
            "not properly set",
            locdesc->ld_kind);
        dwarfstring_append_printf_u(&m," but is %u "
            " which is a libdwarf bug",
            locdesc->ld_lle_value);
        _dwarf_error_string(dbg,error,DW_DLE_LOCATION_ERROR,
            dwarfstring_string(&m));
        dwarfstring_destructor(&m);
        return DW_DLV_ERROR;
    }
    switch(locdesc->ld_lle_value) {
    case DW_LLEX_end_of_list_entry:
    case DW_LLEX_base_address_selection_entry:
    case DW_LLEX_start_end_entry:
    case DW_LLEX_start_length_entry:
    case DW_LLEX_offset_pair_entry:
        return DW_DLV_OK;
    default: break; /* ERROR */
    }
    {
        dwarfstring_constructor(&m);
        dwarfstring_append_printf_s(&m,
            "DW_DLE_LOCATION_ERROR: For location kind %s (",
            (char *)get_loc_kind_str(locdesc->ld_kind));
        dwarfstring_append_printf_u(&m,"%u) the DW_LLEX value is "
            "not properly set",
            locdesc->ld_kind);
        dwarfstring_append_printf_u(&m," but is %u "
            " which is a libdwarf bug",
            locdesc->ld_lle_value);
        _dwarf_error_string(dbg,error,DW_DLE_LOCATION_ERROR,
            dwarfstring_string(&m));
        dwarfstring_destructor(&m);
    }
    return DW_DLV_ERROR;
}
/*  Sets locdesc operator list information in locdesc.
    Sets the locdesc values (rawlow, rawhigh etc).
    This synthesizes the ld_lle_value of the locdesc
    if it's not already provided.
    Not passing in locdesc pointer, the locdesc_index suffices
    to index to the relevant locdesc pointer.
    See also dwarf_loclists.c: build_array_of_lle*/
int
_dwarf_fill_in_locdesc_op_c(Dwarf_Debug dbg,
    Dwarf_Unsigned locdesc_index,
    Dwarf_Loc_Head_c loc_head,
    Dwarf_Block_c * loc_block,
    Dwarf_Half address_size,
    Dwarf_Half offset_size,
    Dwarf_Small version_stamp,
    Dwarf_Addr lowpc,
    Dwarf_Addr highpc,
    Dwarf_Half lle_op,
    Dwarf_Error * error)
{
    /* Offset of current operator from start of block. */
    Dwarf_Unsigned offset = 0;

    /*  Chain the  DW_OPerator structs. */
    Dwarf_Loc_Chain new_loc = NULL;
    Dwarf_Loc_Chain prev_loc = NULL;
    Dwarf_Loc_Chain head_loc = NULL;
    Dwarf_Loc_Chain *plast = &head_loc;

    Dwarf_Unsigned  op_count = 0;

    /*  Contiguous block of Dwarf_Loc_Expr_Op_s
        for Dwarf_Locdesc. */
    Dwarf_Loc_Expr_Op block_loc = 0;

    Dwarf_Locdesc_c locdesc = loc_head->ll_locdesc + locdesc_index;
    Dwarf_Unsigned  i = 0;
    int             res = 0;
    Dwarf_Small    *section_start = 0;
    Dwarf_Unsigned  section_size = 0;
    Dwarf_Small    *section_end = 0;
    const char     *section_name = 0;
    Dwarf_Small    *blockdataptr = 0;
    unsigned lkind = loc_head->ll_kind;

    /* ***** BEGIN CODE ***** */
    blockdataptr = loc_block->bl_data;
    if (!blockdataptr || !loc_block->bl_len) {
        /*  an empty block has no operations so
            no section or tests need be done.. */
    } else {
        res = _dwarf_what_section_are_we(dbg,
            blockdataptr,&section_name,&section_start,
            &section_size,&section_end);
        if (res != DW_DLV_OK) {
            _dwarf_error(dbg, error,DW_DLE_POINTER_SECTION_UNKNOWN);
            return DW_DLV_ERROR;
        }
        res = _dwarf_loc_block_sanity_check(dbg,loc_block,error);
        if (res != DW_DLV_OK) {
            return res;
        }
    }
    /* New loop getting Loc operators. Non DWO */
    while (offset <= loc_block->bl_len) {
        Dwarf_Unsigned nextoffset = 0;
        struct Dwarf_Loc_Expr_Op_s temp_loc;

        /*  This call is ok even if bl_data NULL and bl_len 0 */
        res = _dwarf_read_loc_expr_op(dbg,loc_block,
            op_count,
            version_stamp,
            offset_size,
            address_size,
            offset,
            section_end,
            &nextoffset,
            &temp_loc,
            error);
        if (res == DW_DLV_ERROR) {
            _dwarf_free_op_chain(dbg,head_loc);
            return res;
        }
        if (res == DW_DLV_NO_ENTRY) {
            /*  Normal end.
                Also the end for an empty loc_block.  */
            break;
        }
        op_count++;
        new_loc = (Dwarf_Loc_Chain) _dwarf_get_alloc(dbg,
            DW_DLA_LOC_CHAIN, 1);
        if (new_loc == NULL) {
            _dwarf_free_op_chain(dbg,head_loc);
            _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
            return DW_DLV_ERROR;
        }

        /* Copying all the fields. DWARF 2,3,4,5. */
        new_loc->lc_atom    = temp_loc.lr_atom;
        new_loc->lc_opnumber= temp_loc.lr_opnumber;
        new_loc->lc_number  = temp_loc.lr_number;
        new_loc->lc_number2 = temp_loc.lr_number2;
        new_loc->lc_number3 = temp_loc.lr_number3;
        new_loc->lc_offset  = temp_loc.lr_offset;
        *plast = new_loc;
        plast= &(new_loc->lc_next);
        offset = nextoffset;
    }
    block_loc =
        (Dwarf_Loc_Expr_Op ) _dwarf_get_alloc(dbg,
        DW_DLA_LOC_BLOCK_C, op_count);
    new_loc = head_loc;
    if (!block_loc) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        for (i = 0; i < op_count; i++) {
            prev_loc = new_loc;
            new_loc = prev_loc->lc_next;
            dwarf_dealloc(dbg, prev_loc, DW_DLA_LOC_CHAIN);
        }
        return DW_DLV_ERROR;
    }

    /* op_count could be zero. */
    new_loc = head_loc;
    for (i = 0; i < op_count; i++) {
        /* Copying only the fields needed by DWARF 2,3,4 */
        (block_loc + i)->lr_atom = new_loc->lc_atom;
        (block_loc + i)->lr_number = new_loc->lc_number;
        (block_loc + i)->lr_number2 = new_loc->lc_number2;
        (block_loc + i)->lr_number3 = new_loc->lc_number3;
        (block_loc + i)->lr_offset = new_loc->lc_offset;
        (block_loc + i)->lr_opnumber = new_loc->lc_opnumber;
        prev_loc = new_loc;
        new_loc = prev_loc->lc_next;
        dwarf_dealloc(dbg, prev_loc, DW_DLA_LOC_CHAIN);
    }
    /*  Synthesizing the DW_LLE values for the old loclist
        versions. */
    switch(loc_head->ll_kind) {
    case DW_LKIND_loclist: {
        if (highpc == 0 && lowpc == 0) {
            locdesc->ld_lle_value =  DW_LLE_end_of_list;
        } else if (lowpc == MAX_ADDR) {
            locdesc->ld_lle_value = DW_LLE_base_address;
        } else {
            locdesc->ld_lle_value = DW_LLE_offset_pair;
        }
        }
        break;
    case DW_LKIND_GNU_exp_list:
        /* DW_LKIND_GNU_exp_list */
        locdesc->ld_lle_value = lle_op;
        break;
    case DW_LKIND_expression:
        /*  This is a kind of fake, but better than 0 */
        locdesc->ld_lle_value =  DW_LLE_start_end;
        break;
    case DW_LKIND_loclists:
        /* ld_lle_value already set */
        break;
    default:  {
        dwarfstring m;

        dwarfstring_constructor(&m);
        dwarfstring_append_printf_u(&m,
            "DW_DLE_LOCATION_ERROR: An impossible DW_LKIND"
            " value of %u encountered, likely internal "
            "libdwarf error or data corruption",
            (unsigned)loc_head->ll_kind);
        _dwarf_error_string(dbg,error,
            DW_DLE_LOCATION_ERROR,
            dwarfstring_string(&m));
        dwarfstring_destructor(&m);
        dwarf_dealloc(dbg,block_loc,DW_DLA_LOC_BLOCK_C);
        return DW_DLV_ERROR;
        }
    }
    locdesc->ld_cents = op_count;
    locdesc->ld_s = block_loc;

    locdesc->ld_kind = lkind;
    locdesc->ld_section_offset = loc_block->bl_section_offset;
    locdesc->ld_locdesc_offset = loc_block->bl_locdesc_offset;
    locdesc->ld_rawlow = lowpc;
    locdesc->ld_rawhigh = highpc;

    res = validate_lle_value(dbg,locdesc,error);
    if (res != DW_DLV_OK) {
        dwarf_dealloc(dbg,block_loc,DW_DLA_LOC_BLOCK_C);
        locdesc->ld_s = 0;
        return res;
    }
    /*  Leaving the cooked values zero. Filled in later. */
    /*  We have not yet looked for debug_addr, so we'll
        set it as not-missing. */
    locdesc->ld_index_failed = FALSE;
    return DW_DLV_OK;
}

/* Non-standard DWARF4 dwo loclist */
static int
_dwarf_read_loc_section_dwo(Dwarf_Debug dbg,
    Dwarf_Block_c * return_block,
    Dwarf_Addr * lowpc,
    Dwarf_Addr * highpc,
    Dwarf_Bool *at_end,
    Dwarf_Half * lle_op,
    Dwarf_Off sec_offset,
    Dwarf_Half address_size,
    Dwarf_Half lkind,
    Dwarf_Error * error)
{
    Dwarf_Small *beg = dbg->de_debug_loc.dss_data + sec_offset;
    Dwarf_Small *locptr = 0;
    Dwarf_Small llecode = 0;
    Dwarf_Unsigned expr_offset  = sec_offset;
    Dwarf_Byte_Ptr section_end = dbg->de_debug_loc.dss_data
        + dbg->de_debug_loc.dss_size;

    if (sec_offset >= dbg->de_debug_loc.dss_size) {
        /* We're at the end. No more present. */
        return DW_DLV_NO_ENTRY;
    }
    memset(return_block,0,sizeof(*return_block));

    /* not the same as non-split loclist, but still a list. */
    return_block->bl_kind = lkind;

    /* This is non-standard  GNU Dwarf4 loclist */
    return_block->bl_locdesc_offset = sec_offset;
    llecode = *beg;
    locptr = beg +1;
    expr_offset++;
    switch(llecode) {
    case DW_LLEX_end_of_list_entry:
        *at_end = TRUE;
        return_block->bl_section_offset = expr_offset;
        expr_offset++;
        break;
    case DW_LLEX_base_address_selection_entry: {
        Dwarf_Unsigned addr_index = 0;

        DECODE_LEB128_UWORD_CK(locptr,addr_index,
            dbg,error,section_end);
        return_block->bl_section_offset = expr_offset;
        /* So this behaves much like non-dwo loclist */
        *lowpc=MAX_ADDR;
        *highpc=addr_index;
        }
        break;
    case DW_LLEX_start_end_entry: {
        Dwarf_Unsigned addr_indexs = 0;
        Dwarf_Unsigned addr_indexe= 0;
        Dwarf_Unsigned exprlen = 0;
        Dwarf_Unsigned leb128_length = 0;

        DECODE_LEB128_UWORD_LEN_CK(locptr,addr_indexs,
            leb128_length,
            dbg,error,section_end);
        expr_offset += leb128_length;

        DECODE_LEB128_UWORD_LEN_CK(locptr,addr_indexe,
            leb128_length,
            dbg,error,section_end);
        expr_offset +=leb128_length;

        *lowpc=addr_indexs;
        *highpc=addr_indexe;

        READ_UNALIGNED_CK(dbg, exprlen, Dwarf_Unsigned, locptr,
            DWARF_HALF_SIZE,
            error,section_end);
        locptr += DWARF_HALF_SIZE;
        expr_offset += DWARF_HALF_SIZE;

        return_block->bl_len = exprlen;
        return_block->bl_data = locptr;
        return_block->bl_section_offset = expr_offset;

        expr_offset += exprlen;
        if (expr_offset > dbg->de_debug_loc.dss_size) {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append(&m,
                "DW_DLE_DEBUG_LOC_SECTION_SHORT:");
            dwarfstring_append_printf_u(&m,
                " in DW_LLEX_start_end_entry "
                "The expression offset is 0x%x",
                expr_offset);
            dwarfstring_append_printf_u(&m,
                " which is greater than the section size"
                " of 0x%x. Corrupt Dwarf.",
                dbg->de_debug_loc.dss_size);
            _dwarf_error_string(dbg,error,
                DW_DLE_DEBUG_LOC_SECTION_SHORT,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
        }
        }
        break;
    case DW_LLEX_start_length_entry: {
        Dwarf_Unsigned addr_index = 0;
        Dwarf_Unsigned  range_length = 0;
        Dwarf_Unsigned exprlen = 0;
        Dwarf_Unsigned leb128_length = 0;

        DECODE_LEB128_UWORD_LEN_CK(locptr,addr_index,
            leb128_length,
            dbg,error,section_end);
        expr_offset +=leb128_length;

        READ_UNALIGNED_CK(dbg, range_length, Dwarf_Unsigned, locptr,
            DWARF_32BIT_SIZE,
            error,section_end);
        locptr += DWARF_32BIT_SIZE;
        expr_offset += DWARF_32BIT_SIZE;

        READ_UNALIGNED_CK(dbg, exprlen, Dwarf_Unsigned, locptr,
            DWARF_HALF_SIZE,
            error,section_end);
        locptr += DWARF_HALF_SIZE;
        expr_offset += DWARF_HALF_SIZE;

        *lowpc = addr_index;
        *highpc = range_length;
        return_block->bl_len = exprlen;
        return_block->bl_data = locptr;
        return_block->bl_section_offset = expr_offset;
        /* exprblock_size can be zero, means no expression */

        expr_offset += exprlen;
        if (expr_offset > dbg->de_debug_loc.dss_size) {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append(&m,
                "DW_DLE_DEBUG_LOC_SECTION_SHORT:");
            dwarfstring_append_printf_u(&m,
                " in DW_LLEX_start_length_entry "
                "The expression offset is 0x%x",
                expr_offset);
            dwarfstring_append_printf_u(&m,
                " which is greater than the section size"
                " of 0x%x. Corrupt Dwarf.",
                dbg->de_debug_loc.dss_size);
            _dwarf_error_string(dbg,error,
                DW_DLE_DEBUG_LOC_SECTION_SHORT,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
        }
        }
        break;
    case DW_LLEX_offset_pair_entry: {
        Dwarf_Unsigned  startoffset = 0;
        Dwarf_Unsigned  endoffset = 0;
        Dwarf_Unsigned exprlen = 0;

        READ_UNALIGNED_CK(dbg, startoffset,
            Dwarf_Unsigned, locptr,
            DWARF_32BIT_SIZE,
            error,section_end);
        locptr += DWARF_32BIT_SIZE;
        expr_offset += DWARF_32BIT_SIZE;

        READ_UNALIGNED_CK(dbg, endoffset,
            Dwarf_Unsigned, locptr,
            DWARF_32BIT_SIZE,
            error,section_end);
        locptr += DWARF_32BIT_SIZE;
        expr_offset +=  DWARF_32BIT_SIZE;
        *lowpc= startoffset;
        *highpc = endoffset;

        READ_UNALIGNED_CK(dbg, exprlen, Dwarf_Unsigned, locptr,
            DWARF_HALF_SIZE,
            error,section_end);
        locptr += DWARF_HALF_SIZE;
        expr_offset += DWARF_HALF_SIZE;

        return_block->bl_len = exprlen;
        return_block->bl_data = locptr;
        return_block->bl_section_offset = expr_offset;

        expr_offset += exprlen;
        if (expr_offset > dbg->de_debug_loc.dss_size) {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append(&m,
                "DW_DLE_DEBUG_LOC_SECTION_SHORT:");
            dwarfstring_append_printf_u(&m,
                " in DW_LLEX_offset_pair_entry "
                "The expression offset is 0x%x",
                expr_offset);
            dwarfstring_append_printf_u(&m,
                " which is greater than the section size"
                " of 0x%x. Corrupt Dwarf.",
                dbg->de_debug_loc.dss_size);
            _dwarf_error_string(dbg,error,
                DW_DLE_DEBUG_LOC_SECTION_SHORT,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
        }
        }
        break;
    default: {
        dwarfstring m;

        dwarfstring_constructor(&m);
        dwarfstring_append(&m,
            "DW_DLE_LLE_CODE_UNKNOWN:");
        dwarfstring_append_printf_u(&m,
            " in DW_LLEX_ code value "
            " is 0x%x ,not an expected value.",
            llecode);
        _dwarf_error_string(dbg,error,
            DW_DLE_LLE_CODE_UNKNOWN,
            dwarfstring_string(&m));
        dwarfstring_destructor(&m);
        return DW_DLV_ERROR;
    }
    }
    *lle_op = llecode;
    return DW_DLV_OK;
}

int
dwarf_get_loclist_head_kind(Dwarf_Loc_Head_c ll_header,
    unsigned int * kind,
    Dwarf_Error  * error)
{
    if (!ll_header) {
        _dwarf_error_string(NULL, error,DW_DLE_DBG_NULL,
            "DW_DLE_DBG_NULL: "
            "NULL Dwarf_Loc_Head_c "
            "argument passed to "
            "dwarf_get_loclist_head_kind()");
        return DW_DLV_ERROR;
    }
    *kind = ll_header->ll_kind;
    return DW_DLV_OK;
}

static int
_dwarf_original_loclist_build(Dwarf_Debug dbg,
    Dwarf_Loc_Head_c llhead,
    Dwarf_Attribute  attr,
    Dwarf_Error     *error)
{
    Dwarf_Unsigned loclist_offset = 0;
    Dwarf_Unsigned starting_loclist_offset = 0;
    int            off_res  = DW_DLV_ERROR;
    int            count_res = DW_DLV_ERROR;
    int            loclist_count = 0;
    Dwarf_Unsigned lli = 0;
    unsigned       lkind = llhead->ll_kind;
    unsigned       address_size = llhead->ll_address_size;
    Dwarf_Unsigned listlen = 0;
    Dwarf_Locdesc_c llbuf = 0;
    Dwarf_CU_Context cucontext;

    off_res = _dwarf_get_loclist_header_start(dbg,
        attr, &loclist_offset, error);
    if (off_res != DW_DLV_OK) {
        return off_res;
    }
    starting_loclist_offset = loclist_offset;

    if (lkind == DW_LKIND_GNU_exp_list) {
        count_res = _dwarf_get_loclist_lle_count_dwo(dbg,
            loclist_offset,
            address_size,
            lkind,
            &loclist_count,
            error);
    } else {
        count_res = _dwarf_get_loclist_lle_count(dbg,
            loclist_offset, address_size,
            &loclist_count,
            error);
    }
    if (count_res != DW_DLV_OK) {
        return count_res;
    }
    if (loclist_count == 0) {
        return DW_DLV_NO_ENTRY;
    }

    listlen = loclist_count;
    llbuf = (Dwarf_Locdesc_c)
        _dwarf_get_alloc(dbg, DW_DLA_LOCDESC_C, listlen);
    if (!llbuf) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }
    llbuf->ld_magic = LOCLISTS_MAGIC;
    llhead->ll_locdesc = llbuf;
    llhead->ll_locdesc_count = listlen;
    cucontext = llhead->ll_context;
    llhead->ll_llearea_offset = loclist_offset;
    /* Now get loc ops */
    for (lli = 0; lli < listlen; ++lli) {
        int lres = 0;
        Dwarf_Half lle_op = 0;
        Dwarf_Bool at_end = 0;
        Dwarf_Block_c loc_block;
        Dwarf_Unsigned rawlowpc = 0;
        Dwarf_Unsigned rawhighpc = 0;
        int blkres = 0;

        memset(&loc_block,0,sizeof(loc_block));
        if (lkind == DW_LKIND_GNU_exp_list) {
            blkres = _dwarf_read_loc_section_dwo(dbg,
                &loc_block,
                &rawlowpc, &rawhighpc,
                &at_end, &lle_op,
                loclist_offset,
                address_size,
                lkind,
                error);
        } else {
            blkres = _dwarf_read_loc_section(dbg,
                &loc_block,
                &rawlowpc, &rawhighpc,
                &lle_op,
                loclist_offset,
                address_size,
                error);
        }
        if (blkres != DW_DLV_OK) {
            return blkres;
        }
        /* Fills in the locdesc and its operators list at index lli */
        lres = _dwarf_fill_in_locdesc_op_c(dbg,
            lli,
            llhead,
            &loc_block,
            address_size,
            cucontext->cc_length_size,
            cucontext->cc_version_stamp,
            rawlowpc,
            rawhighpc,
            lle_op,
            error);
        if (lres != DW_DLV_OK) {
            return lres;
        }
        /* Now get to next loclist entry offset. */
        loclist_offset = loc_block.bl_section_offset +
            loc_block.bl_len;
    }
    /*  We need to calculate the cooked values for
        each locldesc entry, that will be done
        in dwarf_get_loclist_c(). */

    llhead->ll_bytes_total = loclist_offset -
        starting_loclist_offset;
    return DW_DLV_OK;
}

static int
_dwarf_original_expression_build(Dwarf_Debug dbg,
    Dwarf_Loc_Head_c llhead,
    Dwarf_Attribute attr,
    Dwarf_Error *error)
{

    Dwarf_Block_c loc_blockc;
    Dwarf_Unsigned rawlowpc = 0;
    Dwarf_Unsigned rawhighpc = 0;
    unsigned form = llhead->ll_attrform;
    int blkres = 0;
    Dwarf_Locdesc_c llbuf = 0;
    unsigned listlen = 1;
    Dwarf_CU_Context cucontext = llhead->ll_context;
    unsigned address_size = llhead->ll_address_size;

    memset(&loc_blockc,0,sizeof(loc_blockc));
    if (form == DW_FORM_exprloc) {
        /*  A bit ugly. dwarf_formexprloc should use a
            Dwarf_Block argument. */
        blkres = dwarf_formexprloc(attr,&loc_blockc.bl_len,
            (Dwarf_Ptr)&loc_blockc.bl_data,error);
        if (blkres != DW_DLV_OK) {
            return blkres;
        }
        loc_blockc.bl_kind = llhead->ll_kind;
        loc_blockc.bl_section_offset  =
            (char *)loc_blockc.bl_data -
            (char *)dbg->de_debug_info.dss_data;
        loc_blockc.bl_locdesc_offset = 0; /* not relevant */
    } else {
        Dwarf_Block loc_block;

        memset(&loc_block,0,sizeof(loc_block));
        blkres = _dwarf_formblock_internal(dbg,attr,
            llhead->ll_context,
            &loc_block,
            error);
        if (blkres != DW_DLV_OK) {
            return blkres;
        }
        loc_blockc.bl_len = loc_block.bl_len;
        loc_blockc.bl_data = loc_block.bl_data;
        loc_blockc.bl_kind = llhead->ll_kind;
        loc_blockc.bl_section_offset =
            loc_block.bl_section_offset;
        loc_blockc.bl_locdesc_offset = 0; /* not relevant */
    }
    /*  We will mark the Locdesc_c DW_LLE_start_end
        shortly. Here we fake the address range
        as 'all addresses'. */
    rawlowpc = 0;
    rawhighpc = MAX_ADDR;

    llbuf = (Dwarf_Locdesc_c)
        _dwarf_get_alloc(dbg, DW_DLA_LOCDESC_C, listlen);
    if (!llbuf) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }
    llbuf->ld_magic = LOCLISTS_MAGIC;
    llhead->ll_locdesc = llbuf;
    /* Count is one by definition of a location entry. */
    llhead->ll_locdesc_count = listlen;
    llbuf = 0;

    /*  An empty location description (block length 0)
        means the code generator emitted no variable,
        the variable was not generated, it was unused
        or perhaps never tested after being set. Dwarf2,
        section 2.4.1 In other words, it is not an error,
        and we don't test for block length 0 specially here. */

    /*  Fills in the locdesc and its operators list
        at index 0 */
    blkres = _dwarf_fill_in_locdesc_op_c(dbg,
        0, /* fake locdesc is index 0 */
        llhead,
        &loc_blockc,
        llhead->ll_address_size,
        cucontext->cc_length_size,
        cucontext->cc_version_stamp,
        rawlowpc, rawhighpc,
        0,
        error);
    llhead->ll_bytes_total += loc_blockc.bl_len;
    if (blkres != DW_DLV_OK) {
        /* low level error already set: let problem be passed back */
        dwarf_dealloc(dbg,llhead->ll_locdesc,DW_DLA_LOCDESC_C);
        llhead->ll_locdesc = 0;
        llhead->ll_locdesc_count = 0;
        return blkres;
    }
    return DW_DLV_OK;
}

/*  Following the original loclist definition the low
    value is all one bits, the high value is the base
    address. */
static int
cook_original_loclist_contents(Dwarf_Debug dbg,
    Dwarf_Loc_Head_c llhead,
    Dwarf_Error *error)
{
    Dwarf_Unsigned baseaddress = llhead->ll_cu_base_address;
    Dwarf_Unsigned count = llhead->ll_locdesc_count;
    Dwarf_Unsigned i = 0;

    for ( i = 0 ; i < count; ++i) {
        Dwarf_Locdesc_c  llc = 0;

        llc = llhead->ll_locdesc +i;
        switch(llc->ld_lle_value) {
        case DW_LLE_end_of_list: {
            /* nothing to do */
            break;
            }
        case DW_LLE_base_address: {
            llc->ld_lopc =  llc->ld_rawhigh;
            llc->ld_highpc =  llc->ld_rawhigh;
            baseaddress =  llc->ld_rawhigh;
            break;
            }
        case DW_LLE_offset_pair: {
            llc->ld_lopc = llc->ld_rawlow + baseaddress;
            llc->ld_highpc = llc->ld_rawhigh + baseaddress;
            break;
            }
        default: {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append_printf_u(&m,
                "DW_DLE_LOCLISTS_ERROR: "
                "improper synthesized LLE code "
                "of 0x%x is unknown. In standard DWARF3/4 loclist",
                llc->ld_lle_value);
            _dwarf_error_string(dbg,error,
                DW_DLE_LOCLISTS_ERROR,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
            }
        }
    }
    return DW_DLV_OK;
}

static int
cook_gnu_loclist_contents(Dwarf_Debug dbg,
    Dwarf_Loc_Head_c llhead,
    Dwarf_Error *error)
{
    Dwarf_Unsigned baseaddress = llhead->ll_cu_base_address;
    Dwarf_Unsigned count = llhead->ll_locdesc_count;
    Dwarf_Unsigned i = 0;
    Dwarf_CU_Context cucontext = llhead->ll_context;
    int res = 0;

    for (i = 0 ; i < count ; ++i) {
        Dwarf_Locdesc_c  llc = 0;

        llc = llhead->ll_locdesc +i;
        switch(llc->ld_lle_value) {
        case DW_LLEX_base_address_selection_entry:{
            Dwarf_Addr targaddr = 0;

            res = _dwarf_look_in_local_and_tied_by_index(
                dbg,cucontext,llc->ld_rawhigh,&targaddr,
                error);
            if (res != DW_DLV_OK) {
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                llc->ld_highpc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_lopc = targaddr;
                llc->ld_highpc = targaddr;
            }
            break;
            }
        case DW_LLEX_end_of_list_entry:{
            /* Nothing to do. */
            break;
            }
        case DW_LLEX_start_length_entry:{
            Dwarf_Addr targaddr = 0;
            res = _dwarf_look_in_local_and_tied_by_index(
                dbg,cucontext,llc->ld_rawlow,&targaddr,
                error);
            if (res != DW_DLV_OK) {
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_lopc = targaddr;
                llc->ld_highpc = llc->ld_lopc +llc->ld_rawhigh;
            }
            break;
            }
        case DW_LLEX_offset_pair_entry:{
            llc->ld_lopc = llc->ld_rawlow + baseaddress;
            llc->ld_highpc = llc->ld_rawhigh + baseaddress;
            break;
            }
        case DW_LLEX_start_end_entry:{
            Dwarf_Addr targaddr = 0;
            res = _dwarf_look_in_local_and_tied_by_index(
                dbg,cucontext,llc->ld_rawlow,&targaddr,
                error);
            if (res != DW_DLV_OK) {
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_lopc = targaddr;
            }
            res = _dwarf_look_in_local_and_tied_by_index(
                dbg,cucontext,llc->ld_rawlow,&targaddr,
                error);
            if (res != DW_DLV_OK) {
                llc->ld_index_failed = TRUE;
                llc->ld_highpc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_highpc = targaddr;
            }

            break;
            }
        default:{
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append_printf_u(&m,
                "DW_DLE_LOCLISTS_ERROR: improper LLEX code "
                "of 0x%x is unknown. GNU LLEX dwo loclists error",
                llc->ld_lle_value);
            _dwarf_error_string(dbg,error,
                DW_DLE_LOCLISTS_ERROR,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;

            break;
            }
        }
    }
    return DW_DLV_OK;
}

/* DWARF5 */
static int
cook_loclists_contents(Dwarf_Debug dbg,
    Dwarf_Loc_Head_c llhead,
    Dwarf_Error *error)
{
    Dwarf_Unsigned baseaddress = llhead->ll_cu_base_address;
    Dwarf_Unsigned count = llhead->ll_locdesc_count;
    Dwarf_Unsigned i = 0;
    Dwarf_CU_Context cucontext = llhead->ll_context;
    int res = 0;
    Dwarf_Bool base_address_fail = FALSE;
    Dwarf_Bool debug_addr_fail = FALSE;

    if (!llhead->ll_cu_base_address_present) {
        base_address_fail = TRUE;
    }
    for (i = 0 ; i < count ; ++i) {
        Dwarf_Locdesc_c  llc = 0;

        llc = llhead->ll_locdesc +i;
        switch(llc->ld_lle_value) {
        case DW_LLE_base_addressx: {
            Dwarf_Addr targaddr = 0;
            if (debug_addr_fail) {
                res = DW_DLV_NO_ENTRY;
            } else {
                res = _dwarf_look_in_local_and_tied_by_index(
                    dbg,cucontext,llc->ld_rawlow,&targaddr,
                    error);
            }
            if (res != DW_DLV_OK) {
                debug_addr_fail = TRUE;
                base_address_fail = TRUE;
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                base_address_fail = FALSE;
                baseaddress = targaddr;
                llc->ld_lopc = targaddr;
            }
            break;
        }
        case DW_LLE_startx_endx:{
            /* two indexes into debug_addr */
            Dwarf_Addr targaddr = 0;
            if (debug_addr_fail) {
                res = DW_DLV_NO_ENTRY;
            } else {
                res = _dwarf_look_in_local_and_tied_by_index(
                    dbg,cucontext,llc->ld_rawlow,&targaddr,
                    error);
            }
            if (res != DW_DLV_OK) {
                debug_addr_fail = TRUE;
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_lopc = targaddr;
            }
            if (debug_addr_fail) {
                res = DW_DLV_NO_ENTRY;
            } else {
                res = _dwarf_look_in_local_and_tied_by_index(
                    dbg,cucontext,llc->ld_rawhigh,&targaddr,
                    error);

            }
            if (res != DW_DLV_OK) {
                debug_addr_fail = TRUE;
                llc->ld_index_failed = TRUE;
                llc->ld_highpc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_highpc = targaddr;
            }
            break;
        }
        case DW_LLE_startx_length:{
            /* one index to debug_addr other a length */
            Dwarf_Addr targaddr = 0;
            if (debug_addr_fail) {
                res = DW_DLV_NO_ENTRY;
            } else  {
                res = _dwarf_look_in_local_and_tied_by_index(
                    dbg,cucontext,llc->ld_rawlow,&targaddr,
                    error);
            }
            if (res != DW_DLV_OK) {
                debug_addr_fail = TRUE;
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                if (res == DW_DLV_ERROR) {
                    dwarf_dealloc_error(dbg, *error);
                    *error = 0;
                }
            } else {
                llc->ld_lopc = targaddr;
                llc->ld_highpc = targaddr + llc->ld_rawhigh;
            }
            break;
        }
        case DW_LLE_offset_pair:{
            if (base_address_fail) {
                llc->ld_index_failed = TRUE;
                llc->ld_lopc = 0;
                llc->ld_highpc = 0;
            } else {
                /*offsets of the current base address*/
                llc->ld_lopc = llc->ld_rawlow +baseaddress;
                llc->ld_highpc = llc->ld_rawhigh +baseaddress;
            }
            break;
        }
        case DW_LLE_default_location:{
            /*  nothing to do here, just has a counted
                location description */
            break;
        }
        case DW_LLE_base_address:{
            llc->ld_lopc = llc->ld_rawlow;
            llc->ld_highpc = llc->ld_rawlow;
            baseaddress = llc->ld_rawlow;
            base_address_fail = FALSE;
            break;
        }
        case DW_LLE_start_end:{
            llc->ld_lopc = llc->ld_rawlow;
            llc->ld_highpc = llc->ld_rawhigh;
            break;
        }
        case DW_LLE_start_length:{
            llc->ld_lopc = llc->ld_rawlow;
            llc->ld_highpc = llc->ld_rawlow + llc->ld_rawhigh;
            break;
        }
        case DW_LLE_end_of_list:{
            /* do nothing */
            break;
        }
        default: {
            dwarfstring m;

            dwarfstring_constructor(&m);
            dwarfstring_append_printf_u(&m,
                "DW_DLE_LOCLISTS_ERROR: improper DW_LLE code "
                "of 0x%x is unknown. DWARF5 loclists error",
                llc->ld_lle_value);
            _dwarf_error_string(dbg,error,
                DW_DLE_LOCLISTS_ERROR,
                dwarfstring_string(&m));
            dwarfstring_destructor(&m);
            return DW_DLV_ERROR;
        }
        }
    }
    return DW_DLV_OK;
}

/*  New October 2015
    This interface requires the use of interface functions
    to get data from Dwarf_Locdesc_c.  The structures
    are not visible to callers. */
int
dwarf_get_loclist_c(Dwarf_Attribute attr,
    Dwarf_Loc_Head_c * ll_header_out,
    Dwarf_Unsigned   * listlen_out,
    Dwarf_Error      * error)
{
    Dwarf_Debug dbg = 0;
    Dwarf_Half form          = 0;
    Dwarf_Loc_Head_c llhead  = 0;
    Dwarf_CU_Context cucontext = 0;
    unsigned address_size    = 0;
    int cuversionstamp       = 0;
    Dwarf_Bool is_cu         = FALSE;
    Dwarf_Unsigned attrnum   = 0;
    Dwarf_Bool is_dwo        = 0;
    int setup_res            = DW_DLV_ERROR;
    int lkind                = 0;

    if (!attr) {
        _dwarf_error_string(dbg, error,DW_DLE_ATTR_NULL,
            "DW_DLE_ATTR_NULL"
            "NULL Dwarf_Attribute "
            "argument passed to "
            "dwarf_get_loclist_c()");
        return DW_DLV_ERROR;
    }
    dbg = attr->ar_dbg;
    if (!dbg || dbg->de_magic != DBG_IS_VALID) {
        _dwarf_error_string(dbg, error,DW_DLE_DBG_NULL,
            "DW_DLE_DBG_NULL"
            "NULL Dwarf_Debug, improper Dwarf_Attribute "
            "argument passed to "
            "dwarf_get_loclist_c()");
        return DW_DLV_ERROR;
    }

    /* ***** BEGIN CODE ***** */
    setup_res = _dwarf_setup_loc(attr, &dbg,&cucontext, &form, error);
    if (setup_res != DW_DLV_OK) {
        return setup_res;
    }
    attrnum = attr->ar_attribute;
    cuversionstamp = cucontext->cc_version_stamp;
    address_size = cucontext->cc_address_size;
    is_dwo = cucontext->cc_is_dwo;
    lkind = determine_location_lkind(cuversionstamp,
        form, is_dwo);
    if (lkind == DW_LKIND_unknown) {
        dwarfstring m;
        const char * formname = "<unknownform>";
        const char * attrname = "<unknown attribute>";

        dwarfstring_constructor(&m);
        dwarf_get_FORM_name(form,&formname);
        dwarf_get_AT_name(attrnum,&attrname);
        dwarfstring_append_printf_u(&m,
            "DW_DLE_LOC_EXPR_BAD: For Compilation Unit "
            "version %u",cuversionstamp);
        dwarfstring_append_printf_u(&m,
            ", attribute 0x%x (",attrnum);
        dwarfstring_append(&m,(char *)attrname);
        dwarfstring_append_printf_u(&m,
            ") form 0x%x (",form);
        dwarfstring_append(&m,(char *)formname);
        if (is_dwo) {
            dwarfstring_append(&m,") (the CU is a .dwo) ");
        } else {
            dwarfstring_append(&m,") (the CU is not a .dwo) ");
        }
        dwarfstring_append(&m," we don't understand the location");
        _dwarf_error_string(dbg,error,DW_DLE_LOC_EXPR_BAD,
            dwarfstring_string(&m));
        dwarfstring_destructor(&m);
        return DW_DLV_ERROR;
    }
    /*  Doing this early (first) to avoid repeating the alloc code
        for each type  */
    llhead = (Dwarf_Loc_Head_c)
        _dwarf_get_alloc(dbg, DW_DLA_LOC_HEAD_C, 1);
    if (!llhead) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }
    llhead->ll_cuversion = cuversionstamp;
    llhead->ll_kind = lkind;
    llhead->ll_attrnum = attrnum;
    llhead->ll_attrform = form;
    llhead->ll_dbg = dbg;
    llhead->ll_address_size = address_size;
    llhead->ll_offset_size = cucontext->cc_length_size;
    llhead->ll_context = cucontext;
    llhead->ll_magic = LOCLISTS_MAGIC;

    llhead->ll_at_loclists_base_present =
        cucontext->cc_loclists_base_present;
    llhead->ll_at_loclists_base =  cucontext->cc_loclists_base;
    llhead->ll_cu_base_address_present = cucontext->cc_low_pc_present;
    llhead->ll_cu_base_address = cucontext->cc_low_pc;
    llhead->ll_cu_addr_base = cucontext->cc_addr_base;
    llhead->ll_cu_addr_base_present =
        cucontext->cc_addr_base_present;

    if (lkind == DW_LKIND_loclist ||
        lkind == DW_LKIND_GNU_exp_list) {
        int ores = 0;
        /* Here we have a loclist to deal with. */
        ores = context_is_cu_not_tu(cucontext,&is_cu);
        if (ores != DW_DLV_OK) {
            dwarf_dealloc_loc_head_c(llhead);
            return setup_res;
        }
        ores = _dwarf_original_loclist_build(dbg,
            llhead, attr, error);
        if (ores != DW_DLV_OK) {
            dwarf_dealloc_loc_head_c(llhead);
            return ores;
        }
        if (lkind == DW_LKIND_loclist) {
            ores = cook_original_loclist_contents(dbg,llhead,
                error);
        } else {
            ores = cook_gnu_loclist_contents(dbg,llhead,error);
        }
        if (ores != DW_DLV_OK) {
            dwarf_dealloc_loc_head_c(llhead);
            return ores;
        }
    } else if (lkind == DW_LKIND_expression) {
        /* DWARF2,3,4,5 */
        int eres = 0;
        eres = _dwarf_original_expression_build(dbg,
            llhead, attr, error);
        if (eres != DW_DLV_OK) {
            dwarf_dealloc_loc_head_c(llhead);
            return eres;
        }
    } else if (lkind == DW_LKIND_loclists) {
        /* DWARF5! */
        int leres = 0;

        leres = _dwarf_loclists_fill_in_lle_head(dbg,
            attr,llhead,error);
        if (leres != DW_DLV_OK) {
            dwarf_dealloc_loc_head_c(llhead);
            return leres;
        }
        leres = cook_loclists_contents(dbg,llhead,error);
        if (leres != DW_DLV_OK) {
            dwarf_dealloc_loc_head_c(llhead);
            return leres;
        }
    } /* ASSERT else impossible */
    *ll_header_out = llhead;
    *listlen_out = llhead->ll_locdesc_count;
    return DW_DLV_OK;
}

/*  An interface giving us no cu context!
    This is not going to be quite right. */
int
dwarf_loclist_from_expr_c(Dwarf_Debug dbg,
    Dwarf_Ptr expression_in,
    Dwarf_Unsigned expression_length,
    Dwarf_Half address_size,
    Dwarf_Half offset_size,
    Dwarf_Small dwarf_version,
    Dwarf_Loc_Head_c *loc_head,
    Dwarf_Unsigned * listlen,
    Dwarf_Error * error)
{
    /* Dwarf_Block that describes a single location expression. */
    Dwarf_Block_c loc_block;
    Dwarf_Loc_Head_c llhead = 0;
    Dwarf_Locdesc_c llbuf = 0;
    int local_listlen = 1;
    Dwarf_Addr rawlowpc = 0;
    Dwarf_Addr rawhighpc = MAX_ADDR;
    Dwarf_Small version_stamp = dwarf_version;
    int res = 0;

    if (!dbg || dbg->de_magic != DBG_IS_VALID) {
        _dwarf_error_string(dbg, error,DW_DLE_DBG_NULL,
            "DW_DLE_DBG_NULL"
            "NULL or bad Dwarf_Debug "
            "argument passed to "
            "dwarf_loclist_from_expr_c()");
        return DW_DLV_ERROR;
    }
    llhead = (Dwarf_Loc_Head_c)_dwarf_get_alloc(dbg,
        DW_DLA_LOC_HEAD_C, 1);
    if (!llhead) {
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }
    llhead->ll_magic = LOCLISTS_MAGIC;
    memset(&loc_block,0,sizeof(loc_block));
    loc_block.bl_len = expression_length;
    loc_block.bl_data = expression_in;
    loc_block.bl_kind = DW_LKIND_expression; /* Not from loclist. */
    loc_block.bl_section_offset = 0; /* Fake. Not meaningful. */
    loc_block.bl_locdesc_offset = 0; /* Fake. Not meaningful. */
    llbuf = (Dwarf_Locdesc_c)
        _dwarf_get_alloc(dbg, DW_DLA_LOCDESC_C, local_listlen);
    if (!llbuf) {
        dwarf_dealloc_loc_head_c(llhead);
        _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
        return DW_DLV_ERROR;
    }
    llbuf->ld_magic = LOCLISTS_MAGIC;
    llhead->ll_locdesc = llbuf;
    llhead->ll_locdesc_count = local_listlen;
    llhead->ll_context = 0; /* Not available! */
    llhead->ll_dbg = dbg;
    llhead->ll_kind = DW_LKIND_expression;

    /*  An empty location description (block length 0)
        means the code generator emitted no variable,
        the variable was not generated,
        it was unused or perhaps never tested
        after being set. Dwarf2,
        section 2.4.1 In other words, it is not
        an error, and we don't
        test for block length 0 specially here.  */

    /* Fills in the locdesc and its operators list at index 0 */
    res = _dwarf_fill_in_locdesc_op_c(dbg,
        0,
        llhead,
        &loc_block,
        address_size,
        offset_size,
        version_stamp,
        rawlowpc,
        rawhighpc,
        DW_LKIND_expression,
        error);
    if (res != DW_DLV_OK) {
        /* low level error already set: let it be passed back */
        dwarf_dealloc(dbg,llbuf,DW_DLA_LOCDESC_C);
        llhead->ll_locdesc = 0;
        llhead->ll_locdesc_count = 0;
        dwarf_dealloc_loc_head_c(llhead);
        return DW_DLV_ERROR;
    }
    *loc_head = llhead;
    *listlen = local_listlen;
    return DW_DLV_OK;
}

/*  New June 2020  Supports all versions of DWARF.
    Distinguishes location entry values as in the
    file directly (raw) from  the computed
    value (lowpc_out,hipc_out) after
    applying base values (if any). */
int
dwarf_get_locdesc_entry_d(Dwarf_Loc_Head_c loclist_head,
    Dwarf_Unsigned   index,
    Dwarf_Small    * lle_value_out,
    Dwarf_Unsigned * rawval1,
    Dwarf_Unsigned * rawval2,
    Dwarf_Bool     * debug_addr_unavailable,
    Dwarf_Addr     * lowpc_out, /* 'cooked' value */
    Dwarf_Addr     * hipc_out, /* 'cooked' value */
    Dwarf_Unsigned * loclist_expr_op_count_out,
    /* Returns pointer to the specific locdesc of the index; */
    Dwarf_Locdesc_c* locdesc_entry_out,
    Dwarf_Small    * loclist_source_out, /* 0,1, or 2 */
    Dwarf_Unsigned * expression_offset_out,
    Dwarf_Unsigned * locdesc_offset_out,
    Dwarf_Error    * error)
{
    Dwarf_Locdesc_c descs_base =  0;
    Dwarf_Locdesc_c desc =  0;
    Dwarf_Unsigned  desc_count = 0;
    Dwarf_Debug     dbg = 0;

    if (!loclist_head || loclist_head->ll_magic != LOCLISTS_MAGIC) {
        _dwarf_error_string(dbg, error,DW_DLE_DBG_NULL,
            "DW_DLE_DBG_NULL: "
            "Dwarf_Loc_Head_c NULL or not "
            "marked LOCLISTS_MAGIC "
            "in calling "
            "dwarf_get_locdesc_entry_d()");
        return DW_DLV_ERROR;
    }
    desc_count = loclist_head->ll_locdesc_count;
    descs_base  = loclist_head->ll_locdesc;
    dbg = loclist_head->ll_dbg;
    if (index >= desc_count) {
        _dwarf_error(dbg, error, DW_DLE_LOCLIST_INDEX_ERROR);
        return DW_DLV_ERROR;
    }
    desc = descs_base + index;
    *lle_value_out = desc->ld_lle_value;
    *rawval1 = desc->ld_rawlow;
    *rawval2 = desc->ld_rawhigh;
    *lowpc_out = desc->ld_lopc;
    *hipc_out = desc->ld_highpc;
    *debug_addr_unavailable = desc->ld_index_failed;
    *loclist_expr_op_count_out = desc->ld_cents;
    *locdesc_entry_out = desc;
    *loclist_source_out = desc->ld_kind;
    *expression_offset_out = desc->ld_section_offset;
    *locdesc_offset_out = desc->ld_locdesc_offset;
    return DW_DLV_OK;
}

int
dwarf_get_location_op_value_c(Dwarf_Locdesc_c locdesc,
    Dwarf_Unsigned   index,
    Dwarf_Small    * atom_out,
    Dwarf_Unsigned * operand1,
    Dwarf_Unsigned * operand2,
    Dwarf_Unsigned * operand3,
    Dwarf_Unsigned * offset_for_branch,
    Dwarf_Error*     error)
{
    Dwarf_Loc_Expr_Op op = 0;
    Dwarf_Unsigned max = 0;

    if (!locdesc) {
        _dwarf_error_string(NULL, error,DW_DLE_DBG_NULL,
            "DW_DLE_DBG_NULL"
            "Dwarf_Locdesc_c_Head_c NULL "
            "in calling "
            "dwarf_get_location_op_value_c()");
        return DW_DLV_ERROR;
    }
    max = locdesc->ld_cents;
    if (index >= max) {
        Dwarf_Debug dbg = locdesc->ld_loclist_head->ll_dbg;
        _dwarf_error(dbg, error, DW_DLE_LOCLIST_INDEX_ERROR);
        return DW_DLV_ERROR;
    }
    op = locdesc->ld_s + index;
    *atom_out = op->lr_atom;
    *operand1 = op->lr_number;
    *operand2 = op->lr_number2;
    *operand3 = op->lr_number3;
    *offset_for_branch = op->lr_offset;
    return DW_DLV_OK;
}
/* ============== End of the October 2015 interfaces. */
