mirror of
https://github.com/golang/go
synced 2024-10-04 08:31:22 -06:00
19fd5c787f
That is, move the pc/ln table and the symbol table into the read-only data segment. This eliminates the need for a special load command to map the symbol table into memory, which makes the information available on systems that couldn't handle the magic load to 0x99000000, like NaCl and ARM QEMU and Linux without config_highmem=y. It also eliminates an #ifdef and some clumsy code to find the symbol table on Windows. The bad news is that the binary appears to be bigger than it used to be. This is not actually the case, though: the same amount of data is being mapped into memory as before, and the tables are still read-only, so they're still shared across multiple instances of the binary as they were before. The difference is just that the tables aren't squirreled away in some section that "size" doesn't know to look at. This is a checkpoint. It probably breaks Windows and breaks NaCl more than it used to be broken, but those will be fixed. The logic involving -s needs to be revisited too. Fixes #871. R=ken2 CC=golang-dev https://golang.org/cl/2587041
404 lines
7.8 KiB
C
404 lines
7.8 KiB
C
// 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.
|
|
|
|
// Runtime symbol table access. Work in progress.
|
|
// The Plan 9 symbol table is not in a particularly convenient form.
|
|
// The routines here massage it into a more usable form; eventually
|
|
// we'll change 6l to do this for us, but it is easier to experiment
|
|
// here than to change 6l and all the other tools.
|
|
//
|
|
// The symbol table also needs to be better integrated with the type
|
|
// strings table in the future. This is just a quick way to get started
|
|
// and figure out exactly what we want.
|
|
|
|
#include "runtime.h"
|
|
#include "defs.h"
|
|
#include "os.h"
|
|
#include "arch.h"
|
|
|
|
extern byte pclntab[], epclntab[], symtab[], esymtab[];
|
|
|
|
typedef struct Sym Sym;
|
|
struct Sym
|
|
{
|
|
uintptr value;
|
|
byte symtype;
|
|
byte *name;
|
|
// byte *gotype;
|
|
};
|
|
|
|
// Walk over symtab, calling fn(&s) for each symbol.
|
|
static void
|
|
walksymtab(void (*fn)(Sym*))
|
|
{
|
|
byte *p, *ep, *q;
|
|
Sym s;
|
|
|
|
p = symtab;
|
|
ep = esymtab;
|
|
while(p < ep) {
|
|
if(p + 7 > ep)
|
|
break;
|
|
s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]);
|
|
|
|
if(!(p[4]&0x80))
|
|
break;
|
|
s.symtype = p[4] & ~0x80;
|
|
p += 5;
|
|
s.name = p;
|
|
if(s.symtype == 'z' || s.symtype == 'Z') {
|
|
// path reference string - skip first byte,
|
|
// then 2-byte pairs ending at two zeros.
|
|
q = p+1;
|
|
for(;;) {
|
|
if(q+2 > ep)
|
|
return;
|
|
if(q[0] == '\0' && q[1] == '\0')
|
|
break;
|
|
q += 2;
|
|
}
|
|
p = q+2;
|
|
}else{
|
|
q = mchr(p, '\0', ep);
|
|
if(q == nil)
|
|
break;
|
|
p = q+1;
|
|
}
|
|
p += 4; // go type
|
|
fn(&s);
|
|
}
|
|
}
|
|
|
|
// Symtab walker; accumulates info about functions.
|
|
|
|
static Func *func;
|
|
static int32 nfunc;
|
|
|
|
static byte **fname;
|
|
static int32 nfname;
|
|
|
|
static Lock funclock;
|
|
|
|
static void
|
|
dofunc(Sym *sym)
|
|
{
|
|
Func *f;
|
|
|
|
switch(sym->symtype) {
|
|
case 't':
|
|
case 'T':
|
|
case 'l':
|
|
case 'L':
|
|
if(strcmp(sym->name, (byte*)"etext") == 0)
|
|
break;
|
|
if(func == nil) {
|
|
nfunc++;
|
|
break;
|
|
}
|
|
f = &func[nfunc++];
|
|
f->name = gostringnocopy(sym->name);
|
|
f->entry = sym->value;
|
|
if(sym->symtype == 'L' || sym->symtype == 'l')
|
|
f->frame = -sizeof(uintptr);
|
|
break;
|
|
case 'm':
|
|
if(nfunc > 0 && func != nil)
|
|
func[nfunc-1].frame += sym->value;
|
|
break;
|
|
case 'p':
|
|
if(nfunc > 0 && func != nil) {
|
|
f = &func[nfunc-1];
|
|
// args counts 32-bit words.
|
|
// sym->value is the arg's offset.
|
|
// don't know width of this arg, so assume it is 64 bits.
|
|
if(f->args < sym->value/4 + 2)
|
|
f->args = sym->value/4 + 2;
|
|
}
|
|
break;
|
|
case 'f':
|
|
if(fname == nil) {
|
|
if(sym->value >= nfname) {
|
|
if(sym->value >= 0x10000) {
|
|
printf("invalid symbol file index %p\n", sym->value);
|
|
throw("mangled symbol table");
|
|
}
|
|
nfname = sym->value+1;
|
|
}
|
|
break;
|
|
}
|
|
fname[sym->value] = sym->name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// put together the path name for a z entry.
|
|
// the f entries have been accumulated into fname already.
|
|
static void
|
|
makepath(byte *buf, int32 nbuf, byte *path)
|
|
{
|
|
int32 n, len;
|
|
byte *p, *ep, *q;
|
|
|
|
if(nbuf <= 0)
|
|
return;
|
|
|
|
p = buf;
|
|
ep = buf + nbuf;
|
|
*p = '\0';
|
|
for(;;) {
|
|
if(path[0] == 0 && path[1] == 0)
|
|
break;
|
|
n = (path[0]<<8) | path[1];
|
|
path += 2;
|
|
if(n >= nfname)
|
|
break;
|
|
q = fname[n];
|
|
len = findnull(q);
|
|
if(p+1+len >= ep)
|
|
break;
|
|
if(p > buf && p[-1] != '/')
|
|
*p++ = '/';
|
|
mcpy(p, q, len+1);
|
|
p += len;
|
|
}
|
|
}
|
|
|
|
// walk symtab accumulating path names for use by pc/ln table.
|
|
// don't need the full generality of the z entry history stack because
|
|
// there are no includes in go (and only sensible includes in our c);
|
|
// assume code only appear in top-level files.
|
|
static void
|
|
dosrcline(Sym *sym)
|
|
{
|
|
static byte srcbuf[1000];
|
|
static struct {
|
|
String srcstring;
|
|
int32 aline;
|
|
int32 delta;
|
|
} files[200];
|
|
static int32 incstart;
|
|
static int32 nfunc, nfile, nhist;
|
|
Func *f;
|
|
int32 i;
|
|
|
|
switch(sym->symtype) {
|
|
case 't':
|
|
case 'T':
|
|
if(strcmp(sym->name, (byte*)"etext") == 0)
|
|
break;
|
|
f = &func[nfunc++];
|
|
// find source file
|
|
for(i = 0; i < nfile - 1; i++) {
|
|
if (files[i+1].aline > f->ln0)
|
|
break;
|
|
}
|
|
f->src = files[i].srcstring;
|
|
f->ln0 -= files[i].delta;
|
|
break;
|
|
case 'z':
|
|
if(sym->value == 1) {
|
|
// entry for main source file for a new object.
|
|
makepath(srcbuf, sizeof srcbuf, sym->name+1);
|
|
nhist = 0;
|
|
nfile = 0;
|
|
if(nfile == nelem(files))
|
|
return;
|
|
files[nfile].srcstring = gostring(srcbuf);
|
|
files[nfile].aline = 0;
|
|
files[nfile++].delta = 0;
|
|
} else {
|
|
// push or pop of included file.
|
|
makepath(srcbuf, sizeof srcbuf, sym->name+1);
|
|
if(srcbuf[0] != '\0') {
|
|
if(nhist++ == 0)
|
|
incstart = sym->value;
|
|
if(nhist == 0 && nfile < nelem(files)) {
|
|
// new top-level file
|
|
files[nfile].srcstring = gostring(srcbuf);
|
|
files[nfile].aline = sym->value;
|
|
// this is "line 0"
|
|
files[nfile++].delta = sym->value - 1;
|
|
}
|
|
}else{
|
|
if(--nhist == 0)
|
|
files[nfile-1].delta += sym->value - incstart;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Interpret pc/ln table, saving the subpiece for each func.
|
|
static void
|
|
splitpcln(void)
|
|
{
|
|
int32 line;
|
|
uintptr pc;
|
|
byte *p, *ep;
|
|
Func *f, *ef;
|
|
int32 pcquant;
|
|
|
|
if(pclntab == epclntab || nfunc == 0)
|
|
return;
|
|
|
|
switch(thechar) {
|
|
case '5':
|
|
pcquant = 4;
|
|
break;
|
|
default: // 6, 8
|
|
pcquant = 1;
|
|
break;
|
|
}
|
|
|
|
// pc/ln table bounds
|
|
p = pclntab;
|
|
ep = epclntab;
|
|
|
|
f = func;
|
|
ef = func + nfunc;
|
|
pc = func[0].entry; // text base
|
|
f->pcln.array = p;
|
|
f->pc0 = pc - pcquant;
|
|
line = 0;
|
|
for(; p < ep; p++) {
|
|
if(f < ef && pc > (f+1)->entry) {
|
|
f->pcln.len = p - f->pcln.array;
|
|
f->pcln.cap = f->pcln.len;
|
|
f++;
|
|
f->pcln.array = p;
|
|
f->pc0 = pc;
|
|
f->ln0 = line;
|
|
}
|
|
if(*p == 0) {
|
|
// 4 byte add to line
|
|
line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
|
|
p += 4;
|
|
} else if(*p <= 64) {
|
|
line += *p;
|
|
} else if(*p <= 128) {
|
|
line -= *p - 64;
|
|
} else {
|
|
pc += pcquant*(*p - 129);
|
|
}
|
|
pc += pcquant;
|
|
}
|
|
if(f < ef) {
|
|
f->pcln.len = p - f->pcln.array;
|
|
f->pcln.cap = f->pcln.len;
|
|
}
|
|
}
|
|
|
|
|
|
// Return actual file line number for targetpc in func f.
|
|
// (Source file is f->src.)
|
|
// NOTE(rsc): If you edit this function, also edit extern.go:/FileLine
|
|
int32
|
|
funcline(Func *f, uint64 targetpc)
|
|
{
|
|
byte *p, *ep;
|
|
uintptr pc;
|
|
int32 line;
|
|
int32 pcquant;
|
|
|
|
switch(thechar) {
|
|
case '5':
|
|
pcquant = 4;
|
|
break;
|
|
default: // 6, 8
|
|
pcquant = 1;
|
|
break;
|
|
}
|
|
|
|
p = f->pcln.array;
|
|
ep = p + f->pcln.len;
|
|
pc = f->pc0;
|
|
line = f->ln0;
|
|
for(; p < ep && pc <= targetpc; p++) {
|
|
if(*p == 0) {
|
|
line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
|
|
p += 4;
|
|
} else if(*p <= 64) {
|
|
line += *p;
|
|
} else if(*p <= 128) {
|
|
line -= *p - 64;
|
|
} else {
|
|
pc += pcquant*(*p - 129);
|
|
}
|
|
pc += pcquant;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
static void
|
|
buildfuncs(void)
|
|
{
|
|
extern byte etext[];
|
|
|
|
if(func != nil)
|
|
return;
|
|
|
|
// Memory profiling uses this code;
|
|
// can deadlock if the profiler ends
|
|
// up back here.
|
|
m->nomemprof++;
|
|
|
|
// count funcs, fnames
|
|
nfunc = 0;
|
|
nfname = 0;
|
|
walksymtab(dofunc);
|
|
|
|
// initialize tables
|
|
func = mal((nfunc+1)*sizeof func[0]);
|
|
func[nfunc].entry = (uint64)etext;
|
|
fname = mal(nfname*sizeof fname[0]);
|
|
nfunc = 0;
|
|
walksymtab(dofunc);
|
|
|
|
// split pc/ln table by func
|
|
splitpcln();
|
|
|
|
// record src file and line info for each func
|
|
walksymtab(dosrcline);
|
|
|
|
m->nomemprof--;
|
|
}
|
|
|
|
Func*
|
|
findfunc(uintptr addr)
|
|
{
|
|
Func *f;
|
|
int32 nf, n;
|
|
|
|
lock(&funclock);
|
|
if(func == nil)
|
|
buildfuncs();
|
|
unlock(&funclock);
|
|
|
|
if(nfunc == 0)
|
|
return nil;
|
|
if(addr < func[0].entry || addr >= func[nfunc].entry)
|
|
return nil;
|
|
|
|
// binary search to find func with entry <= addr.
|
|
f = func;
|
|
nf = nfunc;
|
|
while(nf > 0) {
|
|
n = nf/2;
|
|
if(f[n].entry <= addr && addr < f[n+1].entry)
|
|
return &f[n];
|
|
else if(addr < f[n].entry)
|
|
nf = n;
|
|
else {
|
|
f += n+1;
|
|
nf -= n+1;
|
|
}
|
|
}
|
|
|
|
// can't get here -- we already checked above
|
|
// that the address was in the table bounds.
|
|
// this can only happen if the table isn't sorted
|
|
// by address or if the binary search above is buggy.
|
|
prints("findfunc unreachable\n");
|
|
return nil;
|
|
}
|