blob: e43e0c10904f28665ffd669d533203bcd1070395 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Representation of source level types. tytypes.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2008-2008 OpenWorks LLP
info@open-works.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#include "pub_core_basics.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcprint.h"
#include "pub_core_xarray.h" /* to keep priv_tytypes.h happy */
#include "priv_misc.h" /* dinfo_zalloc/free/strdup */
#include "priv_d3basics.h" /* ML_(evaluate_Dwarf3_Expr) et al */
#include "priv_tytypes.h" /* self */
TyAdmin* ML_(new_TyAdmin) ( UWord cuOff, TyAdmin* next ) {
TyAdmin* admin = ML_(dinfo_zalloc)( sizeof(TyAdmin) );
admin->cuOff = cuOff;
admin->next = next;
return admin;
}
TyAtom* ML_(new_TyAtom) ( UChar* name, Long value ) {
TyAtom* atom = ML_(dinfo_zalloc)( sizeof(TyAtom) );
atom->name = name;
atom->value = value;
return atom;
}
TyField* ML_(new_TyField) ( UChar* name,
Type* typeR, D3Expr* loc ) {
TyField* field = ML_(dinfo_zalloc)( sizeof(TyField) );
field->name = name;
field->typeR = typeR;
field->loc = loc;
return field;
}
TyBounds* ML_(new_TyBounds) ( void ) {
TyBounds* bounds = ML_(dinfo_zalloc)( sizeof(TyBounds) );
bounds->magic = TyBounds_MAGIC;
return bounds;
}
D3Expr* ML_(new_D3Expr) ( UChar* bytes, UWord nbytes ) {
D3Expr* expr = ML_(dinfo_zalloc)( sizeof(D3Expr) );
expr->bytes = bytes;
expr->nbytes = nbytes;
return expr;
}
Type* ML_(new_Type) ( void ) {
Type* type = ML_(dinfo_zalloc)( sizeof(Type) );
return type;
}
static void delete_TyAtom ( TyAtom* atom ) {
/* .name is in DebugInfo.strchunks */
ML_(dinfo_free)(atom);
}
static void delete_TyField ( TyField* field ) {
/* .name is in DebugInfo.strchunks */
/* typeR and loc will be on the admin list; no need to free */
ML_(dinfo_free)(field);
}
static void delete_TyBounds ( TyBounds* bounds ) {
ML_(dinfo_free)(bounds);
}
static void delete_D3Expr ( D3Expr* expr ) {
/* .bytes is in DebugInfo.strchunks */
ML_(dinfo_free)(expr);
}
static void delete_Type ( Type* ty ) {
switch (ty->tag) {
case Ty_Base:
/* .name is in DebugInfo.strchunks */
break;
case Ty_PorR:
/* typeR will be on the admin list */
break;
case Ty_TyDef:
/* .name is in DebugInfo.strchunks */
/* typeR will be on the admin list */
break;
case Ty_StOrUn:
/* .name is in DebugInfo.strchunks */
/* Just dump the containing XArray. The fields themselves
will be on the admin list. */
if (ty->Ty.StOrUn.fields)
VG_(deleteXA)(ty->Ty.StOrUn.fields);
break;
case Ty_Enum:
/* .name is in DebugInfo.strchunks */
if (ty->Ty.Enum.atomRs)
VG_(deleteXA)( ty->Ty.Enum.atomRs);
/* Just dump the containing XArray. The atoms themselves
will be on the admin list. */
break;
case Ty_Array:
if (ty->Ty.Array.bounds)
VG_(deleteXA)( ty->Ty.Array.bounds);
/* Just dump the containing XArray. The bounds themselves
will be on the admin list. */
break;
case Ty_Fn:
break;
case Ty_Qual:
/* typeR will be on the admin list */
break;
case Ty_Void:
break;
default:
vg_assert(0);
}
}
void ML_(delete_TyAdmin_and_payload) ( TyAdmin* ad ) {
vg_assert(ad->payload);
switch (ad->tag) {
case TyA_Type: delete_Type(ad->payload); break;
case TyA_Atom: delete_TyAtom(ad->payload); break;
case TyA_Expr: delete_D3Expr(ad->payload); break;
case TyA_Field: delete_TyField(ad->payload); break;
case TyA_Bounds: delete_TyBounds(ad->payload); break;
default: vg_assert(0);
}
ML_(dinfo_free)(ad);
}
static void pp_XArray_of_pointersOrRefs ( XArray* xa ) {
Word i;
VG_(printf)("{");
for (i = 0; i < VG_(sizeXA)(xa); i++) {
void* ptr = *(void**) VG_(indexXA)(xa, i);
VG_(printf)("0x%05lx", ptr);
if (i+1 < VG_(sizeXA)(xa))
VG_(printf)(",");
}
VG_(printf)("}");
}
void ML_(pp_TyAtom) ( TyAtom* atom ) {
VG_(printf)("TyAtom(%lld,\"%s\")", atom->value, atom->name);
}
void ML_(pp_D3Expr) ( D3Expr* expr ) {
VG_(printf)("D3Expr(%p,%lu)", expr->bytes, expr->nbytes);
}
void ML_(pp_TyField) ( TyField* field ) {
VG_(printf)("TyField(0x%05lx,%p,\"%s\")",
field->typeR, field->loc,
field->name ? field->name : (UChar*)"");
}
void ML_(pp_TyBounds) ( TyBounds* bounds ) {
vg_assert(bounds->magic == TyBounds_MAGIC);
VG_(printf)("TyBounds[");
if (bounds->knownL)
VG_(printf)("%lld", bounds->boundL);
else
VG_(printf)("??");
VG_(printf)(",");
if (bounds->knownU)
VG_(printf)("%lld", bounds->boundU);
else
VG_(printf)("??");
VG_(printf)("]");
}
static void pp_TyBounds_C_ishly ( TyBounds* bounds ) {
vg_assert(bounds->magic == TyBounds_MAGIC);
if (bounds->knownL && bounds->knownU && bounds->boundL == 0) {
VG_(printf)("[%lld]", 1 + bounds->boundU);
}
else
if (bounds->knownL && (!bounds->knownU) && bounds->boundL == 0) {
VG_(printf)("[]");
}
else
ML_(pp_TyBounds)( bounds );
}
void ML_(pp_Type) ( Type* ty )
{
if (!ty) {
VG_(printf)("**type=NULL**");
return;
}
switch (ty->tag) {
case Ty_Base:
VG_(printf)("Ty_Base(%d,%c,\"%s\")",
ty->Ty.Base.szB, ty->Ty.Base.enc,
ty->Ty.Base.name ? ty->Ty.Base.name
: (UChar*)"(null)" );
break;
case Ty_PorR:
VG_(printf)("Ty_PorR(%d,%c,0x%05lx)",
ty->Ty.PorR.szB,
ty->Ty.PorR.isPtr ? 'P' : 'R',
ty->Ty.PorR.typeR);
break;
case Ty_Enum:
VG_(printf)("Ty_Enum(%d,%p,\"%s\")",
ty->Ty.Enum.szB, ty->Ty.Enum.atomRs,
ty->Ty.Enum.name ? ty->Ty.Enum.name
: (UChar*)"" );
if (ty->Ty.Enum.atomRs)
pp_XArray_of_pointersOrRefs( ty->Ty.Enum.atomRs );
break;
case Ty_StOrUn:
if (ty->Ty.StOrUn.complete) {
VG_(printf)("Ty_StOrUn(%d,%c,%p,\"%s\")",
ty->Ty.StOrUn.szB,
ty->Ty.StOrUn.isStruct ? 'S' : 'U',
ty->Ty.StOrUn.fields,
ty->Ty.StOrUn.name ? ty->Ty.StOrUn.name
: (UChar*)"" );
if (ty->Ty.StOrUn.fields)
pp_XArray_of_pointersOrRefs( ty->Ty.StOrUn.fields );
} else {
VG_(printf)("Ty_StOrUn(INCOMPLETE,\"%s\")",
ty->Ty.StOrUn.name);
}
break;
case Ty_Array:
VG_(printf)("Ty_Array(0x%05lx,%p)",
ty->Ty.Array.typeR, ty->Ty.Array.bounds);
if (ty->Ty.Array.bounds)
pp_XArray_of_pointersOrRefs( ty->Ty.Array.bounds );
break;
case Ty_TyDef:
VG_(printf)("Ty_TyDef(0x%05lx,\"%s\")",
ty->Ty.TyDef.typeR,
ty->Ty.TyDef.name ? ty->Ty.TyDef.name
: (UChar*)"" );
break;
case Ty_Fn:
VG_(printf)("Ty_Fn");
break;
case Ty_Qual:
VG_(printf)("Ty_Qual(%c,0x%05lx)", ty->Ty.Qual.qual,
ty->Ty.Qual.typeR);
break;
case Ty_Void:
VG_(printf)("Ty_Void%s",
ty->Ty.Void.isFake ? "(fake)" : "");
break;
default: VG_(printf)("pp_Type:???");
break;
}
}
void ML_(pp_TyAdmin) ( TyAdmin* admin ) {
if (admin->cuOff != -1UL) {
VG_(printf)("<%05lx,%p> ", admin->cuOff, admin->payload);
} else {
VG_(printf)("<ff..f,%p> ", admin->payload);
}
switch (admin->tag) {
case TyA_Type: ML_(pp_Type)(admin->payload); break;
case TyA_Atom: ML_(pp_TyAtom)(admin->payload); break;
case TyA_Expr: ML_(pp_D3Expr)(admin->payload); break;
case TyA_Field: ML_(pp_TyField)(admin->payload); break;
case TyA_Bounds: ML_(pp_TyBounds)(admin->payload); break;
default: VG_(printf)("pp_TyAdmin:???"); break;
}
}
/* NOTE: this assumes that the types have all been 'resolved' (that
is, inter-type references expressed as .debug_info offsets have
been converted into pointers) */
void ML_(pp_Type_C_ishly) ( Type* ty )
{
if (!ty) {
VG_(printf)("**type=NULL**");
return;
}
switch (ty->tag) {
case Ty_Base:
if (!ty->Ty.Base.name) goto unhandled;
VG_(printf)("%s", ty->Ty.Base.name);
break;
case Ty_PorR:
ML_(pp_Type_C_ishly)(ty->Ty.PorR.typeR);
VG_(printf)("%s", ty->Ty.PorR.isPtr ? "*" : "&");
break;
case Ty_Enum:
if (!ty->Ty.Enum.name) goto unhandled;
VG_(printf)("enum %s", ty->Ty.Enum.name);
break;
case Ty_StOrUn:
if (!ty->Ty.StOrUn.name) goto unhandled;
VG_(printf)("%s %s",
ty->Ty.StOrUn.isStruct ? "struct" : "union",
ty->Ty.StOrUn.name);
break;
case Ty_Array:
ML_(pp_Type_C_ishly)(ty->Ty.Array.typeR);
if (ty->Ty.Array.bounds) {
Word w;
XArray* xa = ty->Ty.Array.bounds;
for (w = 0; w < VG_(sizeXA)(xa); w++) {
pp_TyBounds_C_ishly( *(TyBounds**)VG_(indexXA)(xa, w) );
}
} else {
VG_(printf)("%s", "[??]");
}
break;
case Ty_TyDef:
if (!ty->Ty.TyDef.name) goto unhandled;
VG_(printf)("%s", ty->Ty.TyDef.name);
break;
case Ty_Fn:
VG_(printf)("%s", "<function_type>");
break;
case Ty_Qual:
switch (ty->Ty.Qual.qual) {
case 'C': VG_(printf)("const "); break;
case 'V': VG_(printf)("volatile "); break;
default: goto unhandled;
}
ML_(pp_Type_C_ishly)(ty->Ty.Qual.typeR);
break;
case Ty_Void:
VG_(printf)("%svoid",
ty->Ty.Void.isFake ? "fake" : "");
break;
default: VG_(printf)("pp_Type_C_ishly:???");
break;
}
return;
unhandled:
ML_(pp_Type)(ty);
}
static MaybeUWord mk_MaybeUWord_Nothing ( void ) {
MaybeUWord muw;
muw.w = 0;
muw.b = False;
return muw;
}
static MaybeUWord mk_MaybeUWord_Just ( UWord w ) {
MaybeUWord muw;
muw.w = w;
muw.b = True;
return muw;
}
static MaybeUWord mul_MaybeUWord ( MaybeUWord muw1, MaybeUWord muw2 ) {
if (!muw1.b) { vg_assert(muw1.w == 0); return muw1; }
if (!muw2.b) { vg_assert(muw2.w == 0); return muw2; }
muw1.w *= muw2.w;
return muw1;
}
/* How big is this type? (post-resolved only) */
/* FIXME: check all pointers before dereferencing */
MaybeUWord ML_(sizeOfType)( Type* ty )
{
Word i;
MaybeUWord eszB;
vg_assert(ty);
switch (ty->tag) {
case Ty_Base:
vg_assert(ty->Ty.Base.szB > 0);
return mk_MaybeUWord_Just( ty->Ty.Base.szB );
case Ty_Qual:
return ML_(sizeOfType)( ty->Ty.Qual.typeR );
case Ty_TyDef:
if (!ty->Ty.TyDef.typeR)
return mk_MaybeUWord_Nothing(); /*UNKNOWN*/
return ML_(sizeOfType)( ty->Ty.TyDef.typeR );
case Ty_PorR:
vg_assert(ty->Ty.PorR.szB == 4 || ty->Ty.PorR.szB == 8);
return mk_MaybeUWord_Just( ty->Ty.PorR.szB );
case Ty_StOrUn:
return ty->Ty.StOrUn.complete
? mk_MaybeUWord_Just( ty->Ty.StOrUn.szB )
: mk_MaybeUWord_Nothing();
case Ty_Enum:
return mk_MaybeUWord_Just( ty->Ty.Enum.szB );
case Ty_Array:
if (!ty->Ty.Array.typeR)
return mk_MaybeUWord_Nothing(); /*UNKNOWN*/
eszB = ML_(sizeOfType)( ty->Ty.Array.typeR );
for (i = 0; i < VG_(sizeXA)( ty->Ty.Array.bounds ); i++) {
TyBounds* bo
= *(TyBounds**)VG_(indexXA)(ty->Ty.Array.bounds, i);
vg_assert(bo);
if (!(bo->knownL && bo->knownU))
return mk_MaybeUWord_Nothing(); /*UNKNOWN*/
eszB = mul_MaybeUWord(
eszB,
mk_MaybeUWord_Just( bo->boundU - bo->boundL + 1 ));
}
return eszB;
default:
VG_(printf)("ML_(sizeOfType): unhandled: ");
ML_(pp_Type)(ty);
VG_(printf)("\n");
vg_assert(0);
}
}
static void copy_UWord_into_XA ( XArray* /* of UChar */ xa,
UWord uw ) {
UChar buf[32];
VG_(memset)(buf, 0, sizeof(buf));
VG_(sprintf)(buf, "%lu", uw);
VG_(addBytesToXA)( xa, buf, VG_(strlen)(buf));
}
/* Describe where in the type 'offset' falls. Caller must
deallocate the resulting XArray. */
XArray* /*UChar*/ ML_(describe_type)( /*OUT*/OffT* residual_offset,
Type* ty, OffT offset )
{
XArray* xa = VG_(newXA)( ML_(dinfo_zalloc), ML_(dinfo_free),
sizeof(UChar) );
vg_assert(xa);
while (True) {
vg_assert(ty);
switch (ty->tag) {
/* These are all atomic types; there is nothing useful we can
do. */
case Ty_Enum:
case Ty_Fn:
case Ty_Void:
case Ty_PorR:
case Ty_Base:
goto done;
case Ty_StOrUn: {
Word i;
GXResult res;
MaybeUWord muw;
TyField *field = NULL, *fields;
OffT offMin = 0, offMax1 = 0;
if (!ty->Ty.StOrUn.isStruct) goto done;
fields = ty->Ty.StOrUn.fields;
if ((!fields) || VG_(sizeXA)(fields) == 0) goto done;
for (i = 0; i < VG_(sizeXA)( fields ); i++ ) {
field = *(TyField**)VG_(indexXA)( fields, i );
vg_assert(field);
vg_assert(field->loc);
/* Re data_bias in this call, we should really send in
a legitimate value. But the expression is expected
to be a constant expression, evaluation of which
will not need to use DW_OP_addr and hence we can
avoid the trouble of plumbing the data bias through
to this point (if, indeed, it has any meaning; from
which DebugInfo would we take the data bias? */
res = ML_(evaluate_Dwarf3_Expr)(
field->loc->bytes, field->loc->nbytes,
NULL/*fbGX*/, NULL/*RegSummary*/,
0/*data_bias*/,
True/*push_initial_zero*/);
if (0) {
VG_(printf)("QQQ ");
ML_(pp_GXResult)(res);
VG_(printf)("\n");
}
if (res.kind != GXR_Value)
continue;
muw = ML_(sizeOfType)( field->typeR );
if (muw.b != True)
goto done; /* size of field is unknown (?!) */
offMin = res.word;
offMax1 = offMin + muw.w;
if (offMin == offMax1)
continue;
vg_assert(offMin < offMax1);
if (offset >= offMin && offset < offMax1)
break;
}
/* Did we find a suitable field? */
vg_assert(i >= 0 && i <= VG_(sizeXA)( fields ));
if (i == VG_(sizeXA)( fields ))
goto done; /* No. Give up. */
/* Yes. 'field' is it. */
if (!field->name) goto done;
VG_(addBytesToXA)( xa, ".", 1 );
VG_(addBytesToXA)( xa, field->name,
VG_(strlen)(field->name) );
offset -= offMin;
ty = field->typeR;
if (!ty) goto done;
/* keep going; look inside the field. */
break;
}
case Ty_Array: {
MaybeUWord muw;
TyBounds* bounds;
UWord size, eszB, ix;
/* Just deal with the simple, common C-case: 1-D array,
zero based, known size. */
if (!(ty->Ty.Array.typeR && ty->Ty.Array.bounds))
goto done;
if (VG_(sizeXA)( ty->Ty.Array.bounds ) != 1) goto done;
bounds = *(TyBounds**)VG_(indexXA)( ty->Ty.Array.bounds, 0 );
vg_assert(bounds);
vg_assert(bounds->magic == TyBounds_MAGIC);
if (!(bounds->knownL && bounds->knownU && bounds->boundL == 0
&& bounds->boundU >= bounds->boundL))
goto done;
size = bounds->boundU - bounds->boundL + 1;
vg_assert(size >= 1);
muw = ML_(sizeOfType)( ty->Ty.Array.typeR );
if (muw.b != True)
goto done; /* size of element type not known */
eszB = muw.w;
if (eszB == 0) goto done;
ix = offset / eszB;
VG_(addBytesToXA)( xa, "[", 1 );
copy_UWord_into_XA( xa, ix );
VG_(addBytesToXA)( xa, "]", 1 );
ty = ty->Ty.Array.typeR;
offset -= ix * eszB;
/* keep going; look inside the array element. */
break;
}
case Ty_Qual: {
if (!ty->Ty.Qual.typeR) goto done;
ty = ty->Ty.Qual.typeR;
break;
}
case Ty_TyDef: {
if (!ty->Ty.TyDef.typeR) goto done;
ty = ty->Ty.TyDef.typeR;
break;
}
default: {
VG_(printf)("ML_(describe_type): unhandled: ");
ML_(pp_Type)(ty);
VG_(printf)("\n");
vg_assert(0);
}
}
}
done:
*residual_offset = offset;
VG_(addBytesToXA)( xa, "\0", 1 );
return xa;
}
/*--------------------------------------------------------------------*/
/*--- end tytypes.c ---*/
/*--------------------------------------------------------------------*/