summaryrefslogtreecommitdiff
path: root/src/libmach/machdata.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmach/machdata.c')
-rw-r--r--src/libmach/machdata.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/src/libmach/machdata.c b/src/libmach/machdata.c
new file mode 100644
index 000000000..425a92179
--- /dev/null
+++ b/src/libmach/machdata.c
@@ -0,0 +1,477 @@
+// 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+%#lux", 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, "%lux", 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(%.8lux%.8lux)", 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(%.8lux%.8lux)", h&((1L<<20)-1L), 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(%.8lux)", 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;
+}