Commit 1d718164 authored by Russ Cox's avatar Russ Cox

cmd/prof, libmach: delete

We have never released cmd/prof and don't plan to.
Now that nm, addr2line, and objdump have been rewritten in Go,
cmd/prof is the only thing keeping us from deleting libmach.

Delete cmd/prof, and then since nothing is using libmach, delete libmach.

13,000 lines of C deleted.

LGTM=minux.ma
R=golang-codereviews, minux.ma
CC=golang-codereviews, iant, r
https://golang.org/cl/87020044
parent a8d90ec3
// 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];
uint32 phys;
uint32 virt;
uint32 size;
uint32 fptr;
uint32 fptrreloc;
uint32 fptrlineno;
uint32 nrelocnlineno;
uint32 flags;
};
/*
* proprietary exec headers, needed to bootstrap various machines
*/
struct mipsexec
{
short mmagic; /* (0x160) mips magic number */
short nscns; /* (unused) number of sections */
int32 timdat; /* (unused) time & date stamp */
int32 symptr; /* offset to symbol table */
int32 nsyms; /* size of symbol table */
short opthdr; /* (0x38) sizeof(optional hdr) */
short pcszs; /* flags */
short amagic; /* see above */
short vstamp; /* version stamp */
int32 tsize; /* text size in bytes */
int32 dsize; /* initialized data */
int32 bsize; /* uninitialized data */
int32 mentry; /* entry pt. */
int32 text_start; /* base of text used for this file */
int32 data_start; /* base of data used for this file */
int32 bss_start; /* base of bss used for this file */
int32 gprmask; /* general purpose register mask */
union{
int32 cprmask[4]; /* co-processor register masks */
int32 pcsize;
};
int32 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 */
uint32 stext;
uint32 sdata;
uint32 sbss;
uint32 ssyms;
uint32 sentry;
uint32 strsize;
uint32 sdrsize;
};
struct nextexec
{
/* UNUSED
struct nexthdr{
uint32 nmagic;
uint32 ncputype;
uint32 ncpusubtype;
uint32 nfiletype;
uint32 ncmds;
uint32 nsizeofcmds;
uint32 nflags;
};
struct nextcmd{
uint32 cmd;
uint32 cmdsize;
uchar segname[16];
uint32 vmaddr;
uint32 vmsize;
uint32 fileoff;
uint32 filesize;
uint32 maxprot;
uint32 initprot;
uint32 nsects;
uint32 flags;
}textc;
struct nextsect{
char sectname[16];
char segname[16];
uint32 addr;
uint32 size;
uint32 offset;
uint32 align;
uint32 reloff;
uint32 nreloc;
uint32 flags;
uint32 reserved1;
uint32 reserved2;
}texts;
struct nextcmd datac;
struct nextsect datas;
struct nextsect bsss;
struct nextsym{
uint32 cmd;
uint32 cmdsize;
uint32 symoff;
uint32 nsyms;
uint32 spoff;
uint32 pcoff;
}symc;
*/
};
struct i386exec
{
/* UNUSED
struct i386coff{
uint32 isectmagic;
uint32 itime;
uint32 isyms;
uint32 insyms;
uint32 iflags;
};
struct i386hdr{
uint32 imagic;
uint32 itextsize;
uint32 idatasize;
uint32 ibsssize;
uint32 ientry;
uint32 itextstart;
uint32 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
*/
typedef struct Exec Exec;
struct Exec
{
int32 magic; /* magic number */
int32 text; /* size of text segment */
int32 data; /* size of initialized data */
int32 bss; /* size of uninitialized data */
int32 syms; /* size of symbol table */
int32 entry; /* entry point */
int32 spsz; /* size of pc/sp offset table */
int32 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;
vlong gotype;
int sequence; // order in file
};
/*
* 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 */
FWINPE, /* windows PE executable */
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;
typedef struct Seg Seg;
typedef int Maprw(Map *m, Seg *s, uvlong addr, void *v, uint n, int isread);
struct Seg {
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 */
Maprw *rw; /* read/write fn for seg */
};
/*
* Structure to map a segment to data
*/
struct Map {
int pid;
int tid;
int nsegs; /* number of segments */
Seg 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 */
int32 regsize; /* sizeof registers in bytes */
int32 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 */
uint32 (*swal)(uint32); /* uint32 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 */
uint32 (*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;
int32 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 */
int32 txtsz; /* text size */
int32 datsz; /* size of data seg */
int32 bsssz; /* size of bss */
int32 symsz; /* size of symbol table */
int32 sppcsz; /* size of sp-pc table */
int32 lnpcsz; /* size of line number-pc table */
} Fhdr;
extern int asstype; /* dissembler type - machdata.c */
extern Machdata *machdata; /* jump vector - machdata.c */
int beieee80ftos(char*, int, void*);
int beieeesftos(char*, int, void*);
int beieeedftos(char*, int, void*);
ushort beswab(ushort);
uint32 beswal(uint32);
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*, int32);
int fileelem(Sym**, uchar *, char*, int);
int32 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, uint32*);
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*, uint32, int);
int ieeesftos(char*, int, uint32);
int ieeedftos(char*, int, uint32, uint32);
int isar(Biobuf*);
int leieee80ftos(char*, int, void*);
int leieeesftos(char*, int, void*);
int leieeedftos(char*, int, void*);
ushort leswab(ushort);
uint32 leswal(uint32);
uvlong leswav(uvlong);
uvlong line2addr(int32, 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);
int32 pc2line(uvlong);
int put1(Map*, uvlong, uchar*, int);
int put2(Map*, uvlong, ushort);
int put4(Map*, uvlong, uint32);
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*, Maprw *rw);
Sym* symbase(int32*);
int syminit(int, Fhdr*);
int symoff(char*, int, uvlong, int);
void textseg(uvlong, Fhdr*);
int textsym(Symbol*, int);
void unusemap(Map*, int);
Map* attachproc(int pid, Fhdr *fp);
int ctlproc(int pid, char *msg);
void detachproc(Map *m);
int procnotes(int pid, char ***pnotes);
char* proctextfile(int pid);
int procthreadpids(int pid, int *tid, int ntid);
char* procstatus(int);
Maprw fdrw;
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "../mach.h"
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "../ureg_amd64.h"
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "../ureg_arm.h"
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "../ureg_x86.h"
// 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.
typedef struct UregAmd64 UregAmd64;
struct UregAmd64 {
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/ureg5.h
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/ureg5.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.
typedef struct UregArm UregArm;
struct UregArm {
uint r0;
uint r1;
uint r2;
uint r3;
uint r4;
uint r5;
uint r6;
uint r7;
uint r8;
uint r9;
uint r10;
uint r11;
uint r12;
uint r13;
uint r14;
uint link;
uint type;
uint psr;
uint pc;
};
// 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.
typedef struct Ureg386 Ureg386;
struct Ureg386
{
uint32 di; /* general registers */
uint32 si; /* ... */
uint32 bp; /* ... */
uint32 nsp;
uint32 bx; /* ... */
uint32 dx; /* ... */
uint32 cx; /* ... */
uint32 ax; /* ... */
uint32 gs; /* data segments */
uint32 fs; /* ... */
uint32 es; /* ... */
uint32 ds; /* ... */
uint32 trap; /* trap type */
uint32 ecode; /* error code (or zero) */
uint32 pc; /* pc */
uint32 cs; /* old context */
uint32 flags; /* old flags */
union {
uint32 usp;
uint32 sp;
};
uint32 ss; /* old stack segment */
};
...@@ -373,7 +373,6 @@ static char *oldtool[] = { ...@@ -373,7 +373,6 @@ static char *oldtool[] = {
// Unreleased directories (relative to $GOROOT) that should // Unreleased directories (relative to $GOROOT) that should
// not be in release branches. // not be in release branches.
static char *unreleased[] = { static char *unreleased[] = {
"src/cmd/prof",
"src/pkg/old", "src/pkg/old",
}; };
...@@ -517,19 +516,6 @@ static struct { ...@@ -517,19 +516,6 @@ static struct {
"$GOROOT/include/libc.h", "$GOROOT/include/libc.h",
"$GOROOT/include/bio.h", "$GOROOT/include/bio.h",
}}, }},
{"libmach", {
"$GOROOT/include/u.h",
"$GOROOT/include/utf.h",
"$GOROOT/include/fmt.h",
"$GOROOT/include/libc.h",
"$GOROOT/include/bio.h",
"$GOROOT/include/ar.h",
"$GOROOT/include/bootexec.h",
"$GOROOT/include/mach.h",
"$GOROOT/include/ureg_amd64.h",
"$GOROOT/include/ureg_arm.h",
"$GOROOT/include/ureg_x86.h",
}},
{"liblink", { {"liblink", {
"$GOROOT/include/u.h", "$GOROOT/include/u.h",
"$GOROOT/include/utf.h", "$GOROOT/include/utf.h",
...@@ -607,7 +593,6 @@ static struct { ...@@ -607,7 +593,6 @@ static struct {
}}, }},
{"cmd/", { {"cmd/", {
"$GOROOT/pkg/obj/$GOOS_$GOARCH/liblink.a", "$GOROOT/pkg/obj/$GOOS_$GOARCH/liblink.a",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libmach.a",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a", "$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/lib9.a", "$GOROOT/pkg/obj/$GOOS_$GOARCH/lib9.a",
}}, }},
...@@ -696,13 +681,6 @@ install(char *dir) ...@@ -696,13 +681,6 @@ install(char *dir)
goto out; goto out;
} }
// For release, cmd/prof is not included.
if((streq(dir, "cmd/prof")) && !isdir(bstr(&path))) {
if(vflag > 1)
errprintf("skipping %s - does not exist\n", dir);
goto out;
}
// set up gcc command line on first run. // set up gcc command line on first run.
if(gccargs.len == 0) { if(gccargs.len == 0) {
bprintf(&b, "%s %s", defaultcc, defaultcflags); bprintf(&b, "%s %s", defaultcc, defaultcflags);
...@@ -1327,13 +1305,10 @@ dopack(char *dst, char *src, char **extra, int nextra) ...@@ -1327,13 +1305,10 @@ dopack(char *dst, char *src, char **extra, int nextra)
static char *buildorder[] = { static char *buildorder[] = {
"lib9", "lib9",
"libbio", "libbio",
"libmach",
"liblink", "liblink",
"misc/pprof", "misc/pprof",
"cmd/prof",
"cmd/cc", // must be before c "cmd/cc", // must be before c
"cmd/gc", // must be before g "cmd/gc", // must be before g
"cmd/%sl", // must be before a, c, g "cmd/%sl", // must be before a, c, g
...@@ -1408,10 +1383,8 @@ static char *cleantab[] = { ...@@ -1408,10 +1383,8 @@ static char *cleantab[] = {
"cmd/cc", "cmd/cc",
"cmd/gc", "cmd/gc",
"cmd/go", "cmd/go",
"cmd/prof",
"lib9", "lib9",
"libbio", "libbio",
"libmach",
"liblink", "liblink",
"pkg/bufio", "pkg/bufio",
"pkg/bytes", "pkg/bytes",
...@@ -1467,8 +1440,6 @@ clean(void) ...@@ -1467,8 +1440,6 @@ clean(void)
vinit(&dir); vinit(&dir);
for(i=0; i<nelem(cleantab); i++) { for(i=0; i<nelem(cleantab); i++) {
if((streq(cleantab[i], "cmd/prof")) && !isdir(cleantab[i]))
continue;
bpathf(&path, "%s/src/%s", goroot, cleantab[i]); bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
xreaddir(&dir, bstr(&path)); xreaddir(&dir, bstr(&path));
// Remove generated files. // Remove generated files.
......
# Copyright 2012 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../../Make.dist
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
/*
Prof is a rudimentary real-time profiler.
Given a command to run or the process id (pid) of a command already
running, it samples the program's state at regular intervals and reports
on its behavior. With no options, it prints a histogram of the locations
in the code that were sampled during execution.
Since it is a real-time profiler, unlike a traditional profiler it samples
the program's state even when it is not running, such as when it is
asleep or waiting for I/O. Each thread contributes equally to the
statistics.
Usage:
go tool prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...]
The output modes (default -h) are:
-P file.prof:
Write the profile information to file.prof, in the format used by pprof.
At the moment, this only works on Linux amd64 binaries and requires that the
binary be written using 6l -e to produce ELF debug info.
See http://code.google.com/p/google-perftools for details.
-h: histograms
How many times a sample occurred at each location.
-f: dynamic functions
At each sample period, print the name of the executing function.
-l: dynamic file and line numbers
At each sample period, print the file and line number of the executing instruction.
-r: dynamic registers
At each sample period, print the register contents.
-s: dynamic function stack traces
At each sample period, print the symbolic stack trace.
Flag -t sets the maximum real time to sample, in seconds, and -d
sets the sampling interval in milliseconds. The default is to sample
every 100ms until the program completes.
It is installed as go tool prof and is architecture-independent.
*/
package main
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !plan9
#include <u.h>
#include <time.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <ureg_amd64.h>
#include <ureg_x86.h>
#include <mach.h>
char* file = "6.out";
static Fhdr fhdr;
int have_syms;
int fd;
struct UregAmd64 ureg_amd64;
struct Ureg386 ureg_x86;
int total_sec = 0;
int delta_msec = 100;
int nsample;
int nsamplethread;
// pprof data, stored as sequences of N followed by N PC values.
// See http://code.google.com/p/google-perftools .
uvlong *ppdata; // traces
Biobuf* pproffd; // file descriptor to write trace info
long ppstart; // start position of current trace
long nppdata; // length of data
long ppalloc; // size of allocated data
char ppmapdata[10*1024]; // the map information for the output file
// output formats
int pprof; // print pprof output to named file
int functions; // print functions
int histograms; // print histograms
int linenums; // print file and line numbers rather than function names
int registers; // print registers
int stacks; // print stack traces
int pid; // main process pid
int nthread; // number of threads
int thread[32]; // thread pids
Map *map[32]; // thread maps
void
Usage(void)
{
fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n");
fprint(2, " prof [-t total_secs] [-d delta_msec] 6.out args ...\n");
fprint(2, "\tformats (default -h):\n");
fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n");
fprint(2, "\t\t-h: histograms\n");
fprint(2, "\t\t-f: dynamic functions\n");
fprint(2, "\t\t-l: dynamic file and line numbers\n");
fprint(2, "\t\t-r: dynamic registers\n");
fprint(2, "\t\t-s: dynamic function stack traces\n");
fprint(2, "\t\t-hs: include stack info in histograms\n");
exit(2);
}
typedef struct PC PC;
struct PC {
uvlong pc;
uvlong callerpc;
unsigned int count;
PC* next;
};
enum {
Ncounters = 256
};
PC *counters[Ncounters];
// Set up by setarch() to make most of the code architecture-independent.
typedef struct Arch Arch;
struct Arch {
char* name;
void (*regprint)(void);
int (*getregs)(Map*);
int (*getPC)(Map*);
int (*getSP)(Map*);
uvlong (*uregPC)(void);
uvlong (*uregSP)(void);
void (*ppword)(uvlong w);
};
void
amd64_regprint(void)
{
fprint(2, "ax\t0x%llux\n", ureg_amd64.ax);
fprint(2, "bx\t0x%llux\n", ureg_amd64.bx);
fprint(2, "cx\t0x%llux\n", ureg_amd64.cx);
fprint(2, "dx\t0x%llux\n", ureg_amd64.dx);
fprint(2, "si\t0x%llux\n", ureg_amd64.si);
fprint(2, "di\t0x%llux\n", ureg_amd64.di);
fprint(2, "bp\t0x%llux\n", ureg_amd64.bp);
fprint(2, "r8\t0x%llux\n", ureg_amd64.r8);
fprint(2, "r9\t0x%llux\n", ureg_amd64.r9);
fprint(2, "r10\t0x%llux\n", ureg_amd64.r10);
fprint(2, "r11\t0x%llux\n", ureg_amd64.r11);
fprint(2, "r12\t0x%llux\n", ureg_amd64.r12);
fprint(2, "r13\t0x%llux\n", ureg_amd64.r13);
fprint(2, "r14\t0x%llux\n", ureg_amd64.r14);
fprint(2, "r15\t0x%llux\n", ureg_amd64.r15);
fprint(2, "ds\t0x%llux\n", ureg_amd64.ds);
fprint(2, "es\t0x%llux\n", ureg_amd64.es);
fprint(2, "fs\t0x%llux\n", ureg_amd64.fs);
fprint(2, "gs\t0x%llux\n", ureg_amd64.gs);
fprint(2, "type\t0x%llux\n", ureg_amd64.type);
fprint(2, "error\t0x%llux\n", ureg_amd64.error);
fprint(2, "pc\t0x%llux\n", ureg_amd64.ip);
fprint(2, "cs\t0x%llux\n", ureg_amd64.cs);
fprint(2, "flags\t0x%llux\n", ureg_amd64.flags);
fprint(2, "sp\t0x%llux\n", ureg_amd64.sp);
fprint(2, "ss\t0x%llux\n", ureg_amd64.ss);
}
int
amd64_getregs(Map *map)
{
int i;
union {
uvlong regs[1];
struct UregAmd64 ureg;
} u;
for(i = 0; i < sizeof ureg_amd64; i+=8) {
if(get8(map, (uvlong)i, &u.regs[i/8]) < 0)
return -1;
}
ureg_amd64 = u.ureg;
return 0;
}
int
amd64_getPC(Map *map)
{
uvlong x;
int r;
r = get8(map, offsetof(struct UregAmd64, ip), &x);
ureg_amd64.ip = x;
return r;
}
int
amd64_getSP(Map *map)
{
uvlong x;
int r;
r = get8(map, offsetof(struct UregAmd64, sp), &x);
ureg_amd64.sp = x;
return r;
}
uvlong
amd64_uregPC(void)
{
return ureg_amd64.ip;
}
uvlong
amd64_uregSP(void)
{
return ureg_amd64.sp;
}
void
amd64_ppword(uvlong w)
{
uchar buf[8];
buf[0] = w;
buf[1] = w >> 8;
buf[2] = w >> 16;
buf[3] = w >> 24;
buf[4] = w >> 32;
buf[5] = w >> 40;
buf[6] = w >> 48;
buf[7] = w >> 56;
Bwrite(pproffd, buf, 8);
}
void
x86_regprint(void)
{
fprint(2, "ax\t0x%ux\n", ureg_x86.ax);
fprint(2, "bx\t0x%ux\n", ureg_x86.bx);
fprint(2, "cx\t0x%ux\n", ureg_x86.cx);
fprint(2, "dx\t0x%ux\n", ureg_x86.dx);
fprint(2, "si\t0x%ux\n", ureg_x86.si);
fprint(2, "di\t0x%ux\n", ureg_x86.di);
fprint(2, "bp\t0x%ux\n", ureg_x86.bp);
fprint(2, "ds\t0x%ux\n", ureg_x86.ds);
fprint(2, "es\t0x%ux\n", ureg_x86.es);
fprint(2, "fs\t0x%ux\n", ureg_x86.fs);
fprint(2, "gs\t0x%ux\n", ureg_x86.gs);
fprint(2, "cs\t0x%ux\n", ureg_x86.cs);
fprint(2, "flags\t0x%ux\n", ureg_x86.flags);
fprint(2, "pc\t0x%ux\n", ureg_x86.pc);
fprint(2, "sp\t0x%ux\n", ureg_x86.sp);
fprint(2, "ss\t0x%ux\n", ureg_x86.ss);
}
int
x86_getregs(Map *map)
{
int i;
for(i = 0; i < sizeof ureg_x86; i+=4) {
if(get4(map, (uvlong)i, &((uint32*)&ureg_x86)[i/4]) < 0)
return -1;
}
return 0;
}
int
x86_getPC(Map* map)
{
return get4(map, offsetof(struct Ureg386, pc), &ureg_x86.pc);
}
int
x86_getSP(Map* map)
{
return get4(map, offsetof(struct Ureg386, sp), &ureg_x86.sp);
}
uvlong
x86_uregPC(void)
{
return (uvlong)ureg_x86.pc;
}
uvlong
x86_uregSP(void)
{
return (uvlong)ureg_x86.sp;
}
void
x86_ppword(uvlong w)
{
uchar buf[4];
buf[0] = w;
buf[1] = w >> 8;
buf[2] = w >> 16;
buf[3] = w >> 24;
Bwrite(pproffd, buf, 4);
}
Arch archtab[] = {
{
"amd64",
amd64_regprint,
amd64_getregs,
amd64_getPC,
amd64_getSP,
amd64_uregPC,
amd64_uregSP,
amd64_ppword,
},
{
"386",
x86_regprint,
x86_getregs,
x86_getPC,
x86_getSP,
x86_uregPC,
x86_uregSP,
x86_ppword,
},
{
nil
}
};
Arch *arch;
int
setarch(void)
{
int i;
if(mach != nil) {
for(i = 0; archtab[i].name != nil; i++) {
if (strcmp(mach->name, archtab[i].name) == 0) {
arch = &archtab[i];
return 0;
}
}
}
return -1;
}
int
getthreads(void)
{
int i, j, curn, found;
Map *curmap[nelem(map)];
int curthread[nelem(map)];
static int complained = 0;
curn = procthreadpids(pid, curthread, nelem(curthread));
if(curn <= 0)
return curn;
if(curn > nelem(map)) {
if(complained == 0) {
fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map));
complained = 1;
}
curn = nelem(map);
}
if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0)
return curn; // no changes
// Number of threads has changed (might be the init case).
// A bit expensive but rare enough not to bother being clever.
for(i = 0; i < curn; i++) {
found = 0;
for(j = 0; j < nthread; j++) {
if(curthread[i] == thread[j]) {
found = 1;
curmap[i] = map[j];
map[j] = nil;
break;
}
}
if(found)
continue;
// map new thread
curmap[i] = attachproc(curthread[i], &fhdr);
if(curmap[i] == nil) {
fprint(2, "prof: can't attach to %d: %r\n", curthread[i]);
return -1;
}
}
for(j = 0; j < nthread; j++)
if(map[j] != nil)
detachproc(map[j]);
nthread = curn;
memmove(thread, curthread, nthread*sizeof thread[0]);
memmove(map, curmap, sizeof map);
return nthread;
}
int
sample(Map *map)
{
static int n;
n++;
if(registers) {
if(arch->getregs(map) < 0)
goto bad;
} else {
// we need only two registers
if(arch->getPC(map) < 0)
goto bad;
if(arch->getSP(map) < 0)
goto bad;
}
return 1;
bad:
if(n == 1)
fprint(2, "prof: can't read registers: %r\n");
return 0;
}
void
addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
{
int h;
PC *x;
USED(sp);
h = (pc + callerpc*101) % Ncounters;
for(x = counters[h]; x != NULL; x = x->next) {
if(x->pc == pc && x->callerpc == callerpc) {
x->count++;
return;
}
}
x = malloc(sizeof(PC));
if(x == nil)
sysfatal("out of memory");
x->pc = pc;
x->callerpc = callerpc;
x->count = 1;
x->next = counters[h];
counters[h] = x;
}
void
addppword(uvlong pc)
{
if(pc == 0) {
return;
}
if(nppdata == ppalloc) {
ppalloc = (1000+nppdata)*2;
ppdata = realloc(ppdata, ppalloc * sizeof ppdata[0]);
if(ppdata == nil) {
fprint(2, "prof: realloc failed: %r\n");
exit(2);
}
}
ppdata[nppdata++] = pc;
}
void
startpptrace(void)
{
ppstart = nppdata;
addppword(~0);
}
void
endpptrace(void)
{
ppdata[ppstart] = nppdata-ppstart-1;
}
uvlong nextpc;
void
xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
{
USED(map);
char buf[1024];
if(sym == nil){
fprint(2, "syms\n");
return;
}
if(histograms)
addtohistogram(nextpc, pc, sp);
if(!histograms || stacks > 1 || pprof) {
if(nextpc == 0)
nextpc = sym->value;
if(stacks){
fprint(2, "%s(", sym->name);
fprint(2, ")");
if(nextpc != sym->value)
fprint(2, "+%#llux ", nextpc - sym->value);
if(have_syms && linenums && fileline(buf, sizeof buf, pc)) {
fprint(2, " %s", buf);
}
fprint(2, "\n");
}
if (pprof) {
addppword(nextpc);
}
}
nextpc = pc;
}
void
stacktracepcsp(Map *map, uvlong pc, uvlong sp)
{
nextpc = pc;
if(pprof){
startpptrace();
}
if(machdata->ctrace==nil)
fprint(2, "no machdata->ctrace\n");
else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0)
fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp);
else {
addtohistogram(nextpc, 0, sp);
if(stacks)
fprint(2, "\n");
}
if(pprof){
endpptrace();
}
}
void
printpc(Map *map, uvlong pc, uvlong sp)
{
char buf[1024];
if(registers)
arch->regprint();
if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc))
fprint(2, "%s\n", buf);
if(have_syms > 0 && functions) {
symoff(buf, sizeof(buf), pc, CANY);
fprint(2, "%s\n", buf);
}
if(stacks || pprof){
stacktracepcsp(map, pc, sp);
}
else if(histograms){
addtohistogram(pc, 0, sp);
}
}
void
ppmaps(void)
{
int fd, n;
char tmp[100];
Seg *seg;
// If it's Linux, the info is in /proc/$pid/maps
snprint(tmp, sizeof tmp, "/proc/%d/maps", pid);
fd = open(tmp, 0);
if(fd >= 0) {
n = read(fd, ppmapdata, sizeof ppmapdata - 1);
close(fd);
if(n < 0) {
fprint(2, "prof: can't read %s: %r\n", tmp);
exit(2);
}
ppmapdata[n] = 0;
return;
}
// It's probably a mac. Synthesize an entry for the text file.
// The register segment may come first but it has a zero offset, so grab the first non-zero offset segment.
for(n = 0; n < 3; n++){
seg = &map[0]->seg[n];
if(seg->b == 0) {
continue;
}
snprint(ppmapdata, sizeof ppmapdata,
"%.16x-%.16x r-xp %d 00:00 34968549 %s\n",
seg->b, seg->e, seg->f, "/home/r/6.out"
);
return;
}
fprint(2, "prof: no text segment in maps for %s\n", file);
exit(2);
}
void
samples(void)
{
int i, pid, msec;
struct timespec req;
int getmaps;
req.tv_sec = delta_msec/1000;
req.tv_nsec = 1000000*(delta_msec % 1000);
getmaps = 0;
if(pprof)
getmaps= 1;
for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) {
nsample++;
nsamplethread += nthread;
for(i = 0; i < nthread; i++) {
pid = thread[i];
if(ctlproc(pid, "stop") < 0)
return;
if(!sample(map[i])) {
ctlproc(pid, "start");
return;
}
printpc(map[i], arch->uregPC(), arch->uregSP());
ctlproc(pid, "start");
}
nanosleep(&req, NULL);
getthreads();
if(nthread == 0)
break;
if(getmaps) {
getmaps = 0;
ppmaps();
}
}
}
typedef struct Func Func;
struct Func
{
Func *next;
Symbol s;
uint onstack;
uint leaf;
};
Func *func[257];
int nfunc;
Func*
findfunc(uvlong pc)
{
Func *f;
uint h;
Symbol s;
if(pc == 0)
return nil;
if(!findsym(pc, CTEXT, &s))
return nil;
h = s.value % nelem(func);
for(f = func[h]; f != NULL; f = f->next)
if(f->s.value == s.value)
return f;
f = malloc(sizeof *f);
if(f == nil)
sysfatal("out of memory");
memset(f, 0, sizeof *f);
f->s = s;
f->next = func[h];
func[h] = f;
nfunc++;
return f;
}
int
compareleaf(const void *va, const void *vb)
{
Func *a, *b;
a = *(Func**)va;
b = *(Func**)vb;
if(a->leaf != b->leaf)
return b->leaf - a->leaf;
if(a->onstack != b->onstack)
return b->onstack - a->onstack;
return strcmp(a->s.name, b->s.name);
}
void
dumphistogram(void)
{
int i, h, n;
PC *x;
Func *f, **ff;
if(!histograms)
return;
// assign counts to functions.
for(h = 0; h < Ncounters; h++) {
for(x = counters[h]; x != NULL; x = x->next) {
f = findfunc(x->pc);
if(f) {
f->onstack += x->count;
f->leaf += x->count;
}
f = findfunc(x->callerpc);
if(f)
f->leaf -= x->count;
}
}
// build array
ff = malloc(nfunc*sizeof ff[0]);
if(ff == nil)
sysfatal("out of memory");
n = 0;
for(h = 0; h < nelem(func); h++)
for(f = func[h]; f != NULL; f = f->next)
ff[n++] = f;
// sort by leaf counts
qsort(ff, nfunc, sizeof ff[0], compareleaf);
// print.
fprint(2, "%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample);
for(i = 0; i < nfunc; i++) {
f = ff[i];
fprint(2, "%6.2f%%\t", 100.0*(double)f->leaf/nsample);
if(stacks)
fprint(2, "%6.2f%%\t", 100.0*(double)f->onstack/nsample);
fprint(2, "%s\n", f->s.name);
}
}
typedef struct Trace Trace;
struct Trace {
int count;
int npc;
uvlong *pc;
Trace *next;
};
void
dumppprof(void)
{
uvlong i, n, *p, *e;
int ntrace;
Trace *trace, *tp, *up, *prev;
if(!pprof)
return;
e = ppdata + nppdata;
// Create list of traces. First, count the traces
ntrace = 0;
for(p = ppdata; p < e;) {
n = *p++;
p += n;
if(n == 0)
continue;
ntrace++;
}
if(ntrace <= 0)
return;
// Allocate and link the traces together.
trace = malloc(ntrace * sizeof(Trace));
if(trace == nil)
sysfatal("out of memory");
tp = trace;
for(p = ppdata; p < e;) {
n = *p++;
if(n == 0)
continue;
tp->count = 1;
tp->npc = n;
tp->pc = p;
tp->next = tp+1;
tp++;
p += n;
}
trace[ntrace-1].next = nil;
// Eliminate duplicates. Lousy algorithm, although not as bad as it looks because
// the list collapses fast.
for(tp = trace; tp != nil; tp = tp->next) {
prev = tp;
for(up = tp->next; up != nil; up = up->next) {
if(up->npc == tp->npc && memcmp(up->pc, tp->pc, up->npc*sizeof up->pc[0]) == 0) {
tp->count++;
prev->next = up->next;
} else {
prev = up;
}
}
}
// Write file.
// See http://code.google.com/p/google-perftools/source/browse/trunk/doc/cpuprofile-fileformat.html
// 1) Header
arch->ppword(0); // must be zero
arch->ppword(3); // 3 words follow in header
arch->ppword(0); // must be zero
arch->ppword(delta_msec * 1000); // sampling period in microseconds
arch->ppword(0); // must be zero (padding)
// 2) One record for each trace.
for(tp = trace; tp != nil; tp = tp->next) {
arch->ppword(tp->count);
arch->ppword(tp->npc);
for(i = 0; i < tp->npc; i++) {
arch->ppword(tp->pc[i]);
}
}
// 3) Binary trailer
arch->ppword(0); // must be zero
arch->ppword(1); // must be one
arch->ppword(0); // must be zero
// 4) Mapped objects.
Bwrite(pproffd, ppmapdata, strlen(ppmapdata));
// 5) That's it.
Bterm(pproffd);
}
int
startprocess(char **argv)
{
int pid;
if((pid = fork()) == 0) {
pid = getpid();
if(ctlproc(pid, "hang") < 0){
fprint(2, "prof: child process could not hang\n");
exits(0);
}
execv(argv[0], argv);
fprint(2, "prof: could not exec %s: %r\n", argv[0]);
exits(0);
}
if(pid == -1) {
fprint(2, "prof: could not fork\n");
exit(1);
}
if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) {
fprint(2, "prof: could not attach to child process: %r\n");
exit(1);
}
return pid;
}
void
detach(void)
{
int i;
for(i = 0; i < nthread; i++)
detachproc(map[i]);
}
int
main(int argc, char *argv[])
{
int i;
char *ppfile;
ARGBEGIN{
case 'P':
pprof =1;
ppfile = EARGF(Usage());
pproffd = Bopen(ppfile, OWRITE);
if(pproffd == nil) {
fprint(2, "prof: cannot open %s: %r\n", ppfile);
exit(2);
}
break;
case 'd':
delta_msec = atoi(EARGF(Usage()));
break;
case 't':
total_sec = atoi(EARGF(Usage()));
break;
case 'p':
pid = atoi(EARGF(Usage()));
break;
case 'f':
functions = 1;
break;
case 'h':
histograms = 1;
break;
case 'l':
linenums = 1;
break;
case 'r':
registers = 1;
break;
case 's':
stacks++;
break;
default:
Usage();
}ARGEND
if(pid <= 0 && argc == 0)
Usage();
if(functions+linenums+registers+stacks+pprof == 0)
histograms = 1;
if(!machbyname("amd64")) {
fprint(2, "prof: no amd64 support\n", pid);
exit(1);
}
if(argc > 0)
file = argv[0];
else if(pid) {
file = proctextfile(pid);
if (file == NULL) {
fprint(2, "prof: can't find file for pid %d: %r\n", pid);
fprint(2, "prof: on Darwin, need to provide file name explicitly\n");
exit(1);
}
}
fd = open(file, 0);
if(fd < 0) {
fprint(2, "prof: can't open %s: %r\n", file);
exit(1);
}
if(crackhdr(fd, &fhdr)) {
have_syms = syminit(fd, &fhdr);
if(!have_syms) {
fprint(2, "prof: no symbols for %s: %r\n", file);
}
} else {
fprint(2, "prof: crack header for %s: %r\n", file);
exit(1);
}
if(pid <= 0)
pid = startprocess(argv);
attachproc(pid, &fhdr); // initializes thread list
if(setarch() < 0) {
detach();
fprint(2, "prof: can't identify binary architecture for pid %d\n", pid);
exit(1);
}
if(getthreads() <= 0) {
detach();
fprint(2, "prof: can't find threads for pid %d\n", pid);
exit(1);
}
for(i = 0; i < nthread; i++)
ctlproc(thread[i], "start");
samples();
detach();
dumphistogram();
dumppprof();
exit(0);
}
// Inferno libmach/5.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/5.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.
/*
* arm definition
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "ureg_arm.h"
#include <mach.h>
#define REGOFF(x) (uintptr) (&((struct UregArm *) 0)->x)
#define SP REGOFF(r13)
#define PC REGOFF(pc)
#define REGSIZE sizeof(struct UregArm)
Reglist armreglist[] =
{
{"LINK", REGOFF(link), RINT|RRDONLY, 'X'},
{"TYPE", REGOFF(type), RINT|RRDONLY, 'X'},
{"PSR", REGOFF(psr), RINT|RRDONLY, 'X'},
{"PC", PC, RINT, 'X'},
{"SP", SP, RINT, 'X'},
{"R15", PC, RINT, 'X'},
{"R14", REGOFF(r14), RINT, 'X'},
{"R13", REGOFF(r13), RINT, 'X'},
{"R12", REGOFF(r12), RINT, 'X'},
{"R11", REGOFF(r11), RINT, 'X'},
{"R10", REGOFF(r10), RINT, 'X'},
{"R9", REGOFF(r9), RINT, 'X'},
{"R8", REGOFF(r8), RINT, 'X'},
{"R7", REGOFF(r7), RINT, 'X'},
{"R6", REGOFF(r6), RINT, 'X'},
{"R5", REGOFF(r5), RINT, 'X'},
{"R4", REGOFF(r4), RINT, 'X'},
{"R3", REGOFF(r3), RINT, 'X'},
{"R2", REGOFF(r2), RINT, 'X'},
{"R1", REGOFF(r1), RINT, 'X'},
{"R0", REGOFF(r0), RINT, 'X'},
{ 0 }
};
/* the machine description */
Mach marm =
{
"arm",
MARM, /* machine type */
armreglist, /* register set */
REGSIZE, /* register set size */
0, /* fp register set size */
"PC", /* name of PC */
"SP", /* name of SP */
"R15", /* name of link register */
"setR12", /* static base register name */
0, /* static base register value */
0x1000, /* page size */
0xC0000000, /* kernel base */
0, /* kernel text mask */
4, /* quantization of pc */
4, /* szaddr */
4, /* szreg */
4, /* szfloat */
8, /* szdouble */
};
// Inferno libmach/5db.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/5db.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 "ureg_arm.h"
#include <mach.h>
static int debug = 0;
#define BITS(a, b) ((1<<(b+1))-(1<<a))
#define LSR(v, s) ((ulong)(v) >> (s))
#define ASR(v, s) ((long)(v) >> (s))
#define ROR(v, s) (LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
typedef struct Instr Instr;
struct Instr
{
Map *map;
ulong w;
ulong addr;
uchar op; /* super opcode */
uchar cond; /* bits 28-31 */
uchar store; /* bit 20 */
uchar rd; /* bits 12-15 */
uchar rn; /* bits 16-19 */
uchar rs; /* bits 0-11 (shifter operand) */
long imm; /* rotated imm */
char* curr; /* fill point in buffer */
char* end; /* end of buffer */
char* err; /* error message */
};
typedef struct Opcode Opcode;
struct Opcode
{
char* o;
void (*fmt)(Opcode*, Instr*);
ulong (*foll)(Map*, Rgetter, Instr*, ulong);
char* a;
};
static void format(char*, Instr*, char*);
static char FRAMENAME[] = ".frame";
/*
* Arm-specific debugger interface
*/
extern char *armexcep(Map*, Rgetter);
static int armfoll(Map*, uvlong, Rgetter, uvlong*);
static int arminst(Map*, uvlong, char, char*, int);
static int armdas(Map*, uvlong, char*, int);
static int arminstlen(Map*, uvlong);
/*
* Debugger interface
*/
Machdata armmach =
{
{0, 0, 0, 0xD}, /* break point */
4, /* break point size */
leswab, /* short to local byte order */
leswal, /* long to local byte order */
leswav, /* long to local byte order */
risctrace, /* C traceback */
riscframe, /* Frame finder */
armexcep, /* print exception */
0, /* breakpoint fixup */
0, /* single precision float printer */
0, /* double precision float printer */
armfoll, /* following addresses */
arminst, /* print instruction */
armdas, /* dissembler */
arminstlen, /* instruction size */
};
char*
armexcep(Map *map, Rgetter rget)
{
long c;
c = (*rget)(map, "TYPE");
switch (c&0x1f) {
case 0x11:
return "Fiq interrupt";
case 0x12:
return "Mirq interrupt";
case 0x13:
return "SVC/SWI Exception";
case 0x17:
return "Prefetch Abort/Data Abort";
case 0x18:
return "Data Abort";
case 0x1b:
return "Undefined instruction/Breakpoint";
case 0x1f:
return "Sys trap";
default:
return "Undefined trap";
}
}
static
char* cond[16] =
{
"EQ", "NE", "CS", "CC",
"MI", "PL", "VS", "VC",
"HI", "LS", "GE", "LT",
"GT", "LE", 0, "NV"
};
static
char* shtype[4] =
{
"<<", ">>", "->", "@>"
};
static
char *hb[4] =
{
"???", "HU", "B", "H"
};
static
char* addsub[2] =
{
"-", "+",
};
int
armclass(long w)
{
int op;
op = (w >> 25) & 0x7;
switch(op) {
case 0: /* data processing r,r,r */
op = ((w >> 4) & 0xf);
if(op == 0x9) {
op = 48+16; /* mul */
if(w & (1<<24)) {
op += 2;
if(w & (1<<22))
op++; /* swap */
break;
}
if(w & (1<<21))
op++; /* mla */
break;
}
if ((op & 0x9) == 0x9) /* ld/st byte/half s/u */
{
op = (48+16+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
break;
}
op = (w >> 21) & 0xf;
if(w & (1<<4))
op += 32;
else
if(w & (31<<7))
op += 16;
break;
case 1: /* data processing i,r,r */
op = (48) + ((w >> 21) & 0xf);
break;
case 2: /* load/store byte/word i(r) */
op = (48+24) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
break;
case 3: /* load/store byte/word (r)(r) */
op = (48+24+4) + ((w >> 22) & 0x1) + ((w >> 19) & 0x2);
break;
case 4: /* block data transfer (r)(r) */
op = (48+24+4+4) + ((w >> 20) & 0x1);
break;
case 5: /* branch / branch link */
op = (48+24+4+4+2) + ((w >> 24) & 0x1);
break;
case 7: /* coprocessor crap */
op = (48+24+4+4+2+2) + ((w >> 3) & 0x2) + ((w >> 20) & 0x1);
break;
default:
op = (48+24+4+4+2+2+4);
break;
}
return op;
}
static int
decode(Map *map, ulong pc, Instr *i)
{
uint32 w;
if(get4(map, pc, &w) < 0) {
werrstr("can't read instruction: %r");
return -1;
}
i->w = w;
i->addr = pc;
i->cond = (w >> 28) & 0xF;
i->op = armclass(w);
i->map = map;
return 1;
}
static void
bprint(Instr *i, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
i->curr = vseprint(i->curr, i->end, fmt, arg);
va_end(arg);
}
static int
plocal(Instr *i)
{
char *reg;
Symbol s;
char *fn;
int class;
int offset;
if(!findsym(i->addr, CTEXT, &s)) {
if(debug)fprint(2,"fn not found @%lux: %r\n", i->addr);
return 0;
}
fn = s.name;
if (!findlocal(&s, FRAMENAME, &s)) {
if(debug)fprint(2,"%s.%s not found @%s: %r\n", fn, FRAMENAME, s.name);
return 0;
}
if(s.value > i->imm) {
class = CAUTO;
offset = s.value-i->imm;
reg = "(SP)";
} else {
class = CPARAM;
offset = i->imm-s.value-4;
reg = "(FP)";
}
if(!getauto(&s, offset, class, &s)) {
if(debug)fprint(2,"%s %s not found @%ux: %r\n", fn,
class == CAUTO ? " auto" : "param", offset);
return 0;
}
bprint(i, "%s%c%d%s", s.name, class == CPARAM ? '+' : '-', s.value, reg);
return 1;
}
/*
* Print value v as name[+offset]
*/
int
gsymoff(char *buf, int n, long 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 || delta >= 4096)
return snprint(buf, n, "#%lux", v);
if (strcmp(s.name, ".string") == 0)
return snprint(buf, n, "#%lux", v);
if (!delta)
return snprint(buf, n, "%s", s.name);
if (s.type != 't' && s.type != 'T')
return snprint(buf, n, "%s+%llux", s.name, v-s.value);
else
return snprint(buf, n, "#%lux", v);
}
static void
armdps(Opcode *o, Instr *i)
{
i->store = (i->w >> 20) & 1;
i->rn = (i->w >> 16) & 0xf;
i->rd = (i->w >> 12) & 0xf;
i->rs = (i->w >> 0) & 0xf;
if(i->rn == 15 && i->rs == 0) {
if(i->op == 8) {
format("MOVW", i,"CPSR, R%d");
return;
} else
if(i->op == 10) {
format("MOVW", i,"SPSR, R%d");
return;
}
} else
if(i->rn == 9 && i->rd == 15) {
if(i->op == 9) {
format("MOVW", i, "R%s, CPSR");
return;
} else
if(i->op == 11) {
format("MOVW", i, "R%s, SPSR");
return;
}
}
format(o->o, i, o->a);
}
static void
armdpi(Opcode *o, Instr *i)
{
ulong v;
int c;
v = (i->w >> 0) & 0xff;
c = (i->w >> 8) & 0xf;
while(c) {
v = (v<<30) | (v>>2);
c--;
}
i->imm = v;
i->store = (i->w >> 20) & 1;
i->rn = (i->w >> 16) & 0xf;
i->rd = (i->w >> 12) & 0xf;
i->rs = i->w&0x0f;
/* RET is encoded as ADD #0,R14,R15 */
if((i->w & 0x0fffffff) == 0x028ef000){
format("RET%C", i, "");
return;
}
if((i->w & 0x0ff0ffff) == 0x0280f000){
format("B%C", i, "0(R%n)");
return;
}
format(o->o, i, o->a);
}
static void
armsdti(Opcode *o, Instr *i)
{
ulong v;
v = i->w & 0xfff;
if(!(i->w & (1<<23)))
v = -v;
i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
i->imm = v;
i->rn = (i->w >> 16) & 0xf;
i->rd = (i->w >> 12) & 0xf;
/* RET is encoded as LW.P x,R13,R15 */
if ((i->w & 0x0ffff000) == 0x049df000)
{
format("RET%C%p", i, "%I");
return;
}
format(o->o, i, o->a);
}
/* arm V4 ld/st halfword, signed byte */
static void
armhwby(Opcode *o, Instr *i)
{
i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
i->imm = (i->w & 0xf) | ((i->w >> 8) & 0xf);
if (!(i->w & (1 << 23)))
i->imm = - i->imm;
i->rn = (i->w >> 16) & 0xf;
i->rd = (i->w >> 12) & 0xf;
i->rs = (i->w >> 0) & 0xf;
format(o->o, i, o->a);
}
static void
armsdts(Opcode *o, Instr *i)
{
i->store = ((i->w >> 23) & 0x2) | ((i->w >>21) & 0x1);
i->rs = (i->w >> 0) & 0xf;
i->rn = (i->w >> 16) & 0xf;
i->rd = (i->w >> 12) & 0xf;
format(o->o, i, o->a);
}
static void
armbdt(Opcode *o, Instr *i)
{
i->store = (i->w >> 21) & 0x3; /* S & W bits */
i->rn = (i->w >> 16) & 0xf;
i->imm = i->w & 0xffff;
if(i->w == 0xe8fd8000)
format("RFE", i, "");
else
format(o->o, i, o->a);
}
/*
static void
armund(Opcode *o, Instr *i)
{
format(o->o, i, o->a);
}
static void
armcdt(Opcode *o, Instr *i)
{
format(o->o, i, o->a);
}
*/
static void
armunk(Opcode *o, Instr *i)
{
format(o->o, i, o->a);
}
static void
armb(Opcode *o, Instr *i)
{
ulong v;
v = i->w & 0xffffff;
if(v & 0x800000)
v |= ~0xffffff;
i->imm = (v<<2) + i->addr + 8;
format(o->o, i, o->a);
}
static void
armco(Opcode *o, Instr *i) /* coprocessor instructions */
{
int op, p, cp;
char buf[1024];
i->rn = (i->w >> 16) & 0xf;
i->rd = (i->w >> 12) & 0xf;
i->rs = i->w&0xf;
cp = (i->w >> 8) & 0xf;
p = (i->w >> 5) & 0x7;
if(i->w&(1<<4)) {
op = (i->w >> 21) & 0x07;
snprint(buf, sizeof(buf), "#%x, #%x, R%d, C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p);
} else {
op = (i->w >> 20) & 0x0f;
snprint(buf, sizeof(buf), "#%x, #%x, C(%d), C(%d), C(%d), #%x\n", cp, op, i->rd, i->rn, i->rs, p);
}
format(o->o, i, buf);
}
static int
armcondpass(Map *map, Rgetter rget, uchar cond)
{
ulong psr;
uchar n;
uchar z;
uchar c;
uchar v;
psr = rget(map, "PSR");
n = (psr >> 31) & 1;
z = (psr >> 30) & 1;
c = (psr >> 29) & 1;
v = (psr >> 28) & 1;
switch(cond) {
case 0: return z;
case 1: return !z;
case 2: return c;
case 3: return !c;
case 4: return n;
case 5: return !n;
case 6: return v;
case 7: return !v;
case 8: return c && !z;
case 9: return !c || z;
case 10: return n == v;
case 11: return n != v;
case 12: return !z && (n == v);
case 13: return z && (n != v);
case 14: return 1;
case 15: return 0;
}
return 0;
}
static ulong
armshiftval(Map *map, Rgetter rget, Instr *i)
{
if(i->w & (1 << 25)) { /* immediate */
ulong imm = i->w & BITS(0, 7);
ulong s = (i->w & BITS(8, 11)) >> 7; /* this contains the *2 */
return ROR(imm, s);
} else {
char buf[8];
ulong v;
ulong s = (i->w & BITS(7,11)) >> 7;
sprint(buf, "R%ld", i->w & 0xf);
v = rget(map, buf);
switch((i->w & BITS(4, 6)) >> 4) {
case 0: /* LSLIMM */
return v << s;
case 1: /* LSLREG */
sprint(buf, "R%lud", s >> 1);
s = rget(map, buf) & 0xFF;
if(s >= 32) return 0;
return v << s;
case 2: /* LSRIMM */
return LSR(v, s);
case 3: /* LSRREG */
sprint(buf, "R%ld", s >> 1);
s = rget(map, buf) & 0xFF;
if(s >= 32) return 0;
return LSR(v, s);
case 4: /* ASRIMM */
if(s == 0) {
if((v & (1U<<31)) == 0)
return 0;
return 0xFFFFFFFF;
}
return ASR(v, s);
case 5: /* ASRREG */
sprint(buf, "R%ld", s >> 1);
s = rget(map, buf) & 0xFF;
if(s >= 32) {
if((v & (1U<<31)) == 0)
return 0;
return 0xFFFFFFFF;
}
return ASR(v, s);
case 6: /* RORIMM */
if(s == 0) {
ulong c = (rget(map, "PSR") >> 29) & 1;
return (c << 31) | LSR(v, 1);
}
return ROR(v, s);
case 7: /* RORREG */
sprint(buf, "R%ld", (s>>1)&0xF);
s = rget(map, buf);
if(s == 0 || (s & 0xF) == 0)
return v;
return ROR(v, s & 0xF);
}
}
return 0;
}
static int
nbits(ulong v)
{
int n = 0;
int i;
for(i=0; i < 32 ; i++) {
if(v & 1) ++n;
v >>= 1;
}
return n;
}
static ulong
armmaddr(Map *map, Rgetter rget, Instr *i)
{
ulong v;
ulong nb;
char buf[8];
ulong rn;
rn = (i->w >> 16) & 0xf;
sprint(buf,"R%ld", rn);
v = rget(map, buf);
nb = nbits(i->w & ((1 << 15) - 1));
switch((i->w >> 23) & 3) {
case 0: return (v - (nb*4)) + 4;
case 1: return v;
case 2: return v - (nb*4);
case 3: return v + 4;
}
return 0;
}
static ulong
armaddr(Map *map, Rgetter rget, Instr *i)
{
char buf[8];
ulong rn;
sprint(buf, "R%ld", (i->w >> 16) & 0xf);
rn = rget(map, buf);
if((i->w & (1<<24)) == 0) { /* POSTIDX */
sprint(buf, "R%ld", rn);
return rget(map, buf);
}
if((i->w & (1<<25)) == 0) { /* OFFSET */
sprint(buf, "R%ld", rn);
if(i->w & (1U<<23))
return rget(map, buf) + (i->w & BITS(0,11));
return rget(map, buf) - (i->w & BITS(0,11));
} else { /* REGOFF */
ulong index = 0;
uchar c;
uchar rm;
sprint(buf, "R%ld", i->w & 0xf);
rm = rget(map, buf);
switch((i->w & BITS(5,6)) >> 5) {
case 0: index = rm << ((i->w & BITS(7,11)) >> 7); break;
case 1: index = LSR(rm, ((i->w & BITS(7,11)) >> 7)); break;
case 2: index = ASR(rm, ((i->w & BITS(7,11)) >> 7)); break;
case 3:
if((i->w & BITS(7,11)) == 0) {
c = (rget(map, "PSR") >> 29) & 1;
index = c << 31 | LSR(rm, 1);
} else {
index = ROR(rm, ((i->w & BITS(7,11)) >> 7));
}
break;
}
if(i->w & (1<<23))
return rn + index;
return rn - index;
}
}
static ulong
armfadd(Map *map, Rgetter rget, Instr *i, ulong pc)
{
char buf[8];
int r;
r = (i->w >> 12) & 0xf;
if(r != 15 || !armcondpass(map, rget, (i->w >> 28) & 0xf))
return pc+4;
r = (i->w >> 16) & 0xf;
sprint(buf, "R%d", r);
return rget(map, buf) + armshiftval(map, rget, i);
}
static ulong
armfmovm(Map *map, Rgetter rget, Instr *i, ulong pc)
{
uint32 v;
ulong addr;
v = i->w & 1<<15;
if(!v || !armcondpass(map, rget, (i->w>>28)&0xf))
return pc+4;
addr = armmaddr(map, rget, i) + nbits(i->w & BITS(0,15));
if(get4(map, addr, &v) < 0) {
werrstr("can't read addr: %r");
return -1;
}
return v;
}
static ulong
armfbranch(Map *map, Rgetter rget, Instr *i, ulong pc)
{
if(!armcondpass(map, rget, (i->w >> 28) & 0xf))
return pc+4;
return pc + (((signed long)i->w << 8) >> 6) + 8;
}
static ulong
armfmov(Map *map, Rgetter rget, Instr *i, ulong pc)
{
ulong rd;
uint32 v;
rd = (i->w >> 12) & 0xf;
if(rd != 15 || !armcondpass(map, rget, (i->w>>28)&0xf))
return pc+4;
/* LDR */
/* BUG: Needs LDH/B, too */
if(((i->w>>26)&0x3) == 1) {
if(get4(map, armaddr(map, rget, i), &v) < 0) {
werrstr("can't read instruction: %r");
return pc+4;
}
return v;
}
/* MOV */
return armshiftval(map, rget, i);
}
static Opcode opcodes[] =
{
"AND%C%S", armdps, 0, "R%s,R%n,R%d",
"EOR%C%S", armdps, 0, "R%s,R%n,R%d",
"SUB%C%S", armdps, 0, "R%s,R%n,R%d",
"RSB%C%S", armdps, 0, "R%s,R%n,R%d",
"ADD%C%S", armdps, armfadd, "R%s,R%n,R%d",
"ADC%C%S", armdps, 0, "R%s,R%n,R%d",
"SBC%C%S", armdps, 0, "R%s,R%n,R%d",
"RSC%C%S", armdps, 0, "R%s,R%n,R%d",
"TST%C%S", armdps, 0, "R%s,R%n",
"TEQ%C%S", armdps, 0, "R%s,R%n",
"CMP%C%S", armdps, 0, "R%s,R%n",
"CMN%C%S", armdps, 0, "R%s,R%n",
"ORR%C%S", armdps, 0, "R%s,R%n,R%d",
"MOVW%C%S", armdps, armfmov, "R%s,R%d",
"BIC%C%S", armdps, 0, "R%s,R%n,R%d",
"MVN%C%S", armdps, 0, "R%s,R%d",
/* 16 */
"AND%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"EOR%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"SUB%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"RSB%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"ADD%C%S", armdps, armfadd, "(R%s%h%m),R%n,R%d",
"ADC%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"SBC%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"RSC%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"TST%C%S", armdps, 0, "(R%s%h%m),R%n",
"TEQ%C%S", armdps, 0, "(R%s%h%m),R%n",
"CMP%C%S", armdps, 0, "(R%s%h%m),R%n",
"CMN%C%S", armdps, 0, "(R%s%h%m),R%n",
"ORR%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"MOVW%C%S", armdps, armfmov, "(R%s%h%m),R%d",
"BIC%C%S", armdps, 0, "(R%s%h%m),R%n,R%d",
"MVN%C%S", armdps, 0, "(R%s%h%m),R%d",
/* 32 */
"AND%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"EOR%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"SUB%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"RSB%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"ADD%C%S", armdps, armfadd, "(R%s%hR%M),R%n,R%d",
"ADC%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"SBC%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"RSC%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"TST%C%S", armdps, 0, "(R%s%hR%M),R%n",
"TEQ%C%S", armdps, 0, "(R%s%hR%M),R%n",
"CMP%C%S", armdps, 0, "(R%s%hR%M),R%n",
"CMN%C%S", armdps, 0, "(R%s%hR%M),R%n",
"ORR%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"MOVW%C%S", armdps, armfmov, "(R%s%hR%M),R%d",
"BIC%C%S", armdps, 0, "(R%s%hR%M),R%n,R%d",
"MVN%C%S", armdps, 0, "(R%s%hR%M),R%d",
/* 48 */
"AND%C%S", armdpi, 0, "$#%i,R%n,R%d",
"EOR%C%S", armdpi, 0, "$#%i,R%n,R%d",
"SUB%C%S", armdpi, 0, "$#%i,R%n,R%d",
"RSB%C%S", armdpi, 0, "$#%i,R%n,R%d",
"ADD%C%S", armdpi, armfadd, "$#%i,R%n,R%d",
"ADC%C%S", armdpi, 0, "$#%i,R%n,R%d",
"SBC%C%S", armdpi, 0, "$#%i,R%n,R%d",
"RSC%C%S", armdpi, 0, "$#%i,R%n,R%d",
"TST%C%S", armdpi, 0, "$#%i,R%n",
"TEQ%C%S", armdpi, 0, "$#%i,R%n",
"CMP%C%S", armdpi, 0, "$#%i,R%n",
"CMN%C%S", armdpi, 0, "$#%i,R%n",
"ORR%C%S", armdpi, 0, "$#%i,R%n,R%d",
"MOVW%C%S", armdpi, armfmov, "$#%i,R%d",
"BIC%C%S", armdpi, 0, "$#%i,R%n,R%d",
"MVN%C%S", armdpi, 0, "$#%i,R%d",
/* 48+16 */
"MUL%C%S", armdpi, 0, "R%s,R%M,R%n",
"MULA%C%S", armdpi, 0, "R%s,R%M,R%n,R%d",
"SWPW", armdpi, 0, "R%s,(R%n),R%d",
"SWPB", armdpi, 0, "R%s,(R%n),R%d",
/* 48+16+4 */
"MOV%u%C%p", armhwby, 0, "R%d,(R%n%UR%M)",
"MOV%u%C%p", armhwby, 0, "R%d,%I",
"MOV%u%C%p", armhwby, armfmov, "(R%n%UR%M),R%d",
"MOV%u%C%p", armhwby, armfmov, "%I,R%d",
/* 48+24 */
"MOVW%C%p", armsdti, 0, "R%d,%I",
"MOVB%C%p", armsdti, 0, "R%d,%I",
"MOVW%C%p", armsdti, armfmov, "%I,R%d",
"MOVBU%C%p", armsdti, armfmov, "%I,R%d",
"MOVW%C%p", armsdts, 0, "R%d,(R%s%h%m)(R%n)",
"MOVB%C%p", armsdts, 0, "R%d,(R%s%h%m)(R%n)",
"MOVW%C%p", armsdts, armfmov, "(R%s%h%m)(R%n),R%d",
"MOVBU%C%p", armsdts, armfmov, "(R%s%h%m)(R%n),R%d",
"MOVM%C%P%a", armbdt, armfmovm, "[%r],(R%n)",
"MOVM%C%P%a", armbdt, armfmovm, "(R%n),[%r]",
"B%C", armb, armfbranch, "%b",
"BL%C", armb, armfbranch, "%b",
"CDP%C", armco, 0, "",
"CDP%C", armco, 0, "",
"MCR%C", armco, 0, "",
"MRC%C", armco, 0, "",
"UNK", armunk, 0, "",
};
static void
gaddr(Instr *i)
{
*i->curr++ = '$';
i->curr += gsymoff(i->curr, i->end-i->curr, i->imm, CANY);
}
static char *mode[] = { 0, "IA", "DB", "IB" };
static char *pw[] = { "P", "PW", 0, "W" };
static char *sw[] = { 0, "W", "S", "SW" };
static void
format(char *mnemonic, Instr *i, char *f)
{
int j, k, m, n;
int g;
char *fmt;
if(mnemonic)
format(0, i, mnemonic);
if(f == 0)
return;
if(mnemonic)
if(i->curr < i->end)
*i->curr++ = '\t';
for ( ; *f && i->curr < i->end; f++) {
if(*f != '%') {
*i->curr++ = *f;
continue;
}
switch (*++f) {
case 'C': /* .CONDITION */
if(cond[i->cond])
bprint(i, ".%s", cond[i->cond]);
break;
case 'S': /* .STORE */
if(i->store)
bprint(i, ".S");
break;
case 'P': /* P & U bits for block move */
n = (i->w >>23) & 0x3;
if (mode[n])
bprint(i, ".%s", mode[n]);
break;
case 'p': /* P & W bits for single data xfer*/
if (pw[i->store])
bprint(i, ".%s", pw[i->store]);
break;
case 'a': /* S & W bits for single data xfer*/
if (sw[i->store])
bprint(i, ".%s", sw[i->store]);
break;
case 's':
bprint(i, "%d", i->rs & 0xf);
break;
case 'M':
bprint(i, "%d", (i->w>>8) & 0xf);
break;
case 'm':
bprint(i, "%d", (i->w>>7) & 0x1f);
break;
case 'h':
bprint(i, shtype[(i->w>>5) & 0x3]);
break;
case 'u': /* Signed/unsigned Byte/Halfword */
bprint(i, hb[(i->w>>5) & 0x3]);
break;
case 'I':
if (i->rn == 13) {
if (plocal(i))
break;
}
g = 0;
fmt = "#%lx(R%d)";
if (i->rn == 15) {
/* convert load of offset(PC) to a load immediate */
uint32 x;
if (get4(i->map, i->addr+i->imm+8, &x) > 0)
{
i->imm = (int32)x;
g = 1;
fmt = "";
}
}
if (mach->sb)
{
if (i->rd == 11) {
uint32 nxti;
if (get4(i->map, i->addr+4, &nxti) > 0) {
if ((nxti & 0x0e0f0fff) == 0x060c000b) {
i->imm += mach->sb;
g = 1;
fmt = "-SB";
}
}
}
if (i->rn == 12)
{
i->imm += mach->sb;
g = 1;
fmt = "-SB(SB)";
}
}
if (g)
{
gaddr(i);
bprint(i, fmt, i->rn);
}
else
bprint(i, fmt, i->imm, i->rn);
break;
case 'U': /* Add/subtract from base */
bprint(i, addsub[(i->w >> 23) & 1]);
break;
case 'n':
bprint(i, "%d", i->rn);
break;
case 'd':
bprint(i, "%d", i->rd);
break;
case 'i':
bprint(i, "%lux", i->imm);
break;
case 'b':
i->curr += symoff(i->curr, i->end-i->curr,
i->imm, CTEXT);
break;
case 'g':
i->curr += gsymoff(i->curr, i->end-i->curr,
i->imm, CANY);
break;
case 'r':
n = i->imm&0xffff;
j = 0;
k = 0;
while(n) {
m = j;
while(n&0x1) {
j++;
n >>= 1;
}
if(j != m) {
if(k)
bprint(i, ",");
if(j == m+1)
bprint(i, "R%d", m);
else
bprint(i, "R%d-R%d", m, j-1);
k = 1;
}
j++;
n >>= 1;
}
break;
case '\0':
*i->curr++ = '%';
return;
default:
bprint(i, "%%%c", *f);
break;
}
}
*i->curr = 0;
}
static int
printins(Map *map, ulong pc, char *buf, int n)
{
Instr i;
i.curr = buf;
i.end = buf+n-1;
if(decode(map, pc, &i) < 0)
return -1;
(*opcodes[i.op].fmt)(&opcodes[i.op], &i);
return 4;
}
static int
arminst(Map *map, uvlong pc, char modifier, char *buf, int n)
{
USED(modifier);
return printins(map, pc, buf, n);
}
static int
armdas(Map *map, uvlong pc, char *buf, int n)
{
Instr i;
i.curr = buf;
i.end = buf+n;
if(decode(map, pc, &i) < 0)
return -1;
if(i.end-i.curr > 8)
i.curr = _hexify(buf, i.w, 7);
*i.curr = 0;
return 4;
}
static int
arminstlen(Map *map, uvlong pc)
{
Instr i;
if(decode(map, pc, &i) < 0)
return -1;
return 4;
}
static int
armfoll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
{
ulong d;
Instr i;
if(decode(map, pc, &i) < 0)
return -1;
if(opcodes[i.op].foll) {
d = (*opcodes[i.op].foll)(map, rget, &i, pc);
if(d == -1)
return -1;
} else
d = pc+4;
foll[0] = d;
return 1;
}
// 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.h>
#define REGOFF(x) offsetof(struct UregAmd64, x)
#define REGSIZE sizeof(struct UregAmd64)
#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/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 <libc.h>
#include <bio.h>
#include <ureg_x86.h>
#include <mach.h>
#define REGOFF(x) (uintptr)(&((struct Ureg386 *) 0)->x)
#define PC REGOFF(pc)
#define SP REGOFF(sp)
#define AX REGOFF(ax)
#define REGSIZE sizeof(struct Ureg386)
#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 */
0xFFFFFFFFULL, /* 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.h>
#include <ureg_amd64.h>
#include <ureg_x86.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 GOSTARTSYM[] = "sys·goexit";
static char PROFSYM[] = "_mainp";
static char FRAMENAME[] = ".frame";
static char LESSSTACK[] = "sys·lessstack";
static char MORESTACK[] = "sys·morestack";
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 int32 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)
{
uint32 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 %d", c);
return buf;
} else
return excname[c];
}
static int
i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
int i;
uvlong osp, pc1;
Symbol s, f, s1;
extern Mach mamd64;
int isamd64;
uvlong g, m, lessstack, morestack, stktop;
isamd64 = (mach == &mamd64);
// ../pkg/runtime/runtime.h
// G is
// byte* stackguard
// byte* stackbase (= Stktop*)
// TODO(rsc): Need some way to get at the g for other threads.
// Probably need to pass it into the trace function.
g = 0;
if(isamd64)
geta(map, offsetof(struct UregAmd64, r15), &g);
else {
// TODO(rsc): How to fetch g on 386?
}
stktop = 0;
if(g != 0)
geta(map, g+1*mach->szaddr, &stktop);
lessstack = 0;
if(lookup(0, LESSSTACK, &s))
lessstack = s.value;
morestack = 0;
if(lookup(0, MORESTACK, &s))
morestack = s.value;
USED(link);
osp = 0;
i = 0;
for(;;) {
if(!findsym(pc, CTEXT, &s)) {
// check for closure return sequence
uchar buf[8], *p;
if(get1(map, pc, buf, 8) < 0)
break;
// ADDQ $xxx, SP; RET
p = buf;
if(mach == &mamd64) {
if(p[0] != 0x48)
break;
p++;
}
if(p[0] != 0x81 || p[1] != 0xc4 || p[6] != 0xc3)
break;
sp += p[2] | (p[3]<<8) | (p[4]<<16) | (p[5]<<24);
if(geta(map, sp, &pc) < 0)
break;
sp += mach->szaddr;
continue;
}
if (osp == sp)
break;
osp = sp;
if(strcmp(STARTSYM, s.name) == 0 ||
strcmp(GOSTARTSYM, s.name) == 0 ||
strcmp(PROFSYM, s.name) == 0)
break;
if(s.value == morestack) {
if (0) {
// This code is old and won't work anymore.
// But no one uses it anyway.
// Leave it obviously broken until someone needs it.
// In the middle of morestack.
// Caller is m->morepc.
// Caller's caller is in m->morearg.
// TODO(rsc): 386
geta(map, offsetof(struct UregAmd64, r14), &m);
pc = 0;
sp = 0;
pc1 = 0;
s1 = s;
memset(&s, 0, sizeof s);
geta(map, m+1*mach->szaddr, &pc1); // m->morepc
geta(map, m+2*mach->szaddr, &sp); // m->morebuf.sp
geta(map, m+3*mach->szaddr, &pc); // m->morebuf.pc
findsym(pc1, CTEXT, &s);
(*trace)(map, pc1, sp-mach->szaddr, &s1); // morestack symbol; caller's PC/SP
// caller's caller
s1 = s;
findsym(pc, CTEXT, &s);
(*trace)(map, pc, sp, &s1); // morestack's caller; caller's caller's PC/SP
continue;
} else {
werrstr("morestack not implemented correctly");
return -1;
}
}
if(pc == lessstack) {
// ../pkg/runtime/runtime.h
// Stktop is
// byte* stackguard
// byte* stackbase
// Gobuf gobuf
// byte* sp;
// byte* pc;
// G* g;
if(!isamd64)
fprint(2, "warning: cannot unwind stack split on 386\n");
if(stktop == 0)
break;
pc = 0;
sp = 0;
geta(map, stktop+2*mach->szaddr, &sp);
geta(map, stktop+3*mach->szaddr, &pc);
geta(map, stktop+1*mach->szaddr, &stktop);
(*trace)(map, pc, sp, &s1);
continue;
}
s1 = s;
pc1 = 0;
if(pc != s.value) { /* not at first instruction */
if(findlocal(&s, FRAMENAME, &f) == 0)
break;
geta(map, sp, &pc1);
sp += f.value-mach->szaddr;
}
if(geta(map, sp, &pc) < 0)
break;
// If PC is not valid, assume we caught the function
// before it moved the stack pointer down or perhaps
// after it moved the stack pointer back up.
// Try the PC we'd have gotten without the stack
// pointer adjustment above (pc != s.value).
// This only matters for the first frame, and it is only
// a heuristic, but it does help.
if(!findsym(pc, CTEXT, &s) || strcmp(s.name, "etext") == 0)
pc = pc1;
if(pc == 0)
break;
if(pc != lessstack)
(*trace)(map, pc, sp, &s1);
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 */
schar index; /* bits 3-5 of SIB */
schar 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 */
uint32 disp; /* displacement */
uint32 imm; /* immediate */
uint32 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 */
/* be careful: some unix system headers #define R8, R9, etc */
AMD64_R8,
AMD64_R9,
AMD64_R10,
AMD64_R11,
AMD64_R12,
AMD64_R13,
AMD64_R14,
AMD64_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 int32 R/M field with register (/r) */
RMB, /* Byte R/M field with register (/r) */
RMOP, /* Word or int32 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) */
Op_R0, /* Base reg of Mod R/M is literal 0x00 */
Op_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" },
};
static Optable optab0F18[4]=
{
[0x00] = { 0,0, "PREFETCHNTA %e" },
[0x01] = { 0,0, "PREFECTCH0 %e" },
[0x02] = { 0,0, "PREFECTCH1 %e" },
[0x03] = { 0,0, "PREFECTCH2 %e" },
};
/* 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 optab660F38[256]=
{
[0x00] = { RM,0, "PSHUFB %x,%X" },
[0xdc] = { RM,0, "AESENC %x,%X" },
[0xdb] = { RM,0, "AESIMC %x,%X," },
[0xdd] = { RM,0, "AESENCLAST %x,%X" },
[0xde] = { RM,0, "AESDEC %x,%X" },
[0xdf] = { RM,0, "AESDECLAST %x,%X" },
};
static Optable optab660F3A[256]=
{
[0x22] = { RM,Ib, "PINSR%S %i,%e,%X" },
[0xdf] = { RM,Ib, "AESKEYGENASSIST %i,%x,%X" },
};
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" },
[0x38] = { AUX,0, optab660F38 },
[0x3A] = { AUX,0, optab660F3A },
[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 optabF20F38[256]=
{
[0xf0] = { RM,0, "CRC32B %e, %r" },
[0xf1] = { RM,0, "CRC32%S %e, %r" },
};
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" },
[0x38] = { AUX,0, optabF20F38 },
[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" },
[0x18] = { RMOP,0, optab0F18 },
[0x1F] = { RM,0, "NOP%S %e" },
[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" },
[0x77] = { 0,0, "EMMS" },
[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 %x,%X,%#i" },
[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" },
[0x08] = { 0,0, "FCMOVCS %f,F0" },
[0x09] = { 0,0, "FCMOVEQ %f,F0" },
[0x0a] = { 0,0, "FCMOVLS %f,F0" },
[0x0b] = { 0,0, "FCMOVUN %f,F0" },
[0x0d] = { Op_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" },
[0x08] = { 0,0, "FCMOVCC F0,F0" }, /* Mod R/M = 11xx xxxx*/
[0x09] = { 0,0, "FCMOVCC F1,F0" },
[0x0a] = { 0,0, "FCMOVCC F2,F0" },
[0x0b] = { 0,0, "FCMOVCC F3,F0" },
[0x0c] = { 0,0, "FCMOVCC F4,F0" },
[0x0d] = { 0,0, "FCMOVCC F5,F0" },
[0x0e] = { 0,0, "FCMOVCC F6,F0" },
[0x0f] = { 0,0, "FCMOVCC F7,F0" },
[0x10] = { 0,0, "FCMOVNE F0,F0" },
[0x11] = { 0,0, "FCMOVNE F1,F0" },
[0x12] = { 0,0, "FCMOVNE F2,F0" },
[0x13] = { 0,0, "FCMOVNE F3,F0" },
[0x14] = { 0,0, "FCMOVNE F4,F0" },
[0x15] = { 0,0, "FCMOVNE F5,F0" },
[0x16] = { 0,0, "FCMOVNE F6,F0" },
[0x17] = { 0,0, "FCMOVNE F7,F0" },
[0x18] = { 0,0, "FCMOVHI F0,F0" },
[0x19] = { 0,0, "FCMOVHI F1,F0" },
[0x1a] = { 0,0, "FCMOVHI F2,F0" },
[0x1b] = { 0,0, "FCMOVHI F3,F0" },
[0x1c] = { 0,0, "FCMOVHI F4,F0" },
[0x1d] = { 0,0, "FCMOVHI F5,F0" },
[0x1e] = { 0,0, "FCMOVHI F6,F0" },
[0x1f] = { 0,0, "FCMOVHI F7,F0" },
[0x20] = { 0,0, "FCMOVNU F0,F0" },
[0x21] = { 0,0, "FCMOVNU F1,F0" },
[0x22] = { 0,0, "FCMOVNU F2,F0" },
[0x23] = { 0,0, "FCMOVNU F3,F0" },
[0x24] = { 0,0, "FCMOVNU F4,F0" },
[0x25] = { 0,0, "FCMOVNU F5,F0" },
[0x26] = { 0,0, "FCMOVNU F6,F0" },
[0x27] = { 0,0, "FCMOVNU F7,F0" },
[0x2a] = { 0,0, "FCLEX" },
[0x2b] = { 0,0, "FINIT" },
[0x30] = { 0,0, "FUCOMI F0,F0" },
[0x31] = { 0,0, "FUCOMI F1,F0" },
[0x32] = { 0,0, "FUCOMI F2,F0" },
[0x33] = { 0,0, "FUCOMI F3,F0" },
[0x34] = { 0,0, "FUCOMI F4,F0" },
[0x35] = { 0,0, "FUCOMI F5,F0" },
[0x36] = { 0,0, "FUCOMI F6,F0" },
[0x37] = { 0,0, "FUCOMI F7,F0" },
[0x38] = { 0,0, "FCOMI F0,F0" },
[0x39] = { 0,0, "FCOMI F1,F0" },
[0x3a] = { 0,0, "FCOMI F2,F0" },
[0x3b] = { 0,0, "FCOMI F3,F0" },
[0x3c] = { 0,0, "FCOMI F4,F0" },
[0x3d] = { 0,0, "FCOMI F5,F0" },
[0x3e] = { 0,0, "FCOMI F6,F0" },
[0x3f] = { 0,0, "FCOMI F7,F0" },
};
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] = { Op_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] = { Op_R0,0, "FSTSW %OAX" },
[0x0d] = { 0,0, "FUCOMIP F0,%f" },
[0x0e] = { 0,0, "FCOMIP F0,%f" },
};
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+2] =
{
[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 %e,%r" },
[0x101] = { RM,0, "MOVLQZX %e,%r" },
};
/*
* 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, uint32 *lp)
{
ushort s;
int32 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)
{
uint32 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){
if(ip->rex&REXW)
op = &obase[0x100]; /* MOVLQSX */
else
op = &obase[0x101]; /* MOVLQZX */
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 = (int32)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 = (int32)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) {
uint32 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 Op_R0: /* base register must be R0 */
if (ip->base != 0)
goto badop;
break;
case Op_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;
ip->prefix = 0; /* discard REPNE */
break;
case 0xF3:
op = optabF30F;
ip->prefix = 0; /* discard REP */
break;
default:
op = nil;
break;
}
if(op != nil && op[c].proto != nil)
obase = op;
/* 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;
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 */
[AMD64_R8] = "R8",
[AMD64_R9] = "R9",
[AMD64_R10] = "R10",
[AMD64_R11] = "R11",
[AMD64_R12] = "R12",
[AMD64_R13] = "R13",
[AMD64_R14] = "R14",
[AMD64_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;
int32 offset;
Symbol s;
char *reg;
offset = ip->disp;
if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
bprint(ip, "%ux(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, "%ux%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, int32 w, int32 val)
{
Symbol next, tmp;
int32 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;
int32 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+%#ux(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)
{
int base;
base = ip->base;
if(base >= 0 && (ip->rex & REXB))
base += 8;
if (ip->mod == 3) {
if (ip->osize == 'B')
bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]);
else
bprint(ip, "%s%s", ANAME(ip), reg[base]);
return;
}
if (ip->segment)
bprint(ip, ip->segment);
if (ip->asize == 'E' && base == SP)
plocal(ip);
else {
if (ip->base < 0)
immediate(ip, ip->disp);
else {
bprint(ip, "%ux", 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)
{
int sharp;
vlong v;
if (ip->prefix)
bprint(ip, "%s ", ip->prefix);
for (; *fmt && ip->curr < ip->end; fmt++) {
if (*fmt != '%'){
*ip->curr++ = *fmt;
continue;
}
sharp = 0;
if(*++fmt == '#') {
sharp = 1;
++fmt;
}
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':
if(!sharp)
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:%ux", 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 uint32 ip->imm.
*/
v = (int32)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 = (int32)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;
}
# Copyright 2012 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../Make.dist
// 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.h>
static int mget(Map*, uvlong, void*, int);
static int mput(Map*, uvlong, void*, int);
static Seg* reloc(Map*, uvlong, vlong*);
/*
* routines to get/put various types
*/
int
geta(Map *map, uvlong addr, uvlong *x)
{
uint32 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, uint32 *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, uint32 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
mget(Map *map, uvlong addr, void *buf, int size)
{
uvlong off;
Seg *s;
s = reloc(map, addr, (vlong*)&off);
if (!s)
return -1;
if (s->rw == nil) {
werrstr("unreadable map");
return -1;
}
return s->rw(map, s, off, buf, size, 1);
}
static int
mput(Map *map, uvlong addr, void *buf, int size)
{
vlong off;
Seg *s;
s = reloc(map, addr, &off);
if (!s)
return -1;
if (s->rw == nil) {
werrstr("unwritable map");
return -1;
}
return s->rw(map, s, off, buf, size, 0);
}
/*
* convert address to file offset; returns nonzero if ok
*/
static Seg*
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;
}
// 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.
#define __DARWIN_UNIX03 0
#include <u.h>
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <mach/mach.h>
#include <mach/mach_traps.h>
#include <errno.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#include <ureg_x86.h>
#include <ureg_amd64.h>
#undef waitpid /* want Unix waitpid, not Plan 9 */
extern mach_port_t mach_reply_port(void); // should be in system headers, is not
// Mach-error wrapper.
// Takes a mach return code and converts it into 0 / -1,
// setting errstr when it returns -1.
static struct {
int code;
char *name;
} macherr[] = {
KERN_INVALID_ADDRESS, "invalid address",
KERN_PROTECTION_FAILURE, "protection failure",
KERN_NO_SPACE, "no space",
KERN_INVALID_ARGUMENT, "invalid argument",
KERN_FAILURE, "failure",
KERN_RESOURCE_SHORTAGE, "resource shortage",
KERN_NOT_RECEIVER, "not receiver",
KERN_NO_ACCESS, "no access",
KERN_MEMORY_FAILURE, "memory failure",
KERN_MEMORY_ERROR, "memory error",
KERN_ALREADY_IN_SET, "already in set",
KERN_NOT_IN_SET, "not in set",
KERN_NAME_EXISTS, "name exists",
KERN_ABORTED, "aborted",
KERN_INVALID_NAME, "invalid name",
KERN_INVALID_TASK, "invalid task",
KERN_INVALID_RIGHT, "invalid right",
KERN_INVALID_VALUE, "invalid value",
KERN_UREFS_OVERFLOW, "urefs overflow",
KERN_INVALID_CAPABILITY, "invalid capability",
KERN_RIGHT_EXISTS, "right exists",
KERN_INVALID_HOST, "invalid host",
KERN_MEMORY_PRESENT, "memory present",
KERN_MEMORY_DATA_MOVED, "memory data moved",
KERN_MEMORY_RESTART_COPY, "memory restart copy",
KERN_INVALID_PROCESSOR_SET, "invalid processor set",
KERN_POLICY_LIMIT, "policy limit",
KERN_INVALID_POLICY, "invalid policy",
KERN_INVALID_OBJECT, "invalid object",
KERN_ALREADY_WAITING, "already waiting",
KERN_DEFAULT_SET, "default set",
KERN_EXCEPTION_PROTECTED, "exception protected",
KERN_INVALID_LEDGER, "invalid ledger",
KERN_INVALID_MEMORY_CONTROL, "invalid memory control",
KERN_INVALID_SECURITY, "invalid security",
KERN_NOT_DEPRESSED, "not depressed",
KERN_TERMINATED, "terminated",
KERN_LOCK_SET_DESTROYED, "lock set destroyed",
KERN_LOCK_UNSTABLE, "lock unstable",
KERN_LOCK_OWNED, "lock owned",
KERN_LOCK_OWNED_SELF, "lock owned self",
KERN_SEMAPHORE_DESTROYED, "semaphore destroyed",
KERN_RPC_SERVER_TERMINATED, "rpc server terminated",
KERN_RPC_TERMINATE_ORPHAN, "rpc terminate orphan",
KERN_RPC_CONTINUE_ORPHAN, "rpc continue orphan",
KERN_NOT_SUPPORTED, "not supported",
KERN_NODE_DOWN, "node down",
KERN_NOT_WAITING, "not waiting",
KERN_OPERATION_TIMED_OUT, "operation timed out",
KERN_RETURN_MAX, "return max",
MACH_SEND_IN_PROGRESS, "send in progress",
MACH_SEND_INVALID_DATA, "send invalid data",
MACH_SEND_INVALID_DEST, "send invalid dest",
MACH_SEND_TIMED_OUT, "send timed out",
MACH_SEND_INTERRUPTED, "send interrupted",
MACH_SEND_MSG_TOO_SMALL, "send msg too small",
MACH_SEND_INVALID_REPLY, "send invalid reply",
MACH_SEND_INVALID_RIGHT, "send invalid right",
MACH_SEND_INVALID_NOTIFY, "send invalid notify",
MACH_SEND_INVALID_MEMORY, "send invalid memory",
MACH_SEND_NO_BUFFER, "send no buffer",
MACH_SEND_TOO_LARGE, "send too large",
MACH_SEND_INVALID_TYPE, "send invalid type",
MACH_SEND_INVALID_HEADER, "send invalid header",
MACH_SEND_INVALID_TRAILER, "send invalid trailer",
MACH_SEND_INVALID_RT_OOL_SIZE, "send invalid rt ool size",
MACH_RCV_IN_PROGRESS, "rcv in progress",
MACH_RCV_INVALID_NAME, "rcv invalid name",
MACH_RCV_TIMED_OUT, "rcv timed out",
MACH_RCV_TOO_LARGE, "rcv too large",
MACH_RCV_INTERRUPTED, "rcv interrupted",
MACH_RCV_PORT_CHANGED, "rcv port changed",
MACH_RCV_INVALID_NOTIFY, "rcv invalid notify",
MACH_RCV_INVALID_DATA, "rcv invalid data",
MACH_RCV_PORT_DIED, "rcv port died",
MACH_RCV_IN_SET, "rcv in set",
MACH_RCV_HEADER_ERROR, "rcv header error",
MACH_RCV_BODY_ERROR, "rcv body error",
MACH_RCV_INVALID_TYPE, "rcv invalid type",
MACH_RCV_SCATTER_SMALL, "rcv scatter small",
MACH_RCV_INVALID_TRAILER, "rcv invalid trailer",
MACH_RCV_IN_PROGRESS_TIMED, "rcv in progress timed",
MIG_TYPE_ERROR, "mig type error",
MIG_REPLY_MISMATCH, "mig reply mismatch",
MIG_REMOTE_ERROR, "mig remote error",
MIG_BAD_ID, "mig bad id",
MIG_BAD_ARGUMENTS, "mig bad arguments",
MIG_NO_REPLY, "mig no reply",
MIG_EXCEPTION, "mig exception",
MIG_ARRAY_TOO_LARGE, "mig array too large",
MIG_SERVER_DIED, "server died",
MIG_TRAILER_ERROR, "trailer has an unknown format",
};
static int
me(kern_return_t r)
{
int i;
if(r == 0)
return 0;
for(i=0; i<nelem(macherr); i++){
if(r == macherr[i].code){
werrstr("mach: %s", macherr[i].name);
return -1;
}
}
werrstr("mach error %#x", r);
return -1;
}
// Plan 9 and Linux do not distinguish between
// process ids and thread ids, so the interface here doesn't either.
// Unfortunately, Mach has three kinds of identifiers: process ids,
// handles to tasks (processes), and handles to threads within a
// process. All of them are small integers.
//
// To accommodate Mach, we employ a clumsy hack: in this interface,
// if you pass in a positive number, that's a process id.
// If you pass in a negative number, that identifies a thread that
// has been previously returned by procthreadpids (it indexes
// into the Thread table below).
// Table of threads we have handles for.
typedef struct Thread Thread;
struct Thread
{
int pid;
mach_port_t task;
mach_port_t thread;
int stopped;
int exc;
int code[10];
Map *map;
};
static Thread thr[1000];
static int nthr;
static pthread_mutex_t mu;
static pthread_cond_t cond;
static void* excthread(void*);
static void* waitthread(void*);
static mach_port_t excport;
enum {
ExcMask = EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION |
EXC_MASK_ARITHMETIC |
EXC_MASK_BREAKPOINT |
EXC_MASK_SOFTWARE
};
// Add process pid to the thread table.
// If it's already there, don't re-add it (unless force != 0).
static Thread*
addpid(int pid, int force)
{
int i, j;
mach_port_t task;
mach_port_t *thread;
uint nthread;
Thread *ret;
static int first = 1;
if(first){
// Allocate a port for exception messages and
// send all thread exceptions to that port.
// The excthread reads that port and signals
// us if we are waiting on that thread.
pthread_t p;
int err;
excport = mach_reply_port();
pthread_mutex_init(&mu, nil);
pthread_cond_init(&cond, nil);
err = pthread_create(&p, nil, excthread, nil);
if (err != 0) {
fprint(2, "pthread_create failed: %s\n", strerror(err));
abort();
}
err = pthread_create(&p, nil, waitthread, (void*)(uintptr)pid);
if (err != 0) {
fprint(2, "pthread_create failed: %s\n", strerror(err));
abort();
}
first = 0;
}
if(!force){
for(i=0; i<nthr; i++)
if(thr[i].pid == pid)
return &thr[i];
}
if(me(task_for_pid(mach_task_self(), pid, &task)) < 0)
return nil;
if(me(task_threads(task, &thread, &nthread)) < 0)
return nil;
mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND);
if(me(task_set_exception_ports(task, ExcMask,
excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){
fprint(2, "warning: cannot set excport: %r\n");
}
ret = nil;
for(j=0; j<nthread; j++){
if(force){
// If we're forcing a refresh, don't re-add existing threads.
for(i=0; i<nthr; i++)
if(thr[i].pid == pid && thr[i].thread == thread[j]){
if(ret == nil)
ret = &thr[i];
goto skip;
}
}
if(nthr >= nelem(thr))
return nil;
// TODO: We probably should save the old thread exception
// ports for each bit and then put them back when we exit.
// Probably the BSD signal handlers have put stuff there.
mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND);
if(me(thread_set_exception_ports(thread[j], ExcMask,
excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){
fprint(2, "warning: cannot set excport: %r\n");
}
thr[nthr].pid = pid;
thr[nthr].task = task;
thr[nthr].thread = thread[j];
if(ret == nil)
ret = &thr[nthr];
nthr++;
skip:;
}
return ret;
}
static Thread*
idtotable(int id)
{
if(id >= 0)
return addpid(id, 1);
id = -(id+1);
if(id >= nthr)
return nil;
return &thr[id];
}
/*
static int
idtopid(int id)
{
Thread *t;
if((t = idtotable(id)) == nil)
return -1;
return t->pid;
}
*/
static mach_port_t
idtotask(int id)
{
Thread *t;
if((t = idtotable(id)) == nil)
return -1;
return t->task;
}
static mach_port_t
idtothread(int id)
{
Thread *t;
if((t = idtotable(id)) == nil)
return -1;
return t->thread;
}
static int machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr);
static int machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr);
Map*
attachproc(int id, Fhdr *fp)
{
Thread *t;
Map *map;
if((t = idtotable(id)) == nil)
return nil;
if(t->map)
return t->map;
map = newmap(0, 4);
if(!map)
return nil;
map->pid = -((t-thr) + 1);
if(mach->regsize)
setmap(map, -1, 0, mach->regsize, 0, "regs", machregrw);
setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", machsegrw);
setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", machsegrw);
t->map = map;
return map;
}
// Return list of ids for threads in id.
int
procthreadpids(int id, int *out, int nout)
{
Thread *t;
int i, n, pid;
t = idtotable(id);
if(t == nil)
return -1;
pid = t->pid;
addpid(pid, 1); // force refresh of thread list
n = 0;
for(i=0; i<nthr; i++) {
if(thr[i].pid == pid) {
if(n < nout)
out[n] = -(i+1);
n++;
}
}
return n;
}
// Detach from proc.
// TODO(rsc): Perhaps should unsuspend any threads and clean-up the table.
void
detachproc(Map *m)
{
free(m);
}
// Should return array of pending signals (notes)
// but don't know how to do that on OS X.
int
procnotes(int pid, char ***pnotes)
{
USED(pid);
*pnotes = 0;
return 0;
}
// There must be a way to do this. Gdb can do it.
// But I don't see, in the Apple gdb sources, how.
char*
proctextfile(int pid)
{
USED(pid);
return nil;
}
// Read/write from a Mach data segment.
static int
machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
{
mach_port_t task;
int r;
USED(seg);
task = idtotask(map->pid);
if(task == -1)
return -1;
if(isr){
vm_size_t nn;
nn = n;
if(me(vm_read_overwrite(task, addr, n, (uintptr)v, &nn)) < 0) {
fprint(2, "vm_read_overwrite %#llux %d to %p: %r\n", addr, n, v);
return -1;
}
return nn;
}else{
r = vm_write(task, addr, (uintptr)v, n);
if(r == KERN_INVALID_ADDRESS){
// Happens when writing to text segment.
// Change protections.
if(me(vm_protect(task, addr, n, 0, VM_PROT_WRITE|VM_PROT_READ|VM_PROT_EXECUTE)) < 0){
fprint(2, "vm_protect: %s\n", r);
return -1;
}
r = vm_write(task, addr, (uintptr)v, n);
}
if(r != 0){
me(r);
return -1;
}
return n;
}
}
// Convert Ureg offset to x86_thread_state32_t offset.
static int
go2darwin32(uvlong addr)
{
switch(addr){
case offsetof(Ureg386, ax):
return offsetof(x86_thread_state32_t, eax);
case offsetof(Ureg386, bx):
return offsetof(x86_thread_state32_t, ebx);
case offsetof(Ureg386, cx):
return offsetof(x86_thread_state32_t, ecx);
case offsetof(Ureg386, dx):
return offsetof(x86_thread_state32_t, edx);
case offsetof(Ureg386, si):
return offsetof(x86_thread_state32_t, esi);
case offsetof(Ureg386, di):
return offsetof(x86_thread_state32_t, edi);
case offsetof(Ureg386, bp):
return offsetof(x86_thread_state32_t, ebp);
case offsetof(Ureg386, fs):
return offsetof(x86_thread_state32_t, fs);
case offsetof(Ureg386, gs):
return offsetof(x86_thread_state32_t, gs);
case offsetof(Ureg386, pc):
return offsetof(x86_thread_state32_t, eip);
case offsetof(Ureg386, cs):
return offsetof(x86_thread_state32_t, cs);
case offsetof(Ureg386, flags):
return offsetof(x86_thread_state32_t, eflags);
case offsetof(Ureg386, sp):
return offsetof(x86_thread_state32_t, esp);
}
return -1;
}
// Convert Ureg offset to x86_thread_state64_t offset.
static int
go2darwin64(uvlong addr)
{
switch(addr){
case offsetof(UregAmd64, ax):
return offsetof(x86_thread_state64_t, rax);
case offsetof(UregAmd64, bx):
return offsetof(x86_thread_state64_t, rbx);
case offsetof(UregAmd64, cx):
return offsetof(x86_thread_state64_t, rcx);
case offsetof(UregAmd64, dx):
return offsetof(x86_thread_state64_t, rdx);
case offsetof(UregAmd64, si):
return offsetof(x86_thread_state64_t, rsi);
case offsetof(UregAmd64, di):
return offsetof(x86_thread_state64_t, rdi);
case offsetof(UregAmd64, bp):
return offsetof(x86_thread_state64_t, rbp);
case offsetof(UregAmd64, r8):
return offsetof(x86_thread_state64_t, r8);
case offsetof(UregAmd64, r9):
return offsetof(x86_thread_state64_t, r9);
case offsetof(UregAmd64, r10):
return offsetof(x86_thread_state64_t, r10);
case offsetof(UregAmd64, r11):
return offsetof(x86_thread_state64_t, r11);
case offsetof(UregAmd64, r12):
return offsetof(x86_thread_state64_t, r12);
case offsetof(UregAmd64, r13):
return offsetof(x86_thread_state64_t, r13);
case offsetof(UregAmd64, r14):
return offsetof(x86_thread_state64_t, r14);
case offsetof(UregAmd64, r15):
return offsetof(x86_thread_state64_t, r15);
case offsetof(UregAmd64, fs):
return offsetof(x86_thread_state64_t, fs);
case offsetof(UregAmd64, gs):
return offsetof(x86_thread_state64_t, gs);
case offsetof(UregAmd64, ip):
return offsetof(x86_thread_state64_t, rip);
case offsetof(UregAmd64, cs):
return offsetof(x86_thread_state64_t, cs);
case offsetof(UregAmd64, flags):
return offsetof(x86_thread_state64_t, rflags);
case offsetof(UregAmd64, sp):
return offsetof(x86_thread_state64_t, rsp);
}
return -1;
}
extern Mach mi386;
// Read/write from fake register segment.
static int
machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
{
uint nn, count, state;
mach_port_t thread;
int reg;
char buf[100];
union {
x86_thread_state64_t reg64;
x86_thread_state32_t reg32;
uchar p[1];
} u;
uchar *p;
USED(seg);
if(n > 8){
werrstr("asked for %d-byte register", n);
return -1;
}
thread = idtothread(map->pid);
if(thread == -1){
werrstr("no such id");
return -1;
}
if(mach == &mi386) {
count = x86_THREAD_STATE32_COUNT;
state = x86_THREAD_STATE32;
if((reg = go2darwin32(addr)) < 0 || reg+n > sizeof u){
if(isr){
memset(v, 0, n);
return 0;
}
werrstr("register %llud not available", addr);
return -1;
}
} else {
count = x86_THREAD_STATE64_COUNT;
state = x86_THREAD_STATE64;
if((reg = go2darwin64(addr)) < 0 || reg+n > sizeof u){
if(isr){
memset(v, 0, n);
return 0;
}
werrstr("register %llud not available", addr);
return -1;
}
}
if(!isr && me(thread_suspend(thread)) < 0){
werrstr("thread suspend %#x: %r", thread);
return -1;
}
nn = count;
if(me(thread_get_state(thread, state, (void*)u.p, &nn)) < 0){
if(!isr)
thread_resume(thread);
rerrstr(buf, sizeof buf);
if(strstr(buf, "send invalid dest") != nil)
werrstr("process exited");
else
werrstr("thread_get_state: %r");
return -1;
}
p = u.p+reg;
if(isr)
memmove(v, p, n);
else{
memmove(p, v, n);
nn = count;
if(me(thread_set_state(thread, state, (void*)u.p, nn)) < 0){
thread_resume(thread);
werrstr("thread_set_state: %r");
return -1;
}
if(me(thread_resume(thread)) < 0){
werrstr("thread_resume: %r");
return -1;
}
}
return 0;
}
enum
{
FLAGS_TF = 0x100 // x86 single-step processor flag
};
// Is thread t suspended?
static int
threadstopped(Thread *t)
{
struct thread_basic_info info;
uint size;
size = sizeof info;
if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &size)) < 0){
fprint(2, "threadstopped thread_info %#x: %r\n");
return 1;
}
return info.suspend_count > 0;
}
// If thread t is suspended, start it up again.
// If singlestep is set, only let it execute one instruction.
static int
threadstart(Thread *t, int singlestep)
{
int i;
uint n;
struct thread_basic_info info;
if(!threadstopped(t))
return 0;
// Set or clear the processor single-step flag, as appropriate.
if(mach == &mi386) {
x86_thread_state32_t regs;
n = x86_THREAD_STATE32_COUNT;
if(me(thread_get_state(t->thread, x86_THREAD_STATE32,
(thread_state_t)&regs,
&n)) < 0)
return -1;
if(singlestep)
regs.eflags |= FLAGS_TF;
else
regs.eflags &= ~FLAGS_TF;
if(me(thread_set_state(t->thread, x86_THREAD_STATE32,
(thread_state_t)&regs,
x86_THREAD_STATE32_COUNT)) < 0)
return -1;
} else {
x86_thread_state64_t regs;
n = x86_THREAD_STATE64_COUNT;
if(me(thread_get_state(t->thread, x86_THREAD_STATE64,
(thread_state_t)&regs,
&n)) < 0)
return -1;
if(singlestep)
regs.rflags |= FLAGS_TF;
else
regs.rflags &= ~FLAGS_TF;
if(me(thread_set_state(t->thread, x86_THREAD_STATE64,
(thread_state_t)&regs,
x86_THREAD_STATE64_COUNT)) < 0)
return -1;
}
// Run.
n = sizeof info;
if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &n)) < 0)
return -1;
for(i=0; i<info.suspend_count; i++)
if(me(thread_resume(t->thread)) < 0)
return -1;
return 0;
}
// Stop thread t.
static int
threadstop(Thread *t)
{
if(threadstopped(t))
return 0;
if(me(thread_suspend(t->thread)) < 0)
return -1;
return 0;
}
// Callback for exc_server below. Called when a thread we are
// watching has an exception like hitting a breakpoint.
kern_return_t
catch_exception_raise(mach_port_t eport, mach_port_t thread,
mach_port_t task, exception_type_t exception,
exception_data_t code, mach_msg_type_number_t ncode)
{
Thread *t;
int i;
USED(eport);
USED(task);
t = nil;
for(i=0; i<nthr; i++){
if(thr[i].thread == thread){
t = &thr[i];
goto havet;
}
}
if(nthr > 0)
addpid(thr[0].pid, 1);
for(i=0; i<nthr; i++){
if(thr[i].thread == thread){
t = &thr[i];
goto havet;
}
}
fprint(2, "did not find thread in catch_exception_raise\n");
return KERN_SUCCESS; // let thread continue
havet:
t->exc = exception;
if(ncode > nelem(t->code))
ncode = nelem(t->code);
memmove(t->code, code, ncode*sizeof t->code[0]);
// Suspend thread, so that we can look at it & restart it later.
if(me(thread_suspend(thread)) < 0)
fprint(2, "catch_exception_raise thread_suspend: %r\n");
// Synchronize with waitstop below.
pthread_mutex_lock(&mu);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mu);
return KERN_SUCCESS;
}
// Exception watching thread, started in addpid above.
static void*
excthread(void *v)
{
USED(v);
extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *);
mach_msg_server(exc_server, 2048, excport, 0);
return 0;
}
// Wait for pid to exit.
static int exited;
static void*
waitthread(void *v)
{
int pid, status;
pid = (int)(uintptr)v;
waitpid(pid, &status, 0);
exited = 1;
// Synchronize with waitstop below.
pthread_mutex_lock(&mu);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mu);
return nil;
}
// Wait for thread t to stop.
static int
waitstop(Thread *t)
{
pthread_mutex_lock(&mu);
while(!exited && !threadstopped(t))
pthread_cond_wait(&cond, &mu);
pthread_mutex_unlock(&mu);
return 0;
}
int
ctlproc(int id, char *msg)
{
Thread *t;
int status;
// Hang/attached dance is for debugging newly exec'ed programs.
// After fork, the child does ctlproc("hang") before exec,
// and the parent does ctlproc("attached") and then waitstop.
// Using these requires the BSD ptrace interface, unlike everything
// else we do, which uses only the Mach interface. Our goal here
// is to do as little as possible using ptrace and then flip over to Mach.
if(strcmp(msg, "hang") == 0)
return ptrace(PT_TRACE_ME, 0, 0, 0);
if(strcmp(msg, "attached") == 0){
// The pid "id" has done a ctlproc "hang" and then
// exec, so we should find it stoppped just before exec
// of the new program.
#undef waitpid
if(waitpid(id, &status, WUNTRACED) < 0){
fprint(2, "ctlproc attached waitpid: %r\n");
return -1;
}
if(WIFEXITED(status) || !WIFSTOPPED(status)){
fprint(2, "ctlproc attached: bad process state\n");
return -1;
}
// Find Mach thread for pid and suspend it.
t = addpid(id, 1);
if(t == nil) {
fprint(2, "ctlproc attached: addpid: %r\n");
return -1;
}
if(me(thread_suspend(t->thread)) < 0){
fprint(2, "ctlproc attached: thread_suspend: %r\n");
return -1;
}
// Let ptrace tell the process to keep going:
// then ptrace is out of the way and we're back in Mach land.
if(ptrace(PT_CONTINUE, id, (caddr_t)1, 0) < 0) {
fprint(2, "ctlproc attached: ptrace continue: %r\n");
return -1;
}
return 0;
}
// All the other control messages require a Thread structure.
if((t = idtotable(id)) == nil){
werrstr("no such thread");
return -1;
}
if(strcmp(msg, "kill") == 0)
return ptrace(PT_KILL, t->pid, 0, 0);
if(strcmp(msg, "start") == 0)
return threadstart(t, 0);
if(strcmp(msg, "stop") == 0)
return threadstop(t);
if(strcmp(msg, "startstop") == 0){
if(threadstart(t, 0) < 0)
return -1;
return waitstop(t);
}
if(strcmp(msg, "step") == 0){
if(threadstart(t, 1) < 0)
return -1;
return waitstop(t);
}
if(strcmp(msg, "waitstop") == 0)
return waitstop(t);
// sysstop not available on OS X
werrstr("unknown control message");
return -1;
}
char*
procstatus(int id)
{
Thread *t;
if((t = idtotable(id)) == nil)
return "gone!";
if(threadstopped(t))
return "Stopped";
return "Running";
}
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented in DragonFly");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented in DragonFly");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented in DragonFly");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented in DragonFly");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented in DragonFly");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented in DragonFly");
return -1;
}
// 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 */
uint32 elfentry; /* start address */
uint32 phoff; /* phdr file offset */
uint32 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 */
uint32 offset; /* file offset */
uint32 vaddr; /* virtual address */
uint32 paddr; /* physical address */
int filesz; /* file size */
uint32 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 {
uint32 name; /* section name */
uint32 type; /* SHT_... */
uint32 flags; /* SHF_... */
uint32 addr; /* virtual address */
uint32 offset; /* file offset */
uint32 size; /* section size */
uint32 link; /* misc info */
uint32 info; /* misc info */
uint32 addralign; /* memory alignment */
uint32 entsize; /* entry size if table */
} Shdr32;
typedef struct {
uint32 name; /* section name */
uint32 type; /* SHT_... */
uvlong flags; /* SHF_... */
uvlong addr; /* virtual address */
uvlong offset; /* file offset */
uvlong size; /* section size */
uint32 link; /* misc info */
uint32 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.h>
#include "elf.h"
#include "macho.h"
/*
* All a.out header types. The dummy entry allows canonical
* processing of the union as a sequence of int32s
*/
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 */
Machhdr machhdr; /* macho.h */
} e;
int32 dummy; /* padding to ensure extra int32 */
} 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 machdotout(int, Fhdr*, ExecHdr*);
static int armdotout(int, Fhdr*, ExecHdr*);
static int pedotout(int, Fhdr*, ExecHdr*);
static void setsym(Fhdr*, vlong, int32, vlong, int32, vlong, int32);
static void setdata(Fhdr*, uvlong, int32, vlong, int32);
static void settext(Fhdr*, uvlong, uvlong, int32, vlong);
static void hswal(void*, int, uint32(*)(uint32));
static uvlong _round(uvlong, uint32);
/*
* definition of per-executable file type structures
*/
typedef struct Exectable{
int32 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 */
int32 hsize; /* header size */
uint32 (*swal)(uint32); /* 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 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 },
{ MACH64_MAG, /* 64-bit MACH (apple mac) */
"mach executable",
nil,
FAMD64,
0,
&mamd64,
sizeof(Machhdr),
nil,
machdotout },
{ MACH32_MAG, /* 32-bit MACH (apple mac) */
"mach executable",
nil,
FI386,
0,
&mi386,
sizeof(Machhdr),
nil,
machdotout },
{ 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 },
{ 0x4d5a9000, /* see dosstub[] in pe.c */
"windows PE executable",
nil,
FWINPE,
0,
&mi386,
sizeof(Exec), /* TODO */
nil,
pedotout },
{ 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;
uint32 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(uint32), 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, uint32 (*swap)(uint32))
{
uint32 *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)
{
int32 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, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz);
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)
{
int32 pgsize;
uvlong entry;
USED(unused);
hswal(&hp->e, sizeof(Exec)/sizeof(int32), 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;
union {
char *p;
uvlong *v;
} u;
u.p = (char*)&hp->e.exechdr;
entry = beswav(*u.v);
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, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz);
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)
{
USED(fd);
USED(fp);
USED(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)
{
USED(fd);
USED(fp);
USED(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)
{
USED(fd);
USED(fp);
USED(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)
{
USED(fd);
USED(fp);
USED(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);
uint32 (*swal)(uint32);
ushort (*swab)(ushort);
Ehdr64 *ep;
Phdr64 *ph, *pph;
Shdr64 *sh;
int i, it, id, is, phsz, shsz;
/* 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);
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 AMD64:
mach = &mamd64;
fp->type = FAMD64;
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(uint32), swal);
shsz = sizeof(Shdr64)*ep->shnum;
sh = malloc(shsz);
if(sh) {
seek(fd, ep->shoff, 0);
if(read(fd, sh, shsz) < 0) {
free(sh);
sh = 0;
} else
hswal(sh, shsz/sizeof(uint32), 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) {
uint32 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].offset, ph[1].filesz, 0, 0, 0, ph[1].memsz);
free(ph);
return 1;
}
werrstr("No TEXT or DATA sections");
free(ph);
free(sh);
return 0;
}
settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
pph = ph + id;
setdata(fp, pph->vaddr, pph->filesz, pph->offset, pph->memsz - pph->filesz);
if(is != -1)
setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz);
else if(sh != 0){
char *buf;
uvlong symsize = 0;
uvlong symoff = 0;
uvlong pclnsz = 0;
uvlong pclnoff = 0;
/* load shstrtab names */
buf = malloc(sh[ep->shstrndx].size);
if (buf == 0)
goto done;
memset(buf, 0, sh[ep->shstrndx].size);
seek(fd, sh[ep->shstrndx].offset, 0);
i = read(fd, buf, sh[ep->shstrndx].size);
USED(i); // shut up ubuntu gcc
for(i = 0; i < ep->shnum; i++) {
if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
symsize = sh[i].size;
symoff = sh[i].offset;
}
if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
pclnsz = sh[i].size;
pclnoff = sh[i].offset;
}
}
setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsz);
free(buf);
}
done:
free(ph);
free(sh);
return 1;
}
static int
elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
{
uint32 (*swal)(uint32);
ushort (*swab)(ushort);
Ehdr32 *ep;
Phdr32 *ph;
int i, it, id, is, phsz, shsz;
Shdr32 *sh;
/* 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 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(uint32), swal);
shsz = sizeof(Shdr32)*ep->shnum;
sh = malloc(shsz);
if(sh) {
seek(fd, ep->shoff, 0);
if(read(fd, sh, shsz) < 0) {
free(sh);
sh = 0;
} else
hswal(sh, shsz/sizeof(uint32), 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) {
uint32 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].offset, ph[1].filesz, 0, 0, 0, ph[1].memsz);
free(ph);
return 1;
}
werrstr("No TEXT or DATA sections");
free(sh);
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].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz);
else if(sh != 0){
char *buf;
uvlong symsize = 0;
uvlong symoff = 0;
uvlong pclnsize = 0;
uvlong pclnoff = 0;
/* load shstrtab names */
buf = malloc(sh[ep->shstrndx].size);
if (buf == 0)
goto done;
memset(buf, 0, sh[ep->shstrndx].size);
seek(fd, sh[ep->shstrndx].offset, 0);
i = read(fd, buf, sh[ep->shstrndx].size);
USED(i); // shut up ubuntu gcc
for(i = 0; i < ep->shnum; i++) {
if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
symsize = sh[i].size;
symoff = sh[i].offset;
}
if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
pclnsize = sh[i].size;
pclnoff = sh[i].offset;
}
}
setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsize);
free(buf);
}
done:
free(sh);
free(ph);
return 1;
}
static int
machdotout(int fd, Fhdr *fp, ExecHdr *hp)
{
uvlong (*swav)(uvlong);
uint32 (*swal)(uint32);
Machhdr *mp;
MachCmd **cmd;
MachSymSeg *symtab;
MachSymSeg *pclntab;
MachSeg64 *seg;
MachSect64 *sect;
MachSeg32 *seg32;
MachSect32 *sect32;
uvlong textsize, datasize, bsssize;
uchar *cmdbuf;
uchar *cmdp;
int i, j, hdrsize;
uint32 textva, textoff, datava, dataoff, symoff, symsize, pclnoff, pclnsize;
mp = &hp->e.machhdr;
if (leswal(mp->filetype) != MACH_EXECUTABLE_TYPE) {
werrstr("bad MACH executable type %#ux", leswal(mp->filetype));
return 0;
}
swal = leswal;
swav = leswav;
mp->magic = swal(mp->magic);
mp->cputype = swal(mp->cputype);
mp->cpusubtype = swal(mp->cpusubtype);
mp->filetype = swal(mp->filetype);
mp->ncmds = swal(mp->ncmds);
mp->sizeofcmds = swal(mp->sizeofcmds);
mp->flags = swal(mp->flags);
mp->reserved = swal(mp->reserved);
switch(mp->magic) {
case 0xFEEDFACE: // 32-bit mach
if (mp->cputype != MACH_CPU_TYPE_X86) {
werrstr("bad MACH cpu type - not 386");
return 0;
}
if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86) {
werrstr("bad MACH cpu subtype - not 386");
return 0;
}
if (mp->filetype != MACH_EXECUTABLE_TYPE) {
werrstr("bad MACH executable type");
return 0;
}
mach = &mi386;
fp->type = FI386;
hdrsize = 28;
break;
case 0xFEEDFACF: // 64-bit mach
if (mp->cputype != MACH_CPU_TYPE_X86_64) {
werrstr("bad MACH cpu type - not amd64");
return 0;
}
if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86 && mp->cpusubtype != MACH_CPU_SUBTYPE_X86_64) {
werrstr("bad MACH cpu subtype - not amd64");
return 0;
}
mach = &mamd64;
fp->type = FAMD64;
hdrsize = 32;
break;
default:
werrstr("not mach %#ux", mp->magic);
return 0;
}
cmdbuf = malloc(mp->sizeofcmds);
if(!cmdbuf) {
werrstr("out of memory");
return 0;
}
seek(fd, hdrsize, 0);
if(read(fd, cmdbuf, mp->sizeofcmds) != mp->sizeofcmds) {
free(cmdbuf);
return 0;
}
cmd = malloc(mp->ncmds * sizeof(MachCmd*));
if(!cmd) {
free(cmdbuf);
werrstr("out of memory");
return 0;
}
cmdp = cmdbuf;
textva = 0;
textoff = 0;
dataoff = 0;
datava = 0;
symtab = 0;
pclntab = 0;
textsize = 0;
datasize = 0;
bsssize = 0;
symoff = 0;
symsize = 0;
pclnoff = 0;
pclnsize = 0;
for (i = 0; i < mp->ncmds; i++) {
MachCmd *c;
cmd[i] = (MachCmd*)cmdp;
c = cmd[i];
c->type = swal(c->type);
c->size = swal(c->size);
switch(c->type) {
case MACH_SEGMENT_32:
if(mp->magic != 0xFEEDFACE) {
werrstr("segment 32 in mach 64");
goto bad;
}
seg32 = (MachSeg32*)c;
seg32->vmaddr = swav(seg32->vmaddr);
seg32->vmsize = swav(seg32->vmsize);
seg32->fileoff = swav(seg32->fileoff);
seg32->filesize = swav(seg32->filesize);
seg32->maxprot = swal(seg32->maxprot);
seg32->initprot = swal(seg32->initprot);
seg32->nsects = swal(seg32->nsects);
seg32->flags = swal(seg32->flags);
if (strcmp(seg32->segname, "__TEXT") == 0) {
textva = seg32->vmaddr;
textoff = seg32->fileoff;
textsize = seg32->vmsize;
sect32 = (MachSect32*)(cmdp + sizeof(MachSeg32));
for(j = 0; j < seg32->nsects; j++, sect32++) {
if (strcmp(sect32->sectname, "__gosymtab") == 0) {
symoff = swal(sect32->offset);
symsize = swal(sect32->size);
}
if (strcmp(sect32->sectname, "__gopclntab") == 0) {
pclnoff = swal(sect32->offset);
pclnsize = swal(sect32->size);
}
}
}
if (strcmp(seg32->segname, "__DATA") == 0) {
datava = seg32->vmaddr;
dataoff = seg32->fileoff;
datasize = seg32->filesize;
bsssize = seg32->vmsize - seg32->filesize;
}
break;
case MACH_SEGMENT_64:
if(mp->magic != 0xFEEDFACF) {
werrstr("segment 32 in mach 64");
goto bad;
}
seg = (MachSeg64*)c;
seg->vmaddr = swav(seg->vmaddr);
seg->vmsize = swav(seg->vmsize);
seg->fileoff = swav(seg->fileoff);
seg->filesize = swav(seg->filesize);
seg->maxprot = swal(seg->maxprot);
seg->initprot = swal(seg->initprot);
seg->nsects = swal(seg->nsects);
seg->flags = swal(seg->flags);
if (strcmp(seg->segname, "__TEXT") == 0) {
textva = seg->vmaddr;
textoff = seg->fileoff;
textsize = seg->vmsize;
sect = (MachSect64*)(cmdp + sizeof(MachSeg64));
for(j = 0; j < seg->nsects; j++, sect++) {
if (strcmp(sect->sectname, "__gosymtab") == 0) {
symoff = swal(sect->offset);
symsize = swal(sect->size);
}
if (strcmp(sect->sectname, "__gopclntab") == 0) {
pclnoff = swal(sect->offset);
pclnsize = swal(sect->size);
}
}
}
if (strcmp(seg->segname, "__DATA") == 0) {
datava = seg->vmaddr;
dataoff = seg->fileoff;
datasize = seg->filesize;
bsssize = seg->vmsize - seg->filesize;
}
break;
case MACH_UNIXTHREAD:
break;
case MACH_SYMSEG:
if (symtab == 0) {
symtab = (MachSymSeg*)c;
symoff = swal(symtab->fileoff);
symsize = swal(symtab->filesize);
} else if (pclntab == 0) {
pclntab = (MachSymSeg*)c;
pclnoff = swal(pclntab->fileoff);
pclnsize = swal(pclntab->filesize);
}
break;
}
cmdp += c->size;
}
if (textva == 0 || datava == 0) {
free(cmd);
free(cmdbuf);
return 0;
}
/* compute entry by taking address after header - weird - BUG? */
settext(fp, textva+sizeof(Machhdr) + mp->sizeofcmds, textva, textsize, textoff);
setdata(fp, datava, datasize, dataoff, bsssize);
if(symoff > 0)
setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsize);
free(cmd);
free(cmdbuf);
return 1;
bad:
free(cmd);
free(cmdbuf);
return 0;
}
/*
* (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, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.spsz, 0, hp->e.exechdr.pcsz);
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;
}
/*
* Structures needed to parse PE image.
*/
typedef struct {
uint16 Machine;
uint16 NumberOfSections;
uint32 TimeDateStamp;
uint32 PointerToSymbolTable;
uint32 NumberOfSymbols;
uint16 SizeOfOptionalHeader;
uint16 Characteristics;
} IMAGE_FILE_HEADER;
typedef struct {
uint8 Name[8];
uint32 VirtualSize;
uint32 VirtualAddress;
uint32 SizeOfRawData;
uint32 PointerToRawData;
uint32 PointerToRelocations;
uint32 PointerToLineNumbers;
uint16 NumberOfRelocations;
uint16 NumberOfLineNumbers;
uint32 Characteristics;
} IMAGE_SECTION_HEADER;
typedef struct {
uint32 VirtualAddress;
uint32 Size;
} IMAGE_DATA_DIRECTORY;
typedef struct {
uint16 Magic;
uint8 MajorLinkerVersion;
uint8 MinorLinkerVersion;
uint32 SizeOfCode;
uint32 SizeOfInitializedData;
uint32 SizeOfUninitializedData;
uint32 AddressOfEntryPoint;
uint32 BaseOfCode;
uint32 BaseOfData;
uint32 ImageBase;
uint32 SectionAlignment;
uint32 FileAlignment;
uint16 MajorOperatingSystemVersion;
uint16 MinorOperatingSystemVersion;
uint16 MajorImageVersion;
uint16 MinorImageVersion;
uint16 MajorSubsystemVersion;
uint16 MinorSubsystemVersion;
uint32 Win32VersionValue;
uint32 SizeOfImage;
uint32 SizeOfHeaders;
uint32 CheckSum;
uint16 Subsystem;
uint16 DllCharacteristics;
uint32 SizeOfStackReserve;
uint32 SizeOfStackCommit;
uint32 SizeOfHeapReserve;
uint32 SizeOfHeapCommit;
uint32 LoaderFlags;
uint32 NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER;
typedef struct {
uint16 Magic;
uint8 MajorLinkerVersion;
uint8 MinorLinkerVersion;
uint32 SizeOfCode;
uint32 SizeOfInitializedData;
uint32 SizeOfUninitializedData;
uint32 AddressOfEntryPoint;
uint32 BaseOfCode;
uint64 ImageBase;
uint32 SectionAlignment;
uint32 FileAlignment;
uint16 MajorOperatingSystemVersion;
uint16 MinorOperatingSystemVersion;
uint16 MajorImageVersion;
uint16 MinorImageVersion;
uint16 MajorSubsystemVersion;
uint16 MinorSubsystemVersion;
uint32 Win32VersionValue;
uint32 SizeOfImage;
uint32 SizeOfHeaders;
uint32 CheckSum;
uint16 Subsystem;
uint16 DllCharacteristics;
uint64 SizeOfStackReserve;
uint64 SizeOfStackCommit;
uint64 SizeOfHeapReserve;
uint64 SizeOfHeapCommit;
uint32 LoaderFlags;
uint32 NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[16];
} PE64_IMAGE_OPTIONAL_HEADER;
static int
match8(void *buf, char *cmp)
{
return strncmp((char*)buf, cmp, 8) == 0;
}
/*
* Read from Windows PE/COFF .exe file image.
*/
static int
pedotout(int fd, Fhdr *fp, ExecHdr *hp)
{
uint32 start, magic;
uint32 symtab, esymtab, pclntab, epclntab;
IMAGE_FILE_HEADER fh;
IMAGE_SECTION_HEADER sh;
IMAGE_OPTIONAL_HEADER oh;
PE64_IMAGE_OPTIONAL_HEADER oh64;
uint8 sym[18];
uint32 *valp, ib, entry;
int i, ohoffset;
USED(hp);
seek(fd, 0x3c, 0);
if (readn(fd, &start, sizeof(start)) != sizeof(start)) {
werrstr("crippled PE MSDOS header");
return 0;
}
start = leswal(start);
seek(fd, start, 0);
if (readn(fd, &magic, sizeof(magic)) != sizeof(magic)) {
werrstr("no PE magic number found");
return 0;
}
if (beswal(magic) != 0x50450000) { /* "PE\0\0" */
werrstr("incorrect PE magic number");
return 0;
}
if (readn(fd, &fh, sizeof(fh)) != sizeof(fh)) {
werrstr("crippled PE File Header");
return 0;
}
if (fh.PointerToSymbolTable == 0) {
werrstr("zero pointer to COFF symbol table");
return 0;
}
ohoffset = seek(fd, 0, 1);
if (readn(fd, &oh, sizeof(oh)) != sizeof(oh)) {
werrstr("crippled PE Optional Header");
return 0;
}
switch(oh.Magic) {
case 0x10b: // PE32
fp->type = FI386;
ib = leswal(oh.ImageBase);
entry = leswal(oh.AddressOfEntryPoint);
break;
case 0x20b: // PE32+
fp->type = FAMD64;
seek(fd, ohoffset, 0);
if (readn(fd, &oh64, sizeof(oh64)) != sizeof(oh64)) {
werrstr("crippled PE32+ Optional Header");
return 0;
}
ib = leswal(oh64.ImageBase);
entry = leswal(oh64.AddressOfEntryPoint);
break;
default:
werrstr("invalid PE Optional Header magic number");
return 0;
}
fp->txtaddr = 0;
fp->dataddr = 0;
for (i=0; i<leswab(fh.NumberOfSections); i++) {
if (readn(fd, &sh, sizeof(sh)) != sizeof(sh)) {
werrstr("could not read Section Header %d", i+1);
return 0;
}
if (match8(sh.Name, ".text"))
settext(fp, ib+entry, ib+leswal(sh.VirtualAddress), leswal(sh.VirtualSize), leswal(sh.PointerToRawData));
if (match8(sh.Name, ".data"))
setdata(fp, ib+leswal(sh.VirtualAddress), leswal(sh.SizeOfRawData), leswal(sh.PointerToRawData), leswal(sh.VirtualSize)-leswal(sh.SizeOfRawData));
}
if (fp->txtaddr==0 || fp->dataddr==0) {
werrstr("no .text or .data");
return 0;
}
seek(fd, leswal(fh.PointerToSymbolTable), 0);
symtab = esymtab = pclntab = epclntab = 0;
for (i=0; i<leswal(fh.NumberOfSymbols); i++) {
if (readn(fd, sym, sizeof(sym)) != sizeof(sym)) {
werrstr("crippled COFF symbol %d", i);
return 0;
}
valp = (uint32 *)&sym[8];
if (match8(sym, "symtab"))
symtab = leswal(*valp);
if (match8(sym, "esymtab"))
esymtab = leswal(*valp);
if (match8(sym, "pclntab"))
pclntab = leswal(*valp);
if (match8(sym, "epclntab"))
epclntab = leswal(*valp);
}
if (symtab==0 || esymtab==0 || pclntab==0 || epclntab==0) {
werrstr("no symtab or esymtab or pclntab or epclntab in COFF symbol table");
return 0;
}
setsym(fp, symtab, esymtab-symtab, 0, 0, pclntab, epclntab-pclntab);
return 1;
}
static void
settext(Fhdr *fp, uvlong e, uvlong a, int32 s, vlong off)
{
fp->txtaddr = a;
fp->entry = e;
fp->txtsz = s;
fp->txtoff = off;
}
static void
setdata(Fhdr *fp, uvlong a, int32 s, vlong off, int32 bss)
{
fp->dataddr = a;
fp->datsz = s;
fp->datoff = off;
fp->bsssz = bss;
}
static void
setsym(Fhdr *fp, vlong symoff, int32 symsz, vlong sppcoff, int32 sppcsz, vlong lnpcoff, int32 lnpcsz)
{
fp->symoff = symoff;
fp->symsz = symsz;
if(sppcoff == 0)
sppcoff = symoff+symsz;
fp->sppcoff = symoff;
fp->sppcsz = sppcsz;
if(lnpcoff == 0)
lnpcoff = sppcoff + sppcsz;
fp->lnpcoff = lnpcoff;
fp->lnpcsz = lnpcsz;
}
static uvlong
_round(uvlong a, uint32 b)
{
uvlong w;
w = (a/b)*b;
if (a!=w)
w += b;
return(w);
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
* obj.c
* routines universal to all object files
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ar.h>
#include <mach.h>
#include "obj.h"
int _is2(char* x) { USED(x); return 0; }
int _is7(char* x) { USED(x); return 0; }
int _is9(char* x) { USED(x); return 0; }
int _isk(char* x) { USED(x); return 0; }
int _isq(char* x) { USED(x); return 0; }
int _isv(char* x) { USED(x); return 0; }
int _isu(char* x) { USED(x); return 0; }
int _read2(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
int _read7(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
int _read9(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
int _readk(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
int _readq(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
int _readv(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
int _readu(Biobuf* b, Prog* p) { USED(b); USED(p); return 0; }
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented in FreeBSD");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented in FreeBSD");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented in FreeBSD");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented in FreeBSD");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented in FreeBSD");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented in FreeBSD");
return -1;
}
// Derived from Plan 9 from User Space src/libmach/Linux.c
// http://code.swtch.com/plan9port/src/tip/src/libmach/Linux.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.
#include <u.h>
#include <sys/syscall.h> /* for tkill */
#include <unistd.h>
#include <dirent.h>
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#include <ureg_x86.h>
#include <ureg_amd64.h>
#undef waitpid
// The old glibc used with crosstool compilers on thresher
// doesn't know these numbers, but the Linux kernel
// had them as far back as 2.6.0.
#ifndef WSTOPPED
#define WSTOPPED 2
#define WCONTINUED 8
#define WIFCONTINUED(x) ((x) == 0xffff)
#endif
#ifndef PTRACE_SETOPTIONS
#define PTRACE_SETOPTIONS 0x4200
#define PTRACE_GETEVENTMSG 0x4201
#define PTRACE_O_TRACEFORK 0x2
#define PTRACE_O_TRACEVFORK 0x4
#define PTRACE_O_TRACECLONE 0x8
#define PTRACE_O_TRACEEXEC 0x10
#define PTRACE_O_TRACEVFORKDONE 0x20
#define PTRACE_O_TRACEEXIT 0x40
#define PTRACE_EVENT_FORK 0x1
#define PTRACE_EVENT_VFORK 0x2
#define PTRACE_EVENT_CLONE 0x3
#define PTRACE_EVENT_EXEC 0x4
#define PTRACE_EVENT_VFORK_DONE 0x5
#define PTRACE_EVENT_EXIT 0x6
#endif
static Maprw ptracesegrw;
static Maprw ptraceregrw;
// /usr/include/asm-x86_64/user.h
struct user_regs_struct {
unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10;
unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
unsigned long rip,cs,eflags;
unsigned long rsp,ss;
unsigned long fs_base, gs_base;
unsigned long ds,es,fs,gs;
};
// Linux gets very upset if a debugger forgets the reported state
// of a debugged process, so we keep everything we know about
// a debugged process in the LinuxThread structure.
//
// We can poll for state changes by calling waitpid and interpreting
// the integer status code that comes back. Wait1 does this.
//
// If the process is already running, it is an error to PTRACE_CONT it.
//
// If the process is already stopped, it is an error to stop it again.
//
// If the process is stopped because of a signal, the debugger must
// relay the signal to the PTRACE_CONT call, or else the signal is
// dropped.
//
// If the process exits, the debugger should detach so that the real
// parent can reap the zombie.
//
// On first attach, the debugger should set a handful of flags in order
// to catch future events like fork, clone, exec, etc.
// One for every attached thread.
typedef struct LinuxThread LinuxThread;
struct LinuxThread
{
int pid;
int tid;
int state;
int signal;
int child;
int exitcode;
};
static int trace = 0;
static LinuxThread **thr;
static int nthr;
static int mthr;
static int realpid(int pid);
enum
{
Unknown,
Detached,
Attached,
AttachStop,
Stopped,
Running,
Forking,
Vforking,
VforkDone,
Cloning,
Execing,
Exiting,
Exited,
Killed,
NSTATE,
};
static char* statestr[NSTATE] = {
"Unknown",
"Detached",
"Attached",
"AttachStop",
"Stopped",
"Running",
"Forking",
"Vforking",
"VforkDone",
"Cloning",
"Execing",
"Exiting",
"Exited",
"Killed"
};
static LinuxThread*
attachthread(int pid, int tid, int *new, int newstate)
{
int i, n, status;
LinuxThread **p, *t;
uintptr flags;
if(new)
*new = 0;
for(i=0; i<nthr; i++)
if((pid == 0 || thr[i]->pid == pid) && thr[i]->tid == tid) {
t = thr[i];
goto fixup;
}
if(!new)
return nil;
if(nthr >= mthr) {
n = mthr;
if(n == 0)
n = 64;
else
n *= 2;
p = realloc(thr, n*sizeof thr[0]);
if(p == nil)
return nil;
thr = p;
mthr = n;
}
t = malloc(sizeof *t);
if(t == nil)
return nil;
memset(t, 0, sizeof *t);
thr[nthr++] = t;
if(pid == 0 && nthr > 0)
pid = thr[0]->pid;
t->pid = pid;
t->tid = tid;
t->state = newstate;
if(trace)
fprint(2, "new thread %d %d\n", t->pid, t->tid);
if(new)
*new = 1;
fixup:
if(t->state == Detached) {
if(ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
fprint(2, "ptrace ATTACH %d: %r\n", tid);
return nil;
}
t->state = Attached;
}
if(t->state == Attached) {
// wait for stop, so we can set options
if(waitpid(tid, &status, __WALL|WUNTRACED|WSTOPPED) < 0)
return nil;
if(!WIFSTOPPED(status)) {
fprint(2, "waitpid %d: status=%#x not stopped\n", tid);
return nil;
}
t->state = AttachStop;
}
if(t->state == AttachStop) {
// set options so we'll find out about new threads
flags = PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK |
PTRACE_O_TRACECLONE |
PTRACE_O_TRACEEXEC |
PTRACE_O_TRACEVFORKDONE;
if(ptrace(PTRACE_SETOPTIONS, tid, 0, (void*)flags) < 0) {
fprint(2, "ptrace PTRACE_SETOPTIONS %d: %r\n", tid);
return nil;
}
t->state = Stopped;
}
return t;
}
static LinuxThread*
findthread(int tid)
{
return attachthread(0, tid, nil, 0);
}
int
procthreadpids(int pid, int *p, int np)
{
int i, n;
LinuxThread *t;
n = 0;
for(i=0; i<nthr; i++) {
t = thr[i];
if(t->pid == pid) {
switch(t->state) {
case Exited:
case Detached:
case Killed:
break;
default:
if(n < np)
p[n] = t->tid;
n++;
break;
}
}
}
return n;
}
// Execute a single wait and update the corresponding thread.
static int
wait1(int nohang)
{
int tid, new, status, event;
ulong data;
LinuxThread *t;
enum
{
NormalStop = 0x137f
};
if(nohang != 0)
nohang = WNOHANG;
status = 0;
tid = waitpid(-1, &status, __WALL|WUNTRACED|WSTOPPED|WCONTINUED|nohang);
if(tid < 0)
return -1;
if(tid == 0)
return 0;
if(trace > 0 && status != NormalStop)
fprint(2, "TID %d: %#x\n", tid, status);
t = findthread(tid);
if(t == nil) {
// Sometimes the kernel tells us about new threads
// before we see the parent clone.
t = attachthread(0, tid, &new, Stopped);
if(t == nil) {
fprint(2, "failed to attach to new thread %d\n", tid);
return -1;
}
}
if(WIFSTOPPED(status)) {
t->state = Stopped;
t->signal = WSTOPSIG(status);
if(trace)
fprint(2, "tid %d: stopped %#x%s\n", tid, status,
status != NormalStop ? " ***" : "");
if(t->signal == SIGTRAP && (event = status>>16) != 0) { // ptrace event
switch(event) {
case PTRACE_EVENT_FORK:
t->state = Forking;
goto child;
case PTRACE_EVENT_VFORK:
t->state = Vforking;
goto child;
case PTRACE_EVENT_CLONE:
t->state = Cloning;
goto child;
child:
if(ptrace(PTRACE_GETEVENTMSG, t->tid, 0, &data) < 0) {
fprint(2, "ptrace GETEVENTMSG tid %d: %r\n", tid);
break;
}
t->child = data;
attachthread(t->pid, t->child, &new, Running);
break;
case PTRACE_EVENT_EXEC:
t->state = Execing;
break;
case PTRACE_EVENT_VFORK_DONE:
t->state = VforkDone;
break;
case PTRACE_EVENT_EXIT:
// We won't see this unless we set PTRACE_O_TRACEEXIT.
// The debuggers assume that a read or write on a Map
// will fail for a thread that has exited. This event
// breaks that assumption. It's not a big deal: we
// only lose the ability to see the register state at
// the time of exit.
if(trace)
fprint(2, "tid %d: exiting %#x\n", tid, status);
t->state = Exiting;
if(ptrace(PTRACE_GETEVENTMSG, t->tid, 0, &data) < 0) {
fprint(2, "ptrace GETEVENTMSG tid %d: %r\n", tid);
break;
}
t->exitcode = data;
break;
}
}
}
if(WIFCONTINUED(status)) {
if(trace)
fprint(2, "tid %d: continued %#x\n", tid, status);
t->state = Running;
}
if(WIFEXITED(status)) {
if(trace)
fprint(2, "tid %d: exited %#x\n", tid, status);
t->state = Exited;
t->exitcode = WEXITSTATUS(status);
t->signal = -1;
ptrace(PTRACE_DETACH, t->tid, 0, 0);
if(trace)
fprint(2, "tid %d: detach exited\n", tid);
}
if(WIFSIGNALED(status)) {
if(trace)
fprint(2, "tid %d: signaled %#x\n", tid, status);
t->state = Exited;
t->signal = WTERMSIG(status);
t->exitcode = -1;
ptrace(PTRACE_DETACH, t->tid, 0, 0);
if(trace)
fprint(2, "tid %d: detach signaled\n", tid);
}
return 1;
}
static int
waitstop(LinuxThread *t)
{
while(t->state == Running)
if(wait1(0) < 0)
return -1;
return 0;
}
// Attach to and stop all threads in process pid.
// Must stop everyone in order to make sure we set
// the "tell me about new threads" option in every
// task.
int
attachallthreads(int pid)
{
int tid, foundnew, new;
char buf[100];
DIR *d;
struct dirent *de;
LinuxThread *t;
if(pid == 0) {
fprint(2, "attachallthreads(0)\n");
return -1;
}
pid = realpid(pid);
snprint(buf, sizeof buf, "/proc/%d/task", pid);
if((d = opendir(buf)) == nil) {
fprint(2, "opendir %s: %r\n", buf);
return -1;
}
// Loop in case new threads are being created right now.
// We stop every thread as we find it, so eventually
// this has to stop (or the system runs out of procs).
do {
foundnew = 0;
while((de = readdir(d)) != nil) {
tid = atoi(de->d_name);
if(tid == 0)
continue;
t = attachthread(pid, tid, &new, Detached);
foundnew |= new;
if(t)
waitstop(t);
}
rewinddir(d);
} while(foundnew);
closedir(d);
return 0;
}
Map*
attachproc(int pid, Fhdr *fp)
{
Map *map;
if(pid == 0) {
fprint(2, "attachproc(0)\n");
return nil;
}
if(findthread(pid) == nil && attachallthreads(pid) < 0)
return nil;
map = newmap(0, 4);
if (!map)
return 0;
map->pid = pid;
if(mach->regsize)
setmap(map, -1, 0, mach->regsize, 0, "regs", ptraceregrw);
// if(mach->fpregsize)
// setmap(map, -1, mach->regsize, mach->regsize+mach->fpregsize, 0, "fpregs", ptraceregrw);
setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", ptracesegrw);
setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", ptracesegrw);
return map;
}
void
detachproc(Map *m)
{
LinuxThread *t;
t = findthread(m->pid);
if(t != nil) {
ptrace(PTRACE_DETACH, t->tid, 0, 0);
t->state = Detached;
if(trace)
fprint(2, "tid %d: detachproc\n", t->tid);
// TODO(rsc): Reclaim thread structs somehow?
}
free(m);
}
/* /proc/pid/stat contains
pid
command in parens
0. state
1. ppid
2. pgrp
3. session
4. tty_nr
5. tpgid
6. flags (math=4, traced=10)
7. minflt
8. cminflt
9. majflt
10. cmajflt
11. utime
12. stime
13. cutime
14. cstime
15. priority
16. nice
17. 0
18. itrealvalue
19. starttime
20. vsize
21. rss
22. rlim
23. startcode
24. endcode
25. startstack
26. kstkesp
27. kstkeip
28. pending signal bitmap
29. blocked signal bitmap
30. ignored signal bitmap
31. caught signal bitmap
32. wchan
33. nswap
34. cnswap
35. exit_signal
36. processor
*/
static int
readstat(int pid, char *buf, int nbuf, char **f, int nf)
{
int fd, n;
char *p;
snprint(buf, nbuf, "/proc/%d/stat", pid);
if((fd = open(buf, OREAD)) < 0){
fprint(2, "open %s: %r\n", buf);
return -1;
}
n = read(fd, buf, nbuf-1);
close(fd);
if(n <= 0){
fprint(2, "read %s: %r\n", buf);
return -1;
}
buf[n] = 0;
/* command name is in parens, no parens afterward */
p = strrchr(buf, ')');
if(p == nil || *++p != ' '){
fprint(2, "bad format in /proc/%d/stat\n", pid);
return -1;
}
++p;
nf = tokenize(p, f, nf);
if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
return nf;
}
static char*
readstatus(int pid, char *buf, int nbuf, char *key)
{
int fd, n;
char *p;
snprint(buf, nbuf, "/proc/%d/status", pid);
if((fd = open(buf, OREAD)) < 0){
fprint(2, "open %s: %r\n", buf);
return nil;
}
n = read(fd, buf, nbuf-1);
close(fd);
if(n <= 0){
fprint(2, "read %s: %r\n", buf);
return nil;
}
buf[n] = 0;
p = strstr(buf, key);
if(p)
return p+strlen(key);
return nil;
}
int
procnotes(int pid, char ***pnotes)
{
char buf[1024], *f[40];
int i, n, nf;
char *s, **notes;
ulong sigs;
extern char *_p9sigstr(int, char*);
*pnotes = nil;
nf = readstat(pid, buf, sizeof buf, f, nelem(f));
if(nf <= 28)
return -1;
sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
if(sigs == 0){
*pnotes = nil;
return 0;
}
notes = malloc(32*sizeof(char*));
if(notes == nil)
return -1;
memset(notes, 0, 32*sizeof(char*));
n = 0;
for(i=0; i<32; i++){
if((sigs&(1<<i)) == 0)
continue;
if((s = _p9sigstr(i, nil)) == nil)
continue;
notes[n++] = s;
}
*pnotes = notes;
return n;
}
static int
realpid(int pid)
{
char buf[1024], *p;
p = readstatus(pid, buf, sizeof buf, "\nTgid:");
if(p == nil)
return pid;
return atoi(p);
}
int
ctlproc(int pid, char *msg)
{
int new;
LinuxThread *t;
uintptr data;
while(wait1(1) > 0)
;
if(strcmp(msg, "attached") == 0){
t = attachthread(pid, pid, &new, Attached);
if(t == nil)
return -1;
return 0;
}
if(strcmp(msg, "hang") == 0){
if(pid == getpid())
return ptrace(PTRACE_TRACEME, 0, 0, 0);
werrstr("can only hang self");
return -1;
}
t = findthread(pid);
if(t == nil) {
werrstr("not attached to pid %d", pid);
return -1;
}
if(t->state == Exited) {
werrstr("pid %d has exited", pid);
return -1;
}
if(t->state == Killed) {
werrstr("pid %d has been killed", pid);
return -1;
}
if(strcmp(msg, "kill") == 0) {
if(ptrace(PTRACE_KILL, pid, 0, 0) < 0)
return -1;
t->state = Killed;
return 0;
}
if(strcmp(msg, "startstop") == 0){
if(ctlproc(pid, "start") < 0)
return -1;
return waitstop(t);
}
if(strcmp(msg, "sysstop") == 0){
if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
return -1;
t->state = Running;
return waitstop(t);
}
if(strcmp(msg, "stop") == 0){
if(trace > 1)
fprint(2, "tid %d: tkill stop\n", pid);
if(t->state == Stopped)
return 0;
if(syscall(__NR_tkill, pid, SIGSTOP) < 0)
return -1;
return waitstop(t);
}
if(strcmp(msg, "step") == 0){
if(t->state == Running) {
werrstr("cannot single-step unstopped %d", pid);
return -1;
}
if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
return -1;
return waitstop(t);
}
if(strcmp(msg, "start") == 0) {
if(t->state == Running)
return 0;
data = 0;
if(t->state == Stopped && t->signal != SIGSTOP && t->signal != SIGTRAP)
data = t->signal;
if(trace && data)
fprint(2, "tid %d: continue %lud\n", pid, (ulong)data);
if(ptrace(PTRACE_CONT, pid, 0, (void*)data) < 0)
return -1;
t->state = Running;
return 0;
}
if(strcmp(msg, "waitstop") == 0) {
return waitstop(t);
}
werrstr("unknown control message '%s'", msg);
return -1;
}
char*
proctextfile(int pid)
{
static char buf[1024], pbuf[128];
snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
if(readlink(pbuf, buf, sizeof buf) >= 0)
return strdup(buf);
if(access(pbuf, AEXIST) >= 0)
return strdup(pbuf);
return nil;
}
static int
ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n)
{
int i;
uintptr u, a;
uchar buf[sizeof(uintptr)];
for(i=0; i<n; i+=sizeof(uintptr)){
// Tread carefully here. On recent versions of glibc,
// ptrace is a variadic function which means the third
// argument will be pushed onto the stack as a uvlong.
// This is fine on amd64 but will not work for 386.
// We must convert addr to a uintptr.
a = addr+i;
if(isr){
errno = 0;
u = ptrace(type, pid, a, 0);
if(errno)
goto ptraceerr;
if(n-i >= sizeof(uintptr))
memmove((char*)v+i, &u, sizeof(uintptr));
else{
memmove(buf, &u, sizeof u);
memmove((char*)v+i, buf, n-i);
}
}else{
if(n-i >= sizeof(uintptr))
u = *(uintptr*)((char*)v+i);
else{
errno = 0;
u = ptrace(xtype, pid, a, 0);
if(errno)
return -1;
memmove(buf, &u, sizeof u);
memmove(buf, (char*)v+i, n-i);
memmove(&u, buf, sizeof u);
}
if(ptrace(type, pid, a, u) < 0)
goto ptraceerr;
}
}
return 0;
ptraceerr:
werrstr("ptrace %s addr=%#llux pid=%d: %r", isr ? "read" : "write", addr, pid);
return -1;
}
static int
ptracesegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
{
USED(seg);
return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
isr, map->pid, addr, v, n);
}
// If the debugger is compiled as an x86-64 program,
// then all the ptrace register read/writes are done on
// a 64-bit register set. If the target program
// is a 32-bit program, the debugger is expected to
// read the bottom half of the relevant registers
// out of the 64-bit set.
// Linux 32-bit is
// BX CX DX SI DI BP AX DS ES FS GS OrigAX IP CS EFLAGS SP SS
// Linux 64-bit is
// R15 R14 R13 R12 BP BX R11 R10 R9 R8 AX CX DX SI DI OrigAX IP CS EFLAGS SP SS FSBase GSBase DS ES FS GS
// Go 32-bit is
// DI SI BP NSP BX DX CX AX GS FS ES DS TRAP ECODE PC CS EFLAGS SP SS
uint go32tolinux32tab[] = {
4, 3, 5, 15, 0, 2, 1, 6, 10, 9, 8, 7, -1, -1, 12, 13, 14, 15, 16
};
static int
go32tolinux32(uvlong addr)
{
int r;
if(addr%4 || addr/4 >= nelem(go32tolinux32tab))
return -1;
r = go32tolinux32tab[addr/4];
if(r < 0)
return -1;
return r*4;
}
uint go32tolinux64tab[] = {
14, 13, 4, 19, 5, 12, 11, 10, 26, 25, 24, 23, -1, -1, 16, 17, 18, 19, 20
};
static int
go32tolinux64(uvlong addr)
{
int r;
if(addr%4 || addr/4 >= nelem(go32tolinux64tab))
return -1;
r = go32tolinux64tab[addr/4];
if(r < 0)
return -1;
return r*8;
}
extern Mach mi386;
extern Mach mamd64;
static int
go2linux(uvlong addr)
{
if(sizeof(void*) == 4) {
if(mach == &mi386)
return go32tolinux32(addr);
werrstr("unsupported architecture");
return -1;
}
if(mach == &mi386)
return go32tolinux64(addr);
if(mach != &mamd64) {
werrstr("unsupported architecture");
return -1;
}
switch(addr){
case offsetof(UregAmd64, ax):
return offsetof(struct user_regs_struct, rax);
case offsetof(UregAmd64, bx):
return offsetof(struct user_regs_struct, rbx);
case offsetof(UregAmd64, cx):
return offsetof(struct user_regs_struct, rcx);
case offsetof(UregAmd64, dx):
return offsetof(struct user_regs_struct, rdx);
case offsetof(UregAmd64, si):
return offsetof(struct user_regs_struct, rsi);
case offsetof(UregAmd64, di):
return offsetof(struct user_regs_struct, rdi);
case offsetof(UregAmd64, bp):
return offsetof(struct user_regs_struct, rbp);
case offsetof(UregAmd64, r8):
return offsetof(struct user_regs_struct, r8);
case offsetof(UregAmd64, r9):
return offsetof(struct user_regs_struct, r9);
case offsetof(UregAmd64, r10):
return offsetof(struct user_regs_struct, r10);
case offsetof(UregAmd64, r11):
return offsetof(struct user_regs_struct, r11);
case offsetof(UregAmd64, r12):
return offsetof(struct user_regs_struct, r12);
case offsetof(UregAmd64, r13):
return offsetof(struct user_regs_struct, r13);
case offsetof(UregAmd64, r14):
return offsetof(struct user_regs_struct, r14);
case offsetof(UregAmd64, r15):
return offsetof(struct user_regs_struct, r15);
case offsetof(UregAmd64, ds):
return offsetof(struct user_regs_struct, ds);
case offsetof(UregAmd64, es):
return offsetof(struct user_regs_struct, es);
case offsetof(UregAmd64, fs):
return offsetof(struct user_regs_struct, fs);
case offsetof(UregAmd64, gs):
return offsetof(struct user_regs_struct, gs);
case offsetof(UregAmd64, ip):
return offsetof(struct user_regs_struct, rip);
case offsetof(UregAmd64, cs):
return offsetof(struct user_regs_struct, cs);
case offsetof(UregAmd64, flags):
return offsetof(struct user_regs_struct, eflags);
case offsetof(UregAmd64, sp):
return offsetof(struct user_regs_struct, rsp);
case offsetof(UregAmd64, ss):
return offsetof(struct user_regs_struct, ss);
}
return -1;
}
static int
ptraceregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
{
int laddr;
uvlong u;
USED(seg);
if((laddr = go2linux(addr)) < 0){
if(isr){
memset(v, 0, n);
return 0;
}
werrstr("register %llud not available", addr);
return -1;
}
if(isr){
errno = 0;
u = ptrace(PTRACE_PEEKUSER, map->pid, laddr, 0);
if(errno)
goto ptraceerr;
switch(n){
case 1:
*(uint8*)v = u;
break;
case 2:
*(uint16*)v = u;
break;
case 4:
*(uint32*)v = u;
break;
case 8:
*(uint64*)v = u;
break;
default:
werrstr("bad register size");
return -1;
}
}else{
switch(n){
case 1:
u = *(uint8*)v;
break;
case 2:
u = *(uint16*)v;
break;
case 4:
u = *(uint32*)v;
break;
case 8:
u = *(uint64*)v;
break;
default:
werrstr("bad register size");
return -1;
}
if(ptrace(PTRACE_POKEUSER, map->pid, laddr, (void*)(uintptr)u) < 0)
goto ptraceerr;
}
return 0;
ptraceerr:
werrstr("ptrace %s register laddr=%d pid=%d n=%d: %r", isr ? "read" : "write", laddr, map->pid, n);
return -1;
}
char*
procstatus(int pid)
{
LinuxThread *t;
t = findthread(pid);
if(t == nil)
return "???";
return statestr[t->state];
}
// Inferno libmach/machdata.c
// http://code.google.com/p/inferno-os/source/browse/utils/libmach/machdata.c
//
// Copyright © 1994-1999 Lucent Technologies Inc.
// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
// Portions Copyright © 1997-1999 Vita Nuova Limited.
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/*
* Debugger utilities shared by at least two architectures
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define STARTSYM "_main"
#define PROFSYM "_mainp"
#define FRAMENAME ".frame"
extern Machdata mipsmach;
int asstype = AMIPS; /* disassembler type */
Machdata *machdata; /* machine-dependent functions */
int
localaddr(Map *map, char *fn, char *var, uvlong *r, Rgetter rget)
{
Symbol s;
uvlong fp, pc, sp, link;
if (!lookup(fn, 0, &s)) {
werrstr("function not found");
return -1;
}
pc = rget(map, mach->pc);
sp = rget(map, mach->sp);
if(mach->link)
link = rget(map, mach->link);
else
link = 0;
fp = machdata->findframe(map, s.value, pc, sp, link);
if (fp == 0) {
werrstr("stack frame not found");
return -1;
}
if (!var || !var[0]) {
*r = fp;
return 1;
}
if (findlocal(&s, var, &s) == 0) {
werrstr("local variable not found");
return -1;
}
switch (s.class) {
case CAUTO:
*r = fp - s.value;
break;
case CPARAM: /* assume address size is stack width */
*r = fp + s.value + mach->szaddr;
break;
default:
werrstr("local variable not found: %d", s.class);
return -1;
}
return 1;
}
/*
* Print value v as s.name[+offset] if possible, or just v.
*/
int
symoff(char *buf, int n, uvlong v, int space)
{
Symbol s;
int r;
int32 delta;
r = delta = 0; /* to shut compiler up */
if (v) {
r = findsym(v, space, &s);
if (r)
delta = v-s.value;
if (delta < 0)
delta = -delta;
}
if (v == 0 || r == 0)
return snprint(buf, n, "%llux", v);
if (s.type != 't' && s.type != 'T' && delta >= 4096)
return snprint(buf, n, "%llux", v);
else if (delta)
return snprint(buf, n, "%s+%#ux", s.name, delta);
else
return snprint(buf, n, "%s", s.name);
}
/*
* Format floating point registers
*
* Register codes in format field:
* 'X' - print as 32-bit hexadecimal value
* 'F' - 64-bit double register when modif == 'F'; else 32-bit single reg
* 'f' - 32-bit ieee float
* '8' - big endian 80-bit ieee extended float
* '3' - little endian 80-bit ieee extended float with hole in bytes 8&9
*/
int
fpformat(Map *map, Reglist *rp, char *buf, int n, int modif)
{
char reg[12];
uint32 r;
switch(rp->rformat)
{
case 'X':
if (get4(map, rp->roffs, &r) < 0)
return -1;
snprint(buf, n, "%ux", r);
break;
case 'F': /* first reg of double reg pair */
if (modif == 'F')
if ((rp->rformat=='F') || (((rp+1)->rflags&RFLT) && (rp+1)->rformat == 'f')) {
if (get1(map, rp->roffs, (uchar *)reg, 8) < 0)
return -1;
machdata->dftos(buf, n, reg);
if (rp->rformat == 'F')
return 1;
return 2;
}
/* treat it like 'f' */
if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
return -1;
machdata->sftos(buf, n, reg);
break;
case 'f': /* 32 bit float */
if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
return -1;
machdata->sftos(buf, n, reg);
break;
case '3': /* little endian ieee 80 with hole in bytes 8&9 */
if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
return -1;
memmove(reg+10, reg+8, 2); /* open hole */
memset(reg+8, 0, 2); /* fill it */
leieee80ftos(buf, n, reg);
break;
case '8': /* big-endian ieee 80 */
if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
return -1;
beieee80ftos(buf, n, reg);
break;
default: /* unknown */
break;
}
return 1;
}
char *
_hexify(char *buf, uint32 p, int zeros)
{
uint32 d;
d = p/16;
if(d)
buf = _hexify(buf, d, zeros-1);
else
while(zeros--)
*buf++ = '0';
*buf++ = "0123456789abcdef"[p&0x0f];
return buf;
}
/*
* These routines assume that if the number is representable
* in IEEE floating point, it will be representable in the native
* double format. Naive but workable, probably.
*/
int
ieeedftos(char *buf, int n, uint32 h, uint32 l)
{
double fr;
int exp;
if (n <= 0)
return 0;
if(h & (1L<<31)){
*buf++ = '-';
h &= ~(1L<<31);
}else
*buf++ = ' ';
n--;
if(l == 0 && h == 0)
return snprint(buf, n, "0.");
exp = (h>>20) & ((1L<<11)-1L);
if(exp == 0)
return snprint(buf, n, "DeN(%.8ux%.8ux)", h, l);
if(exp == ((1L<<11)-1L)){
if(l==0 && (h&((1L<<20)-1L)) == 0)
return snprint(buf, n, "Inf");
else
return snprint(buf, n, "NaN(%.8ux%.8ux)", h&((1<<20)-1), l);
}
exp -= (1L<<10) - 2L;
fr = l & ((1L<<16)-1L);
fr /= 1L<<16;
fr += (l>>16) & ((1L<<16)-1L);
fr /= 1L<<16;
fr += (h & (1L<<20)-1L) | (1L<<20);
fr /= 1L<<21;
fr = ldexp(fr, exp);
return snprint(buf, n, "%.18g", fr);
}
int
ieeesftos(char *buf, int n, uint32 h)
{
double fr;
int exp;
if (n <= 0)
return 0;
if(h & (1L<<31)){
*buf++ = '-';
h &= ~(1L<<31);
}else
*buf++ = ' ';
n--;
if(h == 0)
return snprint(buf, n, "0.");
exp = (h>>23) & ((1L<<8)-1L);
if(exp == 0)
return snprint(buf, n, "DeN(%.8ux)", h);
if(exp == ((1L<<8)-1L)){
if((h&((1L<<23)-1L)) == 0)
return snprint(buf, n, "Inf");
else
return snprint(buf, n, "NaN(%.8lux)", h&((1L<<23)-1L));
}
exp -= (1L<<7) - 2L;
fr = (h & ((1L<<23)-1L)) | (1L<<23);
fr /= 1L<<24;
fr = ldexp(fr, exp);
return snprint(buf, n, "%.9g", fr);
}
int
beieeesftos(char *buf, int n, void *s)
{
return ieeesftos(buf, n, beswal(*(uint32*)s));
}
int
beieeedftos(char *buf, int n, void *s)
{
return ieeedftos(buf, n, beswal(*(uint32*)s), beswal(((uint32*)(s))[1]));
}
int
leieeesftos(char *buf, int n, void *s)
{
return ieeesftos(buf, n, leswal(*(uint32*)s));
}
int
leieeedftos(char *buf, int n, void *s)
{
return ieeedftos(buf, n, leswal(((uint32*)(s))[1]), leswal(*(uint32*)s));
}
/* packed in 12 bytes, with s[2]==s[3]==0; mantissa starts at s[4]*/
int
beieee80ftos(char *buf, int n, void *s)
{
uchar *reg = (uchar*)s;
int i;
uint32 x;
uchar ieee[8+8]; /* room for slop */
uchar *p, *q;
memset(ieee, 0, sizeof(ieee));
/* sign */
if(reg[0] & 0x80)
ieee[0] |= 0x80;
/* exponent */
x = ((reg[0]&0x7F)<<8) | reg[1];
if(x == 0) /* number is ±0 */
goto done;
if(x == 0x7FFF){
if(memcmp(reg+4, ieee+1, 8) == 0){ /* infinity */
x = 2047;
}else{ /* NaN */
x = 2047;
ieee[7] = 0x1; /* make sure */
}
ieee[0] |= x>>4;
ieee[1] |= (x&0xF)<<4;
goto done;
}
x -= 0x3FFF; /* exponent bias */
x += 1023;
if(x >= (1<<11) || ((reg[4]&0x80)==0 && x!=0))
return snprint(buf, n, "not in range");
ieee[0] |= x>>4;
ieee[1] |= (x&0xF)<<4;
/* mantissa */
p = reg+4;
q = ieee+1;
for(i=0; i<56; i+=8, p++, q++){ /* move one byte */
x = (p[0]&0x7F) << 1;
if(p[1] & 0x80)
x |= 1;
q[0] |= x>>4;
q[1] |= (x&0xF)<<4;
}
done:
return beieeedftos(buf, n, (void*)ieee);
}
int
leieee80ftos(char *buf, int n, void *s)
{
int i;
char *cp;
char b[12];
cp = (char*) s;
for(i=0; i<12; i++)
b[11-i] = *cp++;
return beieee80ftos(buf, n, b);
}
int
cisctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
Symbol s;
int found, i;
uvlong opc, moved;
USED(link);
i = 0;
opc = 0;
while(pc && opc != pc) {
moved = pc2sp(pc);
if (moved == ~0)
break;
found = findsym(pc, CTEXT, &s);
if (!found)
break;
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
break;
sp += moved;
opc = pc;
if (geta(map, sp, &pc) < 0)
break;
(*trace)(map, pc, sp, &s);
sp += mach->szaddr; /*assumes address size = stack width*/
if(++i > 40)
break;
}
return i;
}
int
risctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
{
int i;
Symbol s, f;
uvlong oldpc;
i = 0;
while(findsym(pc, CTEXT, &s)) {
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
break;
if(pc == s.value) /* at first instruction */
f.value = 0;
else if(findlocal(&s, FRAMENAME, &f) == 0)
break;
oldpc = pc;
if(s.type == 'L' || s.type == 'l' || pc <= s.value+mach->pcquant)
pc = link;
else
if (geta(map, sp, &pc) < 0)
break;
if(pc == 0 || (pc == oldpc && f.value == 0))
break;
sp += f.value;
(*trace)(map, pc-8, sp, &s);
if(++i > 40)
break;
}
return i;
}
uvlong
ciscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
{
Symbol s;
uvlong moved;
USED(link);
for(;;) {
moved = pc2sp(pc);
if (moved == ~0)
break;
sp += moved;
findsym(pc, CTEXT, &s);
if (addr == s.value)
return sp;
if (geta(map, sp, &pc) < 0)
break;
sp += mach->szaddr; /*assumes sizeof(addr) = stack width*/
}
return 0;
}
uvlong
riscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
{
Symbol s, f;
while (findsym(pc, CTEXT, &s)) {
if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
break;
if(pc == s.value) /* at first instruction */
f.value = 0;
else
if(findlocal(&s, FRAMENAME, &f) == 0)
break;
sp += f.value;
if (s.value == addr)
return sp;
if (s.type == 'L' || s.type == 'l' || pc-s.value <= mach->szaddr*2)
pc = link;
else
if (geta(map, sp-f.value, &pc) < 0)
break;
}
return 0;
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
* Definitions needed for accessing MACH object headers.
*/
typedef struct {
uint32 magic; /* mach magic number identifier */
uint32 cputype; /* cpu specifier */
uint32 cpusubtype; /* machine specifier */
uint32 filetype; /* type of file */
uint32 ncmds; /* number of load commands */
uint32 sizeofcmds; /* the size of all the load commands */
uint32 flags; /* flags */
uint32 reserved; /* reserved */
} Machhdr;
typedef struct {
uint32 type; /* type of load command */
uint32 size; /* total size in bytes */
} MachCmd;
typedef struct {
MachCmd cmd;
char segname[16]; /* segment name */
uint32 vmaddr; /* memory address of this segment */
uint32 vmsize; /* memory size of this segment */
uint32 fileoff; /* file offset of this segment */
uint32 filesize; /* amount to map from the file */
uint32 maxprot; /* maximum VM protection */
uint32 initprot; /* initial VM protection */
uint32 nsects; /* number of sections in segment */
uint32 flags; /* flags */
} MachSeg32; /* for 32-bit architectures */
typedef struct {
MachCmd cmd;
char segname[16]; /* segment name */
uvlong vmaddr; /* memory address of this segment */
uvlong vmsize; /* memory size of this segment */
uvlong fileoff; /* file offset of this segment */
uvlong filesize; /* amount to map from the file */
uint32 maxprot; /* maximum VM protection */
uint32 initprot; /* initial VM protection */
uint32 nsects; /* number of sections in segment */
uint32 flags; /* flags */
} MachSeg64; /* for 64-bit architectures */
typedef struct {
MachCmd cmd;
uint32 fileoff; /* file offset of this segment */
uint32 filesize; /* amount to map from the file */
} MachSymSeg;
typedef struct {
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint32 addr; /* memory address of this section */
uint32 size; /* size in bytes of this section */
uint32 offset; /* file offset of this section */
uint32 align; /* section alignment (power of 2) */
uint32 reloff; /* file offset of relocation entries */
uint32 nreloc; /* number of relocation entries */
uint32 flags; /* flags (section type and attributes)*/
uint32 reserved1; /* reserved (for offset or index) */
uint32 reserved2; /* reserved (for count or sizeof) */
} MachSect32; /* for 32-bit architectures */
typedef struct {
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uvlong addr; /* memory address of this section */
uvlong size; /* size in bytes of this section */
uint32 offset; /* file offset of this section */
uint32 align; /* section alignment (power of 2) */
uint32 reloff; /* file offset of relocation entries */
uint32 nreloc; /* number of relocation entries */
uint32 flags; /* flags (section type and attributes)*/
uint32 reserved1; /* reserved (for offset or index) */
uint32 reserved2; /* reserved (for count or sizeof) */
uint32 reserved3; /* reserved */
} MachSect64; /* for 64-bit architectures */
enum {
MACH_CPU_TYPE_X86_64 = (1<<24)|7,
MACH_CPU_TYPE_X86 = 7,
MACH_CPU_SUBTYPE_X86 = 3,
MACH_CPU_SUBTYPE_X86_64 = (1<<31)|3,
MACH_EXECUTABLE_TYPE = 2,
MACH_SEGMENT_32 = 1, /* 32-bit mapped segment */
MACH_SEGMENT_64 = 0x19, /* 64-bit mapped segment */
MACH_SYMSEG = 3, /* obsolete gdb symtab, reused by go */
MACH_UNIXTHREAD = 0x5, /* thread (for stack) */
};
#define MACH64_MAG ((0xcf<<24) | (0xfa<<16) | (0xed<<8) | 0xfe)
#define MACH32_MAG ((0xce<<24) | (0xfa<<16) | (0xed<<8) | 0xfe)
// 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.h>
Map *
newmap(Map *map, int n)
{
int size;
size = sizeof(Map)+(n-1)*sizeof(Seg);
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, Maprw *rw)
{
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;
map->seg[i].rw = rw;
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);
}
*/
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;
}
int
fdrw(Map *map, Seg *s, uvlong addr, void *v, uint n, int isread)
{
int tot, m;
USED(map);
for(tot=0; tot<n; tot+=m){
if(isread)
m = pread(s->fd, (uchar*)v+tot, n-tot, addr+tot);
else
m = pwrite(s->fd, (uchar*)v+tot, n-tot, addr+tot);
if(m == 0){
werrstr("short %s", isread ? "read" : "write");
return -1;
}
if(m < 0){
werrstr("%s %d at %#llux (+%#llux): %r", isread ? "read" : "write", n, addr, s->f);
return -1;
}
}
return 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[0].rw = fdrw;
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";
map->seg[0].rw = fdrw;
return map;
}
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented in NetBSD");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented in NetBSD");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented in NetBSD");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented in NetBSD");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented in NetBSD");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented in NetBSD");
return -1;
}
// 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.h>
#include "obj.h"
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;
}
/*
* look for the next file in an archive
*/
int
nextar(Biobuf *bp, int offset, char *buf)
{
struct ar_hdr a;
int i, r;
int32 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;
}
// 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);
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented in OpenBSD");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented in OpenBSD");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented in OpenBSD");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented in OpenBSD");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented in OpenBSD");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented in OpenBSD");
return -1;
}
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented on Plan 9");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented on Plan 9");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented on Plan 9");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented on Plan 9");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented on Plan 9");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented on Plan 9");
return -1;
}
int
nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
{
USED(rqtp);
USED(rmtp);
sysfatal("nanosleep unimplemented on Plan 9");
return -1;
}
// 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.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, marm;
extern Machdata i386mach, armmach;
/*
* 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, },
{ "arm", /*ARM*/
FARM,
FARMB,
AARM,
&marm,
&armmach, },
#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, },
{ "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;
}
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented in Solaris");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented in Solaris");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented in Solaris");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented in Solaris");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented in Solaris");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented in Solaris");
return -1;
}
// 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 int32
*/
uint32
beswal(uint32 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 int32
*/
uint32
leswal(uint32 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.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 */
int32 line; /* line # where it was included */
int32 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 int32 nauto; /* number of automatics */
static int32 nfiles; /* number of files */
static int32 nglob; /* number of globals */
static int32 nhist; /* number of history stack entries */
static int32 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 uvlong firstinstr; /* as found from symtab; needed for amd64 */
static void cleansyms(void);
static int32 decodename(Biobuf*, Sym*);
static short *encfname(char*);
static int fline(char*, int, int32, 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*, int32*);
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*);
/*
* Go 1.2 pcln table (also contains pcsp).
*/
#define Go12PclnMagic 0xfffffffb
#define Go12PclnMagicRev 0xfbffffff
static int isgo12pcline(void);
static uvlong go12pc2sp(uvlong);
static int32 go12fileline(char*, int, uvlong);
static void go12clean(void);
static uvlong go12file2pc(char*, int);
/*
* initialize the symbol tables
*/
int
syminit(int fd, Fhdr *fp)
{
Sym *p;
int32 i, l, size, symsz;
vlong vl;
Biobuf b;
int svalsz, newformat, shift;
uvlong (*swav)(uvlong);
uint32 (*swal)(uint32);
uchar buf[8], c;
if(fp->symsz == 0)
return 0;
if(fp->type == FNONE)
return 0;
swav = beswav;
swal = beswal;
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 %d bytes", fp->symsz);
return -1;
}
Binit(&b, fd, OREAD);
Bseek(&b, fp->symoff, 0);
memset(buf, 0, sizeof buf);
Bread(&b, buf, sizeof buf);
newformat = 0;
symsz = fp->symsz;
if(memcmp(buf, "\xfd\xff\xff\xff\x00\x00\x00", 7) == 0) {
swav = leswav;
swal = leswal;
newformat = 1;
} else if(memcmp(buf, "\xff\xff\xff\xfd\x00\x00\x00", 7) == 0) {
newformat = 1;
} else if(memcmp(buf, "\xfe\xff\xff\xff\x00\x00", 6) == 0) {
// Table format used between Go 1.0 and Go 1.1:
// little-endian but otherwise same as the old Go 1.0 table.
// Not likely to be seen much in practice, but easy to handle.
swav = leswav;
swal = leswal;
Bseek(&b, fp->symoff+6, 0);
symsz -= 6;
} else {
Bseek(&b, fp->symoff, 0);
}
svalsz = 0;
if(newformat) {
svalsz = buf[7];
if(svalsz != 4 && svalsz != 8) {
werrstr("invalid word size %d bytes", svalsz);
return -1;
}
symsz -= 8;
}
nsym = 0;
size = 0;
for(p = symbols; size < symsz; p++, nsym++) {
if(newformat) {
// Go 1.1 format. See comment at top of ../pkg/runtime/symtab.c.
if(Bread(&b, &c, 1) != 1)
return symerrmsg(1, "symbol");
if((c&0x3F) < 26)
p->type = (c&0x3F)+ 'A';
else
p->type = (c&0x3F) - 26 + 'a';
size++;
if(c&0x40) {
// Fixed-width address.
if(svalsz == 8) {
if(Bread(&b, &vl, 8) != 8)
return symerrmsg(8, "symbol");
p->value = swav(vl);
} else {
if(Bread(&b, &l, 4) != 4)
return symerrmsg(4, "symbol");
p->value = (u32int)swal(l);
}
size += svalsz;
} else {
// Varint address.
shift = 0;
p->value = 0;
for(;;) {
if(Bread(&b, buf, 1) != 1)
return symerrmsg(1, "symbol");
p->value |= (uint64)(buf[0]&0x7F)<<shift;
shift += 7;
size++;
if((buf[0]&0x80) == 0)
break;
}
}
p->gotype = 0;
if(c&0x80) {
// Has Go type. Fixed-width address.
if(svalsz == 8) {
if(Bread(&b, &vl, 8) != 8)
return symerrmsg(8, "symbol");
p->gotype = swav(vl);
} else {
if(Bread(&b, &l, 4) != 4)
return symerrmsg(4, "symbol");
p->gotype = (u32int)swal(l);
}
size += svalsz;
}
// Name.
p->type |= 0x80; // for decodename
i = decodename(&b, p);
if(i < 0)
return -1;
size += i;
} else {
// Go 1.0 format: Plan 9 format + go type symbol.
if(fp->_magic && (fp->magic & HDR_MAGIC)){
svalsz = 8;
if(Bread(&b, &vl, 8) != 8)
return symerrmsg(8, "symbol");
p->value = swav(vl);
}
else{
svalsz = 4;
if(Bread(&b, &l, 4) != 4)
return symerrmsg(4, "symbol");
p->value = (u32int)swal(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);
if(svalsz == 8){
if(Bread(&b, &vl, 8) != 8)
return symerrmsg(8, "symbol");
p->gotype = swav(vl);
}
else{
if(Bread(&b, &l, 4) != 4)
return symerrmsg(4, "symbol");
p->gotype = (u32int)swal(l);
}
size += svalsz;
}
/* 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: %d 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 %d 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 %d 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 int32
decodename(Biobuf *bp, Sym *p)
{
char *cp;
int c1, c2;
int32 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 %d bytes", n);
return -1;
}
Bseek(bp, -n, 1);
if(Bread(bp, p->name, n) != n) {
werrstr("can't read %d 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 %d 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;
go12clean();
}
/*
* 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(int32 *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)
{
int32 i;
int j, nh, ng, nt;
File *f;
Txtsym *tp;
Hist *hp;
Sym *p, **ap;
if(isbuilt)
return 1;
isbuilt = 1;
/* allocate the tables */
firstinstr = 0;
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++) {
//print("sym %d type %c name %s value %llux\n", p-symbols, p->type, p->name, p->value);
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 (firstinstr == 0 || p->value < firstinstr)
firstinstr = 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 */
}
/*
* strcmp, but allow '_' to match center dot (rune 00b7 == bytes c2 b7)
*/
int
cdotstrcmp(char *sym, char *user)
{
for (;;) {
while (*sym == *user) {
if (*sym++ == '\0')
return 0;
user++;
}
/* unequal - but maybe '_' matches center dot */
if (user[0] == '_' && (sym[0]&0xFF) == 0xc2 && (sym[1]&0xFF) == 0xb7) {
/* '_' matches center dot - advance and continue */
user++;
sym += 2;
continue;
}
break;
}
return *user - *sym;
}
/*
* find a function by name
*/
static int
findtext(char *name, Symbol *s)
{
int i;
for(i = 0; i < ntxt; i++) {
if(cdotstrcmp(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)
{
int32 i;
for(i = 0; i < nglob; i++) {
if(cdotstrcmp(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 (cdotstrcmp(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, int32 line)
{
File *fp;
int32 i;
uvlong pc, start, end;
short *name;
if(isgo12pcline())
return go12file2pc(file, line);
if(buildtbls() == 0 || files == 0)
return ~(uvlong)0;
name = encfname(file);
if(name == 0) { /* encode the file name */
werrstr("file %s not found", file);
return ~(uvlong)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 %d in file %s not found", line, file);
return ~(uvlong)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 %d - between: %llux and %llux\n", line, start, end);
pc = line2addr(line, start, end);
if(pc == ~(uvlong)0) {
werrstr("line %d not in file %s", line, file);
return ~(uvlong)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, int32 *line)
{
Hist *hp;
int offset, depth;
int32 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.
*/
int32
fileline(char *str, int n, uvlong dot)
{
int32 line, top, bot, mid;
File *f;
if(isgo12pcline())
return go12fileline(str, n, dot);
*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, int32 line, Hist *base, Hist **ret)
{
Hist *start; /* start of current level */
Hist *h; /* current entry */
int32 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, ":%d", 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(isgo12pcline())
return go12pc2sp(pc);
if(spoff == 0)
return ~(uvlong)0;
currsp = 0;
currpc = txtstart - mach->pcquant;
if(pc<currpc || pc>txtend)
return ~(uvlong)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 ~(uvlong)0;
}
/*
* find the source file line number for a given value of the pc
*/
int32
pc2line(uvlong pc)
{
uchar *c, u;
uvlong currpc;
int32 currline;
if(pcline == 0)
return -1;
currline = 0;
if (firstinstr != 0)
currpc = firstinstr-mach->pcquant;
else
currpc = txtstart-mach->pcquant;
if(pc<currpc || pc>txtend)
return -1;
for(c = pcline; c < pclineend && currpc < pc; c++) {
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 currline;
}
/*
* 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(int32 line, uvlong basepc, uvlong endpc)
{
uchar *c, u;
uvlong currpc, pc;
int32 currline;
int32 delta, d;
int found;
if(pcline == 0 || line == 0)
return ~(uvlong)0;
currline = 0;
currpc = txtstart-mach->pcquant;
pc = ~(uvlong)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 ~(uvlong)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: %x (%d) Offset: %x (%d) 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
// Go 1.2 pcln table
// See golang.org/s/go12symtab.
// Func layout
#define FuncEntry (0)
#define FuncName (pcptrsize)
#define FuncArgs (pcptrsize+4)
#define FuncFrame (pcptrsize+2*4)
#define FuncPCSP (pcptrsize+3*4)
#define FuncPCFile (pcptrsize+4*4)
#define FuncPCLine (pcptrsize+5*4)
static int32 pcquantum;
static int32 pcptrsize;
static uvlong (*pcswav)(uvlong);
static uint32 (*pcswal)(uint32);
static uvlong (*pcuintptr)(uchar*);
static uchar *functab;
static uint32 nfunctab;
static uint32 *filetab;
static uint32 nfiletab;
static uint32
xswal(uint32 v)
{
return (v>>24) | ((v>>8)&0xFF00) | ((v<<8)&0xFF0000) | v<<24;
}
static uvlong
xswav(uvlong v)
{
return (uvlong)xswal(v)<<32 | xswal(v>>32);
}
static uvlong
noswav(uvlong v)
{
return v;
}
static uint32
noswal(uint32 v)
{
return v;
}
static uvlong
readuintptr64(uchar *p)
{
return pcswav(*(uvlong*)p);
}
static uvlong
readuintptr32(uchar *p)
{
return pcswal(*(uint32*)p);
}
static void
go12clean(void)
{
pcquantum = 0;
pcswav = nil;
pcswal = nil;
functab = nil;
nfunctab = 0;
filetab = nil;
nfiletab = 0;
}
static void
go12init(void)
{
uint32 m;
uchar *p;
if(pcquantum != 0)
return;
pcquantum = -1; // not go 1.2
if(pcline == nil || pclineend - pcline < 16 ||
pcline[4] != 0 || pcline[5] != 0 ||
(pcline[6] != 1 && pcline[6] != 4) ||
(pcline[7] != 4 && pcline[7] != 8))
return;
// header is magic, 00 00 pcquantum ptrsize
m = *(uint32*)pcline;
if(m == Go12PclnMagic) {
pcswav = noswav;
pcswal = noswal;
} else {
pcswav = xswav;
pcswal = xswal;
}
pcptrsize = pcline[7];
if(pcptrsize == 4)
pcuintptr = readuintptr32;
else
pcuintptr = readuintptr64;
nfunctab = pcuintptr(pcline+8);
functab = pcline + 8 + pcptrsize;
// functab is 2*nfunctab pointer-sized values.
// The offset to the file table follows.
p = functab + nfunctab*2*pcptrsize + pcptrsize;
if(p+4 > pclineend)
return;
filetab = (uint32*)(pcline + pcswal(*(uint32*)p));
if((uchar*)filetab+4 > pclineend)
return;
// File table begins with count.
nfiletab = pcswal(filetab[0]);
if((uchar*)(filetab + nfiletab) > pclineend)
return;
// Committed.
pcquantum = pcline[6];
}
static int
isgo12pcline(void)
{
go12init();
return pcquantum > 0;
}
static uchar*
go12findfunc(uvlong pc)
{
uchar *f, *fm;
int32 nf, m;
if(pc < pcuintptr(functab) || pc >= pcuintptr(functab+2*nfunctab*pcptrsize))
return nil;
// binary search to find func with entry <= addr.
f = functab;
nf = nfunctab;
while(nf > 0) {
m = nf/2;
fm = f + 2*pcptrsize*m;
if(pcuintptr(fm) <= pc && pc < pcuintptr(fm+2*pcptrsize)) {
f = pcline + pcuintptr(fm+pcptrsize);
if(f >= pclineend)
return nil;
return f;
} else if(pc < pcuintptr(fm))
nf = m;
else {
f += (m+1)*2*pcptrsize;
nf -= m+1;
}
}
return nil;
}
static uint32
readvarint(uchar **pp)
{
uchar *p;
uint32 v;
int32 shift;
v = 0;
p = *pp;
for(shift = 0;; shift += 7) {
v |= (*p & 0x7F) << shift;
if(!(*p++ & 0x80))
break;
}
*pp = p;
return v;
}
static char*
pcstring(uint32 off)
{
if(off == 0 || off >= pclineend - pcline ||
memchr(pcline + off, '\0', pclineend - (pcline + off)) == nil)
return "?";
return (char*)pcline+off;
}
static int
step(uchar **pp, uvlong *pc, int32 *value, int first)
{
uint32 uvdelta, pcdelta;
int32 vdelta;
uvdelta = readvarint(pp);
if(uvdelta == 0 && !first)
return 0;
if(uvdelta&1)
uvdelta = ~(uvdelta>>1);
else
uvdelta >>= 1;
vdelta = (int32)uvdelta;
pcdelta = readvarint(pp) * pcquantum;
*value += vdelta;
*pc += pcdelta;
return 1;
}
static int32
pcvalue(uint32 off, uvlong entry, uvlong targetpc)
{
uvlong pc;
int32 val;
uchar *p;
val = -1;
pc = entry;
if(off == 0 || off >= pclineend - pcline)
return -1;
p = pcline + off;
while(step(&p, &pc, &val, pc == entry)) {
if(targetpc < pc)
return val;
}
return -1;
}
static uvlong
go12pc2sp(uvlong pc)
{
uchar *f;
uint32 off;
uvlong entry;
int32 sp;
f = go12findfunc(pc);
if(f == nil)
return ~(uvlong)0;
entry = pcuintptr(f+FuncEntry);
off = pcswal(*(uint32*)(f+FuncPCSP));
sp = pcvalue(off, entry, pc);
if(sp < 0)
return ~(uvlong)0;
return sp;
}
static int32
go12fileline(char *str, int n, uvlong pc)
{
uchar *f;
uint32 fileoff, lineoff;
uvlong entry;
int lno, fno;
f = go12findfunc(pc);
if(f == nil)
return 0;
entry = pcuintptr(f+FuncEntry);
fileoff = pcswal(*(uint32*)(f+FuncPCFile));
lineoff = pcswal(*(uint32*)(f+FuncPCLine));
lno = pcvalue(lineoff, entry, pc);
fno = pcvalue(fileoff, entry, pc);
if(lno < 0 || fno <= 0 || fno >= nfiletab) {
return 0;
}
snprint(str, n, "%s:%d", pcstring(pcswal(filetab[fno])), lno);
return 1;
}
static uvlong
go12file2pc(char *file, int line)
{
int fno;
int32 i, fval, lval;
uchar *func, *fp, *lp;
uvlong fpc, lpc, fstartpc, lstartpc, entry;
// Map file to file number.
// NOTE(rsc): Could introduce a hash table for repeated
// lookups if anyone ever calls this.
for(fno=1; fno<nfiletab; fno++)
if(strcmp(pcstring(pcswal(filetab[fno])), file) == 0)
goto havefile;
werrstr("cannot find file");
return ~(uvlong)0;
havefile:
// Consider each func.
// Run file number program to find file match,
// then run line number program to find line match.
// Most file number programs are tiny, and most will
// not mention the file number, so this should be fairly
// quick.
for(i=0; i<nfunctab; i++) {
func = pcline + pcuintptr(functab+i*2*pcptrsize+pcptrsize);
entry = pcuintptr(func+FuncEntry);
fp = pcline + pcswal(*(uint32*)(func+FuncPCFile));
lp = pcline + pcswal(*(uint32*)(func+FuncPCLine));
fval = lval = -1;
lpc = entry;
fpc = lpc;
fstartpc = fpc;
while(step(&fp, &fpc, &fval, fpc==entry)) {
if(fval == fno && fstartpc < fpc) {
lstartpc = lpc;
while(lpc < fpc && step(&lp, &lpc, &lval, lpc==entry)) {
if(lval == line) {
if(fstartpc <= lstartpc) {
return lstartpc;
}
if(fstartpc < lpc) {
return fstartpc;
}
}
lstartpc = lpc;
}
}
fstartpc = fpc;
}
}
werrstr("cannot find line in file");
return ~(uvlong)0;
}
// This is stubbed out for the moment. Will revisit when the time comes.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
int
ctlproc(int pid, char *msg)
{
USED(pid);
USED(msg);
sysfatal("ctlproc unimplemented in Windows");
return -1;
}
char*
proctextfile(int pid)
{
USED(pid);
sysfatal("proctextfile unimplemented in Windows");
return nil;
}
char*
procstatus(int pid)
{
USED(pid);
sysfatal("procstatus unimplemented in Windows");
return nil;
}
Map*
attachproc(int pid, Fhdr *fp)
{
USED(pid);
USED(fp);
sysfatal("attachproc unimplemented in Windows");
return nil;
}
void
detachproc(Map *m)
{
USED(m);
sysfatal("detachproc unimplemented in Windows");
}
int
procthreadpids(int pid, int *p, int np)
{
USED(pid);
USED(p);
USED(np);
sysfatal("procthreadpids unimplemented in Windows");
return -1;
}
int
pread(int fd, void *buf, int count, int offset)
{
int oldoffset, n;
oldoffset = seek(fd, offset, 0);
n = read(fd, buf, count);
seek(fd, oldoffset, 0);
return n;
}
int
pwrite(int fd, void *buf, int count, int offset)
{
USED(fd);
USED(buf);
USED(count);
USED(offset);
sysfatal("pwrite unimplemented in Windows");
return -1;
}
int
nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
{
USED(rqtp);
USED(rmtp);
sysfatal("nanosleep unimplemented in Windows");
return -1;
}
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