Commit ed5e39a6 authored by Rob Pike's avatar Rob Pike

Add 6db

SVN=122505
parent ef61a4cb
// Inferno libmach/bootexec.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/bootexec.h
//
// 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.
struct coffsect
{
char name[8];
ulong phys;
ulong virt;
ulong size;
ulong fptr;
ulong fptrreloc;
ulong fptrlineno;
ulong nrelocnlineno;
ulong flags;
};
/*
* proprietary exec headers, needed to bootstrap various machines
*/
struct mipsexec
{
short mmagic; /* (0x160) mips magic number */
short nscns; /* (unused) number of sections */
long timdat; /* (unused) time & date stamp */
long symptr; /* offset to symbol table */
long nsyms; /* size of symbol table */
short opthdr; /* (0x38) sizeof(optional hdr) */
short pcszs; /* flags */
short amagic; /* see above */
short vstamp; /* version stamp */
long tsize; /* text size in bytes */
long dsize; /* initialized data */
long bsize; /* uninitialized data */
long mentry; /* entry pt. */
long text_start; /* base of text used for this file */
long data_start; /* base of data used for this file */
long bss_start; /* base of bss used for this file */
long gprmask; /* general purpose register mask */
union{
long cprmask[4]; /* co-processor register masks */
long pcsize;
};
long gp_value; /* the gp value used for this object */
};
struct mips4kexec
{
struct mipsexec h;
struct coffsect itexts;
struct coffsect idatas;
struct coffsect ibsss;
};
struct sparcexec
{
short sjunk; /* dynamic bit and version number */
short smagic; /* 0407 */
ulong stext;
ulong sdata;
ulong sbss;
ulong ssyms;
ulong sentry;
ulong strsize;
ulong sdrsize;
};
struct nextexec
{
/* UNUSED
struct nexthdr{
ulong nmagic;
ulong ncputype;
ulong ncpusubtype;
ulong nfiletype;
ulong ncmds;
ulong nsizeofcmds;
ulong nflags;
};
struct nextcmd{
ulong cmd;
ulong cmdsize;
uchar segname[16];
ulong vmaddr;
ulong vmsize;
ulong fileoff;
ulong filesize;
ulong maxprot;
ulong initprot;
ulong nsects;
ulong flags;
}textc;
struct nextsect{
char sectname[16];
char segname[16];
ulong addr;
ulong size;
ulong offset;
ulong align;
ulong reloff;
ulong nreloc;
ulong flags;
ulong reserved1;
ulong reserved2;
}texts;
struct nextcmd datac;
struct nextsect datas;
struct nextsect bsss;
struct nextsym{
ulong cmd;
ulong cmdsize;
ulong symoff;
ulong nsyms;
ulong spoff;
ulong pcoff;
}symc;
*/
};
struct i386exec
{
/* UNUSED
struct i386coff{
ulong isectmagic;
ulong itime;
ulong isyms;
ulong insyms;
ulong iflags;
};
struct i386hdr{
ulong imagic;
ulong itextsize;
ulong idatasize;
ulong ibsssize;
ulong ientry;
ulong itextstart;
ulong idatastart;
};
struct coffsect itexts;
struct coffsect idatas;
struct coffsect ibsss;
struct coffsect icomments;
*/
};
// Inferno libmach/a.out.h and libmach/mach.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/a.out.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/mach.h
//
// 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.
/*
* Architecture-dependent application data
* This is just Plan 9's mach.h, tweaked to support only amd64 for now.
*/
/*
* Start of Plan 9 a.out.h
* Don't use system a.out; instead just lay in the Plan 9 one for now.
*/
/*
#include "a.out.h"
*/
typedef struct Exec Exec;
struct Exec
{
long magic; /* magic number */
long text; /* size of text segment */
long data; /* size of initialized data */
long bss; /* size of uninitialized data */
long syms; /* size of symbol table */
long entry; /* entry point */
long spsz; /* size of pc/sp offset table */
long pcsz; /* size of pc/line number table */
};
#define HDR_MAGIC 0x00008000 /* header expansion */
#define _MAGIC(f, b) ((f)|((((4*(b))+0)*(b))+7))
#define A_MAGIC _MAGIC(0, 8) /* 68020 */
#define I_MAGIC _MAGIC(0, 11) /* intel 386 */
#define J_MAGIC _MAGIC(0, 12) /* intel 960 (retired) */
#define K_MAGIC _MAGIC(0, 13) /* sparc */
#define V_MAGIC _MAGIC(0, 16) /* mips 3000 BE */
#define X_MAGIC _MAGIC(0, 17) /* att dsp 3210 (retired) */
#define M_MAGIC _MAGIC(0, 18) /* mips 4000 BE */
#define D_MAGIC _MAGIC(0, 19) /* amd 29000 (retired) */
#define E_MAGIC _MAGIC(0, 20) /* arm */
#define Q_MAGIC _MAGIC(0, 21) /* powerpc */
#define N_MAGIC _MAGIC(0, 22) /* mips 4000 LE */
#define L_MAGIC _MAGIC(0, 23) /* dec alpha */
#define P_MAGIC _MAGIC(0, 24) /* mips 3000 LE */
#define U_MAGIC _MAGIC(0, 25) /* sparc64 */
#define S_MAGIC _MAGIC(HDR_MAGIC, 26) /* amd64 */
#define T_MAGIC _MAGIC(HDR_MAGIC, 27) /* powerpc64 */
#define MIN_MAGIC 8
#define MAX_MAGIC 27 /* <= 90 */
#define DYN_MAGIC 0x80000000 /* dlm */
typedef struct Sym Sym;
struct Sym
{
vlong value;
uint sig;
char type;
char *name;
};
/*
* End of Plan 9 a.out.h
* Don't use system a.out; instead just lay in the Plan 9 one for now.
*/
/*
* Supported architectures:
* mips,
* 68020,
* i386,
* amd64,
* sparc,
* sparc64,
* mips2 (R4000)
* arm
* powerpc,
* powerpc64
* alpha
*/
enum
{
MMIPS, /* machine types */
MSPARC,
M68020,
MI386,
MI960, /* retired */
M3210, /* retired */
MMIPS2,
NMIPS2,
M29000, /* retired */
MARM,
MPOWER,
MALPHA,
NMIPS,
MSPARC64,
MAMD64,
MPOWER64,
/* types of executables */
FNONE = 0, /* unidentified */
FMIPS, /* v.out */
FMIPSB, /* mips bootable */
FSPARC, /* k.out */
FSPARCB, /* Sparc bootable */
F68020, /* 2.out */
F68020B, /* 68020 bootable */
FNEXTB, /* Next bootable */
FI386, /* 8.out */
FI386B, /* I386 bootable */
FI960, /* retired */
FI960B, /* retired */
F3210, /* retired */
FMIPS2BE, /* 4.out */
F29000, /* retired */
FARM, /* 5.out */
FARMB, /* ARM bootable */
FPOWER, /* q.out */
FPOWERB, /* power pc bootable */
FMIPS2LE, /* 0.out */
FALPHA, /* 7.out */
FALPHAB, /* DEC Alpha bootable */
FMIPSLE, /* 3k little endian */
FSPARC64, /* u.out */
FAMD64, /* 6.out */
FAMD64B, /* 6.out bootable */
FPOWER64, /* 9.out */
FPOWER64B, /* 9.out bootable */
ANONE = 0, /* dissembler types */
AMIPS,
AMIPSCO, /* native mips */
ASPARC,
ASUNSPARC, /* native sun */
A68020,
AI386,
AI8086, /* oh god */
AI960, /* retired */
A29000, /* retired */
AARM,
APOWER,
AALPHA,
ASPARC64,
AAMD64,
APOWER64,
/* object file types */
Obj68020 = 0, /* .2 */
ObjSparc, /* .k */
ObjMips, /* .v */
Obj386, /* .8 */
Obj960, /* retired */
Obj3210, /* retired */
ObjMips2, /* .4 */
Obj29000, /* retired */
ObjArm, /* .5 */
ObjPower, /* .q */
ObjMips2le, /* .0 */
ObjAlpha, /* .7 */
ObjSparc64, /* .u */
ObjAmd64, /* .6 */
ObjSpim, /* .0 */
ObjPower64, /* .9 */
Maxobjtype,
CNONE = 0, /* symbol table classes */
CAUTO,
CPARAM,
CSTAB,
CTEXT,
CDATA,
CANY, /* to look for any class */
};
typedef struct Map Map;
typedef struct Symbol Symbol;
typedef struct Reglist Reglist;
typedef struct Mach Mach;
typedef struct Machdata Machdata;
/*
* Structure to map a segment to a position in a file
*/
struct Map {
int nsegs; /* number of segments */
struct segment { /* per-segment map */
char *name; /* the segment name */
int fd; /* file descriptor */
int inuse; /* in use - not in use */
int cache; /* should cache reads? */
uvlong b; /* base */
uvlong e; /* end */
vlong f; /* offset within file */
} seg[1]; /* actually n of these */
};
/*
* Internal structure describing a symbol table entry
*/
struct Symbol {
void *handle; /* used internally - owning func */
struct {
char *name;
vlong value; /* address or stack offset */
char type; /* as in a.out.h */
char class; /* as above */
int index; /* in findlocal, globalsym, textsym */
};
};
/*
* machine register description
*/
struct Reglist {
char *rname; /* register name */
short roffs; /* offset in u-block */
char rflags; /* INTEGER/FLOAT, WRITABLE */
char rformat; /* print format: 'x', 'X', 'f', '8', '3', 'Y', 'W' */
};
enum { /* bits in rflags field */
RINT = (0<<0),
RFLT = (1<<0),
RRDONLY = (1<<1),
};
/*
* Machine-dependent data is stored in two structures:
* Mach - miscellaneous general parameters
* Machdata - jump vector of service functions used by debuggers
*
* Mach is defined in ?.c and set in executable.c
*
* Machdata is defined in ?db.c
* and set in the debugger startup.
*/
struct Mach{
char *name;
int mtype; /* machine type code */
Reglist *reglist; /* register set */
long regsize; /* sizeof registers in bytes */
long fpregsize; /* sizeof fp registers in bytes */
char *pc; /* pc name */
char *sp; /* sp name */
char *link; /* link register name */
char *sbreg; /* static base register name */
uvlong sb; /* static base register value */
int pgsize; /* page size */
uvlong kbase; /* kernel base address */
uvlong ktmask; /* ktzero = kbase & ~ktmask */
uvlong utop; /* user stack top */
int pcquant; /* quantization of pc */
int szaddr; /* sizeof(void*) */
int szreg; /* sizeof(register) */
int szfloat; /* sizeof(float) */
int szdouble; /* sizeof(double) */
};
extern Mach *mach; /* Current machine */
typedef uvlong (*Rgetter)(Map*, char*);
typedef void (*Tracer)(Map*, uvlong, uvlong, Symbol*);
struct Machdata { /* Machine-dependent debugger support */
uchar bpinst[4]; /* break point instr. */
short bpsize; /* size of break point instr. */
ushort (*swab)(ushort); /* ushort to local byte order */
ulong (*swal)(ulong); /* ulong to local byte order */
uvlong (*swav)(uvlong); /* uvlong to local byte order */
int (*ctrace)(Map*, uvlong, uvlong, uvlong, Tracer); /* C traceback */
uvlong (*findframe)(Map*, uvlong, uvlong, uvlong, uvlong);/* frame finder */
char* (*excep)(Map*, Rgetter); /* last exception */
ulong (*bpfix)(uvlong); /* breakpoint fixup */
int (*sftos)(char*, int, void*); /* single precision float */
int (*dftos)(char*, int, void*); /* double precision float */
int (*foll)(Map*, uvlong, Rgetter, uvlong*);/* follow set */
int (*das)(Map*, uvlong, char, char*, int); /* symbolic disassembly */
int (*hexinst)(Map*, uvlong, char*, int); /* hex disassembly */
int (*instsize)(Map*, uvlong); /* instruction size */
};
/*
* Common a.out header describing all architectures
*/
typedef struct Fhdr
{
char *name; /* identifier of executable */
uchar type; /* file type - see codes above */
uchar hdrsz; /* header size */
uchar _magic; /* _MAGIC() magic */
uchar spare;
long magic; /* magic number */
uvlong txtaddr; /* text address */
vlong txtoff; /* start of text in file */
uvlong dataddr; /* start of data segment */
vlong datoff; /* offset to data seg in file */
vlong symoff; /* offset of symbol table in file */
uvlong entry; /* entry point */
vlong sppcoff; /* offset of sp-pc table in file */
vlong lnpcoff; /* offset of line number-pc table in file */
long txtsz; /* text size */
long datsz; /* size of data seg */
long bsssz; /* size of bss */
long symsz; /* size of symbol table */
long sppcsz; /* size of sp-pc table */
long lnpcsz; /* size of line number-pc table */
} Fhdr;
extern int asstype; /* dissembler type - machdata.c */
extern Machdata *machdata; /* jump vector - machdata.c */
Map* attachproc(int, int, int, Fhdr*);
int beieee80ftos(char*, int, void*);
int beieeesftos(char*, int, void*);
int beieeedftos(char*, int, void*);
ushort beswab(ushort);
ulong beswal(ulong);
uvlong beswav(uvlong);
uvlong ciscframe(Map*, uvlong, uvlong, uvlong, uvlong);
int cisctrace(Map*, uvlong, uvlong, uvlong, Tracer);
int crackhdr(int fd, Fhdr*);
uvlong file2pc(char*, long);
int fileelem(Sym**, uchar *, char*, int);
long fileline(char*, int, uvlong);
int filesym(int, char*, int);
int findlocal(Symbol*, char*, Symbol*);
int findseg(Map*, char*);
int findsym(uvlong, int, Symbol *);
int fnbound(uvlong, uvlong*);
int fpformat(Map*, Reglist*, char*, int, int);
int get1(Map*, uvlong, uchar*, int);
int get2(Map*, uvlong, ushort*);
int get4(Map*, uvlong, ulong*);
int get8(Map*, uvlong, uvlong*);
int geta(Map*, uvlong, uvlong*);
int getauto(Symbol*, int, int, Symbol*);
Sym* getsym(int);
int globalsym(Symbol *, int);
char* _hexify(char*, ulong, int);
int ieeesftos(char*, int, ulong);
int ieeedftos(char*, int, ulong, ulong);
int isar(Biobuf*);
int leieee80ftos(char*, int, void*);
int leieeesftos(char*, int, void*);
int leieeedftos(char*, int, void*);
ushort leswab(ushort);
ulong leswal(ulong);
uvlong leswav(uvlong);
uvlong line2addr(long, uvlong, uvlong);
Map* loadmap(Map*, int, Fhdr*);
int localaddr(Map*, char*, char*, uvlong*, Rgetter);
int localsym(Symbol*, int);
int lookup(char*, char*, Symbol*);
void machbytype(int);
int machbyname(char*);
int nextar(Biobuf*, int, char*);
Map* newmap(Map*, int);
void objtraverse(void(*)(Sym*, void*), void*);
int objtype(Biobuf*, char**);
uvlong pc2sp(uvlong);
long pc2line(uvlong);
int put1(Map*, uvlong, uchar*, int);
int put2(Map*, uvlong, ushort);
int put4(Map*, uvlong, ulong);
int put8(Map*, uvlong, uvlong);
int puta(Map*, uvlong, uvlong);
int readar(Biobuf*, int, vlong, int);
int readobj(Biobuf*, int);
uvlong riscframe(Map*, uvlong, uvlong, uvlong, uvlong);
int risctrace(Map*, uvlong, uvlong, uvlong, Tracer);
int setmap(Map*, int, uvlong, uvlong, vlong, char*);
Sym* symbase(long*);
int syminit(int, Fhdr*);
int symoff(char*, int, uvlong, int);
void textseg(uvlong, Fhdr*);
int textsym(Symbol*, int);
void unusemap(Map*, int);
// Inferno utils/libmach/ureg6.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/ureg6.h
//
// 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.
struct Ureg {
u64int ax;
u64int bx;
u64int cx;
u64int dx;
u64int si;
u64int di;
u64int bp;
u64int r8;
u64int r9;
u64int r10;
u64int r11;
u64int r12;
u64int r13;
u64int r14;
u64int r15;
u16int ds;
u16int es;
u16int fs;
u16int gs;
u64int type;
u64int error; /* error code (or zero) */
u64int ip; /* pc */
u64int cs; /* old context */
u64int flags; /* old flags */
u64int sp; /* sp */
u64int ss; /* old stack segment */
};
// Inferno utils/libmach/ureg8.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/ureg8.h
//
// 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.
struct Ureg
{
ulong di; /* general registers */
ulong si; /* ... */
ulong bp; /* ... */
ulong nsp;
ulong bx; /* ... */
ulong dx; /* ... */
ulong cx; /* ... */
ulong ax; /* ... */
ulong gs; /* data segments */
ulong fs; /* ... */
ulong es; /* ... */
ulong ds; /* ... */
ulong trap; /* trap type */
ulong ecode; /* error code (or zero) */
ulong pc; /* pc */
ulong cs; /* old context */
ulong flags; /* old flags */
union {
ulong usp;
ulong sp;
};
ulong ss; /* old stack segment */
};
......@@ -40,3 +40,8 @@ echo; echo; echo %%%% making 6g %%%%; echo
cd 6g
make install
cd ..
echo; echo; echo %%%% making db %%%%; echo
cd db
make install
cd ..
// Inferno libmach/6.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/6.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.
/*
* amd64 definition
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "ureg_amd64.h"
#include <mach_amd64.h>
#define REGOFF(x) offsetof(struct Ureg, x)
#define REGSIZE sizeof(struct Ureg)
#define FP_CTLS(x) (REGSIZE+2*(x))
#define FP_CTL(x) (REGSIZE+4*(x))
#define FP_REG(x) (FP_CTL(8)+16*(x))
#define XM_REG(x) (FP_CTL(8)+8*16+16*(x))
#define FPREGSIZE 512 /* TO DO? currently only 0x1A0 used */
Reglist amd64reglist[] = {
{"AX", REGOFF(ax), RINT, 'Y'},
{"BX", REGOFF(bx), RINT, 'Y'},
{"CX", REGOFF(cx), RINT, 'Y'},
{"DX", REGOFF(dx), RINT, 'Y'},
{"SI", REGOFF(si), RINT, 'Y'},
{"DI", REGOFF(di), RINT, 'Y'},
{"BP", REGOFF(bp), RINT, 'Y'},
{"R8", REGOFF(r8), RINT, 'Y'},
{"R9", REGOFF(r9), RINT, 'Y'},
{"R10", REGOFF(r10), RINT, 'Y'},
{"R11", REGOFF(r11), RINT, 'Y'},
{"R12", REGOFF(r12), RINT, 'Y'},
{"R13", REGOFF(r13), RINT, 'Y'},
{"R14", REGOFF(r14), RINT, 'Y'},
{"R15", REGOFF(r15), RINT, 'Y'},
{"DS", REGOFF(ds), RINT, 'x'},
{"ES", REGOFF(es), RINT, 'x'},
{"FS", REGOFF(fs), RINT, 'x'},
{"GS", REGOFF(gs), RINT, 'x'},
{"TYPE", REGOFF(type), RINT, 'Y'},
{"TRAP", REGOFF(type), RINT, 'Y'}, /* alias for acid */
{"ERROR", REGOFF(error), RINT, 'Y'},
{"IP", REGOFF(ip), RINT, 'Y'},
{"PC", REGOFF(ip), RINT, 'Y'}, /* alias for acid */
{"CS", REGOFF(cs), RINT, 'Y'},
{"FLAGS", REGOFF(flags), RINT, 'Y'},
{"SP", REGOFF(sp), RINT, 'Y'},
{"SS", REGOFF(ss), RINT, 'Y'},
{"FCW", FP_CTLS(0), RFLT, 'x'},
{"FSW", FP_CTLS(1), RFLT, 'x'},
{"FTW", FP_CTLS(2), RFLT, 'b'},
{"FOP", FP_CTLS(3), RFLT, 'x'},
{"RIP", FP_CTL(2), RFLT, 'Y'},
{"RDP", FP_CTL(4), RFLT, 'Y'},
{"MXCSR", FP_CTL(6), RFLT, 'X'},
{"MXCSRMASK", FP_CTL(7), RFLT, 'X'},
{"M0", FP_REG(0), RFLT, 'F'}, /* assumes double */
{"M1", FP_REG(1), RFLT, 'F'},
{"M2", FP_REG(2), RFLT, 'F'},
{"M3", FP_REG(3), RFLT, 'F'},
{"M4", FP_REG(4), RFLT, 'F'},
{"M5", FP_REG(5), RFLT, 'F'},
{"M6", FP_REG(6), RFLT, 'F'},
{"M7", FP_REG(7), RFLT, 'F'},
{"X0", XM_REG(0), RFLT, 'F'}, /* assumes double */
{"X1", XM_REG(1), RFLT, 'F'},
{"X2", XM_REG(2), RFLT, 'F'},
{"X3", XM_REG(3), RFLT, 'F'},
{"X4", XM_REG(4), RFLT, 'F'},
{"X5", XM_REG(5), RFLT, 'F'},
{"X6", XM_REG(6), RFLT, 'F'},
{"X7", XM_REG(7), RFLT, 'F'},
{"X8", XM_REG(8), RFLT, 'F'},
{"X9", XM_REG(9), RFLT, 'F'},
{"X10", XM_REG(10), RFLT, 'F'},
{"X11", XM_REG(11), RFLT, 'F'},
{"X12", XM_REG(12), RFLT, 'F'},
{"X13", XM_REG(13), RFLT, 'F'},
{"X14", XM_REG(14), RFLT, 'F'},
{"X15", XM_REG(15), RFLT, 'F'},
{"X16", XM_REG(16), RFLT, 'F'},
/*
{"F0", FP_REG(7), RFLT, '3'},
{"F1", FP_REG(6), RFLT, '3'},
{"F2", FP_REG(5), RFLT, '3'},
{"F3", FP_REG(4), RFLT, '3'},
{"F4", FP_REG(3), RFLT, '3'},
{"F5", FP_REG(2), RFLT, '3'},
{"F6", FP_REG(1), RFLT, '3'},
{"F7", FP_REG(0), RFLT, '3'},
*/
{ 0 }
};
Mach mamd64=
{
"amd64",
MAMD64, /* machine type */
amd64reglist, /* register list */
REGSIZE, /* size of registers in bytes */
FPREGSIZE, /* size of fp registers in bytes */
"PC", /* name of PC */
"SP", /* name of SP */
0, /* link register */
"setSB", /* static base register name (bogus anyways) */
0, /* static base register value */
0x1000, /* page size */
0xFFFFFFFF80110000ULL, /* kernel base */
0xFFFF800000000000ULL, /* kernel text mask */
0x00007FFFFFFFF000ULL, /* user stack top */
1, /* quantization of pc */
8, /* szaddr */
4, /* szreg */
4, /* szfloat */
8, /* szdouble */
};
// Inferno libmach/6obj.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/6obj.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.
/*
* 6obj.c - identify and parse an amd64 object file
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach_amd64.h>
#include "../cmd/6l/6.out.h"
#include "obj.h"
typedef struct Addr Addr;
struct Addr
{
char sym;
char flags;
};
static Addr addr(Biobuf*);
static char type2char(int);
static void skip(Biobuf*, int);
int
_is6(char *t)
{
uchar *s = (uchar*)t;
return s[0] == (ANAME&0xff) /* aslo = ANAME */
&& s[1] == ((ANAME>>8)&0xff)
&& s[2] == D_FILE /* type */
&& s[3] == 1 /* sym */
&& s[4] == '<'; /* name of file */
}
int
_read6(Biobuf *bp, Prog* p)
{
int as, n, c;
Addr a;
as = Bgetc(bp); /* as(low) */
if(as < 0)
return 0;
c = Bgetc(bp); /* as(high) */
if(c < 0)
return 0;
as |= ((c & 0xff) << 8);
p->kind = aNone;
p->sig = 0;
if(as == ANAME || as == ASIGNAME){
if(as == ASIGNAME){
Bread(bp, &p->sig, 4);
p->sig = leswal(p->sig);
}
p->kind = aName;
p->type = type2char(Bgetc(bp)); /* type */
p->sym = Bgetc(bp); /* sym */
n = 0;
for(;;) {
as = Bgetc(bp);
if(as < 0)
return 0;
n++;
if(as == 0)
break;
}
p->id = malloc(n);
if(p->id == 0)
return 0;
Bseek(bp, -n, 1);
if(Bread(bp, p->id, n) != n)
return 0;
return 1;
}
if(as == ATEXT)
p->kind = aText;
if(as == AGLOBL)
p->kind = aData;
skip(bp, 4); /* lineno(4) */
a = addr(bp);
addr(bp);
if(!(a.flags & T_SYM))
p->kind = aNone;
p->sym = a.sym;
return 1;
}
static Addr
addr(Biobuf *bp)
{
Addr a;
int t;
long l;
vlong off;
off = 0;
a.sym = -1;
a.flags = Bgetc(bp); /* flags */
if(a.flags & T_INDEX)
skip(bp, 2);
if(a.flags & T_OFFSET){
l = Bgetc(bp);
l |= Bgetc(bp) << 8;
l |= Bgetc(bp) << 16;
l |= Bgetc(bp) << 24;
off = l;
if(a.flags & T_64){
l = Bgetc(bp);
l |= Bgetc(bp) << 8;
l |= Bgetc(bp) << 16;
l |= Bgetc(bp) << 24;
off = ((vlong)l << 32) | (off & 0xFFFFFFFF);
}
if(off < 0)
off = -off;
}
if(a.flags & T_SYM)
a.sym = Bgetc(bp);
if(a.flags & T_FCONST)
skip(bp, 8);
else
if(a.flags & T_SCONST)
skip(bp, NSNAME);
if(a.flags & T_TYPE) {
t = Bgetc(bp);
if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
_offset(a.sym, off);
}
return a;
}
static char
type2char(int t)
{
switch(t){
case D_EXTERN: return 'U';
case D_STATIC: return 'b';
case D_AUTO: return 'a';
case D_PARAM: return 'p';
default: return UNKNOWN;
}
}
static void
skip(Biobuf *bp, int n)
{
while (n-- > 0)
Bgetc(bp);
}
// Inferno libmach/8.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8.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.
/*
* 386 definition
*/
#include <u.h>
#include <bio.h>
#include <ureg_x86.h>
#include <mach_amd64.h>
#define REGOFF(x) (ulong)(&((struct Ureg *) 0)->x)
#define PC REGOFF(pc)
#define SP REGOFF(sp)
#define AX REGOFF(ax)
#define REGSIZE sizeof(struct Ureg)
#define FP_CTL(x) (REGSIZE+4*(x))
#define FP_REG(x) (FP_CTL(7)+10*(x))
#define FPREGSIZE (7*4+8*10)
Reglist i386reglist[] = {
{"DI", REGOFF(di), RINT, 'X'},
{"SI", REGOFF(si), RINT, 'X'},
{"BP", REGOFF(bp), RINT, 'X'},
{"BX", REGOFF(bx), RINT, 'X'},
{"DX", REGOFF(dx), RINT, 'X'},
{"CX", REGOFF(cx), RINT, 'X'},
{"AX", REGOFF(ax), RINT, 'X'},
{"GS", REGOFF(gs), RINT, 'X'},
{"FS", REGOFF(fs), RINT, 'X'},
{"ES", REGOFF(es), RINT, 'X'},
{"DS", REGOFF(ds), RINT, 'X'},
{"TRAP", REGOFF(trap), RINT, 'X'},
{"ECODE", REGOFF(ecode), RINT, 'X'},
{"PC", PC, RINT, 'X'},
{"CS", REGOFF(cs), RINT, 'X'},
{"EFLAGS", REGOFF(flags), RINT, 'X'},
{"SP", SP, RINT, 'X'},
{"SS", REGOFF(ss), RINT, 'X'},
{"E0", FP_CTL(0), RFLT, 'X'},
{"E1", FP_CTL(1), RFLT, 'X'},
{"E2", FP_CTL(2), RFLT, 'X'},
{"E3", FP_CTL(3), RFLT, 'X'},
{"E4", FP_CTL(4), RFLT, 'X'},
{"E5", FP_CTL(5), RFLT, 'X'},
{"E6", FP_CTL(6), RFLT, 'X'},
{"F0", FP_REG(0), RFLT, '3'},
{"F1", FP_REG(1), RFLT, '3'},
{"F2", FP_REG(2), RFLT, '3'},
{"F3", FP_REG(3), RFLT, '3'},
{"F4", FP_REG(4), RFLT, '3'},
{"F5", FP_REG(5), RFLT, '3'},
{"F6", FP_REG(6), RFLT, '3'},
{"F7", FP_REG(7), RFLT, '3'},
{ 0 }
};
Mach mi386 =
{
"386",
MI386, /* machine type */
i386reglist, /* register list */
REGSIZE, /* size of registers in bytes */
FPREGSIZE, /* size of fp registers in bytes */
"PC", /* name of PC */
"SP", /* name of SP */
0, /* link register */
"setSB", /* static base register name (bogus anyways) */
0, /* static base register value */
0x1000, /* page size */
0x80100000ULL, /* kernel base */
0xF0000000ULL, /* kernel text mask */
0x7FFFFFFFULL, /* user stack top */
1, /* quantization of pc */
4, /* szaddr */
4, /* szreg */
4, /* szfloat */
8, /* szdouble */
};
// Inferno libmach/8db.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.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.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach_amd64.h>
/*
* i386-specific debugger interface
* also amd64 extensions
*/
static char *i386excep(Map*, Rgetter);
static int i386trace(Map*, uvlong, uvlong, uvlong, Tracer);
static uvlong i386frame(Map*, uvlong, uvlong, uvlong, uvlong);
static int i386foll(Map*, uvlong, Rgetter, uvlong*);
static int i386inst(Map*, uvlong, char, char*, int);
static int i386das(Map*, uvlong, char*, int);
static int i386instlen(Map*, uvlong);
static char STARTSYM[] = "_main";
static char PROFSYM[] = "_mainp";
static char FRAMENAME[] = ".frame";
static char *excname[] =
{
[0] "divide error",
[1] "debug exception",
[4] "overflow",
[5] "bounds check",
[6] "invalid opcode",
[7] "math coprocessor emulation",
[8] "double fault",
[9] "math coprocessor overrun",
[10] "invalid TSS",
[11] "segment not present",
[12] "stack exception",
[13] "general protection violation",
[14] "page fault",
[16] "math coprocessor error",
[17] "alignment check",
[18] "machine check",
[19] "floating-point exception",
[24] "clock",
[25] "keyboard",
[27] "modem status",
[28] "serial line status",
[30] "floppy disk",
[36] "mouse",
[37] "math coprocessor",
[38] "hard disk",
[64] "system call",
};
Machdata i386mach =
{
{0xCC, 0, 0, 0}, /* break point: INT 3 */
1, /* break point size */
leswab, /* convert short to local byte order */
leswal, /* convert long to local byte order */
leswav, /* convert vlong to local byte order */
i386trace, /* C traceback */
i386frame, /* frame finder */
i386excep, /* print exception */
0, /* breakpoint fixup */
leieeesftos, /* single precision float printer */
leieeedftos, /* double precision float printer */
i386foll, /* following addresses */
i386inst, /* print instruction */
i386das, /* dissembler */
i386instlen, /* instruction size calculation */
};
static char*
i386excep(Map *map, Rgetter rget)
{
ulong c;
uvlong pc;
static char buf[16];
c = (*rget)(map, "TRAP");
if(c > 64 || excname[c] == 0) {
if (c == 3) {
pc = (*rget)(map, "PC");
if (get1(map, pc, (uchar*)buf, machdata->bpsize) > 0)
if (memcmp(buf, machdata->bpinst, machdata->bpsize) == 0)
return "breakpoint";
}
snprint(buf, sizeof(buf), "exception %ld", c);
return buf;
} else
return excname[c];
}
static int
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
int i;
uvlong osp;
Symbol s, f;
USED(link);
i = 0;
osp = 0;
while(findsym(pc, CTEXT, &s)) {
if (osp == sp)
break;
osp = sp;
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
break;
if(pc != s.value) { /* not at first instruction */
if(findlocal(&s, FRAMENAME, &f) == 0)
break;
sp += f.value-mach->szaddr;
}
if (geta(map, sp, &pc) < 0)
break;
if(pc == 0)
break;
(*trace)(map, pc, sp, &s);
sp += mach->szaddr;
if(++i > 1000)
break;
}
return i;
}
static uvlong
i386frame(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
{
Symbol s, f;
USED(link);
while (findsym(pc, CTEXT, &s)) {
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
break;
if(pc != s.value) { /* not first instruction */
if(findlocal(&s, FRAMENAME, &f) == 0)
break;
sp += f.value-mach->szaddr;
}
if (s.value == addr)
return sp;
if (geta(map, sp, &pc) < 0)
break;
sp += mach->szaddr;
}
return 0;
}
/* I386/486 - Disassembler and related functions */
/*
* an instruction
*/
typedef struct Instr Instr;
struct Instr
{
uchar mem[1+1+1+1+2+1+1+4+4]; /* raw instruction */
uvlong addr; /* address of start of instruction */
int n; /* number of bytes in instruction */
char *prefix; /* instr prefix */
char *segment; /* segment override */
uchar jumptype; /* set to the operand type for jump/ret/call */
uchar amd64;
uchar rex; /* REX prefix (or zero) */
char osize; /* 'W' or 'L' (or 'Q' on amd64) */
char asize; /* address size 'W' or 'L' (or 'Q' or amd64) */
uchar mod; /* bits 6-7 of mod r/m field */
uchar reg; /* bits 3-5 of mod r/m field */
char ss; /* bits 6-7 of SIB */
char index; /* bits 3-5 of SIB */
char base; /* bits 0-2 of SIB */
char rip; /* RIP-relative in amd64 mode */
uchar opre; /* f2/f3 could introduce media */
short seg; /* segment of far address */
ulong disp; /* displacement */
ulong imm; /* immediate */
ulong imm2; /* second immediate operand */
uvlong imm64; /* big immediate */
char *curr; /* fill level in output buffer */
char *end; /* end of output buffer */
char *err; /* error message */
};
/* 386 register (ha!) set */
enum{
AX=0,
CX,
DX,
BX,
SP,
BP,
SI,
DI,
/* amd64 */
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15
};
/* amd64 rex extension byte */
enum{
REXW = 1<<3, /* =1, 64-bit operand size */
REXR = 1<<2, /* extend modrm reg */
REXX = 1<<1, /* extend sib index */
REXB = 1<<0 /* extend modrm r/m, sib base, or opcode reg */
};
/* Operand Format codes */
/*
%A - address size register modifier (!asize -> 'E')
%C - Control register CR0/CR1/CR2
%D - Debug register DR0/DR1/DR2/DR3/DR6/DR7
%I - second immediate operand
%O - Operand size register modifier (!osize -> 'E')
%T - Test register TR6/TR7
%S - size code ('W' or 'L')
%W - Weird opcode: OSIZE == 'W' => "CBW"; else => "CWDE"
%d - displacement 16-32 bits
%e - effective address - Mod R/M value
%f - floating point register F0-F7 - from Mod R/M register
%g - segment register
%i - immediate operand 8-32 bits
%p - PC-relative - signed displacement in immediate field
%r - Reg from Mod R/M
%w - Weird opcode: OSIZE == 'W' => "CWD"; else => "CDQ"
*/
typedef struct Optable Optable;
struct Optable
{
char operand[2];
void *proto; /* actually either (char*) or (Optable*) */
};
/* Operand decoding codes */
enum {
Ib = 1, /* 8-bit immediate - (no sign extension)*/
Ibs, /* 8-bit immediate (sign extended) */
Jbs, /* 8-bit sign-extended immediate in jump or call */
Iw, /* 16-bit immediate -> imm */
Iw2, /* 16-bit immediate -> imm2 */
Iwd, /* Operand-sized immediate (no sign extension)*/
Iwdq, /* Operand-sized immediate, possibly 64 bits */
Awd, /* Address offset */
Iwds, /* Operand-sized immediate (sign extended) */
RM, /* Word or long R/M field with register (/r) */
RMB, /* Byte R/M field with register (/r) */
RMOP, /* Word or long R/M field with op code (/digit) */
RMOPB, /* Byte R/M field with op code (/digit) */
RMR, /* R/M register only (mod = 11) */
RMM, /* R/M memory only (mod = 0/1/2) */
R0, /* Base reg of Mod R/M is literal 0x00 */
R1, /* Base reg of Mod R/M is literal 0x01 */
FRMOP, /* Floating point R/M field with opcode */
FRMEX, /* Extended floating point R/M field with opcode */
JUMP, /* Jump or Call flag - no operand */
RET, /* Return flag - no operand */
OA, /* literal 0x0a byte */
PTR, /* Seg:Displacement addr (ptr16:16 or ptr16:32) */
AUX, /* Multi-byte op code - Auxiliary table */
AUXMM, /* multi-byte op code - auxiliary table chosen by prefix */
PRE, /* Instr Prefix */
OPRE, /* Instr Prefix or media op extension */
SEG, /* Segment Prefix */
OPOVER, /* Operand size override */
ADDOVER, /* Address size override */
};
static Optable optab0F00[8]=
{
[0x00] 0,0, "MOVW LDT,%e",
[0x01] 0,0, "MOVW TR,%e",
[0x02] 0,0, "MOVW %e,LDT",
[0x03] 0,0, "MOVW %e,TR",
[0x04] 0,0, "VERR %e",
[0x05] 0,0, "VERW %e",
};
static Optable optab0F01[8]=
{
[0x00] 0,0, "MOVL GDTR,%e",
[0x01] 0,0, "MOVL IDTR,%e",
[0x02] 0,0, "MOVL %e,GDTR",
[0x03] 0,0, "MOVL %e,IDTR",
[0x04] 0,0, "MOVW MSW,%e", /* word */
[0x06] 0,0, "MOVW %e,MSW", /* word */
[0x07] 0,0, "INVLPG %e", /* or SWAPGS */
};
static Optable optab0F01F8[1]=
{
[0x00] 0,0, "SWAPGS",
};
/* 0F71 */
/* 0F72 */
/* 0F73 */
static Optable optab0FAE[8]=
{
[0x00] 0,0, "FXSAVE %e",
[0x01] 0,0, "FXRSTOR %e",
[0x02] 0,0, "LDMXCSR %e",
[0x03] 0,0, "STMXCSR %e",
[0x05] 0,0, "LFENCE",
[0x06] 0,0, "MFENCE",
[0x07] 0,0, "SFENCE",
};
/* 0F18 */
/* 0F0D */
static Optable optab0FBA[8]=
{
[0x04] Ib,0, "BT%S %i,%e",
[0x05] Ib,0, "BTS%S %i,%e",
[0x06] Ib,0, "BTR%S %i,%e",
[0x07] Ib,0, "BTC%S %i,%e",
};
static Optable optab0F0F[256]=
{
[0x0c] 0,0, "PI2FW %m,%M",
[0x0d] 0,0, "PI2L %m,%M",
[0x1c] 0,0, "PF2IW %m,%M",
[0x1d] 0,0, "PF2IL %m,%M",
[0x8a] 0,0, "PFNACC %m,%M",
[0x8e] 0,0, "PFPNACC %m,%M",
[0x90] 0,0, "PFCMPGE %m,%M",
[0x94] 0,0, "PFMIN %m,%M",
[0x96] 0,0, "PFRCP %m,%M",
[0x97] 0,0, "PFRSQRT %m,%M",
[0x9a] 0,0, "PFSUB %m,%M",
[0x9e] 0,0, "PFADD %m,%M",
[0xa0] 0,0, "PFCMPGT %m,%M",
[0xa4] 0,0, "PFMAX %m,%M",
[0xa6] 0,0, "PFRCPIT1 %m,%M",
[0xa7] 0,0, "PFRSQIT1 %m,%M",
[0xaa] 0,0, "PFSUBR %m,%M",
[0xae] 0,0, "PFACC %m,%M",
[0xb0] 0,0, "PFCMPEQ %m,%M",
[0xb4] 0,0, "PFMUL %m,%M",
[0xb6] 0,0, "PFRCPI2T %m,%M",
[0xb7] 0,0, "PMULHRW %m,%M",
[0xbb] 0,0, "PSWAPL %m,%M",
};
static Optable optab0FC7[8]=
{
[0x01] 0,0, "CMPXCHG8B %e",
};
static Optable optab660F71[8]=
{
[0x02] Ib,0, "PSRLW %i,%X",
[0x04] Ib,0, "PSRAW %i,%X",
[0x06] Ib,0, "PSLLW %i,%X",
};
static Optable optab660F72[8]=
{
[0x02] Ib,0, "PSRLL %i,%X",
[0x04] Ib,0, "PSRAL %i,%X",
[0x06] Ib,0, "PSLLL %i,%X",
};
static Optable optab660F73[8]=
{
[0x02] Ib,0, "PSRLQ %i,%X",
[0x03] Ib,0, "PSRLO %i,%X",
[0x06] Ib,0, "PSLLQ %i,%X",
[0x07] Ib,0, "PSLLO %i,%X",
};
static Optable optab660F[256]=
{
[0x2B] RM,0, "MOVNTPD %x,%e",
[0x2E] RM,0, "UCOMISD %x,%X",
[0x2F] RM,0, "COMISD %x,%X",
[0x5A] RM,0, "CVTPD2PS %x,%X",
[0x5B] RM,0, "CVTPS2PL %x,%X",
[0x6A] RM,0, "PUNPCKHLQ %x,%X",
[0x6B] RM,0, "PACKSSLW %x,%X",
[0x6C] RM,0, "PUNPCKLQDQ %x,%X",
[0x6D] RM,0, "PUNPCKHQDQ %x,%X",
[0x6E] RM,0, "MOV%S %e,%X",
[0x6F] RM,0, "MOVO %x,%X", /* MOVDQA */
[0x70] RM,Ib, "PSHUFL %i,%x,%X",
[0x71] RMOP,0, optab660F71,
[0x72] RMOP,0, optab660F72,
[0x73] RMOP,0, optab660F73,
[0x7E] RM,0, "MOV%S %X,%e",
[0x7F] RM,0, "MOVO %X,%x",
[0xC4] RM,Ib, "PINSRW %i,%e,%X",
[0xC5] RMR,Ib, "PEXTRW %i,%X,%e",
[0xD4] RM,0, "PADDQ %x,%X",
[0xD5] RM,0, "PMULLW %x,%X",
[0xD6] RM,0, "MOVQ %X,%x",
[0xE6] RM,0, "CVTTPD2PL %x,%X",
[0xE7] RM,0, "MOVNTO %X,%e",
[0xF7] RM,0, "MASKMOVOU %x,%X",
};
static Optable optabF20F[256]=
{
[0x10] RM,0, "MOVSD %x,%X",
[0x11] RM,0, "MOVSD %X,%x",
[0x2A] RM,0, "CVTS%S2SD %e,%X",
[0x2C] RM,0, "CVTTSD2S%S %x,%r",
[0x2D] RM,0, "CVTSD2S%S %x,%r",
[0x5A] RM,0, "CVTSD2SS %x,%X",
[0x6F] RM,0, "MOVOU %x,%X",
[0x70] RM,Ib, "PSHUFLW %i,%x,%X",
[0x7F] RM,0, "MOVOU %X,%x",
[0xD6] RM,0, "MOVQOZX %M,%X",
[0xE6] RM,0, "CVTPD2PL %x,%X",
};
static Optable optabF30F[256]=
{
[0x10] RM,0, "MOVSS %x,%X",
[0x11] RM,0, "MOVSS %X,%x",
[0x2A] RM,0, "CVTS%S2SS %e,%X",
[0x2C] RM,0, "CVTTSS2S%S %x,%r",
[0x2D] RM,0, "CVTSS2S%S %x,%r",
[0x5A] RM,0, "CVTSS2SD %x,%X",
[0x5B] RM,0, "CVTTPS2PL %x,%X",
[0x6F] RM,0, "MOVOU %x,%X",
[0x70] RM,Ib, "PSHUFHW %i,%x,%X",
[0x7E] RM,0, "MOVQOZX %x,%X",
[0x7F] RM,0, "MOVOU %X,%x",
[0xD6] RM,0, "MOVQOZX %m*,%X",
[0xE6] RM,0, "CVTPL2PD %x,%X",
};
static Optable optab0F[256]=
{
[0x00] RMOP,0, optab0F00,
[0x01] RMOP,0, optab0F01,
[0x02] RM,0, "LAR %e,%r",
[0x03] RM,0, "LSL %e,%r",
[0x05] 0,0, "SYSCALL",
[0x06] 0,0, "CLTS",
[0x07] 0,0, "SYSRET",
[0x08] 0,0, "INVD",
[0x09] 0,0, "WBINVD",
[0x0B] 0,0, "UD2",
[0x0F] RM,AUX, optab0F0F, /* 3DNow! */
[0x10] RM,0, "MOVU%s %x,%X",
[0x11] RM,0, "MOVU%s %X,%x",
[0x12] RM,0, "MOV[H]L%s %x,%X", /* TO DO: H if source is XMM */
[0x13] RM,0, "MOVL%s %X,%e",
[0x14] RM,0, "UNPCKL%s %x,%X",
[0x15] RM,0, "UNPCKH%s %x,%X",
[0x16] RM,0, "MOV[L]H%s %x,%X", /* TO DO: L if source is XMM */
[0x17] RM,0, "MOVH%s %X,%x",
[0x20] RMR,0, "MOVL %C,%e",
[0x21] RMR,0, "MOVL %D,%e",
[0x22] RMR,0, "MOVL %e,%C",
[0x23] RMR,0, "MOVL %e,%D",
[0x24] RMR,0, "MOVL %T,%e",
[0x26] RMR,0, "MOVL %e,%T",
[0x28] RM,0, "MOVA%s %x,%X",
[0x29] RM,0, "MOVA%s %X,%x",
[0x2A] RM,0, "CVTPL2%s %m*,%X",
[0x2B] RM,0, "MOVNT%s %X,%e",
[0x2C] RM,0, "CVTT%s2PL %x,%M",
[0x2D] RM,0, "CVT%s2PL %x,%M",
[0x2E] RM,0, "UCOMISS %x,%X",
[0x2F] RM,0, "COMISS %x,%X",
[0x30] 0,0, "WRMSR",
[0x31] 0,0, "RDTSC",
[0x32] 0,0, "RDMSR",
[0x33] 0,0, "RDPMC",
[0x42] RM,0, "CMOVC %e,%r", /* CF */
[0x43] RM,0, "CMOVNC %e,%r", /* ¬ CF */
[0x44] RM,0, "CMOVZ %e,%r", /* ZF */
[0x45] RM,0, "CMOVNZ %e,%r", /* ¬ ZF */
[0x46] RM,0, "CMOVBE %e,%r", /* CF ∨ ZF */
[0x47] RM,0, "CMOVA %e,%r", /* ¬CF ∧ ¬ZF */
[0x48] RM,0, "CMOVS %e,%r", /* SF */
[0x49] RM,0, "CMOVNS %e,%r", /* ¬ SF */
[0x4A] RM,0, "CMOVP %e,%r", /* PF */
[0x4B] RM,0, "CMOVNP %e,%r", /* ¬ PF */
[0x4C] RM,0, "CMOVLT %e,%r", /* LT ≡ OF ≠ SF */
[0x4D] RM,0, "CMOVGE %e,%r", /* GE ≡ ZF ∨ SF */
[0x4E] RM,0, "CMOVLE %e,%r", /* LE ≡ ZF ∨ LT */
[0x4F] RM,0, "CMOVGT %e,%r", /* GT ≡ ¬ZF ∧ GE */
[0x50] RM,0, "MOVMSK%s %X,%r", /* TO DO: check */
[0x51] RM,0, "SQRT%s %x,%X",
[0x52] RM,0, "RSQRT%s %x,%X",
[0x53] RM,0, "RCP%s %x,%X",
[0x54] RM,0, "AND%s %x,%X",
[0x55] RM,0, "ANDN%s %x,%X",
[0x56] RM,0, "OR%s %x,%X", /* TO DO: S/D */
[0x57] RM,0, "XOR%s %x,%X", /* S/D */
[0x58] RM,0, "ADD%s %x,%X", /* S/P S/D */
[0x59] RM,0, "MUL%s %x,%X",
[0x5A] RM,0, "CVTPS2PD %x,%X",
[0x5B] RM,0, "CVTPL2PS %x,%X",
[0x5C] RM,0, "SUB%s %x,%X",
[0x5D] RM,0, "MIN%s %x,%X",
[0x5E] RM,0, "DIV%s %x,%X", /* TO DO: S/P S/D */
[0x5F] RM,0, "MAX%s %x,%X",
[0x60] RM,0, "PUNPCKLBW %m,%M",
[0x61] RM,0, "PUNPCKLWL %m,%M",
[0x62] RM,0, "PUNPCKLLQ %m,%M",
[0x63] RM,0, "PACKSSWB %m,%M",
[0x64] RM,0, "PCMPGTB %m,%M",
[0x65] RM,0, "PCMPGTW %m,%M",
[0x66] RM,0, "PCMPGTL %m,%M",
[0x67] RM,0, "PACKUSWB %m,%M",
[0x68] RM,0, "PUNPCKHBW %m,%M",
[0x69] RM,0, "PUNPCKHWL %m,%M",
[0x6A] RM,0, "PUNPCKHLQ %m,%M",
[0x6B] RM,0, "PACKSSLW %m,%M",
[0x6E] RM,0, "MOV%S %e,%M",
[0x6F] RM,0, "MOVQ %m,%M",
[0x70] RM,Ib, "PSHUFW %i,%m,%M",
[0x74] RM,0, "PCMPEQB %m,%M",
[0x75] RM,0, "PCMPEQW %m,%M",
[0x76] RM,0, "PCMPEQL %m,%M",
[0x7E] RM,0, "MOV%S %M,%e",
[0x7F] RM,0, "MOVQ %M,%m",
[0xAE] RMOP,0, optab0FAE,
[0xAA] 0,0, "RSM",
[0xB0] RM,0, "CMPXCHGB %r,%e",
[0xB1] RM,0, "CMPXCHG%S %r,%e",
[0xC0] RMB,0, "XADDB %r,%e",
[0xC1] RM,0, "XADD%S %r,%e",
[0xC2] RM,Ib, "CMP%s %i,%x,%X",
[0xC3] RM,0, "MOVNTI%S %r,%e",
[0xC6] RM,Ib, "SHUF%s %i,%x,%X",
[0xC8] 0,0, "BSWAP AX",
[0xC9] 0,0, "BSWAP CX",
[0xCA] 0,0, "BSWAP DX",
[0xCB] 0,0, "BSWAP BX",
[0xCC] 0,0, "BSWAP SP",
[0xCD] 0,0, "BSWAP BP",
[0xCE] 0,0, "BSWAP SI",
[0xCF] 0,0, "BSWAP DI",
[0xD1] RM,0, "PSRLW %m,%M",
[0xD2] RM,0, "PSRLL %m,%M",
[0xD3] RM,0, "PSRLQ %m,%M",
[0xD5] RM,0, "PMULLW %m,%M",
[0xD6] RM,0, "MOVQOZX %m*,%X",
[0xD7] RM,0, "PMOVMSKB %m,%r",
[0xD8] RM,0, "PSUBUSB %m,%M",
[0xD9] RM,0, "PSUBUSW %m,%M",
[0xDA] RM,0, "PMINUB %m,%M",
[0xDB] RM,0, "PAND %m,%M",
[0xDC] RM,0, "PADDUSB %m,%M",
[0xDD] RM,0, "PADDUSW %m,%M",
[0xDE] RM,0, "PMAXUB %m,%M",
[0xDF] RM,0, "PANDN %m,%M",
[0xE0] RM,0, "PAVGB %m,%M",
[0xE1] RM,0, "PSRAW %m,%M",
[0xE2] RM,0, "PSRAL %m,%M",
[0xE3] RM,0, "PAVGW %m,%M",
[0xE4] RM,0, "PMULHUW %m,%M",
[0xE5] RM,0, "PMULHW %m,%M",
[0xE7] RM,0, "MOVNTQ %M,%e",
[0xE8] RM,0, "PSUBSB %m,%M",
[0xE9] RM,0, "PSUBSW %m,%M",
[0xEA] RM,0, "PMINSW %m,%M",
[0xEB] RM,0, "POR %m,%M",
[0xEC] RM,0, "PADDSB %m,%M",
[0xED] RM,0, "PADDSW %m,%M",
[0xEE] RM,0, "PMAXSW %m,%M",
[0xEF] RM,0, "PXOR %m,%M",
[0xF1] RM,0, "PSLLW %m,%M",
[0xF2] RM,0, "PSLLL %m,%M",
[0xF3] RM,0, "PSLLQ %m,%M",
[0xF4] RM,0, "PMULULQ %m,%M",
[0xF5] RM,0, "PMADDWL %m,%M",
[0xF6] RM,0, "PSADBW %m,%M",
[0xF7] RMR,0, "MASKMOVQ %m,%M",
[0xF8] RM,0, "PSUBB %m,%M",
[0xF9] RM,0, "PSUBW %m,%M",
[0xFA] RM,0, "PSUBL %m,%M",
[0xFC] RM,0, "PADDB %m,%M",
[0xFD] RM,0, "PADDW %m,%M",
[0xFE] RM,0, "PADDL %m,%M",
[0x80] Iwds,0, "JOS %p",
[0x81] Iwds,0, "JOC %p",
[0x82] Iwds,0, "JCS %p",
[0x83] Iwds,0, "JCC %p",
[0x84] Iwds,0, "JEQ %p",
[0x85] Iwds,0, "JNE %p",
[0x86] Iwds,0, "JLS %p",
[0x87] Iwds,0, "JHI %p",
[0x88] Iwds,0, "JMI %p",
[0x89] Iwds,0, "JPL %p",
[0x8a] Iwds,0, "JPS %p",
[0x8b] Iwds,0, "JPC %p",
[0x8c] Iwds,0, "JLT %p",
[0x8d] Iwds,0, "JGE %p",
[0x8e] Iwds,0, "JLE %p",
[0x8f] Iwds,0, "JGT %p",
[0x90] RMB,0, "SETOS %e",
[0x91] RMB,0, "SETOC %e",
[0x92] RMB,0, "SETCS %e",
[0x93] RMB,0, "SETCC %e",
[0x94] RMB,0, "SETEQ %e",
[0x95] RMB,0, "SETNE %e",
[0x96] RMB,0, "SETLS %e",
[0x97] RMB,0, "SETHI %e",
[0x98] RMB,0, "SETMI %e",
[0x99] RMB,0, "SETPL %e",
[0x9a] RMB,0, "SETPS %e",
[0x9b] RMB,0, "SETPC %e",
[0x9c] RMB,0, "SETLT %e",
[0x9d] RMB,0, "SETGE %e",
[0x9e] RMB,0, "SETLE %e",
[0x9f] RMB,0, "SETGT %e",
[0xa0] 0,0, "PUSHL FS",
[0xa1] 0,0, "POPL FS",
[0xa2] 0,0, "CPUID",
[0xa3] RM,0, "BT%S %r,%e",
[0xa4] RM,Ib, "SHLD%S %r,%i,%e",
[0xa5] RM,0, "SHLD%S %r,CL,%e",
[0xa8] 0,0, "PUSHL GS",
[0xa9] 0,0, "POPL GS",
[0xab] RM,0, "BTS%S %r,%e",
[0xac] RM,Ib, "SHRD%S %r,%i,%e",
[0xad] RM,0, "SHRD%S %r,CL,%e",
[0xaf] RM,0, "IMUL%S %e,%r",
[0xb2] RMM,0, "LSS %e,%r",
[0xb3] RM,0, "BTR%S %r,%e",
[0xb4] RMM,0, "LFS %e,%r",
[0xb5] RMM,0, "LGS %e,%r",
[0xb6] RMB,0, "MOVBZX %e,%R",
[0xb7] RM,0, "MOVWZX %e,%R",
[0xba] RMOP,0, optab0FBA,
[0xbb] RM,0, "BTC%S %e,%r",
[0xbc] RM,0, "BSF%S %e,%r",
[0xbd] RM,0, "BSR%S %e,%r",
[0xbe] RMB,0, "MOVBSX %e,%R",
[0xbf] RM,0, "MOVWSX %e,%R",
[0xc7] RMOP,0, optab0FC7,
};
static Optable optab80[8]=
{
[0x00] Ib,0, "ADDB %i,%e",
[0x01] Ib,0, "ORB %i,%e",
[0x02] Ib,0, "ADCB %i,%e",
[0x03] Ib,0, "SBBB %i,%e",
[0x04] Ib,0, "ANDB %i,%e",
[0x05] Ib,0, "SUBB %i,%e",
[0x06] Ib,0, "XORB %i,%e",
[0x07] Ib,0, "CMPB %e,%i",
};
static Optable optab81[8]=
{
[0x00] Iwd,0, "ADD%S %i,%e",
[0x01] Iwd,0, "OR%S %i,%e",
[0x02] Iwd,0, "ADC%S %i,%e",
[0x03] Iwd,0, "SBB%S %i,%e",
[0x04] Iwd,0, "AND%S %i,%e",
[0x05] Iwd,0, "SUB%S %i,%e",
[0x06] Iwd,0, "XOR%S %i,%e",
[0x07] Iwd,0, "CMP%S %e,%i",
};
static Optable optab83[8]=
{
[0x00] Ibs,0, "ADD%S %i,%e",
[0x01] Ibs,0, "OR%S %i,%e",
[0x02] Ibs,0, "ADC%S %i,%e",
[0x03] Ibs,0, "SBB%S %i,%e",
[0x04] Ibs,0, "AND%S %i,%e",
[0x05] Ibs,0, "SUB%S %i,%e",
[0x06] Ibs,0, "XOR%S %i,%e",
[0x07] Ibs,0, "CMP%S %e,%i",
};
static Optable optabC0[8] =
{
[0x00] Ib,0, "ROLB %i,%e",
[0x01] Ib,0, "RORB %i,%e",
[0x02] Ib,0, "RCLB %i,%e",
[0x03] Ib,0, "RCRB %i,%e",
[0x04] Ib,0, "SHLB %i,%e",
[0x05] Ib,0, "SHRB %i,%e",
[0x07] Ib,0, "SARB %i,%e",
};
static Optable optabC1[8] =
{
[0x00] Ib,0, "ROL%S %i,%e",
[0x01] Ib,0, "ROR%S %i,%e",
[0x02] Ib,0, "RCL%S %i,%e",
[0x03] Ib,0, "RCR%S %i,%e",
[0x04] Ib,0, "SHL%S %i,%e",
[0x05] Ib,0, "SHR%S %i,%e",
[0x07] Ib,0, "SAR%S %i,%e",
};
static Optable optabD0[8] =
{
[0x00] 0,0, "ROLB %e",
[0x01] 0,0, "RORB %e",
[0x02] 0,0, "RCLB %e",
[0x03] 0,0, "RCRB %e",
[0x04] 0,0, "SHLB %e",
[0x05] 0,0, "SHRB %e",
[0x07] 0,0, "SARB %e",
};
static Optable optabD1[8] =
{
[0x00] 0,0, "ROL%S %e",
[0x01] 0,0, "ROR%S %e",
[0x02] 0,0, "RCL%S %e",
[0x03] 0,0, "RCR%S %e",
[0x04] 0,0, "SHL%S %e",
[0x05] 0,0, "SHR%S %e",
[0x07] 0,0, "SAR%S %e",
};
static Optable optabD2[8] =
{
[0x00] 0,0, "ROLB CL,%e",
[0x01] 0,0, "RORB CL,%e",
[0x02] 0,0, "RCLB CL,%e",
[0x03] 0,0, "RCRB CL,%e",
[0x04] 0,0, "SHLB CL,%e",
[0x05] 0,0, "SHRB CL,%e",
[0x07] 0,0, "SARB CL,%e",
};
static Optable optabD3[8] =
{
[0x00] 0,0, "ROL%S CL,%e",
[0x01] 0,0, "ROR%S CL,%e",
[0x02] 0,0, "RCL%S CL,%e",
[0x03] 0,0, "RCR%S CL,%e",
[0x04] 0,0, "SHL%S CL,%e",
[0x05] 0,0, "SHR%S CL,%e",
[0x07] 0,0, "SAR%S CL,%e",
};
static Optable optabD8[8+8] =
{
[0x00] 0,0, "FADDF %e,F0",
[0x01] 0,0, "FMULF %e,F0",
[0x02] 0,0, "FCOMF %e,F0",
[0x03] 0,0, "FCOMFP %e,F0",
[0x04] 0,0, "FSUBF %e,F0",
[0x05] 0,0, "FSUBRF %e,F0",
[0x06] 0,0, "FDIVF %e,F0",
[0x07] 0,0, "FDIVRF %e,F0",
[0x08] 0,0, "FADDD %f,F0",
[0x09] 0,0, "FMULD %f,F0",
[0x0a] 0,0, "FCOMD %f,F0",
[0x0b] 0,0, "FCOMPD %f,F0",
[0x0c] 0,0, "FSUBD %f,F0",
[0x0d] 0,0, "FSUBRD %f,F0",
[0x0e] 0,0, "FDIVD %f,F0",
[0x0f] 0,0, "FDIVRD %f,F0",
};
/*
* optabD9 and optabDB use the following encoding:
* if (0 <= modrm <= 2) instruction = optabDx[modrm&0x07];
* else instruction = optabDx[(modrm&0x3f)+8];
*
* the instructions for MOD == 3, follow the 8 instructions
* for the other MOD values stored at the front of the table.
*/
static Optable optabD9[64+8] =
{
[0x00] 0,0, "FMOVF %e,F0",
[0x02] 0,0, "FMOVF F0,%e",
[0x03] 0,0, "FMOVFP F0,%e",
[0x04] 0,0, "FLDENV%S %e",
[0x05] 0,0, "FLDCW %e",
[0x06] 0,0, "FSTENV%S %e",
[0x07] 0,0, "FSTCW %e",
[0x08] 0,0, "FMOVD F0,F0", /* Mod R/M = 11xx xxxx*/
[0x09] 0,0, "FMOVD F1,F0",
[0x0a] 0,0, "FMOVD F2,F0",
[0x0b] 0,0, "FMOVD F3,F0",
[0x0c] 0,0, "FMOVD F4,F0",
[0x0d] 0,0, "FMOVD F5,F0",
[0x0e] 0,0, "FMOVD F6,F0",
[0x0f] 0,0, "FMOVD F7,F0",
[0x10] 0,0, "FXCHD F0,F0",
[0x11] 0,0, "FXCHD F1,F0",
[0x12] 0,0, "FXCHD F2,F0",
[0x13] 0,0, "FXCHD F3,F0",
[0x14] 0,0, "FXCHD F4,F0",
[0x15] 0,0, "FXCHD F5,F0",
[0x16] 0,0, "FXCHD F6,F0",
[0x17] 0,0, "FXCHD F7,F0",
[0x18] 0,0, "FNOP",
[0x28] 0,0, "FCHS",
[0x29] 0,0, "FABS",
[0x2c] 0,0, "FTST",
[0x2d] 0,0, "FXAM",
[0x30] 0,0, "FLD1",
[0x31] 0,0, "FLDL2T",
[0x32] 0,0, "FLDL2E",
[0x33] 0,0, "FLDPI",
[0x34] 0,0, "FLDLG2",
[0x35] 0,0, "FLDLN2",
[0x36] 0,0, "FLDZ",
[0x38] 0,0, "F2XM1",
[0x39] 0,0, "FYL2X",
[0x3a] 0,0, "FPTAN",
[0x3b] 0,0, "FPATAN",
[0x3c] 0,0, "FXTRACT",
[0x3d] 0,0, "FPREM1",
[0x3e] 0,0, "FDECSTP",
[0x3f] 0,0, "FNCSTP",
[0x40] 0,0, "FPREM",
[0x41] 0,0, "FYL2XP1",
[0x42] 0,0, "FSQRT",
[0x43] 0,0, "FSINCOS",
[0x44] 0,0, "FRNDINT",
[0x45] 0,0, "FSCALE",
[0x46] 0,0, "FSIN",
[0x47] 0,0, "FCOS",
};
static Optable optabDA[8+8] =
{
[0x00] 0,0, "FADDL %e,F0",
[0x01] 0,0, "FMULL %e,F0",
[0x02] 0,0, "FCOML %e,F0",
[0x03] 0,0, "FCOMLP %e,F0",
[0x04] 0,0, "FSUBL %e,F0",
[0x05] 0,0, "FSUBRL %e,F0",
[0x06] 0,0, "FDIVL %e,F0",
[0x07] 0,0, "FDIVRL %e,F0",
[0x0d] R1,0, "FUCOMPP",
};
static Optable optabDB[8+64] =
{
[0x00] 0,0, "FMOVL %e,F0",
[0x02] 0,0, "FMOVL F0,%e",
[0x03] 0,0, "FMOVLP F0,%e",
[0x05] 0,0, "FMOVX %e,F0",
[0x07] 0,0, "FMOVXP F0,%e",
[0x2a] 0,0, "FCLEX",
[0x2b] 0,0, "FINIT",
};
static Optable optabDC[8+8] =
{
[0x00] 0,0, "FADDD %e,F0",
[0x01] 0,0, "FMULD %e,F0",
[0x02] 0,0, "FCOMD %e,F0",
[0x03] 0,0, "FCOMDP %e,F0",
[0x04] 0,0, "FSUBD %e,F0",
[0x05] 0,0, "FSUBRD %e,F0",
[0x06] 0,0, "FDIVD %e,F0",
[0x07] 0,0, "FDIVRD %e,F0",
[0x08] 0,0, "FADDD F0,%f",
[0x09] 0,0, "FMULD F0,%f",
[0x0c] 0,0, "FSUBRD F0,%f",
[0x0d] 0,0, "FSUBD F0,%f",
[0x0e] 0,0, "FDIVRD F0,%f",
[0x0f] 0,0, "FDIVD F0,%f",
};
static Optable optabDD[8+8] =
{
[0x00] 0,0, "FMOVD %e,F0",
[0x02] 0,0, "FMOVD F0,%e",
[0x03] 0,0, "FMOVDP F0,%e",
[0x04] 0,0, "FRSTOR%S %e",
[0x06] 0,0, "FSAVE%S %e",
[0x07] 0,0, "FSTSW %e",
[0x08] 0,0, "FFREED %f",
[0x0a] 0,0, "FMOVD %f,F0",
[0x0b] 0,0, "FMOVDP %f,F0",
[0x0c] 0,0, "FUCOMD %f,F0",
[0x0d] 0,0, "FUCOMDP %f,F0",
};
static Optable optabDE[8+8] =
{
[0x00] 0,0, "FADDW %e,F0",
[0x01] 0,0, "FMULW %e,F0",
[0x02] 0,0, "FCOMW %e,F0",
[0x03] 0,0, "FCOMWP %e,F0",
[0x04] 0,0, "FSUBW %e,F0",
[0x05] 0,0, "FSUBRW %e,F0",
[0x06] 0,0, "FDIVW %e,F0",
[0x07] 0,0, "FDIVRW %e,F0",
[0x08] 0,0, "FADDDP F0,%f",
[0x09] 0,0, "FMULDP F0,%f",
[0x0b] R1,0, "FCOMPDP",
[0x0c] 0,0, "FSUBRDP F0,%f",
[0x0d] 0,0, "FSUBDP F0,%f",
[0x0e] 0,0, "FDIVRDP F0,%f",
[0x0f] 0,0, "FDIVDP F0,%f",
};
static Optable optabDF[8+8] =
{
[0x00] 0,0, "FMOVW %e,F0",
[0x02] 0,0, "FMOVW F0,%e",
[0x03] 0,0, "FMOVWP F0,%e",
[0x04] 0,0, "FBLD %e",
[0x05] 0,0, "FMOVL %e,F0",
[0x06] 0,0, "FBSTP %e",
[0x07] 0,0, "FMOVLP F0,%e",
[0x0c] R0,0, "FSTSW %OAX",
};
static Optable optabF6[8] =
{
[0x00] Ib,0, "TESTB %i,%e",
[0x02] 0,0, "NOTB %e",
[0x03] 0,0, "NEGB %e",
[0x04] 0,0, "MULB AL,%e",
[0x05] 0,0, "IMULB AL,%e",
[0x06] 0,0, "DIVB AL,%e",
[0x07] 0,0, "IDIVB AL,%e",
};
static Optable optabF7[8] =
{
[0x00] Iwd,0, "TEST%S %i,%e",
[0x02] 0,0, "NOT%S %e",
[0x03] 0,0, "NEG%S %e",
[0x04] 0,0, "MUL%S %OAX,%e",
[0x05] 0,0, "IMUL%S %OAX,%e",
[0x06] 0,0, "DIV%S %OAX,%e",
[0x07] 0,0, "IDIV%S %OAX,%e",
};
static Optable optabFE[8] =
{
[0x00] 0,0, "INCB %e",
[0x01] 0,0, "DECB %e",
};
static Optable optabFF[8] =
{
[0x00] 0,0, "INC%S %e",
[0x01] 0,0, "DEC%S %e",
[0x02] JUMP,0, "CALL* %e",
[0x03] JUMP,0, "CALLF* %e",
[0x04] JUMP,0, "JMP* %e",
[0x05] JUMP,0, "JMPF* %e",
[0x06] 0,0, "PUSHL %e",
};
static Optable optable[256+1] =
{
[0x00] RMB,0, "ADDB %r,%e",
[0x01] RM,0, "ADD%S %r,%e",
[0x02] RMB,0, "ADDB %e,%r",
[0x03] RM,0, "ADD%S %e,%r",
[0x04] Ib,0, "ADDB %i,AL",
[0x05] Iwd,0, "ADD%S %i,%OAX",
[0x06] 0,0, "PUSHL ES",
[0x07] 0,0, "POPL ES",
[0x08] RMB,0, "ORB %r,%e",
[0x09] RM,0, "OR%S %r,%e",
[0x0a] RMB,0, "ORB %e,%r",
[0x0b] RM,0, "OR%S %e,%r",
[0x0c] Ib,0, "ORB %i,AL",
[0x0d] Iwd,0, "OR%S %i,%OAX",
[0x0e] 0,0, "PUSHL CS",
[0x0f] AUXMM,0, optab0F,
[0x10] RMB,0, "ADCB %r,%e",
[0x11] RM,0, "ADC%S %r,%e",
[0x12] RMB,0, "ADCB %e,%r",
[0x13] RM,0, "ADC%S %e,%r",
[0x14] Ib,0, "ADCB %i,AL",
[0x15] Iwd,0, "ADC%S %i,%OAX",
[0x16] 0,0, "PUSHL SS",
[0x17] 0,0, "POPL SS",
[0x18] RMB,0, "SBBB %r,%e",
[0x19] RM,0, "SBB%S %r,%e",
[0x1a] RMB,0, "SBBB %e,%r",
[0x1b] RM,0, "SBB%S %e,%r",
[0x1c] Ib,0, "SBBB %i,AL",
[0x1d] Iwd,0, "SBB%S %i,%OAX",
[0x1e] 0,0, "PUSHL DS",
[0x1f] 0,0, "POPL DS",
[0x20] RMB,0, "ANDB %r,%e",
[0x21] RM,0, "AND%S %r,%e",
[0x22] RMB,0, "ANDB %e,%r",
[0x23] RM,0, "AND%S %e,%r",
[0x24] Ib,0, "ANDB %i,AL",
[0x25] Iwd,0, "AND%S %i,%OAX",
[0x26] SEG,0, "ES:",
[0x27] 0,0, "DAA",
[0x28] RMB,0, "SUBB %r,%e",
[0x29] RM,0, "SUB%S %r,%e",
[0x2a] RMB,0, "SUBB %e,%r",
[0x2b] RM,0, "SUB%S %e,%r",
[0x2c] Ib,0, "SUBB %i,AL",
[0x2d] Iwd,0, "SUB%S %i,%OAX",
[0x2e] SEG,0, "CS:",
[0x2f] 0,0, "DAS",
[0x30] RMB,0, "XORB %r,%e",
[0x31] RM,0, "XOR%S %r,%e",
[0x32] RMB,0, "XORB %e,%r",
[0x33] RM,0, "XOR%S %e,%r",
[0x34] Ib,0, "XORB %i,AL",
[0x35] Iwd,0, "XOR%S %i,%OAX",
[0x36] SEG,0, "SS:",
[0x37] 0,0, "AAA",
[0x38] RMB,0, "CMPB %r,%e",
[0x39] RM,0, "CMP%S %r,%e",
[0x3a] RMB,0, "CMPB %e,%r",
[0x3b] RM,0, "CMP%S %e,%r",
[0x3c] Ib,0, "CMPB %i,AL",
[0x3d] Iwd,0, "CMP%S %i,%OAX",
[0x3e] SEG,0, "DS:",
[0x3f] 0,0, "AAS",
[0x40] 0,0, "INC%S %OAX",
[0x41] 0,0, "INC%S %OCX",
[0x42] 0,0, "INC%S %ODX",
[0x43] 0,0, "INC%S %OBX",
[0x44] 0,0, "INC%S %OSP",
[0x45] 0,0, "INC%S %OBP",
[0x46] 0,0, "INC%S %OSI",
[0x47] 0,0, "INC%S %ODI",
[0x48] 0,0, "DEC%S %OAX",
[0x49] 0,0, "DEC%S %OCX",
[0x4a] 0,0, "DEC%S %ODX",
[0x4b] 0,0, "DEC%S %OBX",
[0x4c] 0,0, "DEC%S %OSP",
[0x4d] 0,0, "DEC%S %OBP",
[0x4e] 0,0, "DEC%S %OSI",
[0x4f] 0,0, "DEC%S %ODI",
[0x50] 0,0, "PUSH%S %OAX",
[0x51] 0,0, "PUSH%S %OCX",
[0x52] 0,0, "PUSH%S %ODX",
[0x53] 0,0, "PUSH%S %OBX",
[0x54] 0,0, "PUSH%S %OSP",
[0x55] 0,0, "PUSH%S %OBP",
[0x56] 0,0, "PUSH%S %OSI",
[0x57] 0,0, "PUSH%S %ODI",
[0x58] 0,0, "POP%S %OAX",
[0x59] 0,0, "POP%S %OCX",
[0x5a] 0,0, "POP%S %ODX",
[0x5b] 0,0, "POP%S %OBX",
[0x5c] 0,0, "POP%S %OSP",
[0x5d] 0,0, "POP%S %OBP",
[0x5e] 0,0, "POP%S %OSI",
[0x5f] 0,0, "POP%S %ODI",
[0x60] 0,0, "PUSHA%S",
[0x61] 0,0, "POPA%S",
[0x62] RMM,0, "BOUND %e,%r",
[0x63] RM,0, "ARPL %r,%e",
[0x64] SEG,0, "FS:",
[0x65] SEG,0, "GS:",
[0x66] OPOVER,0, "",
[0x67] ADDOVER,0, "",
[0x68] Iwd,0, "PUSH%S %i",
[0x69] RM,Iwd, "IMUL%S %e,%i,%r",
[0x6a] Ib,0, "PUSH%S %i",
[0x6b] RM,Ibs, "IMUL%S %e,%i,%r",
[0x6c] 0,0, "INSB DX,(%ODI)",
[0x6d] 0,0, "INS%S DX,(%ODI)",
[0x6e] 0,0, "OUTSB (%ASI),DX",
[0x6f] 0,0, "OUTS%S (%ASI),DX",
[0x70] Jbs,0, "JOS %p",
[0x71] Jbs,0, "JOC %p",
[0x72] Jbs,0, "JCS %p",
[0x73] Jbs,0, "JCC %p",
[0x74] Jbs,0, "JEQ %p",
[0x75] Jbs,0, "JNE %p",
[0x76] Jbs,0, "JLS %p",
[0x77] Jbs,0, "JHI %p",
[0x78] Jbs,0, "JMI %p",
[0x79] Jbs,0, "JPL %p",
[0x7a] Jbs,0, "JPS %p",
[0x7b] Jbs,0, "JPC %p",
[0x7c] Jbs,0, "JLT %p",
[0x7d] Jbs,0, "JGE %p",
[0x7e] Jbs,0, "JLE %p",
[0x7f] Jbs,0, "JGT %p",
[0x80] RMOPB,0, optab80,
[0x81] RMOP,0, optab81,
[0x83] RMOP,0, optab83,
[0x84] RMB,0, "TESTB %r,%e",
[0x85] RM,0, "TEST%S %r,%e",
[0x86] RMB,0, "XCHGB %r,%e",
[0x87] RM,0, "XCHG%S %r,%e",
[0x88] RMB,0, "MOVB %r,%e",
[0x89] RM,0, "MOV%S %r,%e",
[0x8a] RMB,0, "MOVB %e,%r",
[0x8b] RM,0, "MOV%S %e,%r",
[0x8c] RM,0, "MOVW %g,%e",
[0x8d] RM,0, "LEA%S %e,%r",
[0x8e] RM,0, "MOVW %e,%g",
[0x8f] RM,0, "POP%S %e",
[0x90] 0,0, "NOP",
[0x91] 0,0, "XCHG %OCX,%OAX",
[0x92] 0,0, "XCHG %ODX,%OAX",
[0x93] 0,0, "XCHG %OBX,%OAX",
[0x94] 0,0, "XCHG %OSP,%OAX",
[0x95] 0,0, "XCHG %OBP,%OAX",
[0x96] 0,0, "XCHG %OSI,%OAX",
[0x97] 0,0, "XCHG %ODI,%OAX",
[0x98] 0,0, "%W", /* miserable CBW or CWDE */
[0x99] 0,0, "%w", /* idiotic CWD or CDQ */
[0x9a] PTR,0, "CALL%S %d",
[0x9b] 0,0, "WAIT",
[0x9c] 0,0, "PUSHF",
[0x9d] 0,0, "POPF",
[0x9e] 0,0, "SAHF",
[0x9f] 0,0, "LAHF",
[0xa0] Awd,0, "MOVB %i,AL",
[0xa1] Awd,0, "MOV%S %i,%OAX",
[0xa2] Awd,0, "MOVB AL,%i",
[0xa3] Awd,0, "MOV%S %OAX,%i",
[0xa4] 0,0, "MOVSB (%ASI),(%ADI)",
[0xa5] 0,0, "MOVS%S (%ASI),(%ADI)",
[0xa6] 0,0, "CMPSB (%ASI),(%ADI)",
[0xa7] 0,0, "CMPS%S (%ASI),(%ADI)",
[0xa8] Ib,0, "TESTB %i,AL",
[0xa9] Iwd,0, "TEST%S %i,%OAX",
[0xaa] 0,0, "STOSB AL,(%ADI)",
[0xab] 0,0, "STOS%S %OAX,(%ADI)",
[0xac] 0,0, "LODSB (%ASI),AL",
[0xad] 0,0, "LODS%S (%ASI),%OAX",
[0xae] 0,0, "SCASB (%ADI),AL",
[0xaf] 0,0, "SCAS%S (%ADI),%OAX",
[0xb0] Ib,0, "MOVB %i,AL",
[0xb1] Ib,0, "MOVB %i,CL",
[0xb2] Ib,0, "MOVB %i,DL",
[0xb3] Ib,0, "MOVB %i,BL",
[0xb4] Ib,0, "MOVB %i,AH",
[0xb5] Ib,0, "MOVB %i,CH",
[0xb6] Ib,0, "MOVB %i,DH",
[0xb7] Ib,0, "MOVB %i,BH",
[0xb8] Iwdq,0, "MOV%S %i,%OAX",
[0xb9] Iwdq,0, "MOV%S %i,%OCX",
[0xba] Iwdq,0, "MOV%S %i,%ODX",
[0xbb] Iwdq,0, "MOV%S %i,%OBX",
[0xbc] Iwdq,0, "MOV%S %i,%OSP",
[0xbd] Iwdq,0, "MOV%S %i,%OBP",
[0xbe] Iwdq,0, "MOV%S %i,%OSI",
[0xbf] Iwdq,0, "MOV%S %i,%ODI",
[0xc0] RMOPB,0, optabC0,
[0xc1] RMOP,0, optabC1,
[0xc2] Iw,0, "RET %i",
[0xc3] RET,0, "RET",
[0xc4] RM,0, "LES %e,%r",
[0xc5] RM,0, "LDS %e,%r",
[0xc6] RMB,Ib, "MOVB %i,%e",
[0xc7] RM,Iwd, "MOV%S %i,%e",
[0xc8] Iw2,Ib, "ENTER %i,%I", /* loony ENTER */
[0xc9] RET,0, "LEAVE", /* bizarre LEAVE */
[0xca] Iw,0, "RETF %i",
[0xcb] RET,0, "RETF",
[0xcc] 0,0, "INT 3",
[0xcd] Ib,0, "INTB %i",
[0xce] 0,0, "INTO",
[0xcf] 0,0, "IRET",
[0xd0] RMOPB,0, optabD0,
[0xd1] RMOP,0, optabD1,
[0xd2] RMOPB,0, optabD2,
[0xd3] RMOP,0, optabD3,
[0xd4] OA,0, "AAM",
[0xd5] OA,0, "AAD",
[0xd7] 0,0, "XLAT",
[0xd8] FRMOP,0, optabD8,
[0xd9] FRMEX,0, optabD9,
[0xda] FRMOP,0, optabDA,
[0xdb] FRMEX,0, optabDB,
[0xdc] FRMOP,0, optabDC,
[0xdd] FRMOP,0, optabDD,
[0xde] FRMOP,0, optabDE,
[0xdf] FRMOP,0, optabDF,
[0xe0] Jbs,0, "LOOPNE %p",
[0xe1] Jbs,0, "LOOPE %p",
[0xe2] Jbs,0, "LOOP %p",
[0xe3] Jbs,0, "JCXZ %p",
[0xe4] Ib,0, "INB %i,AL",
[0xe5] Ib,0, "IN%S %i,%OAX",
[0xe6] Ib,0, "OUTB AL,%i",
[0xe7] Ib,0, "OUT%S %OAX,%i",
[0xe8] Iwds,0, "CALL %p",
[0xe9] Iwds,0, "JMP %p",
[0xea] PTR,0, "JMP %d",
[0xeb] Jbs,0, "JMP %p",
[0xec] 0,0, "INB DX,AL",
[0xed] 0,0, "IN%S DX,%OAX",
[0xee] 0,0, "OUTB AL,DX",
[0xef] 0,0, "OUT%S %OAX,DX",
[0xf0] PRE,0, "LOCK",
[0xf2] OPRE,0, "REPNE",
[0xf3] OPRE,0, "REP",
[0xf4] 0,0, "HLT",
[0xf5] 0,0, "CMC",
[0xf6] RMOPB,0, optabF6,
[0xf7] RMOP,0, optabF7,
[0xf8] 0,0, "CLC",
[0xf9] 0,0, "STC",
[0xfa] 0,0, "CLI",
[0xfb] 0,0, "STI",
[0xfc] 0,0, "CLD",
[0xfd] 0,0, "STD",
[0xfe] RMOPB,0, optabFE,
[0xff] RMOP,0, optabFF,
[0x100] RM,0, "MOVLQSX %r,%e",
};
/*
* get a byte of the instruction
*/
static int
igetc(Map *map, Instr *ip, uchar *c)
{
if(ip->n+1 > sizeof(ip->mem)){
werrstr("instruction too long");
return -1;
}
if (get1(map, ip->addr+ip->n, c, 1) < 0) {
werrstr("can't read instruction: %r");
return -1;
}
ip->mem[ip->n++] = *c;
return 1;
}
/*
* get two bytes of the instruction
*/
static int
igets(Map *map, Instr *ip, ushort *sp)
{
uchar c;
ushort s;
if (igetc(map, ip, &c) < 0)
return -1;
s = c;
if (igetc(map, ip, &c) < 0)
return -1;
s |= (c<<8);
*sp = s;
return 1;
}
/*
* get 4 bytes of the instruction
*/
static int
igetl(Map *map, Instr *ip, ulong *lp)
{
ushort s;
long l;
if (igets(map, ip, &s) < 0)
return -1;
l = s;
if (igets(map, ip, &s) < 0)
return -1;
l |= (s<<16);
*lp = l;
return 1;
}
/*
* get 8 bytes of the instruction
*/
static int
igetq(Map *map, Instr *ip, vlong *qp)
{
ulong l;
uvlong q;
if (igetl(map, ip, &l) < 0)
return -1;
q = l;
if (igetl(map, ip, &l) < 0)
return -1;
q |= ((uvlong)l<<32);
*qp = q;
return 1;
}
static int
getdisp(Map *map, Instr *ip, int mod, int rm, int code, int pcrel)
{
uchar c;
ushort s;
if (mod > 2)
return 1;
if (mod == 1) {
if (igetc(map, ip, &c) < 0)
return -1;
if (c&0x80)
ip->disp = c|0xffffff00;
else
ip->disp = c&0xff;
} else if (mod == 2 || rm == code) {
if (ip->asize == 'E') {
if (igetl(map, ip, &ip->disp) < 0)
return -1;
if (mod == 0)
ip->rip = pcrel;
} else {
if (igets(map, ip, &s) < 0)
return -1;
if (s&0x8000)
ip->disp = s|0xffff0000;
else
ip->disp = s;
}
if (mod == 0)
ip->base = -1;
}
return 1;
}
static int
modrm(Map *map, Instr *ip, uchar c)
{
uchar rm, mod;
mod = (c>>6)&3;
rm = c&7;
ip->mod = mod;
ip->base = rm;
ip->reg = (c>>3)&7;
ip->rip = 0;
if (mod == 3) /* register */
return 1;
if (ip->asize == 0) { /* 16-bit mode */
switch(rm) {
case 0:
ip->base = BX; ip->index = SI;
break;
case 1:
ip->base = BX; ip->index = DI;
break;
case 2:
ip->base = BP; ip->index = SI;
break;
case 3:
ip->base = BP; ip->index = DI;
break;
case 4:
ip->base = SI;
break;
case 5:
ip->base = DI;
break;
case 6:
ip->base = BP;
break;
case 7:
ip->base = BX;
break;
default:
break;
}
return getdisp(map, ip, mod, rm, 6, 0);
}
if (rm == 4) { /* scummy sib byte */
if (igetc(map, ip, &c) < 0)
return -1;
ip->ss = (c>>6)&0x03;
ip->index = (c>>3)&0x07;
if (ip->index == 4)
ip->index = -1;
ip->base = c&0x07;
return getdisp(map, ip, mod, ip->base, 5, 0);
}
return getdisp(map, ip, mod, rm, 5, ip->amd64);
}
static Optable *
mkinstr(Map *map, Instr *ip, uvlong pc)
{
int i, n, norex;
uchar c;
ushort s;
Optable *op, *obase;
char buf[128];
memset(ip, 0, sizeof(*ip));
norex = 1;
ip->base = -1;
ip->index = -1;
if(asstype == AI8086)
ip->osize = 'W';
else {
ip->osize = 'L';
ip->asize = 'E';
ip->amd64 = asstype != AI386;
norex = 0;
}
ip->addr = pc;
if (igetc(map, ip, &c) < 0)
return 0;
obase = optable;
newop:
if(ip->amd64 && !norex){
if(c >= 0x40 && c <= 0x4f) {
ip->rex = c;
if(igetc(map, ip, &c) < 0)
return 0;
}
if(c == 0x63){
op = &obase[0x100]; /* MOVLQSX */
goto hack;
}
}
op = &obase[c];
hack:
if (op->proto == 0) {
badop:
n = snprint(buf, sizeof(buf), "opcode: ??");
for (i = 0; i < ip->n && n < sizeof(buf)-3; i++, n+=2)
_hexify(buf+n, ip->mem[i], 1);
strcpy(buf+n, "??");
werrstr(buf);
return 0;
}
for(i = 0; i < 2 && op->operand[i]; i++) {
switch(op->operand[i]) {
case Ib: /* 8-bit immediate - (no sign extension)*/
if (igetc(map, ip, &c) < 0)
return 0;
ip->imm = c&0xff;
ip->imm64 = ip->imm;
break;
case Jbs: /* 8-bit jump immediate (sign extended) */
if (igetc(map, ip, &c) < 0)
return 0;
if (c&0x80)
ip->imm = c|0xffffff00;
else
ip->imm = c&0xff;
ip->imm64 = (long)ip->imm;
ip->jumptype = Jbs;
break;
case Ibs: /* 8-bit immediate (sign extended) */
if (igetc(map, ip, &c) < 0)
return 0;
if (c&0x80)
if (ip->osize == 'L')
ip->imm = c|0xffffff00;
else
ip->imm = c|0xff00;
else
ip->imm = c&0xff;
ip->imm64 = (long)ip->imm;
break;
case Iw: /* 16-bit immediate -> imm */
if (igets(map, ip, &s) < 0)
return 0;
ip->imm = s&0xffff;
ip->imm64 = ip->imm;
ip->jumptype = Iw;
break;
case Iw2: /* 16-bit immediate -> in imm2*/
if (igets(map, ip, &s) < 0)
return 0;
ip->imm2 = s&0xffff;
break;
case Iwd: /* Operand-sized immediate (no sign extension unless 64 bits)*/
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
ip->imm64 = ip->imm;
if(ip->rex&REXW && (ip->imm & (1<<31)) != 0)
ip->imm64 |= (vlong)~0 << 32;
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->imm = s&0xffff;
ip->imm64 = ip->imm;
}
break;
case Iwdq: /* Operand-sized immediate, possibly big */
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
ip->imm64 = ip->imm;
if (ip->rex & REXW) {
ulong l;
if (igetl(map, ip, &l) < 0)
return 0;
ip->imm64 |= (uvlong)l << 32;
}
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->imm = s&0xffff;
}
break;
case Awd: /* Address-sized immediate (no sign extension)*/
if (ip->asize == 'E') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
/* TO DO: REX */
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->imm = s&0xffff;
}
break;
case Iwds: /* Operand-sized immediate (sign extended) */
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->imm) < 0)
return 0;
} else {
if (igets(map, ip, &s)< 0)
return 0;
if (s&0x8000)
ip->imm = s|0xffff0000;
else
ip->imm = s&0xffff;
}
ip->jumptype = Iwds;
break;
case OA: /* literal 0x0a byte */
if (igetc(map, ip, &c) < 0)
return 0;
if (c != 0x0a)
goto badop;
break;
case R0: /* base register must be R0 */
if (ip->base != 0)
goto badop;
break;
case R1: /* base register must be R1 */
if (ip->base != 1)
goto badop;
break;
case RMB: /* R/M field with byte register (/r)*/
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
ip->osize = 'B';
break;
case RM: /* R/M field with register (/r) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
break;
case RMOPB: /* R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
c = ip->reg; /* secondary op code */
obase = (Optable*)op->proto;
ip->osize = 'B';
goto newop;
case RMOP: /* R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
obase = (Optable*)op->proto;
if(ip->amd64 && obase == optab0F01 && c == 0xF8)
return optab0F01F8;
c = ip->reg;
goto newop;
case FRMOP: /* FP R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
if ((c&0xc0) == 0xc0)
c = ip->reg+8; /* 16 entry table */
else
c = ip->reg;
obase = (Optable*)op->proto;
goto newop;
case FRMEX: /* Extended FP R/M field with op code (/digit) */
if (igetc(map, ip, &c) < 0)
return 0;
if (modrm(map, ip, c) < 0)
return 0;
if ((c&0xc0) == 0xc0)
c = (c&0x3f)+8; /* 64-entry table */
else
c = ip->reg;
obase = (Optable*)op->proto;
goto newop;
case RMR: /* R/M register only (mod = 11) */
if (igetc(map, ip, &c) < 0)
return 0;
if ((c&0xc0) != 0xc0) {
werrstr("invalid R/M register: %x", c);
return 0;
}
if (modrm(map, ip, c) < 0)
return 0;
break;
case RMM: /* R/M register only (mod = 11) */
if (igetc(map, ip, &c) < 0)
return 0;
if ((c&0xc0) == 0xc0) {
werrstr("invalid R/M memory mode: %x", c);
return 0;
}
if (modrm(map, ip, c) < 0)
return 0;
break;
case PTR: /* Seg:Displacement addr (ptr16:16 or ptr16:32) */
if (ip->osize == 'L') {
if (igetl(map, ip, &ip->disp) < 0)
return 0;
} else {
if (igets(map, ip, &s)< 0)
return 0;
ip->disp = s&0xffff;
}
if (igets(map, ip, (ushort*)&ip->seg) < 0)
return 0;
ip->jumptype = PTR;
break;
case AUXMM: /* Multi-byte op code; prefix determines table selection */
if (igetc(map, ip, &c) < 0)
return 0;
obase = (Optable*)op->proto;
switch (ip->opre) {
case 0x66: op = optab660F; break;
case 0xF2: op = optabF20F; break;
case 0xF3: op = optabF30F; break;
default: op = nil; break;
}
if(op != nil && op[c].proto != nil)
obase = op;
norex = 1; /* no more rex prefixes */
/* otherwise the optab entry captures it */
goto newop;
case AUX: /* Multi-byte op code - Auxiliary table */
obase = (Optable*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
goto newop;
case OPRE: /* Instr Prefix or media op */
ip->opre = c;
/* fall through */
case PRE: /* Instr Prefix */
ip->prefix = (char*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
if (ip->opre && c == 0x0F)
ip->prefix = 0;
goto newop;
case SEG: /* Segment Prefix */
ip->segment = (char*)op->proto;
if (igetc(map, ip, &c) < 0)
return 0;
goto newop;
case OPOVER: /* Operand size override */
ip->opre = c;
ip->osize = 'W';
if (igetc(map, ip, &c) < 0)
return 0;
if (c == 0x0F)
ip->osize = 'L';
else if (ip->amd64 && (c&0xF0) == 0x40)
ip->osize = 'Q';
goto newop;
case ADDOVER: /* Address size override */
ip->asize = 0;
if (igetc(map, ip, &c) < 0)
return 0;
goto newop;
case JUMP: /* mark instruction as JUMP or RET */
case RET:
ip->jumptype = op->operand[i];
break;
default:
werrstr("bad operand type %d", op->operand[i]);
return 0;
}
}
return op;
}
#pragma varargck argpos bprint 2
static void
bprint(Instr *ip, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
ip->curr = vseprint(ip->curr, ip->end, fmt, arg);
va_end(arg);
}
/*
* if we want to call 16 bit regs AX,BX,CX,...
* and 32 bit regs EAX,EBX,ECX,... then
* change the defs of ANAME and ONAME to:
* #define ANAME(ip) ((ip->asize == 'E' ? "E" : "")
* #define ONAME(ip) ((ip)->osize == 'L' ? "E" : "")
*/
#define ANAME(ip) ""
#define ONAME(ip) ""
static char *reg[] = {
[AX] "AX",
[CX] "CX",
[DX] "DX",
[BX] "BX",
[SP] "SP",
[BP] "BP",
[SI] "SI",
[DI] "DI",
/* amd64 */
[R8] "R8",
[R9] "R9",
[R10] "R10",
[R11] "R11",
[R12] "R12",
[R13] "R13",
[R14] "R14",
[R15] "R15",
};
static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
static char *breg64[] = { "AL", "CL", "DL", "BL", "SPB", "BPB", "SIB", "DIB",
"R8B", "R9B", "R10B", "R11B", "R12B", "R13B", "R14B", "R15B" };
static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
static void
plocal(Instr *ip)
{
int ret;
long offset;
Symbol s;
char *reg;
offset = ip->disp;
if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
bprint(ip, "%lux(SP)", offset);
return;
}
if (s.value > ip->disp) {
ret = getauto(&s, s.value-ip->disp-mach->szaddr, CAUTO, &s);
reg = "(SP)";
} else {
offset -= s.value;
ret = getauto(&s, offset, CPARAM, &s);
reg = "(FP)";
}
if (ret)
bprint(ip, "%s+", s.name);
else
offset = ip->disp;
bprint(ip, "%lux%s", offset, reg);
}
static int
isjmp(Instr *ip)
{
switch(ip->jumptype){
case Iwds:
case Jbs:
case JUMP:
return 1;
default:
return 0;
}
}
/*
* This is too smart for its own good, but it really is nice
* to have accurate translations when debugging, and it
* helps us identify which code is different in binaries that
* are changed on sources.
*/
static int
issymref(Instr *ip, Symbol *s, long w, long val)
{
Symbol next, tmp;
long isstring, size;
if (isjmp(ip))
return 1;
if (s->class==CTEXT && w==0)
return 1;
if (s->class==CDATA) {
/* use first bss symbol (or "end") rather than edata */
if (s->name[0]=='e' && strcmp(s->name, "edata") == 0){
if((s ->index >= 0 && globalsym(&tmp, s->index+1) && tmp.value==s->value)
|| (s->index > 0 && globalsym(&tmp, s->index-1) && tmp.value==s->value))
*s = tmp;
}
if (w == 0)
return 1;
for (next=*s; next.value==s->value; next=tmp)
if (!globalsym(&tmp, next.index+1))
break;
size = next.value - s->value;
if (w >= size)
return 0;
if (w > size-w)
w = size-w;
/* huge distances are usually wrong except in .string */
isstring = (s->name[0]=='.' && strcmp(s->name, ".string") == 0);
if (w > 8192 && !isstring)
return 0;
/* medium distances are tricky - look for constants */
/* near powers of two */
if ((val&(val-1)) == 0 || (val&(val+1)) == 0)
return 0;
return 1;
}
return 0;
}
static void
immediate(Instr *ip, vlong val)
{
Symbol s;
long w;
if (findsym(val, CANY, &s)) { /* TO DO */
w = val - s.value;
if (w < 0)
w = -w;
if (issymref(ip, &s, w, val)) {
if (w)
bprint(ip, "%s+%lux(SB)", s.name, w);
else
bprint(ip, "%s(SB)", s.name);
return;
}
/*
if (s.class==CDATA && globalsym(&s, s.index+1)) {
w = s.value - val;
if (w < 0)
w = -w;
if (w < 4096) {
bprint(ip, "%s-%lux(SB)", s.name, w);
return;
}
}
*/
}
if((ip->rex & REXW) == 0)
bprint(ip, "%lux", (long)val);
else
bprint(ip, "%llux", val);
}
static void
pea(Instr *ip)
{
if (ip->mod == 3) {
if (ip->osize == 'B')
bprint(ip, (ip->rex & REXB? breg64: breg)[ip->base]);
else if(ip->rex & REXB)
bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]);
else
bprint(ip, "%s%s", ANAME(ip), reg[ip->base]);
return;
}
if (ip->segment)
bprint(ip, ip->segment);
if (ip->asize == 'E' && ip->base == SP)
plocal(ip);
else {
if (ip->base < 0)
immediate(ip, ip->disp);
else {
bprint(ip, "%lux", ip->disp);
if(ip->rip)
bprint(ip, "(RIP)");
bprint(ip,"(%s%s)", ANAME(ip), reg[ip->rex&REXB? ip->base+8: ip->base]);
}
}
if (ip->index >= 0)
bprint(ip,"(%s%s*%d)", ANAME(ip), reg[ip->rex&REXX? ip->index+8: ip->index], 1<<ip->ss);
}
static void
prinstr(Instr *ip, char *fmt)
{
vlong v;
if (ip->prefix)
bprint(ip, "%s ", ip->prefix);
for (; *fmt && ip->curr < ip->end; fmt++) {
if (*fmt != '%'){
*ip->curr++ = *fmt;
continue;
}
switch(*++fmt){
case '%':
*ip->curr++ = '%';
break;
case 'A':
bprint(ip, "%s", ANAME(ip));
break;
case 'C':
bprint(ip, "CR%d", ip->reg);
break;
case 'D':
if (ip->reg < 4 || ip->reg == 6 || ip->reg == 7)
bprint(ip, "DR%d",ip->reg);
else
bprint(ip, "???");
break;
case 'I':
bprint(ip, "$");
immediate(ip, ip->imm2);
break;
case 'O':
bprint(ip,"%s", ONAME(ip));
break;
case 'i':
bprint(ip, "$");
v = ip->imm;
if(ip->rex & REXW)
v = ip->imm64;
immediate(ip, v);
break;
case 'R':
bprint(ip, "%s%s", ONAME(ip), reg[ip->rex&REXR? ip->reg+8: ip->reg]);
break;
case 'S':
if(ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
bprint(ip, "Q");
else
bprint(ip, "%c", ip->osize);
break;
case 's':
if(ip->opre == 0 || ip->opre == 0x66)
bprint(ip, "P");
else
bprint(ip, "S");
if(ip->opre == 0xf2 || ip->opre == 0x66)
bprint(ip, "D");
else
bprint(ip, "S");
break;
case 'T':
if (ip->reg == 6 || ip->reg == 7)
bprint(ip, "TR%d",ip->reg);
else
bprint(ip, "???");
break;
case 'W':
if (ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
bprint(ip, "CDQE");
else if (ip->osize == 'L')
bprint(ip,"CWDE");
else
bprint(ip, "CBW");
break;
case 'd':
bprint(ip,"%ux:%lux",ip->seg,ip->disp);
break;
case 'm':
if (ip->mod == 3 && ip->osize != 'B') {
if(fmt[1] != '*'){
if(ip->opre != 0) {
bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
break;
}
} else
fmt++;
bprint(ip, "M%d", ip->base);
break;
}
pea(ip);
break;
case 'e':
pea(ip);
break;
case 'f':
bprint(ip, "F%d", ip->base);
break;
case 'g':
if (ip->reg < 6)
bprint(ip,"%s",sreg[ip->reg]);
else
bprint(ip,"???");
break;
case 'p':
/*
* signed immediate in the ulong ip->imm.
*/
v = (long)ip->imm;
immediate(ip, v+ip->addr+ip->n);
break;
case 'r':
if (ip->osize == 'B')
bprint(ip,"%s", (ip->rex? breg64: breg)[ip->rex&REXR? ip->reg+8: ip->reg]);
else
bprint(ip, reg[ip->rex&REXR? ip->reg+8: ip->reg]);
break;
case 'w':
if (ip->osize == 'Q' || ip->rex & REXW)
bprint(ip, "CQO");
else if (ip->osize == 'L')
bprint(ip,"CDQ");
else
bprint(ip, "CWD");
break;
case 'M':
if(ip->opre != 0)
bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
else
bprint(ip, "M%d", ip->reg);
break;
case 'x':
if (ip->mod == 3 && ip->osize != 'B') {
bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
break;
}
pea(ip);
break;
case 'X':
bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
break;
default:
bprint(ip, "%%%c", *fmt);
break;
}
}
*ip->curr = 0; /* there's always room for 1 byte */
}
static int
i386inst(Map *map, uvlong pc, char modifier, char *buf, int n)
{
Instr instr;
Optable *op;
USED(modifier);
op = mkinstr(map, &instr, pc);
if (op == 0) {
errstr(buf, n);
return -1;
}
instr.curr = buf;
instr.end = buf+n-1;
prinstr(&instr, op->proto);
return instr.n;
}
static int
i386das(Map *map, uvlong pc, char *buf, int n)
{
Instr instr;
int i;
if (mkinstr(map, &instr, pc) == 0) {
errstr(buf, n);
return -1;
}
for(i = 0; i < instr.n && n > 2; i++) {
_hexify(buf, instr.mem[i], 1);
buf += 2;
n -= 2;
}
*buf = 0;
return instr.n;
}
static int
i386instlen(Map *map, uvlong pc)
{
Instr i;
if (mkinstr(map, &i, pc))
return i.n;
return -1;
}
static int
i386foll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
{
Instr i;
Optable *op;
ushort s;
uvlong l, addr;
vlong v;
int n;
op = mkinstr(map, &i, pc);
if (!op)
return -1;
n = 0;
switch(i.jumptype) {
case RET: /* RETURN or LEAVE */
case Iw: /* RETURN */
if (strcmp(op->proto, "LEAVE") == 0) {
if (geta(map, (*rget)(map, "BP"), &l) < 0)
return -1;
} else if (geta(map, (*rget)(map, mach->sp), &l) < 0)
return -1;
foll[0] = l;
return 1;
case Iwds: /* pc relative JUMP or CALL*/
case Jbs: /* pc relative JUMP or CALL */
v = (long)i.imm;
foll[0] = pc+v+i.n;
n = 1;
break;
case PTR: /* seg:displacement JUMP or CALL */
foll[0] = (i.seg<<4)+i.disp;
return 1;
case JUMP: /* JUMP or CALL EA */
if(i.mod == 3) {
foll[0] = (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]);
return 1;
}
/* calculate the effective address */
addr = i.disp;
if (i.base >= 0) {
if (geta(map, (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]), &l) < 0)
return -1;
addr += l;
}
if (i.index >= 0) {
if (geta(map, (*rget)(map, reg[i.rex&REXX? i.index+8: i.index]), &l) < 0)
return -1;
addr += l*(1<<i.ss);
}
/* now retrieve a seg:disp value at that address */
if (get2(map, addr, &s) < 0) /* seg */
return -1;
foll[0] = s<<4;
addr += 2;
if (i.asize == 'L') {
if (geta(map, addr, &l) < 0) /* disp32 */
return -1;
foll[0] += l;
} else { /* disp16 */
if (get2(map, addr, &s) < 0)
return -1;
foll[0] += s;
}
return 1;
default:
break;
}
if (strncmp(op->proto,"JMP", 3) == 0 || strncmp(op->proto,"CALL", 4) == 0)
return 1;
foll[n++] = pc+i.n;
return n;
}
# Derived from Inferno libmach/mkfile
# http://code.google.com/p/inferno-os/source/browse/utils/libmach/mkfile
#
# 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.
CFLAGS=-I$(GOROOT)/include
O=o
LIB=libmach_amd64.a
OFILES=\
executable.$O\
map.$O\
obj.$O\
swap.$O\
sym.$O\
access.$O\
machdata.$O\
setmach.$O\
6.$O\
8.$O\
8db.$O\
6obj.$O\
# v.$O\
# k.$O\
# u.$O\
# q.$O\
# 0.$O\
# 2.$O\
# 5.$O\
# 7.$O\
# 9.$O\
# vdb.$O\
# kdb.$O\
# udb.$O\
# qdb.$O\
# 2db.$O\
# 5db.$O\
# 7db.$O\
# vobj.$O\
# kobj.$O\
# uobj.$O\
# 2obj.$O\
# 5obj.$O\
# 7obj.$O\
# 8obj.$O\
# 9obj.$O\
# qobj.$O\
# vcodas.$O\
HFILES=$(GOROOT)/include/mach_amd64.h elf.h obj.h
install: $(LIB)
cp $(LIB) $(GOROOT)/lib
$(LIB): $(OFILES)
ar rsc $(LIB) $(OFILES)
$(OFILES): $(HFILES)
clean:
rm -f $(OFILES) $(LIB)
// Inferno libmach/access.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/access.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.
/*
* functions to read and write an executable or file image
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach_amd64.h>
static int mget(Map*, uvlong, void*, int);
static int mput(Map*, uvlong, void*, int);
static struct segment* reloc(Map*, uvlong, vlong*);
/*
* routines to get/put various types
*/
int
geta(Map *map, uvlong addr, uvlong *x)
{
ulong l;
uvlong vl;
if (mach->szaddr == 8){
if (get8(map, addr, &vl) < 0)
return -1;
*x = vl;
return 1;
}
if (get4(map, addr, &l) < 0)
return -1;
*x = l;
return 1;
}
int
get8(Map *map, uvlong addr, uvlong *x)
{
if (!map) {
werrstr("get8: invalid map");
return -1;
}
if (map->nsegs == 1 && map->seg[0].fd < 0) {
*x = addr;
return 1;
}
if (mget(map, addr, x, 8) < 0)
return -1;
*x = machdata->swav(*x);
return 1;
}
int
get4(Map *map, uvlong addr, ulong *x)
{
if (!map) {
werrstr("get4: invalid map");
return -1;
}
if (map->nsegs == 1 && map->seg[0].fd < 0) {
*x = addr;
return 1;
}
if (mget(map, addr, x, 4) < 0)
return -1;
*x = machdata->swal(*x);
return 1;
}
int
get2(Map *map, uvlong addr, ushort *x)
{
if (!map) {
werrstr("get2: invalid map");
return -1;
}
if (map->nsegs == 1 && map->seg[0].fd < 0) {
*x = addr;
return 1;
}
if (mget(map, addr, x, 2) < 0)
return -1;
*x = machdata->swab(*x);
return 1;
}
int
get1(Map *map, uvlong addr, uchar *x, int size)
{
uchar *cp;
if (!map) {
werrstr("get1: invalid map");
return -1;
}
if (map->nsegs == 1 && map->seg[0].fd < 0) {
cp = (uchar*)&addr;
while (cp < (uchar*)(&addr+1) && size-- > 0)
*x++ = *cp++;
while (size-- > 0)
*x++ = 0;
} else
return mget(map, addr, x, size);
return 1;
}
int
puta(Map *map, uvlong addr, uvlong v)
{
if (mach->szaddr == 8)
return put8(map, addr, v);
return put4(map, addr, v);
}
int
put8(Map *map, uvlong addr, uvlong v)
{
if (!map) {
werrstr("put8: invalid map");
return -1;
}
v = machdata->swav(v);
return mput(map, addr, &v, 8);
}
int
put4(Map *map, uvlong addr, ulong v)
{
if (!map) {
werrstr("put4: invalid map");
return -1;
}
v = machdata->swal(v);
return mput(map, addr, &v, 4);
}
int
put2(Map *map, uvlong addr, ushort v)
{
if (!map) {
werrstr("put2: invalid map");
return -1;
}
v = machdata->swab(v);
return mput(map, addr, &v, 2);
}
int
put1(Map *map, uvlong addr, uchar *v, int size)
{
if (!map) {
werrstr("put1: invalid map");
return -1;
}
return mput(map, addr, v, size);
}
static int
spread(struct segment *s, void *buf, int n, uvlong off)
{
uvlong base;
static struct {
struct segment *s;
char a[8192];
uvlong off;
} cache;
if(s->cache){
base = off&~(sizeof cache.a-1);
if(cache.s != s || cache.off != base){
cache.off = ~0;
if(seek(s->fd, base, 0) >= 0
&& readn(s->fd, cache.a, sizeof cache.a) == sizeof cache.a){
cache.s = s;
cache.off = base;
}
}
if(cache.s == s && cache.off == base){
off &= sizeof cache.a-1;
if(off+n > sizeof cache.a)
n = sizeof cache.a - off;
memmove(buf, cache.a+off, n);
return n;
}
}
return pread(s->fd, buf, n, off);
}
static int
mget(Map *map, uvlong addr, void *buf, int size)
{
uvlong off;
int i, j, k;
struct segment *s;
s = reloc(map, addr, (vlong*)&off);
if (!s)
return -1;
if (s->fd < 0) {
werrstr("unreadable map");
return -1;
}
for (i = j = 0; i < 2; i++) { /* in case read crosses page */
k = spread(s, buf, size-j, off+j);
if (k < 0) {
werrstr("can't read address %llux: %r", addr);
return -1;
}
j += k;
if (j == size)
return j;
}
werrstr("partial read at address %llux (size %d j %d)", addr, size, j);
return -1;
}
static int
mput(Map *map, uvlong addr, void *buf, int size)
{
vlong off;
int i, j, k;
struct segment *s;
s = reloc(map, addr, &off);
if (!s)
return -1;
if (s->fd < 0) {
werrstr("unwritable map");
return -1;
}
seek(s->fd, off, 0);
for (i = j = 0; i < 2; i++) { /* in case read crosses page */
k = write(s->fd, buf, size-j);
if (k < 0) {
werrstr("can't write address %llux: %r", addr);
return -1;
}
j += k;
if (j == size)
return j;
}
werrstr("partial write at address %llux", addr);
return -1;
}
/*
* convert address to file offset; returns nonzero if ok
*/
static struct segment*
reloc(Map *map, uvlong addr, vlong *offp)
{
int i;
for (i = 0; i < map->nsegs; i++) {
if (map->seg[i].inuse)
if (map->seg[i].b <= addr && addr < map->seg[i].e) {
*offp = addr + map->seg[i].f - map->seg[i].b;
return &map->seg[i];
}
}
werrstr("can't translate address %llux", addr);
return 0;
}
// Inferno libmach/elf.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/elf.h
//
// 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.
/*
* Definitions needed for accessing ELF headers.
* 32-bit and 64-bit structs differ.
*/
typedef struct {
uchar ident[16]; /* ident bytes */
ushort type; /* file type */
ushort machine; /* target machine */
int version; /* file version */
ulong elfentry; /* start address */
ulong phoff; /* phdr file offset */
ulong shoff; /* shdr file offset */
int flags; /* file flags */
ushort ehsize; /* sizeof ehdr */
ushort phentsize; /* sizeof phdr */
ushort phnum; /* number phdrs */
ushort shentsize; /* sizeof shdr */
ushort shnum; /* number shdrs */
ushort shstrndx; /* shdr string index */
} Ehdr32;
typedef struct {
uchar ident[16]; /* ident bytes */
ushort type; /* file type */
ushort machine; /* target machine */
int version; /* file version */
uvlong elfentry; /* start address */
uvlong phoff; /* phdr file offset */
uvlong shoff; /* shdr file offset */
int flags; /* file flags */
ushort ehsize; /* sizeof ehdr */
ushort phentsize; /* sizeof phdr */
ushort phnum; /* number phdrs */
ushort shentsize; /* sizeof shdr */
ushort shnum; /* number shdrs */
ushort shstrndx; /* shdr string index */
} Ehdr64;
typedef struct {
int type; /* entry type */
ulong offset; /* file offset */
ulong vaddr; /* virtual address */
ulong paddr; /* physical address */
int filesz; /* file size */
ulong memsz; /* memory size */
int flags; /* entry flags */
int align; /* memory/file alignment */
} Phdr32;
typedef struct {
int type; /* entry type */
int flags; /* entry flags */
uvlong offset; /* file offset */
uvlong vaddr; /* virtual address */
uvlong paddr; /* physical address */
uvlong filesz; /* file size */
uvlong memsz; /* memory size */
uvlong align; /* memory/file alignment */
} Phdr64;
typedef struct {
ulong name; /* section name */
ulong type; /* SHT_... */
ulong flags; /* SHF_... */
ulong addr; /* virtual address */
ulong offset; /* file offset */
ulong size; /* section size */
ulong link; /* misc info */
ulong info; /* misc info */
ulong addralign; /* memory alignment */
ulong entsize; /* entry size if table */
} Shdr32;
typedef struct {
ulong name; /* section name */
ulong type; /* SHT_... */
uvlong flags; /* SHF_... */
uvlong addr; /* virtual address */
uvlong offset; /* file offset */
uvlong size; /* section size */
ulong link; /* misc info */
ulong info; /* misc info */
uvlong addralign; /* memory alignment */
uvlong entsize; /* entry size if table */
} Shdr64;
enum {
/* Ehdr codes */
MAG0 = 0, /* ident[] indexes */
MAG1 = 1,
MAG2 = 2,
MAG3 = 3,
CLASS = 4,
DATA = 5,
VERSION = 6,
ELFCLASSNONE = 0, /* ident[CLASS] */
ELFCLASS32 = 1,
ELFCLASS64 = 2,
ELFCLASSNUM = 3,
ELFDATANONE = 0, /* ident[DATA] */
ELFDATA2LSB = 1,
ELFDATA2MSB = 2,
ELFDATANUM = 3,
NOETYPE = 0, /* type */
REL = 1,
EXEC = 2,
DYN = 3,
CORE = 4,
NONE = 0, /* machine */
M32 = 1, /* AT&T WE 32100 */
SPARC = 2, /* Sun SPARC */
I386 = 3, /* Intel 80386 */
M68K = 4, /* Motorola 68000 */
M88K = 5, /* Motorola 88000 */
I486 = 6, /* Intel 80486 */
I860 = 7, /* Intel i860 */
MIPS = 8, /* Mips R2000 */
S370 = 9, /* Amdhal */
SPARC64 = 18, /* Sun SPARC v9 */
POWER = 20, /* PowerPC */
ARM = 40, /* ARM */
AMD64 = 62, /* Amd64 */
NO_VERSION = 0, /* version, ident[VERSION] */
CURRENT = 1,
/* Phdr Codes */
NOPTYPE = 0, /* type */
LOAD = 1,
DYNAMIC = 2,
INTERP = 3,
NOTE = 4,
SHLIB = 5,
PHDR = 6,
R = 0x4, /* flags */
W = 0x2,
X = 0x1,
/* Shdr Codes */
Progbits = 1, /* section types */
Strtab = 3,
Nobits = 8,
Swrite = 1, /* section attributes */
Salloc = 2,
Sexec = 4,
};
#define ELF_MAG ((0x7f<<24) | ('E'<<16) | ('L'<<8) | 'F')
// Inferno libmach/executable.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/executable.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.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <bootexec.h>
#include <mach_amd64.h>
#include "elf.h"
/*
* All a.out header types. The dummy entry allows canonical
* processing of the union as a sequence of longs
*/
typedef struct {
union{
/*struct { */
Exec exechdr; /* a.out.h */
/* uvlong hdr[1];*/
/*};*/
Ehdr32 elfhdr32; /* elf.h */
Ehdr64 elfhdr64; /* elf.h */
struct mipsexec mips; /* bootexec.h */
struct mips4kexec mipsk4; /* bootexec.h */
struct sparcexec sparc; /* bootexec.h */
struct nextexec next; /* bootexec.h */
} e;
long dummy; /* padding to ensure extra long */
} ExecHdr;
static int nextboot(int, Fhdr*, ExecHdr*);
static int sparcboot(int, Fhdr*, ExecHdr*);
static int mipsboot(int, Fhdr*, ExecHdr*);
static int mips4kboot(int, Fhdr*, ExecHdr*);
static int common(int, Fhdr*, ExecHdr*);
static int commonllp64(int, Fhdr*, ExecHdr*);
static int adotout(int, Fhdr*, ExecHdr*);
static int elfdotout(int, Fhdr*, ExecHdr*);
static int armdotout(int, Fhdr*, ExecHdr*);
static void setsym(Fhdr*, long, long, long, vlong);
static void setdata(Fhdr*, uvlong, long, vlong, long);
static void settext(Fhdr*, uvlong, uvlong, long, vlong);
static void hswal(void*, int, ulong(*)(ulong));
static uvlong _round(uvlong, ulong);
/*
* definition of per-executable file type structures
*/
typedef struct Exectable{
long magic; /* big-endian magic number of file */
char *name; /* executable identifier */
char *dlmname; /* dynamically loadable module identifier */
uchar type; /* Internal code */
uchar _magic; /* _MAGIC() magic */
Mach *mach; /* Per-machine data */
long hsize; /* header size */
ulong (*swal)(ulong); /* beswal or leswal */
int (*hparse)(int, Fhdr*, ExecHdr*);
} ExecTable;
extern Mach mmips;
extern Mach mmips2le;
extern Mach mmips2be;
extern Mach msparc;
extern Mach msparc64;
extern Mach m68020;
extern Mach mi386;
extern Mach mamd64;
extern Mach marm;
extern Mach mpower;
extern Mach mpower64;
extern Mach malpha;
/* BUG: FIX THESE WHEN NEEDED */
Mach mmips;
Mach mmips2le;
Mach mmips2be;
Mach msparc;
Mach msparc64;
Mach m68020;
Mach marm;
Mach mpower;
Mach mpower64;
Mach malpha;
ExecTable exectab[] =
{
{ V_MAGIC, /* Mips v.out */
"mips plan 9 executable BE",
"mips plan 9 dlm BE",
FMIPS,
1,
&mmips,
sizeof(Exec),
beswal,
adotout },
{ P_MAGIC, /* Mips 0.out (r3k le) */
"mips plan 9 executable LE",
"mips plan 9 dlm LE",
FMIPSLE,
1,
&mmips,
sizeof(Exec),
beswal,
adotout },
{ M_MAGIC, /* Mips 4.out */
"mips 4k plan 9 executable BE",
"mips 4k plan 9 dlm BE",
FMIPS2BE,
1,
&mmips2be,
sizeof(Exec),
beswal,
adotout },
{ N_MAGIC, /* Mips 0.out */
"mips 4k plan 9 executable LE",
"mips 4k plan 9 dlm LE",
FMIPS2LE,
1,
&mmips2le,
sizeof(Exec),
beswal,
adotout },
{ 0x160<<16, /* Mips boot image */
"mips plan 9 boot image",
nil,
FMIPSB,
0,
&mmips,
sizeof(struct mipsexec),
beswal,
mipsboot },
{ (0x160<<16)|3, /* Mips boot image */
"mips 4k plan 9 boot image",
nil,
FMIPSB,
0,
&mmips2be,
sizeof(struct mips4kexec),
beswal,
mips4kboot },
{ K_MAGIC, /* Sparc k.out */
"sparc plan 9 executable",
"sparc plan 9 dlm",
FSPARC,
1,
&msparc,
sizeof(Exec),
beswal,
adotout },
{ 0x01030107, /* Sparc boot image */
"sparc plan 9 boot image",
nil,
FSPARCB,
0,
&msparc,
sizeof(struct sparcexec),
beswal,
sparcboot },
{ U_MAGIC, /* Sparc64 u.out */
"sparc64 plan 9 executable",
"sparc64 plan 9 dlm",
FSPARC64,
1,
&msparc64,
sizeof(Exec),
beswal,
adotout },
{ A_MAGIC, /* 68020 2.out & boot image */
"68020 plan 9 executable",
"68020 plan 9 dlm",
F68020,
1,
&m68020,
sizeof(Exec),
beswal,
common },
{ 0xFEEDFACE, /* Next boot image */
"next plan 9 boot image",
nil,
FNEXTB,
0,
&m68020,
sizeof(struct nextexec),
beswal,
nextboot },
{ I_MAGIC, /* I386 8.out & boot image */
"386 plan 9 executable",
"386 plan 9 dlm",
FI386,
1,
&mi386,
sizeof(Exec),
beswal,
common },
{ S_MAGIC, /* amd64 6.out & boot image */
"amd64 plan 9 executable",
"amd64 plan 9 dlm",
FAMD64,
1,
&mamd64,
sizeof(Exec)+8,
nil,
commonllp64 },
{ Q_MAGIC, /* PowerPC q.out & boot image */
"power plan 9 executable",
"power plan 9 dlm",
FPOWER,
1,
&mpower,
sizeof(Exec),
beswal,
common },
{ T_MAGIC, /* power64 9.out & boot image */
"power64 plan 9 executable",
"power64 plan 9 dlm",
FPOWER64,
1,
&mpower64,
sizeof(Exec)+8,
nil,
commonllp64 },
{ ELF_MAG, /* any elf32 or elf64 */
"elf executable",
nil,
FNONE,
0,
&mi386,
sizeof(Ehdr64),
nil,
elfdotout },
{ E_MAGIC, /* Arm 5.out and boot image */
"arm plan 9 executable",
"arm plan 9 dlm",
FARM,
1,
&marm,
sizeof(Exec),
beswal,
common },
{ (143<<16)|0413, /* (Free|Net)BSD Arm */
"arm *bsd executable",
nil,
FARM,
0,
&marm,
sizeof(Exec),
leswal,
armdotout },
{ L_MAGIC, /* alpha 7.out */
"alpha plan 9 executable",
"alpha plan 9 dlm",
FALPHA,
1,
&malpha,
sizeof(Exec),
beswal,
common },
{ 0x0700e0c3, /* alpha boot image */
"alpha plan 9 boot image",
nil,
FALPHA,
0,
&malpha,
sizeof(Exec),
beswal,
common },
{ 0 },
};
Mach *mach = &mi386; /* Global current machine table */
static ExecTable*
couldbe4k(ExecTable *mp)
{
Dir *d;
ExecTable *f;
if((d=dirstat("/proc/1/regs")) == nil)
return mp;
if(d->length < 32*8){ /* R3000 */
free(d);
return mp;
}
free(d);
for (f = exectab; f->magic; f++)
if(f->magic == M_MAGIC) {
f->name = "mips plan 9 executable on mips2 kernel";
return f;
}
return mp;
}
int
crackhdr(int fd, Fhdr *fp)
{
ExecTable *mp;
ExecHdr d;
int nb, ret;
ulong magic;
fp->type = FNONE;
nb = read(fd, (char *)&d.e, sizeof(d.e));
if (nb <= 0)
return 0;
ret = 0;
magic = beswal(d.e.exechdr.magic); /* big-endian */
for (mp = exectab; mp->magic; mp++) {
if (nb < mp->hsize)
continue;
/*
* The magic number has morphed into something
* with fields (the straw was DYN_MAGIC) so now
* a flag is needed in Fhdr to distinguish _MAGIC()
* magic numbers from foreign magic numbers.
*
* This code is creaking a bit and if it has to
* be modified/extended much more it's probably
* time to step back and redo it all.
*/
if(mp->_magic){
if(mp->magic != (magic & ~DYN_MAGIC))
continue;
if(mp->magic == V_MAGIC)
mp = couldbe4k(mp);
if ((magic & DYN_MAGIC) && mp->dlmname != nil)
fp->name = mp->dlmname;
else
fp->name = mp->name;
}
else{
if(mp->magic != magic)
continue;
fp->name = mp->name;
}
fp->type = mp->type;
fp->hdrsz = mp->hsize; /* will be zero on bootables */
fp->_magic = mp->_magic;
fp->magic = magic;
mach = mp->mach;
if(mp->swal != nil)
hswal(&d, sizeof(d.e)/sizeof(ulong), mp->swal);
ret = mp->hparse(fd, fp, &d);
seek(fd, mp->hsize, 0); /* seek to end of header */
break;
}
if(mp->magic == 0)
werrstr("unknown header type");
return ret;
}
/*
* Convert header to canonical form
*/
static void
hswal(void *v, int n, ulong (*swap)(ulong))
{
ulong *ulp;
for(ulp = v; n--; ulp++)
*ulp = (*swap)(*ulp);
}
/*
* Crack a normal a.out-type header
*/
static int
adotout(int fd, Fhdr *fp, ExecHdr *hp)
{
long pgsize;
USED(fd);
pgsize = mach->pgsize;
settext(fp, hp->e.exechdr.entry, pgsize+sizeof(Exec),
hp->e.exechdr.text, sizeof(Exec));
setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize),
hp->e.exechdr.data, fp->txtsz+sizeof(Exec), hp->e.exechdr.bss);
setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz);
return 1;
}
static void
commonboot(Fhdr *fp)
{
if (!(fp->entry & mach->ktmask))
return;
switch(fp->type) { /* boot image */
case F68020:
fp->type = F68020B;
fp->name = "68020 plan 9 boot image";
break;
case FI386:
fp->type = FI386B;
fp->txtaddr = (u32int)fp->entry;
fp->name = "386 plan 9 boot image";
fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
break;
case FARM:
fp->type = FARMB;
fp->txtaddr = (u32int)fp->entry;
fp->name = "ARM plan 9 boot image";
fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
return;
case FALPHA:
fp->type = FALPHAB;
fp->txtaddr = (u32int)fp->entry;
fp->name = "alpha plan 9 boot image";
fp->dataddr = fp->txtaddr+fp->txtsz;
break;
case FPOWER:
fp->type = FPOWERB;
fp->txtaddr = (u32int)fp->entry;
fp->name = "power plan 9 boot image";
fp->dataddr = fp->txtaddr+fp->txtsz;
break;
case FAMD64:
fp->type = FAMD64B;
fp->txtaddr = fp->entry;
fp->name = "amd64 plan 9 boot image";
fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
break;
default:
return;
}
fp->hdrsz = 0; /* header stripped */
}
/*
* _MAGIC() style headers and
* alpha plan9-style bootable images for axp "headerless" boot
*
*/
static int
common(int fd, Fhdr *fp, ExecHdr *hp)
{
adotout(fd, fp, hp);
if(hp->e.exechdr.magic & DYN_MAGIC) {
fp->txtaddr = 0;
fp->dataddr = fp->txtsz;
return 1;
}
commonboot(fp);
return 1;
}
static int
commonllp64(int unused, Fhdr *fp, ExecHdr *hp)
{
long pgsize;
uvlong entry;
hswal(&hp->e, sizeof(Exec)/sizeof(long), beswal);
if(!(hp->e.exechdr.magic & HDR_MAGIC))
return 0;
/*
* There can be more magic here if the
* header ever needs more expansion.
* For now just catch use of any of the
* unused bits.
*/
if((hp->e.exechdr.magic & ~DYN_MAGIC)>>16)
return 0;
entry = beswav(*(uvlong*)&hp->e.exechdr);
pgsize = mach->pgsize;
settext(fp, entry, pgsize+fp->hdrsz, hp->e.exechdr.text, fp->hdrsz);
setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize),
hp->e.exechdr.data, fp->txtsz+fp->hdrsz, hp->e.exechdr.bss);
setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz);
if(hp->e.exechdr.magic & DYN_MAGIC) {
fp->txtaddr = 0;
fp->dataddr = fp->txtsz;
return 1;
}
commonboot(fp);
return 1;
}
/*
* mips bootable image.
*/
static int
mipsboot(int fd, Fhdr *fp, ExecHdr *hp)
{
abort();
#ifdef unused
USED(fd);
fp->type = FMIPSB;
switch(hp->e.exechdr.amagic) {
default:
case 0407: /* some kind of mips */
settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
hp->e.tsize, sizeof(struct mipsexec)+4);
setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
fp->txtoff+hp->e.tsize, hp->e.bsize);
break;
case 0413: /* some kind of mips */
settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
hp->e.tsize, 0);
setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
hp->e.tsize, hp->e.bsize);
break;
}
setsym(fp, hp->e.nsyms, 0, hp->e.pcsize, hp->e.symptr);
fp->hdrsz = 0; /* header stripped */
#endif
return 1;
}
/*
* mips4k bootable image.
*/
static int
mips4kboot(int fd, Fhdr *fp, ExecHdr *hp)
{
abort();
#ifdef unused
USED(fd);
fp->type = FMIPSB;
switch(hp->e.h.amagic) {
default:
case 0407: /* some kind of mips */
settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
hp->e.h.tsize, sizeof(struct mips4kexec));
setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
fp->txtoff+hp->e.h.tsize, hp->e.h.bsize);
break;
case 0413: /* some kind of mips */
settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
hp->e.h.tsize, 0);
setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
hp->e.h.tsize, hp->e.h.bsize);
break;
}
setsym(fp, hp->e.h.nsyms, 0, hp->e.h.pcsize, hp->e.h.symptr);
fp->hdrsz = 0; /* header stripped */
#endif
return 1;
}
/*
* sparc bootable image
*/
static int
sparcboot(int fd, Fhdr *fp, ExecHdr *hp)
{
abort();
#ifdef unused
USED(fd);
fp->type = FSPARCB;
settext(fp, hp->e.sentry, hp->e.sentry, hp->e.stext,
sizeof(struct sparcexec));
setdata(fp, hp->e.sentry+hp->e.stext, hp->e.sdata,
fp->txtoff+hp->e.stext, hp->e.sbss);
setsym(fp, hp->e.ssyms, 0, hp->e.sdrsize, fp->datoff+hp->e.sdata);
fp->hdrsz = 0; /* header stripped */
#endif
return 1;
}
/*
* next bootable image
*/
static int
nextboot(int fd, Fhdr *fp, ExecHdr *hp)
{
abort();
#ifdef unused
USED(fd);
fp->type = FNEXTB;
settext(fp, hp->e.textc.vmaddr, hp->e.textc.vmaddr,
hp->e.texts.size, hp->e.texts.offset);
setdata(fp, hp->e.datac.vmaddr, hp->e.datas.size,
hp->e.datas.offset, hp->e.bsss.size);
setsym(fp, hp->e.symc.nsyms, hp->e.symc.spoff, hp->e.symc.pcoff,
hp->e.symc.symoff);
fp->hdrsz = 0; /* header stripped */
#endif
return 1;
}
/*
* Elf32 and Elf64 binaries.
*/
static int
elf64dotout(int fd, Fhdr *fp, ExecHdr *hp)
{
uvlong (*swav)(uvlong);
ulong (*swal)(ulong);
ushort (*swab)(ushort);
Ehdr64 *ep;
Phdr64 *ph;
int i, it, id, is, phsz;
/* bitswap the header according to the DATA format */
ep = &hp->e.elfhdr64;
if(ep->ident[CLASS] != ELFCLASS64) {
werrstr("bad ELF class - not 32 bit or 64 bit");
return 0;
}
if(ep->ident[DATA] == ELFDATA2LSB) {
swab = leswab;
swal = leswal;
swav = leswav;
} else if(ep->ident[DATA] == ELFDATA2MSB) {
swab = beswab;
swal = beswal;
swav = beswav;
} else {
werrstr("bad ELF encoding - not big or little endian");
return 0;
}
ep->type = swab(ep->type);
ep->machine = swab(ep->machine);
ep->version = swal(ep->version);
ep->elfentry = swal(ep->elfentry);
print("entry: 0x%x\n", ep->elfentry);
ep->phoff = swav(ep->phoff);
ep->shoff = swav(ep->shoff);
ep->flags = swav(ep->flags);
ep->ehsize = swab(ep->ehsize);
ep->phentsize = swab(ep->phentsize);
ep->phnum = swab(ep->phnum);
ep->shentsize = swab(ep->shentsize);
ep->shnum = swab(ep->shnum);
ep->shstrndx = swab(ep->shstrndx);
if(ep->type != EXEC || ep->version != CURRENT)
return 0;
/* we could definitely support a lot more machines here */
fp->magic = ELF_MAG;
fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
switch(ep->machine) {
case I386:
mach = &mi386;
fp->type = FI386;
break;
case MIPS:
mach = &mmips;
fp->type = FMIPS;
break;
case SPARC64:
mach = &msparc64;
fp->type = FSPARC64;
break;
case POWER:
mach = &mpower;
fp->type = FPOWER;
break;
case AMD64:
mach = &mamd64;
fp->type = FAMD64;
break;
case ARM:
mach = &marm;
fp->type = FARM;
break;
default:
return 0;
}
if(ep->phentsize != sizeof(Phdr64)) {
werrstr("bad ELF header size");
return 0;
}
phsz = sizeof(Phdr64)*ep->phnum;
ph = malloc(phsz);
if(!ph)
return 0;
seek(fd, ep->phoff, 0);
if(read(fd, ph, phsz) < 0) {
free(ph);
return 0;
}
hswal(ph, phsz/sizeof(ulong), swal);
/* find text, data and symbols and install them */
it = id = is = -1;
for(i = 0; i < ep->phnum; i++) {
if(ph[i].type == LOAD
&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
it = i;
else if(ph[i].type == LOAD
&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
id = i;
else if(ph[i].type == NOPTYPE && is == -1)
is = i;
}
if(it == -1 || id == -1) {
/*
* The SPARC64 boot image is something of an ELF hack.
* Text+Data+BSS are represented by ph[0]. Symbols
* are represented by ph[1]:
*
* filesz, memsz, vaddr, paddr, off
* ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
* ph[1] : symsz, lcsz, 0, 0, symoff
*/
if(ep->machine == SPARC64 && ep->phnum == 2) {
ulong txtaddr, txtsz, dataddr, bsssz;
txtaddr = ph[0].vaddr | 0x80000000;
txtsz = ph[0].filesz - ph[0].paddr;
dataddr = txtaddr + txtsz;
bsssz = ph[0].memsz - ph[0].filesz;
settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset);
free(ph);
return 1;
}
werrstr("No TEXT or DATA sections");
free(ph);
return 0;
}
settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
if(is != -1)
setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
free(ph);
return 1;
}
static int
elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
{
ulong (*swal)(ulong);
ushort (*swab)(ushort);
Ehdr32 *ep;
Phdr32 *ph;
int i, it, id, is, phsz;
/* bitswap the header according to the DATA format */
ep = &hp->e.elfhdr32;
if(ep->ident[CLASS] != ELFCLASS32) {
return elf64dotout(fd, fp, hp);
}
if(ep->ident[DATA] == ELFDATA2LSB) {
swab = leswab;
swal = leswal;
} else if(ep->ident[DATA] == ELFDATA2MSB) {
swab = beswab;
swal = beswal;
} else {
werrstr("bad ELF encoding - not big or little endian");
return 0;
}
ep->type = swab(ep->type);
ep->machine = swab(ep->machine);
ep->version = swal(ep->version);
ep->elfentry = swal(ep->elfentry);
ep->phoff = swal(ep->phoff);
ep->shoff = swal(ep->shoff);
ep->flags = swal(ep->flags);
ep->ehsize = swab(ep->ehsize);
ep->phentsize = swab(ep->phentsize);
ep->phnum = swab(ep->phnum);
ep->shentsize = swab(ep->shentsize);
ep->shnum = swab(ep->shnum);
ep->shstrndx = swab(ep->shstrndx);
if(ep->type != EXEC || ep->version != CURRENT)
return 0;
/* we could definitely support a lot more machines here */
fp->magic = ELF_MAG;
fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
switch(ep->machine) {
case I386:
mach = &mi386;
fp->type = FI386;
break;
case MIPS:
mach = &mmips;
fp->type = FMIPS;
break;
case SPARC64:
mach = &msparc64;
fp->type = FSPARC64;
break;
case POWER:
mach = &mpower;
fp->type = FPOWER;
break;
case AMD64:
mach = &mamd64;
fp->type = FAMD64;
break;
case ARM:
mach = &marm;
fp->type = FARM;
break;
default:
return 0;
}
if(ep->phentsize != sizeof(Phdr32)) {
werrstr("bad ELF header size");
return 0;
}
phsz = sizeof(Phdr32)*ep->phnum;
ph = malloc(phsz);
if(!ph)
return 0;
seek(fd, ep->phoff, 0);
if(read(fd, ph, phsz) < 0) {
free(ph);
return 0;
}
hswal(ph, phsz/sizeof(ulong), swal);
/* find text, data and symbols and install them */
it = id = is = -1;
for(i = 0; i < ep->phnum; i++) {
if(ph[i].type == LOAD
&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
it = i;
else if(ph[i].type == LOAD
&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
id = i;
else if(ph[i].type == NOPTYPE && is == -1)
is = i;
}
if(it == -1 || id == -1) {
/*
* The SPARC64 boot image is something of an ELF hack.
* Text+Data+BSS are represented by ph[0]. Symbols
* are represented by ph[1]:
*
* filesz, memsz, vaddr, paddr, off
* ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
* ph[1] : symsz, lcsz, 0, 0, symoff
*/
if(ep->machine == SPARC64 && ep->phnum == 2) {
ulong txtaddr, txtsz, dataddr, bsssz;
txtaddr = ph[0].vaddr | 0x80000000;
txtsz = ph[0].filesz - ph[0].paddr;
dataddr = txtaddr + txtsz;
bsssz = ph[0].memsz - ph[0].filesz;
settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset);
free(ph);
return 1;
}
werrstr("No TEXT or DATA sections");
free(ph);
return 0;
}
settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
if(is != -1)
setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
free(ph);
return 1;
}
/*
* (Free|Net)BSD ARM header.
*/
static int
armdotout(int fd, Fhdr *fp, ExecHdr *hp)
{
uvlong kbase;
USED(fd);
settext(fp, hp->e.exechdr.entry, sizeof(Exec), hp->e.exechdr.text, sizeof(Exec));
setdata(fp, fp->txtsz, hp->e.exechdr.data, fp->txtsz, hp->e.exechdr.bss);
setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz);
kbase = 0xF0000000;
if ((fp->entry & kbase) == kbase) { /* Boot image */
fp->txtaddr = kbase+sizeof(Exec);
fp->name = "ARM *BSD boot image";
fp->hdrsz = 0; /* header stripped */
fp->dataddr = kbase+fp->txtsz;
}
return 1;
}
static void
settext(Fhdr *fp, uvlong e, uvlong a, long s, vlong off)
{
fp->txtaddr = a;
fp->entry = e;
fp->txtsz = s;
fp->txtoff = off;
}
static void
setdata(Fhdr *fp, uvlong a, long s, vlong off, long bss)
{
fp->dataddr = a;
fp->datsz = s;
fp->datoff = off;
fp->bsssz = bss;
}
static void
setsym(Fhdr *fp, long symsz, long sppcsz, long lnpcsz, vlong symoff)
{
fp->symsz = symsz;
fp->symoff = symoff;
fp->sppcsz = sppcsz;
fp->sppcoff = fp->symoff+fp->symsz;
fp->lnpcsz = lnpcsz;
fp->lnpcoff = fp->sppcoff+fp->sppcsz;
}
static uvlong
_round(uvlong a, ulong b)
{
uvlong w;
w = (a/b)*b;
if (a!=w)
w += b;
return(w);
}
// 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;
long 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];
ulong 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, ulong p, int zeros)
{
ulong 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, ulong h, ulong 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, ulong 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(*(ulong*)s));
}
int
beieeedftos(char *buf, int n, void *s)
{
return ieeedftos(buf, n, beswal(*(ulong*)s), beswal(((ulong*)(s))[1]));
}
int
leieeesftos(char *buf, int n, void *s)
{
return ieeesftos(buf, n, leswal(*(ulong*)s));
}
int
leieeedftos(char *buf, int n, void *s)
{
return ieeedftos(buf, n, leswal(((ulong*)(s))[1]), leswal(*(ulong*)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;
ulong 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;
}
// Derived from Inferno libmach/map.c and
// Plan 9 from User Space src/libmach/map.c
//
// http://code.swtch.com/plan9port/src/tip/src/libmach/map.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/map.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 © 2001-2007 Russ Cox.
// 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.
/*
* file map routines
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach_amd64.h>
Map *
newmap(Map *map, int n)
{
int size;
size = sizeof(Map)+(n-1)*sizeof(struct segment);
if (map == 0)
map = malloc(size);
else
map = realloc(map, size);
if (map == 0) {
werrstr("out of memory: %r");
return 0;
}
memset(map, 0, size);
map->nsegs = n;
return map;
}
int
setmap(Map *map, int fd, uvlong b, uvlong e, vlong f, char *name)
{
int i;
if (map == 0)
return 0;
for (i = 0; i < map->nsegs; i++)
if (!map->seg[i].inuse)
break;
if (i >= map->nsegs)
return 0;
map->seg[i].b = b;
map->seg[i].e = e;
map->seg[i].f = f;
map->seg[i].inuse = 1;
map->seg[i].name = name;
map->seg[i].fd = fd;
return 1;
}
static uvlong
stacktop(int pid)
{
char buf[64];
int fd;
int n;
char *cp;
snprint(buf, sizeof(buf), "/proc/%d/segment", pid);
fd = open(buf, 0);
if (fd < 0)
return 0;
n = read(fd, buf, sizeof(buf)-1);
close(fd);
buf[n] = 0;
if (strncmp(buf, "Stack", 5))
return 0;
for (cp = buf+5; *cp && *cp == ' '; cp++)
;
if (!*cp)
return 0;
cp = strchr(cp, ' ');
if (!cp)
return 0;
while (*cp && *cp == ' ')
cp++;
if (!*cp)
return 0;
return strtoull(cp, 0, 16);
}
Map*
attachproc(int pid, int kflag, int corefd, Fhdr *fp)
{
char buf[64], *regs;
int fd;
Map *map;
uvlong n;
int mode;
map = newmap(0, 4);
if (!map)
return 0;
if(kflag) {
regs = "kregs";
mode = OREAD;
} else {
regs = "regs";
mode = ORDWR;
}
if (mach->regsize) {
sprint(buf, "/proc/%d/%s", pid, regs);
fd = open(buf, mode);
if(fd < 0) {
free(map);
return 0;
}
setmap(map, fd, 0, mach->regsize, 0, "regs");
}
if (mach->fpregsize) {
sprint(buf, "/proc/%d/fpregs", pid);
fd = open(buf, mode);
if(fd < 0) {
close(map->seg[0].fd);
free(map);
return 0;
}
setmap(map, fd, mach->regsize, mach->regsize+mach->fpregsize, 0, "fpregs");
}
setmap(map, corefd, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "text");
if(kflag || fp->dataddr >= mach->utop) {
setmap(map, corefd, fp->dataddr, ~0, fp->dataddr, "data");
return map;
}
n = stacktop(pid);
if (n == 0) {
setmap(map, corefd, fp->dataddr, mach->utop, fp->dataddr, "data");
return map;
}
setmap(map, corefd, fp->dataddr, n, fp->dataddr, "data");
return map;
}
int
findseg(Map *map, char *name)
{
int i;
if (!map)
return -1;
for (i = 0; i < map->nsegs; i++)
if (map->seg[i].inuse && !strcmp(map->seg[i].name, name))
return i;
return -1;
}
void
unusemap(Map *map, int i)
{
if (map != 0 && 0 <= i && i < map->nsegs)
map->seg[i].inuse = 0;
}
Map*
loadmap(Map *map, int fd, Fhdr *fp)
{
map = newmap(map, 2);
if (map == 0)
return 0;
map->seg[0].b = fp->txtaddr;
map->seg[0].e = fp->txtaddr+fp->txtsz;
map->seg[0].f = fp->txtoff;
map->seg[0].fd = fd;
map->seg[0].inuse = 1;
map->seg[0].name = "text";
map->seg[1].b = fp->dataddr;
map->seg[1].e = fp->dataddr+fp->datsz;
map->seg[1].f = fp->datoff;
map->seg[1].fd = fd;
map->seg[1].inuse = 1;
map->seg[1].name = "data";
return map;
}
// Inferno libmach/obj.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/obj.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.
/*
* obj.c
* routines universal to all object files
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ar.h>
#include <mach_amd64.h>
#include "obj.h"
#define islocal(t) ((t)=='a' || (t)=='p')
enum
{
NNAMES = 50,
MAXIS = 8, /* max length to determine if a file is a .? file */
MAXOFF = 0x7fffffff, /* larger than any possible local offset */
NHASH = 1024, /* must be power of two */
HASHMUL = 79L,
};
int _is2(char*), /* in [$OS].c */
_is5(char*),
_is6(char*),
_is7(char*),
_is8(char*),
_is9(char*),
_isk(char*),
_isq(char*),
_isv(char*),
_isu(char*),
_read2(Biobuf*, Prog*),
_read5(Biobuf*, Prog*),
_read6(Biobuf*, Prog*),
_read7(Biobuf*, Prog*),
_read8(Biobuf*, Prog*),
_read9(Biobuf*, Prog*),
_readk(Biobuf*, Prog*),
_readq(Biobuf*, Prog*),
_readv(Biobuf*, Prog*),
_readu(Biobuf*, Prog*);
typedef struct Obj Obj;
typedef struct Symtab Symtab;
struct Obj /* functions to handle each intermediate (.$O) file */
{
char *name; /* name of each $O file */
int (*is)(char*); /* test for each type of $O file */
int (*read)(Biobuf*, Prog*); /* read for each type of $O file*/
};
static Obj obj[] =
{ /* functions to identify and parse each type of obj */
[Obj68020] "68020 .2", _is2, _read2,
[ObjAmd64] "amd64 .6", _is6, _read6,
[ObjArm] "arm .5", _is5, _read5,
[ObjAlpha] "alpha .7", _is7, _read7,
[Obj386] "386 .8", _is8, _read8,
[ObjSparc] "sparc .k", _isk, _readk,
[ObjPower] "power .q", _isq, _readq,
[ObjMips] "mips .v", _isv, _readv,
[ObjSparc64] "sparc64 .u", _isu, _readu,
[ObjPower64] "power64 .9", _is9, _read9,
[Maxobjtype] 0, 0
};
struct Symtab
{
struct Sym s;
struct Symtab *next;
};
static Symtab *hash[NHASH];
static Sym *names[NNAMES]; /* working set of active names */
static int processprog(Prog*,int); /* decode each symbol reference */
static void objreset(void);
static void objlookup(int, char *, int, uint);
static void objupdate(int, int);
int
objtype(Biobuf *bp, char **name)
{
int i;
char buf[MAXIS];
if(Bread(bp, buf, MAXIS) < MAXIS)
return -1;
Bseek(bp, -MAXIS, 1);
for (i = 0; i < Maxobjtype; i++) {
if (obj[i].is && (*obj[i].is)(buf)) {
if (name)
*name = obj[i].name;
return i;
}
}
return -1;
}
int
isar(Biobuf *bp)
{
int n;
char magbuf[SARMAG];
n = Bread(bp, magbuf, SARMAG);
if(n == SARMAG && strncmp(magbuf, ARMAG, SARMAG) == 0)
return 1;
return 0;
}
/*
* determine what kind of object file this is and process it.
* return whether or not this was a recognized intermediate file.
*/
int
readobj(Biobuf *bp, int objtype)
{
Prog p;
if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
return 1;
objreset();
while ((*obj[objtype].read)(bp, &p))
if (!processprog(&p, 1))
return 0;
return 1;
}
int
readar(Biobuf *bp, int objtype, vlong end, int doautos)
{
Prog p;
if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
return 1;
objreset();
while ((*obj[objtype].read)(bp, &p) && Boffset(bp) < end)
if (!processprog(&p, doautos))
return 0;
return 1;
}
/*
* decode a symbol reference or definition
*/
static int
processprog(Prog *p, int doautos)
{
if(p->kind == aNone)
return 1;
if(p->sym < 0 || p->sym >= NNAMES)
return 0;
switch(p->kind)
{
case aName:
if (!doautos)
if(p->type != 'U' && p->type != 'b')
break;
objlookup(p->sym, p->id, p->type, p->sig);
break;
case aText:
objupdate(p->sym, 'T');
break;
case aData:
objupdate(p->sym, 'D');
break;
default:
break;
}
return 1;
}
/*
* find the entry for s in the symbol array.
* make a new entry if it is not already there.
*/
static void
objlookup(int id, char *name, int type, uint sig)
{
long h;
char *cp;
Sym *s;
Symtab *sp;
s = names[id];
if(s && strcmp(s->name, name) == 0) {
s->type = type;
s->sig = sig;
return;
}
h = *name;
for(cp = name+1; *cp; h += *cp++)
h *= HASHMUL;
if(h < 0)
h = ~h;
h &= (NHASH-1);
if (type == 'U' || type == 'b' || islocal(type)) {
for(sp = hash[h]; sp; sp = sp->next)
if(strcmp(sp->s.name, name) == 0) {
switch(sp->s.type) {
case 'T':
case 'D':
case 'U':
if (type == 'U') {
names[id] = &sp->s;
return;
}
break;
case 't':
case 'd':
case 'b':
if (type == 'b') {
names[id] = &sp->s;
return;
}
break;
case 'a':
case 'p':
if (islocal(type)) {
names[id] = &sp->s;
return;
}
break;
default:
break;
}
}
}
sp = malloc(sizeof(Symtab));
sp->s.name = name;
sp->s.type = type;
sp->s.sig = sig;
sp->s.value = islocal(type) ? MAXOFF : 0;
names[id] = &sp->s;
sp->next = hash[h];
hash[h] = sp;
return;
}
/*
* traverse the symbol lists
*/
void
objtraverse(void (*fn)(Sym*, void*), void *pointer)
{
int i;
Symtab *s;
for(i = 0; i < NHASH; i++)
for(s = hash[i]; s; s = s->next)
(*fn)(&s->s, pointer);
}
/*
* update the offset information for a 'a' or 'p' symbol in an intermediate file
*/
void
_offset(int id, vlong off)
{
Sym *s;
s = names[id];
if (s && s->name[0] && islocal(s->type) && s->value > off)
s->value = off;
}
/*
* update the type of a global text or data symbol
*/
static void
objupdate(int id, int type)
{
Sym *s;
s = names[id];
if (s && s->name[0])
if (s->type == 'U')
s->type = type;
else if (s->type == 'b')
s->type = tolower(type);
}
/*
* look for the next file in an archive
*/
int
nextar(Biobuf *bp, int offset, char *buf)
{
struct ar_hdr a;
int i, r;
long arsize;
if (offset&01)
offset++;
Bseek(bp, offset, 0);
r = Bread(bp, &a, SAR_HDR);
if(r != SAR_HDR)
return 0;
if(strncmp(a.fmag, ARFMAG, sizeof(a.fmag)))
return -1;
for(i=0; i<sizeof(a.name) && i<SARNAME && a.name[i] != ' '; i++)
buf[i] = a.name[i];
buf[i] = 0;
arsize = strtol(a.size, 0, 0);
if (arsize&1)
arsize++;
return arsize + SAR_HDR;
}
static void
objreset(void)
{
int i;
Symtab *s, *n;
for(i = 0; i < NHASH; i++) {
for(s = hash[i]; s; s = n) {
n = s->next;
free(s->s.name);
free(s);
}
hash[i] = 0;
}
memset(names, 0, sizeof names);
}
// Inferno libmach/obj.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/obj.h
//
// 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.
/*
* obj.h -- defs for dealing with object files
*/
typedef enum Kind /* variable defs and references in obj */
{
aNone, /* we don't care about this prog */
aName, /* introduces a name */
aText, /* starts a function */
aData, /* references to a global object */
} Kind;
typedef struct Prog Prog;
struct Prog /* info from .$O files */
{
Kind kind; /* what kind of symbol */
char type; /* type of the symbol: ie, 'T', 'a', etc. */
char sym; /* index of symbol's name */
char *id; /* name for the symbol, if it introduces one */
uint sig; /* type signature for symbol */
};
#define UNKNOWN '?'
void _offset(int, vlong);
// Inferno libmach/setmach.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/setmach.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.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach_amd64.h>
/* table for selecting machine-dependent parameters */
typedef struct machtab Machtab;
struct machtab
{
char *name; /* machine name */
short type; /* executable type */
short boottype; /* bootable type */
int asstype; /* disassembler code */
Mach *mach; /* machine description */
Machdata *machdata; /* machine functions */
};
/*
extern Mach mmips, msparc, m68020, mi386, mamd64,
marm, mmips2be, mmips2le, mpower, mpower64, malpha, msparc64;
extern Machdata mipsmach, sparcmach, m68020mach, i386mach,
armmach, mipsmach2le, powermach, alphamach, sparc64mach;
*/
extern Mach mi386, mamd64;
extern Machdata i386mach;
/*
* machine selection table. machines with native disassemblers should
* follow the plan 9 variant in the table; native modes are selectable
* only by name.
*/
Machtab machines[] =
{
{ "386", /*plan 9 386*/
FI386,
FI386B,
AI386,
&mi386,
&i386mach, },
{ "amd64", /*amd64*/
FAMD64,
FAMD64B,
AAMD64,
&mamd64,
&i386mach, },
#ifdef unused
{ "68020", /*68020*/
F68020,
F68020B,
A68020,
&m68020,
&m68020mach, },
{ "68020", /*Next 68040 bootable*/
F68020,
FNEXTB,
A68020,
&m68020,
&m68020mach, },
{ "mips2LE", /*plan 9 mips2 little endian*/
FMIPS2LE,
0,
AMIPS,
&mmips2le,
&mipsmach2le, },
{ "mips", /*plan 9 mips*/
FMIPS,
FMIPSB,
AMIPS,
&mmips,
&mipsmach, },
{ "mips2", /*plan 9 mips2*/
FMIPS2BE,
FMIPSB,
AMIPS,
&mmips2be,
&mipsmach, }, /* shares debuggers with native mips */
{ "mipsco", /*native mips - must follow plan 9*/
FMIPS,
FMIPSB,
AMIPSCO,
&mmips,
&mipsmach, },
{ "sparc", /*plan 9 sparc */
FSPARC,
FSPARCB,
ASPARC,
&msparc,
&sparcmach, },
{ "sunsparc", /*native sparc - must follow plan 9*/
FSPARC,
FSPARCB,
ASUNSPARC,
&msparc,
&sparcmach, },
{ "86", /*8086 - a peach of a machine*/
FI386,
FI386B,
AI8086,
&mi386,
&i386mach, },
{ "arm", /*ARM*/
FARM,
FARMB,
AARM,
&marm,
&armmach, },
{ "power", /*PowerPC*/
FPOWER,
FPOWERB,
APOWER,
&mpower,
&powermach, },
{ "power64", /*PowerPC*/
FPOWER64,
FPOWER64B,
APOWER64,
&mpower64,
&powermach, },
{ "alpha", /*Alpha*/
FALPHA,
FALPHAB,
AALPHA,
&malpha,
&alphamach, },
{ "sparc64", /*plan 9 sparc64 */
FSPARC64,
FSPARCB, /* XXX? */
ASPARC64,
&msparc64,
&sparc64mach, },
#endif
{ 0 }, /*the terminator*/
};
/*
* select a machine by executable file type
*/
void
machbytype(int type)
{
Machtab *mp;
for (mp = machines; mp->name; mp++){
if (mp->type == type || mp->boottype == type) {
asstype = mp->asstype;
machdata = mp->machdata;
break;
}
}
}
/*
* select a machine by name
*/
int
machbyname(char *name)
{
Machtab *mp;
if (!name) {
asstype = AAMD64;
machdata = &i386mach;
mach = &mamd64;
return 1;
}
for (mp = machines; mp->name; mp++){
if (strcmp(mp->name, name) == 0) {
asstype = mp->asstype;
machdata = mp->machdata;
mach = mp->mach;
return 1;
}
}
return 0;
}
// Inferno libmach/swap.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/swap.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.
#include <u.h>
/*
* big-endian short
*/
ushort
beswab(ushort s)
{
uchar *p;
p = (uchar*)&s;
return (p[0]<<8) | p[1];
}
/*
* big-endian long
*/
ulong
beswal(ulong l)
{
uchar *p;
p = (uchar*)&l;
return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}
/*
* big-endian vlong
*/
uvlong
beswav(uvlong v)
{
uchar *p;
p = (uchar*)&v;
return ((uvlong)p[0]<<56) | ((uvlong)p[1]<<48) | ((uvlong)p[2]<<40)
| ((uvlong)p[3]<<32) | ((uvlong)p[4]<<24)
| ((uvlong)p[5]<<16) | ((uvlong)p[6]<<8)
| (uvlong)p[7];
}
/*
* little-endian short
*/
ushort
leswab(ushort s)
{
uchar *p;
p = (uchar*)&s;
return (p[1]<<8) | p[0];
}
/*
* little-endian long
*/
ulong
leswal(ulong l)
{
uchar *p;
p = (uchar*)&l;
return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
}
/*
* little-endian vlong
*/
uvlong
leswav(uvlong v)
{
uchar *p;
p = (uchar*)&v;
return ((uvlong)p[7]<<56) | ((uvlong)p[6]<<48) | ((uvlong)p[5]<<40)
| ((uvlong)p[4]<<32) | ((uvlong)p[3]<<24)
| ((uvlong)p[2]<<16) | ((uvlong)p[1]<<8)
| (uvlong)p[0];
}
// Inferno libmach/sym.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/sym.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.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach_amd64.h>
#define HUGEINT 0x7fffffff
#define NNAME 20 /* a relic of the past */
typedef struct txtsym Txtsym;
typedef struct file File;
typedef struct hist Hist;
struct txtsym { /* Text Symbol table */
int n; /* number of local vars */
Sym **locals; /* array of ptrs to autos */
Sym *sym; /* function symbol entry */
};
struct hist { /* Stack of include files & #line directives */
char *name; /* Assumes names Null terminated in file */
long line; /* line # where it was included */
long offset; /* line # of #line directive */
};
struct file { /* Per input file header to history stack */
uvlong addr; /* address of first text sym */
union {
Txtsym *txt; /* first text symbol */
Sym *sym; /* only during initilization */
};
int n; /* size of history stack */
Hist *hist; /* history stack */
};
static int debug = 0;
static Sym **autos; /* Base of auto variables */
static File *files; /* Base of file arena */
static int fmaxi; /* largest file path index */
static Sym **fnames; /* file names path component table */
static Sym **globals; /* globals by addr table */
static Hist *hist; /* base of history stack */
static int isbuilt; /* internal table init flag */
static long nauto; /* number of automatics */
static long nfiles; /* number of files */
static long nglob; /* number of globals */
static long nhist; /* number of history stack entries */
static long nsym; /* number of symbols */
static int ntxt; /* number of text symbols */
static uchar *pcline; /* start of pc-line state table */
static uchar *pclineend; /* end of pc-line table */
static uchar *spoff; /* start of pc-sp state table */
static uchar *spoffend; /* end of pc-sp offset table */
static Sym *symbols; /* symbol table */
static Txtsym *txt; /* Base of text symbol table */
static uvlong txtstart; /* start of text segment */
static uvlong txtend; /* end of text segment */
static void cleansyms(void);
static long decodename(Biobuf*, Sym*);
static short *encfname(char*);
static int fline(char*, int, long, Hist*, Hist**);
static void fillsym(Sym*, Symbol*);
static int findglobal(char*, Symbol*);
static int findlocvar(Symbol*, char *, Symbol*);
static int findtext(char*, Symbol*);
static int hcomp(Hist*, short*);
static int hline(File*, short*, long*);
static void printhist(char*, Hist*, int);
static int buildtbls(void);
static int symcomp(const void*, const void*);
static int symerrmsg(int, char*);
static int txtcomp(const void*, const void*);
static int filecomp(const void*, const void*);
/*
* initialize the symbol tables
*/
int
syminit(int fd, Fhdr *fp)
{
Sym *p;
long i, l, size;
vlong vl;
Biobuf b;
int svalsz;
if(fp->symsz == 0)
return 0;
if(fp->type == FNONE)
return 0;
cleansyms();
textseg(fp->txtaddr, fp);
/* minimum symbol record size = 4+1+2 bytes */
symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym));
if(symbols == 0) {
werrstr("can't malloc %ld bytes", fp->symsz);
return -1;
}
Binit(&b, fd, OREAD);
Bseek(&b, fp->symoff, 0);
nsym = 0;
size = 0;
for(p = symbols; size < fp->symsz; p++, nsym++) {
if(fp->_magic && (fp->magic & HDR_MAGIC)){
svalsz = 8;
if(Bread(&b, &vl, 8) != 8)
return symerrmsg(8, "symbol");
p->value = beswav(vl);
}
else{
svalsz = 4;
if(Bread(&b, &l, 4) != 4)
return symerrmsg(4, "symbol");
p->value = (u32int)beswal(l);
}
if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type))
return symerrmsg(sizeof(p->value), "symbol");
i = decodename(&b, p);
if(i < 0)
return -1;
size += i+svalsz+sizeof(p->type);
/* count global & auto vars, text symbols, and file names */
switch (p->type) {
case 'l':
case 'L':
case 't':
case 'T':
ntxt++;
break;
case 'd':
case 'D':
case 'b':
case 'B':
nglob++;
break;
case 'f':
if(strcmp(p->name, ".frame") == 0) {
p->type = 'm';
nauto++;
}
else if(p->value > fmaxi)
fmaxi = p->value; /* highest path index */
break;
case 'a':
case 'p':
case 'm':
nauto++;
break;
case 'z':
if(p->value == 1) { /* one extra per file */
nhist++;
nfiles++;
}
nhist++;
break;
default:
break;
}
}
if (debug)
print("NG: %ld NT: %d NF: %d\n", nglob, ntxt, fmaxi);
if (fp->sppcsz) { /* pc-sp offset table */
spoff = (uchar *)malloc(fp->sppcsz);
if(spoff == 0) {
werrstr("can't malloc %ld bytes", fp->sppcsz);
return -1;
}
Bseek(&b, fp->sppcoff, 0);
if(Bread(&b, spoff, fp->sppcsz) != fp->sppcsz){
spoff = 0;
return symerrmsg(fp->sppcsz, "sp-pc");
}
spoffend = spoff+fp->sppcsz;
}
if (fp->lnpcsz) { /* pc-line number table */
pcline = (uchar *)malloc(fp->lnpcsz);
if(pcline == 0) {
werrstr("can't malloc %ld bytes", fp->lnpcsz);
return -1;
}
Bseek(&b, fp->lnpcoff, 0);
if(Bread(&b, pcline, fp->lnpcsz) != fp->lnpcsz){
pcline = 0;
return symerrmsg(fp->lnpcsz, "pc-line");
}
pclineend = pcline+fp->lnpcsz;
}
return nsym;
}
static int
symerrmsg(int n, char *table)
{
werrstr("can't read %d bytes of %s table", n, table);
return -1;
}
static long
decodename(Biobuf *bp, Sym *p)
{
char *cp;
int c1, c2;
long n;
vlong o;
if((p->type & 0x80) == 0) { /* old-style, fixed length names */
p->name = malloc(NNAME);
if(p->name == 0) {
werrstr("can't malloc %d bytes", NNAME);
return -1;
}
if(Bread(bp, p->name, NNAME) != NNAME)
return symerrmsg(NNAME, "symbol");
Bseek(bp, 3, 1);
return NNAME+3;
}
p->type &= ~0x80;
if(p->type == 'z' || p->type == 'Z') {
o = Bseek(bp, 0, 1);
if(Bgetc(bp) < 0) {
werrstr("can't read symbol name");
return -1;
}
for(;;) {
c1 = Bgetc(bp);
c2 = Bgetc(bp);
if(c1 < 0 || c2 < 0) {
werrstr("can't read symbol name");
return -1;
}
if(c1 == 0 && c2 == 0)
break;
}
n = Bseek(bp, 0, 1)-o;
p->name = malloc(n);
if(p->name == 0) {
werrstr("can't malloc %ld bytes", n);
return -1;
}
Bseek(bp, -n, 1);
if(Bread(bp, p->name, n) != n) {
werrstr("can't read %ld bytes of symbol name", n);
return -1;
}
} else {
cp = Brdline(bp, '\0');
if(cp == 0) {
werrstr("can't read symbol name");
return -1;
}
n = Blinelen(bp);
p->name = malloc(n);
if(p->name == 0) {
werrstr("can't malloc %ld bytes", n);
return -1;
}
strcpy(p->name, cp);
}
return n;
}
/*
* free any previously loaded symbol tables
*/
static void
cleansyms(void)
{
if(globals)
free(globals);
globals = 0;
nglob = 0;
if(txt)
free(txt);
txt = 0;
ntxt = 0;
if(fnames)
free(fnames);
fnames = 0;
fmaxi = 0;
if(files)
free(files);
files = 0;
nfiles = 0;
if(hist)
free(hist);
hist = 0;
nhist = 0;
if(autos)
free(autos);
autos = 0;
nauto = 0;
isbuilt = 0;
if(symbols)
free(symbols);
symbols = 0;
nsym = 0;
if(spoff)
free(spoff);
spoff = 0;
if(pcline)
free(pcline);
pcline = 0;
}
/*
* delimit the text segment
*/
void
textseg(uvlong base, Fhdr *fp)
{
txtstart = base;
txtend = base+fp->txtsz;
}
/*
* symbase: return base and size of raw symbol table
* (special hack for high access rate operations)
*/
Sym *
symbase(long *n)
{
*n = nsym;
return symbols;
}
/*
* Get the ith symbol table entry
*/
Sym *
getsym(int index)
{
if(index >= 0 && index < nsym)
return &symbols[index];
return 0;
}
/*
* initialize internal symbol tables
*/
static int
buildtbls(void)
{
long i;
int j, nh, ng, nt;
File *f;
Txtsym *tp;
Hist *hp;
Sym *p, **ap;
if(isbuilt)
return 1;
isbuilt = 1;
/* allocate the tables */
if(nglob) {
globals = malloc(nglob*sizeof(*globals));
if(!globals) {
werrstr("can't malloc global symbol table");
return 0;
}
}
if(ntxt) {
txt = malloc(ntxt*sizeof(*txt));
if (!txt) {
werrstr("can't malloc text symbol table");
return 0;
}
}
fnames = malloc((fmaxi+1)*sizeof(*fnames));
if (!fnames) {
werrstr("can't malloc file name table");
return 0;
}
memset(fnames, 0, (fmaxi+1)*sizeof(*fnames));
files = malloc(nfiles*sizeof(*files));
if(!files) {
werrstr("can't malloc file table");
return 0;
}
hist = malloc(nhist*sizeof(Hist));
if(hist == 0) {
werrstr("can't malloc history stack");
return 0;
}
autos = malloc(nauto*sizeof(Sym*));
if(autos == 0) {
werrstr("can't malloc auto symbol table");
return 0;
}
/* load the tables */
ng = nt = nh = 0;
f = 0;
tp = 0;
i = nsym;
hp = hist;
ap = autos;
for(p = symbols; i-- > 0; p++) {
switch(p->type) {
case 'D':
case 'd':
case 'B':
case 'b':
if(debug)
print("Global: %s %llux\n", p->name, p->value);
globals[ng++] = p;
break;
case 'z':
if(p->value == 1) { /* New file */
if(f) {
f->n = nh;
f->hist[nh].name = 0; /* one extra */
hp += nh+1;
f++;
}
else
f = files;
f->hist = hp;
f->sym = 0;
f->addr = 0;
nh = 0;
}
/* alloc one slot extra as terminator */
f->hist[nh].name = p->name;
f->hist[nh].line = p->value;
f->hist[nh].offset = 0;
if(debug)
printhist("-> ", &f->hist[nh], 1);
nh++;
break;
case 'Z':
if(f && nh > 0)
f->hist[nh-1].offset = p->value;
break;
case 'T':
case 't': /* Text: terminate history if first in file */
case 'L':
case 'l':
tp = &txt[nt++];
tp->n = 0;
tp->sym = p;
tp->locals = ap;
if(debug)
print("TEXT: %s at %llux\n", p->name, p->value);
if(f && !f->sym) { /* first */
f->sym = p;
f->addr = p->value;
}
break;
case 'a':
case 'p':
case 'm': /* Local Vars */
if(!tp)
print("Warning: Free floating local var: %s\n",
p->name);
else {
if(debug)
print("Local: %s %llux\n", p->name, p->value);
tp->locals[tp->n] = p;
tp->n++;
ap++;
}
break;
case 'f': /* File names */
if(debug)
print("Fname: %s\n", p->name);
fnames[p->value] = p;
break;
default:
break;
}
}
/* sort global and text tables into ascending address order */
qsort(globals, nglob, sizeof(Sym*), symcomp);
qsort(txt, ntxt, sizeof(Txtsym), txtcomp);
qsort(files, nfiles, sizeof(File), filecomp);
tp = txt;
for(i = 0, f = files; i < nfiles; i++, f++) {
for(j = 0; j < ntxt; j++) {
if(f->sym == tp->sym) {
if(debug) {
print("LINK: %s to at %llux", f->sym->name, f->addr);
printhist("... ", f->hist, 1);
}
f->txt = tp++;
break;
}
if(++tp >= txt+ntxt) /* wrap around */
tp = txt;
}
}
return 1;
}
/*
* find symbol function.var by name.
* fn != 0 && var != 0 => look for fn in text, var in data
* fn != 0 && var == 0 => look for fn in text
* fn == 0 && var != 0 => look for var first in text then in data space.
*/
int
lookup(char *fn, char *var, Symbol *s)
{
int found;
if(buildtbls() == 0)
return 0;
if(fn) {
found = findtext(fn, s);
if(var == 0) /* case 2: fn not in text */
return found;
else if(!found) /* case 1: fn not found */
return 0;
} else if(var) {
found = findtext(var, s);
if(found)
return 1; /* case 3: var found in text */
} else return 0; /* case 4: fn & var == zero */
if(found)
return findlocal(s, var, s); /* case 1: fn found */
return findglobal(var, s); /* case 3: var not found */
}
/*
* find a function by name
*/
static int
findtext(char *name, Symbol *s)
{
int i;
for(i = 0; i < ntxt; i++) {
if(strcmp(txt[i].sym->name, name) == 0) {
fillsym(txt[i].sym, s);
s->handle = (void *) &txt[i];
s->index = i;
return 1;
}
}
return 0;
}
/*
* find global variable by name
*/
static int
findglobal(char *name, Symbol *s)
{
long i;
for(i = 0; i < nglob; i++) {
if(strcmp(globals[i]->name, name) == 0) {
fillsym(globals[i], s);
s->index = i;
return 1;
}
}
return 0;
}
/*
* find the local variable by name within a given function
*/
int
findlocal(Symbol *s1, char *name, Symbol *s2)
{
if(s1 == 0)
return 0;
if(buildtbls() == 0)
return 0;
return findlocvar(s1, name, s2);
}
/*
* find the local variable by name within a given function
* (internal function - does no parameter validation)
*/
static int
findlocvar(Symbol *s1, char *name, Symbol *s2)
{
Txtsym *tp;
int i;
tp = (Txtsym *)s1->handle;
if(tp && tp->locals) {
for(i = 0; i < tp->n; i++)
if (strcmp(tp->locals[i]->name, name) == 0) {
fillsym(tp->locals[i], s2);
s2->handle = (void *)tp;
s2->index = tp->n-1 - i;
return 1;
}
}
return 0;
}
/*
* Get ith text symbol
*/
int
textsym(Symbol *s, int index)
{
if(buildtbls() == 0)
return 0;
if(index < 0 || index >= ntxt)
return 0;
fillsym(txt[index].sym, s);
s->handle = (void *)&txt[index];
s->index = index;
return 1;
}
/*
* Get ith file name
*/
int
filesym(int index, char *buf, int n)
{
Hist *hp;
if(buildtbls() == 0)
return 0;
if(index < 0 || index >= nfiles)
return 0;
hp = files[index].hist;
if(!hp || !hp->name)
return 0;
return fileelem(fnames, (uchar*)hp->name, buf, n);
}
/*
* Lookup name of local variable located at an offset into the frame.
* The type selects either a parameter or automatic.
*/
int
getauto(Symbol *s1, int off, int type, Symbol *s2)
{
Txtsym *tp;
Sym *p;
int i, t;
if(s1 == 0)
return 0;
if(type == CPARAM)
t = 'p';
else if(type == CAUTO)
t = 'a';
else
return 0;
if(buildtbls() == 0)
return 0;
tp = (Txtsym *)s1->handle;
if(tp == 0)
return 0;
for(i = 0; i < tp->n; i++) {
p = tp->locals[i];
if(p->type == t && p->value == off) {
fillsym(p, s2);
s2->handle = s1->handle;
s2->index = tp->n-1 - i;
return 1;
}
}
return 0;
}
/*
* Find text symbol containing addr; binary search assumes text array is sorted by addr
*/
static int
srchtext(uvlong addr)
{
uvlong val;
int top, bot, mid;
Sym *sp;
val = addr;
bot = 0;
top = ntxt;
for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
sp = txt[mid].sym;
if(val < sp->value)
top = mid;
else if(mid != ntxt-1 && val >= txt[mid+1].sym->value)
bot = mid;
else
return mid;
}
return -1;
}
/*
* Find data symbol containing addr; binary search assumes data array is sorted by addr
*/
static int
srchdata(uvlong addr)
{
uvlong val;
int top, bot, mid;
Sym *sp;
bot = 0;
top = nglob;
val = addr;
for(mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
sp = globals[mid];
if(val < sp->value)
top = mid;
else if(mid < nglob-1 && val >= globals[mid+1]->value)
bot = mid;
else
return mid;
}
return -1;
}
/*
* Find symbol containing val in specified search space
* There is a special case when a value falls beyond the end
* of the text segment; if the search space is CTEXT, that value
* (usually etext) is returned. If the search space is CANY, symbols in the
* data space are searched for a match.
*/
int
findsym(uvlong val, int type, Symbol *s)
{
int i;
if(buildtbls() == 0)
return 0;
if(type == CTEXT || type == CANY) {
i = srchtext(val);
if(i >= 0) {
if(type == CTEXT || i != ntxt-1) {
fillsym(txt[i].sym, s);
s->handle = (void *) &txt[i];
s->index = i;
return 1;
}
}
}
if(type == CDATA || type == CANY) {
i = srchdata(val);
if(i >= 0) {
fillsym(globals[i], s);
s->index = i;
return 1;
}
}
return 0;
}
/*
* Find the start and end address of the function containing addr
*/
int
fnbound(uvlong addr, uvlong *bounds)
{
int i;
if(buildtbls() == 0)
return 0;
i = srchtext(addr);
if(0 <= i && i < ntxt-1) {
bounds[0] = txt[i].sym->value;
bounds[1] = txt[i+1].sym->value;
return 1;
}
return 0;
}
/*
* get the ith local symbol for a function
* the input symbol table is reverse ordered, so we reverse
* accesses here to maintain approx. parameter ordering in a stack trace.
*/
int
localsym(Symbol *s, int index)
{
Txtsym *tp;
if(s == 0 || index < 0)
return 0;
if(buildtbls() == 0)
return 0;
tp = (Txtsym *)s->handle;
if(tp && tp->locals && index < tp->n) {
fillsym(tp->locals[tp->n-index-1], s); /* reverse */
s->handle = (void *)tp;
s->index = index;
return 1;
}
return 0;
}
/*
* get the ith global symbol
*/
int
globalsym(Symbol *s, int index)
{
if(s == 0)
return 0;
if(buildtbls() == 0)
return 0;
if(index >=0 && index < nglob) {
fillsym(globals[index], s);
s->index = index;
return 1;
}
return 0;
}
/*
* find the pc given a file name and line offset into it.
*/
uvlong
file2pc(char *file, long line)
{
File *fp;
long i;
uvlong pc, start, end;
short *name;
if(buildtbls() == 0 || files == 0)
return ~0;
name = encfname(file);
if(name == 0) { /* encode the file name */
werrstr("file %s not found", file);
return ~0;
}
/* find this history stack */
for(i = 0, fp = files; i < nfiles; i++, fp++)
if (hline(fp, name, &line))
break;
free(name);
if(i >= nfiles) {
werrstr("line %ld in file %s not found", line, file);
return ~0;
}
start = fp->addr; /* first text addr this file */
if(i < nfiles-1)
end = (fp+1)->addr; /* first text addr next file */
else
end = 0; /* last file in load module */
/*
* At this point, line contains the offset into the file.
* run the state machine to locate the pc closest to that value.
*/
if(debug)
print("find pc for %ld - between: %llux and %llux\n", line, start, end);
pc = line2addr(line, start, end);
if(pc == ~0) {
werrstr("line %ld not in file %s", line, file);
return ~0;
}
return pc;
}
/*
* search for a path component index
*/
static int
pathcomp(char *s, int n)
{
int i;
for(i = 0; i <= fmaxi; i++)
if(fnames[i] && strncmp(s, fnames[i]->name, n) == 0)
return i;
return -1;
}
/*
* Encode a char file name as a sequence of short indices
* into the file name dictionary.
*/
static short*
encfname(char *file)
{
int i, j;
char *cp, *cp2;
short *dest;
if(*file == '/') /* always check first '/' */
cp2 = file+1;
else {
cp2 = strchr(file, '/');
if(!cp2)
cp2 = strchr(file, 0);
}
cp = file;
dest = 0;
for(i = 0; *cp; i++) {
j = pathcomp(cp, cp2-cp);
if(j < 0)
return 0; /* not found */
dest = realloc(dest, (i+1)*sizeof(short));
dest[i] = j;
cp = cp2;
while(*cp == '/') /* skip embedded '/'s */
cp++;
cp2 = strchr(cp, '/');
if(!cp2)
cp2 = strchr(cp, 0);
}
dest = realloc(dest, (i+1)*sizeof(short));
dest[i] = 0;
return dest;
}
/*
* Search a history stack for a matching file name accumulating
* the size of intervening files in the stack.
*/
static int
hline(File *fp, short *name, long *line)
{
Hist *hp;
int offset, depth;
long ln;
for(hp = fp->hist; hp->name; hp++) /* find name in stack */
if(hp->name[1] || hp->name[2]) {
if(hcomp(hp, name))
break;
}
if(!hp->name) /* match not found */
return 0;
if(debug)
printhist("hline found ... ", hp, 1);
/*
* unwind the stack until empty or we hit an entry beyond our line
*/
ln = *line;
offset = hp->line-1;
depth = 1;
for(hp++; depth && hp->name; hp++) {
if(debug)
printhist("hline inspect ... ", hp, 1);
if(hp->name[1] || hp->name[2]) {
if(hp->offset){ /* Z record */
offset = 0;
if(hcomp(hp, name)) {
if(*line <= hp->offset)
break;
ln = *line+hp->line-hp->offset;
depth = 1; /* implicit pop */
} else
depth = 2; /* implicit push */
} else if(depth == 1 && ln < hp->line-offset)
break; /* Beyond our line */
else if(depth++ == 1) /* push */
offset -= hp->line;
} else if(--depth == 1) /* pop */
offset += hp->line;
}
*line = ln+offset;
return 1;
}
/*
* compare two encoded file names
*/
static int
hcomp(Hist *hp, short *sp)
{
uchar *cp;
int i, j;
short *s;
cp = (uchar *)hp->name;
s = sp;
if (*s == 0)
return 0;
for (i = 1; j = (cp[i]<<8)|cp[i+1]; i += 2) {
if(j == 0)
break;
if(*s == j)
s++;
else
s = sp;
}
return *s == 0;
}
/*
* Convert a pc to a "file:line {file:line}" string.
*/
long
fileline(char *str, int n, uvlong dot)
{
long line, top, bot, mid;
File *f;
*str = 0;
if(buildtbls() == 0)
return 0;
/* binary search assumes file list is sorted by addr */
bot = 0;
top = nfiles;
for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
f = &files[mid];
if(dot < f->addr)
top = mid;
else if(mid < nfiles-1 && dot >= (f+1)->addr)
bot = mid;
else {
line = pc2line(dot);
if(line > 0 && fline(str, n, line, f->hist, 0) >= 0)
return 1;
break;
}
}
return 0;
}
/*
* Convert a line number within a composite file to relative line
* number in a source file. A composite file is the source
* file with included files inserted in line.
*/
static int
fline(char *str, int n, long line, Hist *base, Hist **ret)
{
Hist *start; /* start of current level */
Hist *h; /* current entry */
long delta; /* sum of size of files this level */
int k;
start = base;
h = base;
delta = h->line;
while(h && h->name && line > h->line) {
if(h->name[1] || h->name[2]) {
if(h->offset != 0) { /* #line Directive */
delta = h->line-h->offset+1;
start = h;
base = h++;
} else { /* beginning of File */
if(start == base)
start = h++;
else {
k = fline(str, n, line, start, &h);
if(k <= 0)
return k;
}
}
} else {
if(start == base && ret) { /* end of recursion level */
*ret = h;
return 1;
} else { /* end of included file */
delta += h->line-start->line;
h++;
start = base;
}
}
}
if(!h)
return -1;
if(start != base)
line = line-start->line+1;
else
line = line-delta+1;
if(!h->name)
strncpy(str, "<eof>", n);
else {
k = fileelem(fnames, (uchar*)start->name, str, n);
if(k+8 < n)
sprint(str+k, ":%ld", line);
}
/**********Remove comments for complete back-trace of include sequence
* if(start != base) {
* k = strlen(str);
* if(k+2 < n) {
* str[k++] = ' ';
* str[k++] = '{';
* }
* k += fileelem(fnames, (uchar*) base->name, str+k, n-k);
* if(k+10 < n)
* sprint(str+k, ":%ld}", start->line-delta);
* }
********************/
return 0;
}
/*
* convert an encoded file name to a string.
*/
int
fileelem(Sym **fp, uchar *cp, char *buf, int n)
{
int i, j;
char *c, *bp, *end;
bp = buf;
end = buf+n-1;
for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
c = fp[j]->name;
if(bp != buf && bp[-1] != '/' && bp < end)
*bp++ = '/';
while(bp < end && *c)
*bp++ = *c++;
}
*bp = 0;
i = bp-buf;
if(i > 1) {
cleanname(buf);
i = strlen(buf);
}
return i;
}
/*
* compare the values of two symbol table entries.
*/
static int
symcomp(const void *a, const void *b)
{
int i;
i = (*(Sym**)a)->value - (*(Sym**)b)->value;
if (i)
return i;
return strcmp((*(Sym**)a)->name, (*(Sym**)b)->name);
}
/*
* compare the values of the symbols referenced by two text table entries
*/
static int
txtcomp(const void *a, const void *b)
{
return ((Txtsym*)a)->sym->value - ((Txtsym*)b)->sym->value;
}
/*
* compare the values of the symbols referenced by two file table entries
*/
static int
filecomp(const void *a, const void *b)
{
return ((File*)a)->addr - ((File*)b)->addr;
}
/*
* fill an interface Symbol structure from a symbol table entry
*/
static void
fillsym(Sym *sp, Symbol *s)
{
s->type = sp->type;
s->value = sp->value;
s->name = sp->name;
s->index = 0;
switch(sp->type) {
case 'b':
case 'B':
case 'D':
case 'd':
s->class = CDATA;
break;
case 't':
case 'T':
case 'l':
case 'L':
s->class = CTEXT;
break;
case 'a':
s->class = CAUTO;
break;
case 'p':
s->class = CPARAM;
break;
case 'm':
s->class = CSTAB;
break;
default:
s->class = CNONE;
break;
}
s->handle = 0;
}
/*
* find the stack frame, given the pc
*/
uvlong
pc2sp(uvlong pc)
{
uchar *c, u;
uvlong currpc, currsp;
if(spoff == 0)
return ~0;
currsp = 0;
currpc = txtstart - mach->pcquant;
if(pc<currpc || pc>txtend)
return ~0;
for(c = spoff; c < spoffend; c++) {
if (currpc >= pc)
return currsp;
u = *c;
if (u == 0) {
currsp += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
c += 4;
}
else if (u < 65)
currsp += 4*u;
else if (u < 129)
currsp -= 4*(u-64);
else
currpc += mach->pcquant*(u-129);
currpc += mach->pcquant;
}
return ~0;
}
/*
* find the source file line number for a given value of the pc
*/
long
pc2line(uvlong pc)
{
uchar *c, u;
uvlong currpc;
long currline;
if(pcline == 0)
return -1;
currline = 0;
currpc = txtstart-mach->pcquant;
if(pc<currpc || pc>txtend)
return ~0;
for(c = pcline; c < pclineend; c++) {
if(currpc >= pc)
return currline;
u = *c;
if(u == 0) {
currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
c += 4;
}
else if(u < 65)
currline += u;
else if(u < 129)
currline -= (u-64);
else
currpc += mach->pcquant*(u-129);
currpc += mach->pcquant;
}
return ~0;
}
/*
* find the pc associated with a line number
* basepc and endpc are text addresses bounding the search.
* if endpc == 0, the end of the table is used (i.e., no upper bound).
* usually, basepc and endpc contain the first text address in
* a file and the first text address in the following file, respectively.
*/
uvlong
line2addr(long line, uvlong basepc, uvlong endpc)
{
uchar *c, u;
uvlong currpc, pc;
long currline;
long delta, d;
int found;
if(pcline == 0 || line == 0)
return ~0;
currline = 0;
currpc = txtstart-mach->pcquant;
pc = ~0;
found = 0;
delta = HUGEINT;
for(c = pcline; c < pclineend; c++) {
if(endpc && currpc >= endpc) /* end of file of interest */
break;
if(currpc >= basepc) { /* proper file */
if(currline >= line) {
d = currline-line;
found = 1;
} else
d = line-currline;
if(d < delta) {
delta = d;
pc = currpc;
}
}
u = *c;
if(u == 0) {
currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
c += 4;
}
else if(u < 65)
currline += u;
else if(u < 129)
currline -= (u-64);
else
currpc += mach->pcquant*(u-129);
currpc += mach->pcquant;
}
if(found)
return pc;
return ~0;
}
/*
* Print a history stack (debug). if count is 0, prints the whole stack
*/
static void
printhist(char *msg, Hist *hp, int count)
{
int i;
uchar *cp;
char buf[128];
i = 0;
while(hp->name) {
if(count && ++i > count)
break;
print("%s Line: %lx (%ld) Offset: %lx (%ld) Name: ", msg,
hp->line, hp->line, hp->offset, hp->offset);
for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) {
if (cp != (uchar *)hp->name+1)
print("/");
print("%x", (*cp<<8)|cp[1]);
}
fileelem(fnames, (uchar *) hp->name, buf, sizeof(buf));
print(" (%s)\n", buf);
hp++;
}
}
#ifdef DEBUG
/*
* print the history stack for a file. (debug only)
* if (name == 0) => print all history stacks.
*/
void
dumphist(char *name)
{
int i;
File *f;
short *fname;
if(buildtbls() == 0)
return;
if(name)
fname = encfname(name);
for(i = 0, f = files; i < nfiles; i++, f++)
if(fname == 0 || hcomp(f->hist, fname))
printhist("> ", f->hist, f->n);
if(fname)
free(fname);
}
#endif
......@@ -3,7 +3,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
for i in lib9 libbio
for i in lib9 libbio libmach_amd64
do
cd $i
make install
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment