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