blob: 4cad0892472fa6f75fe600b48159dda9cc6b4ef8 [file] [log] [blame]
// 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.
#include <u.h>
#include <libc.h>
#include "go.h"
/*
* architecture-independent object file output
*/
static void outhist(Biobuf *b);
static void dumpglobls(void);
void
dumpobj(void)
{
NodeList *externs, *tmp;
bout = Bopen(outfile, OWRITE);
if(bout == nil) {
flusherrors();
print("can't create %s: %r\n", outfile);
errorexit();
}
Bprint(bout, "go object %s %s %s %s\n", getgoos(), thestring, getgoversion(), expstring());
Bprint(bout, " exports automatically generated from\n");
Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name);
dumpexport();
Bprint(bout, "\n!\n");
outhist(bout);
externs = nil;
if(externdcl != nil)
externs = externdcl->end;
dumpglobls();
dumptypestructs();
// Dump extra globals.
tmp = externdcl;
if(externs != nil)
externdcl = externs->next;
dumpglobls();
externdcl = tmp;
dumpdata();
dumpfuncs();
Bterm(bout);
}
static void
dumpglobls(void)
{
Node *n;
NodeList *l;
// add globals
for(l=externdcl; l; l=l->next) {
n = l->n;
if(n->op != ONAME)
continue;
if(n->type == T)
fatal("external %N nil type\n", n);
if(n->class == PFUNC)
continue;
if(n->sym->pkg != localpkg)
continue;
dowidth(n->type);
ggloblnod(n);
}
for(l=funcsyms; l; l=l->next) {
n = l->n;
dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0);
ggloblsym(n->sym, widthptr, 1, 1);
}
}
void
Bputname(Biobuf *b, Sym *s)
{
Bprint(b, "%s", s->pkg->prefix);
BPUTC(b, '.');
Bwrite(b, s->name, strlen(s->name)+1);
}
static void
outzfile(Biobuf *b, char *p)
{
char *q, *q2;
while(p) {
q = utfrune(p, '/');
if(windows) {
q2 = utfrune(p, '\\');
if(q2 && (!q || q2 < q))
q = q2;
}
if(!q) {
zfile(b, p, strlen(p));
return;
}
if(q > p)
zfile(b, p, q-p);
p = q + 1;
}
}
#define isdelim(c) (c == '/' || c == '\\')
static void
outwinname(Biobuf *b, Hist *h, char *ds, char *p)
{
if(isdelim(p[0])) {
// full rooted name
zfile(b, ds, 3); // leading "c:/"
outzfile(b, p+1);
} else {
// relative name
if(h->offset >= 0 && pathname && pathname[1] == ':') {
if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) {
// using current drive
zfile(b, pathname, 3); // leading "c:/"
outzfile(b, pathname+3);
} else {
// using drive other then current,
// we don't have any simple way to
// determine current working directory
// there, therefore will output name as is
zfile(b, ds, 2); // leading "c:"
}
}
outzfile(b, p);
}
}
static void
outhist(Biobuf *b)
{
Hist *h;
char *p, ds[] = {'c', ':', '/', 0};
char *tofree;
int n;
static int first = 1;
static char *goroot, *goroot_final;
if(first) {
// Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL.
first = 0;
goroot = getenv("GOROOT");
goroot_final = getenv("GOROOT_FINAL");
if(goroot == nil)
goroot = "";
if(goroot_final == nil)
goroot_final = goroot;
if(strcmp(goroot, goroot_final) == 0) {
goroot = nil;
goroot_final = nil;
}
}
tofree = nil;
for(h = hist; h != H; h = h->link) {
p = h->name;
if(p) {
if(goroot != nil) {
n = strlen(goroot);
if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') {
tofree = smprint("%s%s", goroot_final, p+n);
p = tofree;
}
}
if(windows) {
// if windows variable is set, then, we know already,
// pathname is started with windows drive specifier
// and all '\' were replaced with '/' (see lex.c)
if(isdelim(p[0]) && isdelim(p[1])) {
// file name has network name in it,
// like \\server\share\dir\file.go
zfile(b, "//", 2); // leading "//"
outzfile(b, p+2);
} else if(p[1] == ':') {
// file name has drive letter in it
ds[0] = p[0];
outwinname(b, h, ds, p+2);
} else {
// no drive letter in file name
outwinname(b, h, pathname, p);
}
} else {
if(p[0] == '/') {
// full rooted name, like /home/rsc/dir/file.go
zfile(b, "/", 1); // leading "/"
outzfile(b, p+1);
} else {
// relative name, like dir/file.go
if(h->offset >= 0 && pathname && pathname[0] == '/') {
zfile(b, "/", 1); // leading "/"
outzfile(b, pathname+1);
}
outzfile(b, p);
}
}
}
zhist(b, h->line, h->offset);
if(tofree) {
free(tofree);
tofree = nil;
}
}
}
void
ieeedtod(uint64 *ieee, double native)
{
double fr, ho, f;
int exp;
uint32 h, l;
uint64 bits;
if(native < 0) {
ieeedtod(ieee, -native);
*ieee |= 1ULL<<63;
return;
}
if(native == 0) {
*ieee = 0;
return;
}
fr = frexp(native, &exp);
f = 2097152L; /* shouldn't use fp constants here */
fr = modf(fr*f, &ho);
h = ho;
h &= 0xfffffL;
f = 65536L;
fr = modf(fr*f, &ho);
l = ho;
l <<= 16;
l |= (int32)(fr*f);
bits = ((uint64)h<<32) | l;
if(exp < -1021) {
// gradual underflow
bits |= 1LL<<52;
bits >>= -1021 - exp;
exp = -1022;
}
bits |= (uint64)(exp+1022L) << 52;
*ieee = bits;
}
int
duint8(Sym *s, int off, uint8 v)
{
return duintxx(s, off, v, 1);
}
int
duint16(Sym *s, int off, uint16 v)
{
return duintxx(s, off, v, 2);
}
int
duint32(Sym *s, int off, uint32 v)
{
return duintxx(s, off, v, 4);
}
int
duint64(Sym *s, int off, uint64 v)
{
return duintxx(s, off, v, 8);
}
int
duintptr(Sym *s, int off, uint64 v)
{
return duintxx(s, off, v, widthptr);
}
Sym*
stringsym(char *s, int len)
{
static int gen;
Sym *sym;
int off, n, m;
struct {
Strlit lit;
char buf[110];
} tmp;
Pkg *pkg;
if(len > 100) {
// huge strings are made static to avoid long names
snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen);
pkg = localpkg;
} else {
// small strings get named by their contents,
// so that multiple modules using the same string
// can share it.
tmp.lit.len = len;
memmove(tmp.lit.s, s, len);
tmp.lit.s[len] = '\0';
snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit);
pkg = gostringpkg;
}
sym = pkglookup(namebuf, pkg);
// SymUniq flag indicates that data is generated already
if(sym->flags & SymUniq)
return sym;
sym->flags |= SymUniq;
sym->def = newname(sym);
off = 0;
// string header
off = dsymptr(sym, off, sym, widthptr+widthint);
off = duintxx(sym, off, len, widthint);
// string data
for(n=0; n<len; n+=m) {
m = 8;
if(m > len-n)
m = len-n;
off = dsname(sym, off, s+n, m);
}
off = duint8(sym, off, 0); // terminating NUL for runtime
off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment
ggloblsym(sym, off, 1, 1);
return sym;
}