tools/elf4rom/libs/dwarf-20071209/libdwarf/pro_forms.c
changeset 34 92d87f2e53c2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/elf4rom/libs/dwarf-20071209/libdwarf/pro_forms.c	Fri Jan 15 09:07:44 2010 +0000
@@ -0,0 +1,1164 @@
+/*
+
+  Copyright (C) 2000,2004 Silicon Graphics, Inc.  All Rights Reserved.
+  Portions Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+  Portions Copyright 2007 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.
+
+  Contact information:  Silicon Graphics, Inc., 1500 Crittenden Lane,
+  Mountain View, CA 94043, or:
+
+  http://www.sgi.com
+
+  For further information regarding this notice, see:
+
+  http://oss.sgi.com/projects/GenInfo/NoticeExplan
+
+*/
+
+
+
+#include "config.h"
+#include "libdwarfdefs.h"
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include "pro_incl.h"
+#include "pro_expr.h"
+
+#ifndef R_MIPS_NONE
+#define R_MIPS_NONE 0
+#endif
+
+
+    /* Indicates no relocation needed. */
+#define NO_ELF_SYM_INDEX	0
+
+
+/* adds an attribute to a die */
+extern void _dwarf_pro_add_at_to_die(Dwarf_P_Die die,
+				     Dwarf_P_Attribute attr);
+
+/*
+    This function adds an attribute whose value is
+    a target address to the given die.  The attribute
+    is given the name provided by attr.  The address
+    is given in pc_value.
+*/
+
+static Dwarf_P_Attribute
+local_add_AT_address(Dwarf_P_Debug dbg,
+		     Dwarf_P_Die ownerdie,
+		     Dwarf_Half attr,
+		     Dwarf_Signed form,
+		     Dwarf_Unsigned pc_value,
+		     Dwarf_Unsigned sym_index,
+		     Dwarf_Error * error);
+     
+/* old interface */
+Dwarf_P_Attribute
+dwarf_add_AT_targ_address(Dwarf_P_Debug dbg,
+			  Dwarf_P_Die ownerdie,
+			  Dwarf_Half attr,
+			  Dwarf_Unsigned pc_value,
+			  Dwarf_Signed sym_index, Dwarf_Error * error)
+{
+    return 
+	dwarf_add_AT_targ_address_b(dbg,
+				    ownerdie, 
+				    attr,
+				    pc_value, 
+				    (Dwarf_Unsigned) sym_index, error);
+}
+
+/* New interface, replacing dwarf_add_AT_targ_address. 
+   Essentially just makes sym_index a Dwarf_Unsigned
+   so for symbolic relocations it can be a full address.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_targ_address_b(Dwarf_P_Debug dbg,
+			    Dwarf_P_Die ownerdie,
+			    Dwarf_Half attr,
+			    Dwarf_Unsigned pc_value,
+			    Dwarf_Unsigned sym_index,
+			    Dwarf_Error * error)
+{
+    switch (attr) {
+    case DW_AT_low_pc:
+    case DW_AT_high_pc:
+
+    /* added to support location lists */
+    /* no way to check that this is a loclist-style address though */
+    case DW_AT_location:
+    case DW_AT_string_length:
+    case DW_AT_return_addr:
+    case DW_AT_frame_base:
+    case DW_AT_segment:
+    case DW_AT_static_link:
+    case DW_AT_use_location:
+    case DW_AT_vtable_elem_location:
+	
+	break;
+
+    default: 
+	if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	    _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+	break;
+    }
+    
+    return local_add_AT_address(dbg, ownerdie, attr, DW_FORM_addr,
+				pc_value, sym_index, error);
+}
+
+Dwarf_P_Attribute
+dwarf_add_AT_ref_address(Dwarf_P_Debug dbg,
+			 Dwarf_P_Die ownerdie,
+			 Dwarf_Half attr,
+			 Dwarf_Unsigned pc_value,
+			 Dwarf_Unsigned sym_index,
+			 Dwarf_Error * error)
+{
+    switch (attr) {
+    case DW_AT_type:
+    case DW_AT_import:
+	break;
+
+    default: 
+	if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	    _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+	break;
+    }
+    
+    return local_add_AT_address(dbg, ownerdie, attr, DW_FORM_ref_addr,
+				pc_value, sym_index, error);    
+}
+
+
+/* Make sure attribute types are checked before entering here. */
+static Dwarf_P_Attribute
+local_add_AT_address(Dwarf_P_Debug dbg,
+		     Dwarf_P_Die ownerdie,
+		     Dwarf_Half attr,
+		     Dwarf_Signed form,
+		     Dwarf_Unsigned pc_value,
+		     Dwarf_Unsigned sym_index,
+		     Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+    int upointer_size = dbg->de_pointer_size;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    /* attribute types have already been checked */
+    /* switch (attr) { ... } */
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = form;
+    new_attr->ar_nbytes = upointer_size;
+    new_attr->ar_rel_symidx = sym_index;
+    new_attr->ar_reloc_len = upointer_size;
+    new_attr->ar_next = 0;
+    if (sym_index != NO_ELF_SYM_INDEX)
+	new_attr->ar_rel_type = dbg->de_ptr_reloc;
+    else
+	new_attr->ar_rel_type = R_MIPS_NONE;
+
+    new_attr->ar_data = (char *)
+	_dwarf_p_get_alloc(dbg, upointer_size);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    WRITE_UNALIGNED(dbg, new_attr->ar_data,
+		    (const void *) &pc_value,
+		    sizeof(pc_value), upointer_size);
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+/*
+ * Functions to compress and uncompress data from normal
+ * arrays of integral types into arrays of LEB128 numbers.
+ * Extend these functions as needed to handle wider input
+ * variety.  Return values should be freed with _dwarf_p_dealloc
+ * after they aren't needed any more.
+ */
+
+/* return value points to an array of LEB number */
+
+void *
+dwarf_compress_integer_block(
+    Dwarf_P_Debug    dbg,
+    Dwarf_Bool       unit_is_signed,
+    Dwarf_Small      unit_length_in_bits,
+    void*            input_block,
+    Dwarf_Unsigned   input_length_in_units,
+    Dwarf_Unsigned*  output_length_in_bytes_ptr,
+    Dwarf_Error*     error
+)
+{
+    Dwarf_Unsigned output_length_in_bytes;
+    char * output_block;
+    char encode_buffer[ENCODE_SPACE_NEEDED];
+    int unit_length;
+    int i;
+    char * ptr;
+    int remain;
+    int result;
+
+    if (dbg == NULL) {
+        _dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+        return((void *)DW_DLV_BADADDR);
+    }
+    
+    if (unit_is_signed == false ||
+	unit_length_in_bits != 32 ||
+	input_block == NULL ||
+	input_length_in_units == 0 ||
+	output_length_in_bytes_ptr == NULL) {
+	
+	_dwarf_p_error(NULL, error, DW_DLE_BADBITC);
+	return ((void *) DW_DLV_BADADDR);
+    }
+
+    /* At this point we assume the format is: signed 32 bit */
+
+    /* first compress everything to find the total size. */
+
+    output_length_in_bytes = 0;
+    for (i=0; i<input_length_in_units; i++) {
+	int unit_encoded_size;
+	Dwarf_sfixed unit; /* this is fixed at signed-32-bits */
+	
+	unit = ((Dwarf_sfixed*)input_block)[i];
+	
+	result = _dwarf_pro_encode_signed_leb128_nm(unit, &unit_encoded_size,
+					     encode_buffer,sizeof(encode_buffer));
+	if (result !=  DW_DLV_OK) {
+	    _dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	    return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+	}
+	output_length_in_bytes += unit_encoded_size;
+    }
+
+    
+    /* then alloc */
+
+    output_block = (void *)
+        _dwarf_p_get_alloc(dbg, output_length_in_bytes);
+    if (output_block == NULL) {
+        _dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+        return((void*)DW_DLV_BADADDR);
+    }
+    
+    /* then compress again and copy into new buffer */
+
+    ptr = output_block;
+    remain = output_length_in_bytes;
+    for (i=0; i<input_length_in_units; i++) {
+	int unit_encoded_size;
+	Dwarf_sfixed unit; /* this is fixed at signed-32-bits */
+	
+	unit = ((Dwarf_sfixed*)input_block)[i];
+	
+	result = _dwarf_pro_encode_signed_leb128_nm(unit, &unit_encoded_size,
+					     ptr, remain);
+	if (result !=  DW_DLV_OK) {
+	    _dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	    return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+	}
+	remain -= unit_encoded_size;
+	ptr += unit_encoded_size;
+    }
+
+    if (remain != 0) {
+	_dwarf_p_dealloc(dbg, (unsigned char *)output_block);
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+    }
+
+    *output_length_in_bytes_ptr = output_length_in_bytes;
+    return (void*) output_block;
+
+}
+
+void
+dwarf_dealloc_compressed_block(Dwarf_P_Debug dbg, void * space)
+{
+    _dwarf_p_dealloc(dbg, space);
+}
+
+/* This is very similar to targ_address but results in a different FORM */
+/* dbg->de_ar_data_attribute_form is data4 or data8
+   and dwarf4 changes the definition for such on DW_AT_high_pc.
+   DWARF 3: the FORM here has no defined meaning for dwarf3.
+   DWARF 4: the FORM here means that for DW_AT_high_pc the value
+            is not a high address but is instead an offset
+            from a (separate) DW_AT_low_pc. 
+   The intent for DWARF4 is that this is not a relocated
+   address at all.  Instead a simple offset.
+   But this should NOT be called for a simple non-relocated offset.
+   So do not call this with an attr of DW_AT_high_pc.
+   Use dwarf_add_AT_unsigned_const() (for example) instead of
+   dwarf_add_AT_dataref when the value is a simple offset .
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_dataref(
+    Dwarf_P_Debug dbg,
+    Dwarf_P_Die ownerdie,
+    Dwarf_Half attr,
+    Dwarf_Unsigned pc_value,
+    Dwarf_Unsigned sym_index,
+    Dwarf_Error * error)
+{
+    /* TODO: Add checking here */
+    return local_add_AT_address(dbg, ownerdie, attr,
+				dbg->de_ar_data_attribute_form,
+				pc_value,
+				sym_index,
+				error);
+}
+
+
+
+Dwarf_P_Attribute 
+dwarf_add_AT_block(
+    Dwarf_P_Debug	dbg,
+    Dwarf_P_Die 	ownerdie,
+    Dwarf_Half		attr,
+    Dwarf_Small         *block_data,
+    Dwarf_Unsigned      block_size,
+    Dwarf_Error 	*error
+)
+{
+    Dwarf_P_Attribute 	new_attr;
+    int result;
+    char encode_buffer[ENCODE_SPACE_NEEDED];
+    int len_size;
+    char * attrdata;
+
+    if (dbg == NULL) {
+        _dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+        return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+        _dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+        return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+    }
+
+    /* I don't mess with block1, block2, block4, not worth the effort */
+
+    /* So, encode the length into LEB128 */
+    result = _dwarf_pro_encode_leb128_nm(block_size, &len_size,
+					 encode_buffer,sizeof(encode_buffer));
+    if (result !=  DW_DLV_OK) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+    }
+
+    /* Allocate the new attribute */
+    new_attr = (Dwarf_P_Attribute)
+        _dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+        _dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+        return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+    }
+
+    /* Fill in the attribute */
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = DW_FORM_block;
+    new_attr->ar_nbytes = len_size + block_size;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data = attrdata = (char *)
+        _dwarf_p_get_alloc(dbg, len_size + block_size);
+    if (new_attr->ar_data == NULL) {
+	/* free the block we got earlier */
+	_dwarf_p_dealloc(dbg, (unsigned char *) new_attr);
+        _dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+        return((Dwarf_P_Attribute)DW_DLV_BADADDR);
+    }
+
+    /* write length and data to attribute data buffer */
+    memcpy(attrdata, encode_buffer, len_size);
+    attrdata += len_size;
+    memcpy(attrdata, block_data, block_size);
+    
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+
+    return new_attr;
+}
+
+
+/*
+    This function adds attributes whose value
+    is an unsigned constant.  It determines the 
+    size of the value field from the value of 
+    the constant.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_unsigned_const(Dwarf_P_Debug dbg,
+			    Dwarf_P_Die ownerdie,
+			    Dwarf_Half attr,
+			    Dwarf_Unsigned value, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+    Dwarf_Half attr_form;
+    Dwarf_Small size;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    switch (attr) {
+    case DW_AT_ordering:
+    case DW_AT_byte_size:
+    case DW_AT_bit_offset:
+    case DW_AT_bit_size:
+    case DW_AT_inline:
+    case DW_AT_language:
+    case DW_AT_visibility:
+    case DW_AT_virtuality:
+    case DW_AT_accessibility:
+    case DW_AT_address_class:
+    case DW_AT_calling_convention:
+    case DW_AT_encoding:
+    case DW_AT_identifier_case:
+    case DW_AT_MIPS_loop_unroll_factor:
+    case DW_AT_MIPS_software_pipeline_depth:
+	break;
+
+    case DW_AT_decl_column:
+    case DW_AT_decl_file:
+    case DW_AT_decl_line:
+    case DW_AT_const_value:
+    case DW_AT_start_scope:
+    case DW_AT_stride_size:
+    case DW_AT_count:
+    case DW_AT_associated:
+    case DW_AT_allocated:
+    case DW_AT_upper_bound:
+    case DW_AT_lower_bound:
+	break;
+
+        default: {
+	         if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	             _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	             return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	       }
+	       break;
+	    }
+        }
+
+    /* 
+       Compute the number of bytes needed to hold constant. */
+    if (value <= UCHAR_MAX) {
+	attr_form = DW_FORM_data1;
+	size = 1;
+    } else if (value <= USHRT_MAX) {
+	attr_form = DW_FORM_data2;
+	size = 2;
+    } else if (value <= UINT_MAX) {
+	attr_form = DW_FORM_data4;
+	size = 4;
+    } else {
+	attr_form = DW_FORM_data8;
+	size = 8;
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = attr_form;
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* irrelevant: unused with R_MIPS_NONE */
+    new_attr->ar_nbytes = size;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data = (char *)
+	_dwarf_p_get_alloc(dbg, size);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    WRITE_UNALIGNED(dbg, new_attr->ar_data,
+		    (const void *) &value, sizeof(value), size);
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+/*
+    This function adds attributes whose value
+    is an signed constant.  It determines the 
+    size of the value field from the value of 
+    the constant.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_signed_const(Dwarf_P_Debug dbg,
+			  Dwarf_P_Die ownerdie,
+			  Dwarf_Half attr,
+			  Dwarf_Signed value, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+    Dwarf_Half attr_form;
+    Dwarf_Small size;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    switch (attr) {
+    case DW_AT_upper_bound:
+    case DW_AT_lower_bound:
+    case DW_AT_const_value:
+        break;
+
+    default:{ 
+	        if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	             _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	             return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	        }
+	}
+	break;
+    }
+
+    /* 
+       Compute the number of bytes needed to hold constant. */
+    if (value >= SCHAR_MIN && value <= SCHAR_MAX) {
+	attr_form = DW_FORM_data1;
+	size = 1;
+    } else if (value >= SHRT_MIN && value <= SHRT_MAX) {
+	attr_form = DW_FORM_data2;
+	size = 2;
+    } else if (value >= INT_MIN && value <= INT_MAX) {
+	attr_form = DW_FORM_data4;
+	size = 4;
+    } else {
+	attr_form = DW_FORM_data8;
+	size = 8;
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = attr_form;
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* irrelevant: unused with R_MIPS_NONE */
+    new_attr->ar_nbytes = size;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data = (char *)
+	_dwarf_p_get_alloc(dbg, size);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    WRITE_UNALIGNED(dbg, new_attr->ar_data,
+		    (const void *) &value, sizeof(value), size);
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+/*
+    This function adds attributes whose value
+    is a location expression.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_location_expr(Dwarf_P_Debug dbg,
+			   Dwarf_P_Die ownerdie,
+			   Dwarf_Half attr,
+			   Dwarf_P_Expr loc_expr, Dwarf_Error * error)
+{
+    char encode_buffer[ENCODE_SPACE_NEEDED];
+    int res;
+    Dwarf_P_Attribute new_attr;
+    Dwarf_Half attr_form;
+    char *len_str = 0;
+    int len_size;
+    int block_size;
+    char *block_dest_ptr;
+    int do_len_as_int = 0;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (loc_expr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_EXPR_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (loc_expr->ex_dbg != dbg) {
+	_dwarf_p_error(dbg, error, DW_DLE_LOC_EXPR_BAD);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    block_size = loc_expr->ex_next_byte_offset;
+
+    switch (attr) {
+    case DW_AT_location:
+    case DW_AT_string_length:
+    case DW_AT_const_value:
+    case DW_AT_use_location:
+    case DW_AT_return_addr:
+    case DW_AT_data_member_location:
+    case DW_AT_frame_base:
+    case DW_AT_static_link:
+    case DW_AT_vtable_elem_location:
+	case DW_AT_lower_bound:
+	case DW_AT_upper_bound:
+	case DW_AT_count:
+	case DW_AT_associated:
+	case DW_AT_allocated:
+	break;
+
+        default:
+	    if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	    _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+	    break;
+    }
+
+    /* 
+       Compute the number of bytes needed to hold constant. */
+    if (block_size <= UCHAR_MAX) {
+	attr_form = DW_FORM_block1;
+	len_size = 1;
+	do_len_as_int = 1;
+    } else if (block_size <= USHRT_MAX) {
+	attr_form = DW_FORM_block2;
+	len_size = 2;
+	do_len_as_int = 1;
+    } else if (block_size <= UINT_MAX) {
+	attr_form = DW_FORM_block4;
+	len_size = 4;
+	do_len_as_int = 1;
+    } else {
+	attr_form = DW_FORM_block;
+	res = _dwarf_pro_encode_leb128_nm(block_size, &len_size,
+					  encode_buffer,
+					  sizeof(encode_buffer));
+	if (res != DW_DLV_OK) {
+	    _dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+	len_str = (char *) encode_buffer;
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = attr_form;
+    new_attr->ar_reloc_len = dbg->de_pointer_size;
+    if (loc_expr->ex_reloc_sym_index != NO_ELF_SYM_INDEX) {
+	new_attr->ar_rel_type = dbg->de_ptr_reloc;
+    } else {
+	new_attr->ar_rel_type = R_MIPS_NONE;
+    }
+    new_attr->ar_rel_symidx = loc_expr->ex_reloc_sym_index;
+    new_attr->ar_rel_offset =
+	(Dwarf_Word) loc_expr->ex_reloc_offset + len_size;
+
+    new_attr->ar_nbytes = block_size + len_size;
+
+    new_attr->ar_next = 0;
+    new_attr->ar_data = block_dest_ptr =
+	(char *) _dwarf_p_get_alloc(dbg, block_size + len_size);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (do_len_as_int) {
+	WRITE_UNALIGNED(dbg, block_dest_ptr, (const void *) &block_size,
+			sizeof(block_size), len_size);
+    } else {
+	/* Is uleb number form, DW_FORM_block. See above. */
+	memcpy(block_dest_ptr, len_str, len_size);
+    }
+    block_dest_ptr += len_size;
+    memcpy(block_dest_ptr, &(loc_expr->ex_byte_stream[0]), block_size);
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+/*
+    This function adds attributes of reference class.
+    The references here are local CU references, 
+    not DW_FORM_ref_addr.
+    The offset field is 4 bytes for 32-bit objects,
+    and 8-bytes for 64-bit objects.  Otherdie is the
+    that is referenced by ownerdie.
+
+    For reference attributes, the ar_data and ar_nbytes
+    are not needed.  Instead, the ar_ref_die points to
+    the other die, and its di_offset value is used as
+    the reference value.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_reference(Dwarf_P_Debug dbg,
+		       Dwarf_P_Die ownerdie,
+		       Dwarf_Half attr,
+		       Dwarf_P_Die otherdie, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (otherdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    switch (attr) {
+    case DW_AT_specification:
+    case DW_AT_discr:
+    case DW_AT_common_reference:
+    case DW_AT_import:
+    case DW_AT_containing_type:
+    case DW_AT_default_value:
+    case DW_AT_abstract_origin:
+    case DW_AT_friend:
+    case DW_AT_priority:
+    case DW_AT_type:
+    case DW_AT_lower_bound:
+    case DW_AT_upper_bound:
+    case DW_AT_count:
+    case DW_AT_associated:
+    case DW_AT_allocated:
+    case DW_AT_sibling:
+    case DW_AT_namelist_item:
+	break;
+
+    default:
+	if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	    _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+	break;
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = dbg->de_ar_ref_attr_form;
+    new_attr->ar_nbytes = dbg->de_offset_size;
+    new_attr->ar_reloc_len = dbg->de_offset_size;
+    new_attr->ar_ref_die = otherdie;
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_next = 0;
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+/*
+    This function adds attributes of the flag class.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_flag(Dwarf_P_Debug dbg,
+		  Dwarf_P_Die ownerdie,
+		  Dwarf_Half attr,
+		  Dwarf_Small flag, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+#if 0    
+    switch (attr) {
+    case DW_AT_is_optional:
+    case DW_AT_artificial:
+    case DW_AT_declaration:
+    case DW_AT_external:
+    case DW_AT_prototyped:
+    case DW_AT_variable_parameter:
+	break;
+
+        default:
+	    if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	    _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+    	    break;
+    }
+#endif    
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = DW_FORM_flag;
+    new_attr->ar_nbytes = 1;
+    new_attr->ar_reloc_len = 0;	/* not used */
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data = (char *)
+	_dwarf_p_get_alloc(dbg, 1);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    memcpy(new_attr->ar_data, &flag, 1);
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+/*
+    This function adds values of attributes
+    belonging to the string class.
+*/
+Dwarf_P_Attribute
+dwarf_add_AT_string(Dwarf_P_Debug dbg,
+		    Dwarf_P_Die ownerdie,
+		    Dwarf_Half attr, char *string, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+
+    if (dbg == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DBG_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+	_dwarf_p_get_alloc(dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    switch (attr) {
+    case DW_AT_name:
+    case DW_AT_comp_dir:
+    case DW_AT_const_value:
+    case DW_AT_producer:
+	break;
+
+	default:
+	    if ( attr < DW_AT_lo_user || attr > DW_AT_hi_user ) {
+	    _dwarf_p_error(dbg, error, DW_DLE_INPUT_ATTR_BAD);
+	    return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+	}
+	    break;
+    }
+
+    new_attr->ar_attribute = attr;
+    new_attr->ar_attribute_form = DW_FORM_string;
+    new_attr->ar_nbytes = strlen(string) + 1;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data =
+	(char *) _dwarf_p_get_alloc(dbg, strlen(string)+1);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(dbg, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    strcpy(new_attr->ar_data, string);
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* unused for R_MIPS_NONE */
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+Dwarf_P_Attribute
+dwarf_add_AT_const_value_string(Dwarf_P_Die ownerdie,
+				char *string_value, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+        _dwarf_p_get_alloc(ownerdie->di_dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = DW_AT_const_value;
+    new_attr->ar_attribute_form = DW_FORM_string;
+    new_attr->ar_nbytes = strlen(string_value) + 1;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data =
+	(char *) _dwarf_p_get_alloc(ownerdie->di_dbg, strlen(string_value)+1);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    strcpy(new_attr->ar_data, string_value);
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* unused for R_MIPS_NONE */
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+Dwarf_P_Attribute
+dwarf_add_AT_producer(Dwarf_P_Die ownerdie,
+		      char *producer_string, Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+        _dwarf_p_get_alloc(ownerdie->di_dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = DW_AT_producer;
+    new_attr->ar_attribute_form = DW_FORM_string;
+    new_attr->ar_nbytes = strlen(producer_string) + 1;
+    new_attr->ar_next = 0;
+
+    new_attr->ar_data =
+	(char *) _dwarf_p_get_alloc(ownerdie->di_dbg, strlen(producer_string)+1);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    strcpy(new_attr->ar_data, producer_string);
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* unused for R_MIPS_NONE */
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+Dwarf_P_Attribute
+dwarf_add_AT_const_value_signedint(Dwarf_P_Die ownerdie,
+				   Dwarf_Signed signed_value,
+				   Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+    int leb_size;
+    char encode_buffer[ENCODE_SPACE_NEEDED];
+    int res;
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+        _dwarf_p_get_alloc(ownerdie->di_dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = DW_AT_const_value;
+    new_attr->ar_attribute_form = DW_FORM_sdata;
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* unused for R_MIPS_NONE */
+    new_attr->ar_next = 0;
+
+    res = _dwarf_pro_encode_signed_leb128_nm(signed_value, &leb_size,
+					     encode_buffer,
+					     sizeof(encode_buffer));
+    if (res != DW_DLV_OK) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    new_attr->ar_data = (char *)
+        _dwarf_p_get_alloc(ownerdie->di_dbg, leb_size);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    memcpy(new_attr->ar_data, encode_buffer, leb_size);
+    new_attr->ar_nbytes = leb_size;
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}
+
+
+Dwarf_P_Attribute
+dwarf_add_AT_const_value_unsignedint(Dwarf_P_Die ownerdie,
+				     Dwarf_Unsigned unsigned_value,
+				     Dwarf_Error * error)
+{
+    Dwarf_P_Attribute new_attr;
+    int leb_size;
+    char encode_buffer[ENCODE_SPACE_NEEDED];
+    int res;
+
+    if (ownerdie == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_DIE_NULL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr = (Dwarf_P_Attribute)
+        _dwarf_p_get_alloc(ownerdie->di_dbg, sizeof(struct Dwarf_P_Attribute_s));
+    if (new_attr == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+
+    new_attr->ar_attribute = DW_AT_const_value;
+    new_attr->ar_attribute_form = DW_FORM_udata;
+    new_attr->ar_rel_type = R_MIPS_NONE;
+    new_attr->ar_reloc_len = 0;	/* unused for R_MIPS_NONE */
+    new_attr->ar_next = 0;
+
+    res = _dwarf_pro_encode_leb128_nm(unsigned_value, &leb_size,
+				      encode_buffer,
+				      sizeof(encode_buffer));
+    if (res != DW_DLV_OK) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    new_attr->ar_data = (char *)
+	_dwarf_p_get_alloc(ownerdie->di_dbg, leb_size);
+    if (new_attr->ar_data == NULL) {
+	_dwarf_p_error(NULL, error, DW_DLE_ALLOC_FAIL);
+	return ((Dwarf_P_Attribute) DW_DLV_BADADDR);
+    }
+    memcpy(new_attr->ar_data, encode_buffer, leb_size);
+    new_attr->ar_nbytes = leb_size;
+
+    /* add attribute to the die */
+    _dwarf_pro_add_at_to_die(ownerdie, new_attr);
+    return new_attr;
+}