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