| // 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; |
| } |