mirror of
https://github.com/golang/go
synced 2024-10-04 22:21:22 -06:00
7f417d8d66
R=rsc CC=golang-dev https://golang.org/cl/5316059
478 lines
10 KiB
C
478 lines
10 KiB
C
// Inferno libmach/machdata.c
|
|
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/machdata.c
|
|
//
|
|
// Copyright © 1994-1999 Lucent Technologies Inc.
|
|
// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
|
|
// Portions Copyright © 1997-1999 Vita Nuova Limited.
|
|
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
|
|
// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
|
|
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
/*
|
|
* Debugger utilities shared by at least two architectures
|
|
*/
|
|
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <mach.h>
|
|
|
|
#define STARTSYM "_main"
|
|
#define PROFSYM "_mainp"
|
|
#define FRAMENAME ".frame"
|
|
|
|
extern Machdata mipsmach;
|
|
|
|
int asstype = AMIPS; /* disassembler type */
|
|
Machdata *machdata; /* machine-dependent functions */
|
|
|
|
int
|
|
localaddr(Map *map, char *fn, char *var, uvlong *r, Rgetter rget)
|
|
{
|
|
Symbol s;
|
|
uvlong fp, pc, sp, link;
|
|
|
|
if (!lookup(fn, 0, &s)) {
|
|
werrstr("function not found");
|
|
return -1;
|
|
}
|
|
pc = rget(map, mach->pc);
|
|
sp = rget(map, mach->sp);
|
|
if(mach->link)
|
|
link = rget(map, mach->link);
|
|
else
|
|
link = 0;
|
|
fp = machdata->findframe(map, s.value, pc, sp, link);
|
|
if (fp == 0) {
|
|
werrstr("stack frame not found");
|
|
return -1;
|
|
}
|
|
|
|
if (!var || !var[0]) {
|
|
*r = fp;
|
|
return 1;
|
|
}
|
|
|
|
if (findlocal(&s, var, &s) == 0) {
|
|
werrstr("local variable not found");
|
|
return -1;
|
|
}
|
|
|
|
switch (s.class) {
|
|
case CAUTO:
|
|
*r = fp - s.value;
|
|
break;
|
|
case CPARAM: /* assume address size is stack width */
|
|
*r = fp + s.value + mach->szaddr;
|
|
break;
|
|
default:
|
|
werrstr("local variable not found: %d", s.class);
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Print value v as s.name[+offset] if possible, or just v.
|
|
*/
|
|
int
|
|
symoff(char *buf, int n, uvlong v, int space)
|
|
{
|
|
Symbol s;
|
|
int r;
|
|
int32 delta;
|
|
|
|
r = delta = 0; /* to shut compiler up */
|
|
if (v) {
|
|
r = findsym(v, space, &s);
|
|
if (r)
|
|
delta = v-s.value;
|
|
if (delta < 0)
|
|
delta = -delta;
|
|
}
|
|
if (v == 0 || r == 0)
|
|
return snprint(buf, n, "%llux", v);
|
|
if (s.type != 't' && s.type != 'T' && delta >= 4096)
|
|
return snprint(buf, n, "%llux", v);
|
|
else if (delta)
|
|
return snprint(buf, n, "%s+%#ux", s.name, delta);
|
|
else
|
|
return snprint(buf, n, "%s", s.name);
|
|
}
|
|
|
|
/*
|
|
* Format floating point registers
|
|
*
|
|
* Register codes in format field:
|
|
* 'X' - print as 32-bit hexadecimal value
|
|
* 'F' - 64-bit double register when modif == 'F'; else 32-bit single reg
|
|
* 'f' - 32-bit ieee float
|
|
* '8' - big endian 80-bit ieee extended float
|
|
* '3' - little endian 80-bit ieee extended float with hole in bytes 8&9
|
|
*/
|
|
int
|
|
fpformat(Map *map, Reglist *rp, char *buf, int n, int modif)
|
|
{
|
|
char reg[12];
|
|
uint32 r;
|
|
|
|
switch(rp->rformat)
|
|
{
|
|
case 'X':
|
|
if (get4(map, rp->roffs, &r) < 0)
|
|
return -1;
|
|
snprint(buf, n, "%ux", r);
|
|
break;
|
|
case 'F': /* first reg of double reg pair */
|
|
if (modif == 'F')
|
|
if ((rp->rformat=='F') || (((rp+1)->rflags&RFLT) && (rp+1)->rformat == 'f')) {
|
|
if (get1(map, rp->roffs, (uchar *)reg, 8) < 0)
|
|
return -1;
|
|
machdata->dftos(buf, n, reg);
|
|
if (rp->rformat == 'F')
|
|
return 1;
|
|
return 2;
|
|
}
|
|
/* treat it like 'f' */
|
|
if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
|
|
return -1;
|
|
machdata->sftos(buf, n, reg);
|
|
break;
|
|
case 'f': /* 32 bit float */
|
|
if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
|
|
return -1;
|
|
machdata->sftos(buf, n, reg);
|
|
break;
|
|
case '3': /* little endian ieee 80 with hole in bytes 8&9 */
|
|
if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
|
|
return -1;
|
|
memmove(reg+10, reg+8, 2); /* open hole */
|
|
memset(reg+8, 0, 2); /* fill it */
|
|
leieee80ftos(buf, n, reg);
|
|
break;
|
|
case '8': /* big-endian ieee 80 */
|
|
if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
|
|
return -1;
|
|
beieee80ftos(buf, n, reg);
|
|
break;
|
|
default: /* unknown */
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
char *
|
|
_hexify(char *buf, uint32 p, int zeros)
|
|
{
|
|
uint32 d;
|
|
|
|
d = p/16;
|
|
if(d)
|
|
buf = _hexify(buf, d, zeros-1);
|
|
else
|
|
while(zeros--)
|
|
*buf++ = '0';
|
|
*buf++ = "0123456789abcdef"[p&0x0f];
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* These routines assume that if the number is representable
|
|
* in IEEE floating point, it will be representable in the native
|
|
* double format. Naive but workable, probably.
|
|
*/
|
|
int
|
|
ieeedftos(char *buf, int n, uint32 h, uint32 l)
|
|
{
|
|
double fr;
|
|
int exp;
|
|
|
|
if (n <= 0)
|
|
return 0;
|
|
|
|
|
|
if(h & (1L<<31)){
|
|
*buf++ = '-';
|
|
h &= ~(1L<<31);
|
|
}else
|
|
*buf++ = ' ';
|
|
n--;
|
|
if(l == 0 && h == 0)
|
|
return snprint(buf, n, "0.");
|
|
exp = (h>>20) & ((1L<<11)-1L);
|
|
if(exp == 0)
|
|
return snprint(buf, n, "DeN(%.8ux%.8ux)", h, l);
|
|
if(exp == ((1L<<11)-1L)){
|
|
if(l==0 && (h&((1L<<20)-1L)) == 0)
|
|
return snprint(buf, n, "Inf");
|
|
else
|
|
return snprint(buf, n, "NaN(%.8ux%.8ux)", h&((1<<20)-1), l);
|
|
}
|
|
exp -= (1L<<10) - 2L;
|
|
fr = l & ((1L<<16)-1L);
|
|
fr /= 1L<<16;
|
|
fr += (l>>16) & ((1L<<16)-1L);
|
|
fr /= 1L<<16;
|
|
fr += (h & (1L<<20)-1L) | (1L<<20);
|
|
fr /= 1L<<21;
|
|
fr = ldexp(fr, exp);
|
|
return snprint(buf, n, "%.18g", fr);
|
|
}
|
|
|
|
int
|
|
ieeesftos(char *buf, int n, uint32 h)
|
|
{
|
|
double fr;
|
|
int exp;
|
|
|
|
if (n <= 0)
|
|
return 0;
|
|
|
|
if(h & (1L<<31)){
|
|
*buf++ = '-';
|
|
h &= ~(1L<<31);
|
|
}else
|
|
*buf++ = ' ';
|
|
n--;
|
|
if(h == 0)
|
|
return snprint(buf, n, "0.");
|
|
exp = (h>>23) & ((1L<<8)-1L);
|
|
if(exp == 0)
|
|
return snprint(buf, n, "DeN(%.8ux)", h);
|
|
if(exp == ((1L<<8)-1L)){
|
|
if((h&((1L<<23)-1L)) == 0)
|
|
return snprint(buf, n, "Inf");
|
|
else
|
|
return snprint(buf, n, "NaN(%.8lux)", h&((1L<<23)-1L));
|
|
}
|
|
exp -= (1L<<7) - 2L;
|
|
fr = (h & ((1L<<23)-1L)) | (1L<<23);
|
|
fr /= 1L<<24;
|
|
fr = ldexp(fr, exp);
|
|
return snprint(buf, n, "%.9g", fr);
|
|
}
|
|
|
|
int
|
|
beieeesftos(char *buf, int n, void *s)
|
|
{
|
|
return ieeesftos(buf, n, beswal(*(uint32*)s));
|
|
}
|
|
|
|
int
|
|
beieeedftos(char *buf, int n, void *s)
|
|
{
|
|
return ieeedftos(buf, n, beswal(*(uint32*)s), beswal(((uint32*)(s))[1]));
|
|
}
|
|
|
|
int
|
|
leieeesftos(char *buf, int n, void *s)
|
|
{
|
|
return ieeesftos(buf, n, leswal(*(uint32*)s));
|
|
}
|
|
|
|
int
|
|
leieeedftos(char *buf, int n, void *s)
|
|
{
|
|
return ieeedftos(buf, n, leswal(((uint32*)(s))[1]), leswal(*(uint32*)s));
|
|
}
|
|
|
|
/* packed in 12 bytes, with s[2]==s[3]==0; mantissa starts at s[4]*/
|
|
int
|
|
beieee80ftos(char *buf, int n, void *s)
|
|
{
|
|
uchar *reg = (uchar*)s;
|
|
int i;
|
|
uint32 x;
|
|
uchar ieee[8+8]; /* room for slop */
|
|
uchar *p, *q;
|
|
|
|
memset(ieee, 0, sizeof(ieee));
|
|
/* sign */
|
|
if(reg[0] & 0x80)
|
|
ieee[0] |= 0x80;
|
|
|
|
/* exponent */
|
|
x = ((reg[0]&0x7F)<<8) | reg[1];
|
|
if(x == 0) /* number is ±0 */
|
|
goto done;
|
|
if(x == 0x7FFF){
|
|
if(memcmp(reg+4, ieee+1, 8) == 0){ /* infinity */
|
|
x = 2047;
|
|
}else{ /* NaN */
|
|
x = 2047;
|
|
ieee[7] = 0x1; /* make sure */
|
|
}
|
|
ieee[0] |= x>>4;
|
|
ieee[1] |= (x&0xF)<<4;
|
|
goto done;
|
|
}
|
|
x -= 0x3FFF; /* exponent bias */
|
|
x += 1023;
|
|
if(x >= (1<<11) || ((reg[4]&0x80)==0 && x!=0))
|
|
return snprint(buf, n, "not in range");
|
|
ieee[0] |= x>>4;
|
|
ieee[1] |= (x&0xF)<<4;
|
|
|
|
/* mantissa */
|
|
p = reg+4;
|
|
q = ieee+1;
|
|
for(i=0; i<56; i+=8, p++, q++){ /* move one byte */
|
|
x = (p[0]&0x7F) << 1;
|
|
if(p[1] & 0x80)
|
|
x |= 1;
|
|
q[0] |= x>>4;
|
|
q[1] |= (x&0xF)<<4;
|
|
}
|
|
done:
|
|
return beieeedftos(buf, n, (void*)ieee);
|
|
}
|
|
|
|
int
|
|
leieee80ftos(char *buf, int n, void *s)
|
|
{
|
|
int i;
|
|
char *cp;
|
|
char b[12];
|
|
|
|
cp = (char*) s;
|
|
for(i=0; i<12; i++)
|
|
b[11-i] = *cp++;
|
|
return beieee80ftos(buf, n, b);
|
|
}
|
|
|
|
int
|
|
cisctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
|
|
{
|
|
Symbol s;
|
|
int found, i;
|
|
uvlong opc, moved;
|
|
|
|
USED(link);
|
|
i = 0;
|
|
opc = 0;
|
|
while(pc && opc != pc) {
|
|
moved = pc2sp(pc);
|
|
if (moved == ~0)
|
|
break;
|
|
found = findsym(pc, CTEXT, &s);
|
|
if (!found)
|
|
break;
|
|
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
|
|
break;
|
|
|
|
sp += moved;
|
|
opc = pc;
|
|
if (geta(map, sp, &pc) < 0)
|
|
break;
|
|
(*trace)(map, pc, sp, &s);
|
|
sp += mach->szaddr; /*assumes address size = stack width*/
|
|
if(++i > 40)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int
|
|
risctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
|
|
{
|
|
int i;
|
|
Symbol s, f;
|
|
uvlong oldpc;
|
|
|
|
i = 0;
|
|
while(findsym(pc, CTEXT, &s)) {
|
|
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
|
|
break;
|
|
|
|
if(pc == s.value) /* at first instruction */
|
|
f.value = 0;
|
|
else if(findlocal(&s, FRAMENAME, &f) == 0)
|
|
break;
|
|
|
|
oldpc = pc;
|
|
if(s.type == 'L' || s.type == 'l' || pc <= s.value+mach->pcquant)
|
|
pc = link;
|
|
else
|
|
if (geta(map, sp, &pc) < 0)
|
|
break;
|
|
|
|
if(pc == 0 || (pc == oldpc && f.value == 0))
|
|
break;
|
|
|
|
sp += f.value;
|
|
(*trace)(map, pc-8, sp, &s);
|
|
|
|
if(++i > 40)
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
uvlong
|
|
ciscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
|
|
{
|
|
Symbol s;
|
|
uvlong moved;
|
|
|
|
USED(link);
|
|
for(;;) {
|
|
moved = pc2sp(pc);
|
|
if (moved == ~0)
|
|
break;
|
|
sp += moved;
|
|
findsym(pc, CTEXT, &s);
|
|
if (addr == s.value)
|
|
return sp;
|
|
if (geta(map, sp, &pc) < 0)
|
|
break;
|
|
sp += mach->szaddr; /*assumes sizeof(addr) = stack width*/
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uvlong
|
|
riscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
|
|
{
|
|
Symbol s, f;
|
|
|
|
while (findsym(pc, CTEXT, &s)) {
|
|
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
|
|
break;
|
|
|
|
if(pc == s.value) /* at first instruction */
|
|
f.value = 0;
|
|
else
|
|
if(findlocal(&s, FRAMENAME, &f) == 0)
|
|
break;
|
|
|
|
sp += f.value;
|
|
if (s.value == addr)
|
|
return sp;
|
|
|
|
if (s.type == 'L' || s.type == 'l' || pc-s.value <= mach->szaddr*2)
|
|
pc = link;
|
|
else
|
|
if (geta(map, sp-f.value, &pc) < 0)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|