go/src/cmd/dist/buildruntime.c

478 lines
12 KiB
C
Raw Normal View History

// 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 "a.h"
/*
* Helpers for building pkg/runtime.
*/
// mkzversion writes zversion.go:
//
// package runtime
// const defaultGoroot = <goroot>
// const theVersion = <version>
//
void
mkzversion(char *dir, char *file)
{
Buf b, out;
USED(dir);
binit(&b);
binit(&out);
bwritestr(&out, bprintf(&b,
"// auto generated by go tool dist\n"
"\n"
"package runtime\n"
"\n"
"const defaultGoroot = `%s`\n"
"const theVersion = `%s`\n", goroot_final, goversion));
writefile(&out, file, 0);
bfree(&b);
bfree(&out);
}
// mkzexperiment writes zaexperiment.h (sic):
//
// #define GOEXPERIMENT "experiment string"
//
void
mkzexperiment(char *dir, char *file)
{
Buf b, out, exp;
USED(dir);
binit(&b);
binit(&out);
binit(&exp);
xgetenv(&exp, "GOEXPERIMENT");
bwritestr(&out, bprintf(&b,
"// auto generated by go tool dist\n"
"\n"
"#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
writefile(&out, file, 0);
bfree(&b);
bfree(&out);
bfree(&exp);
}
// mkzgoarch writes zgoarch_$GOARCH.go:
//
// package runtime
// const theGoarch = <goarch>
//
void
mkzgoarch(char *dir, char *file)
{
Buf b, out;
USED(dir);
binit(&b);
binit(&out);
bwritestr(&out, bprintf(&b,
"// auto generated by go tool dist\n"
"\n"
"package runtime\n"
"\n"
"const theGoarch = `%s`\n", goarch));
writefile(&out, file, 0);
bfree(&b);
bfree(&out);
}
// mkzgoos writes zgoos_$GOOS.go:
//
// package runtime
// const theGoos = <goos>
//
void
mkzgoos(char *dir, char *file)
{
Buf b, out;
USED(dir);
binit(&b);
binit(&out);
bwritestr(&out, bprintf(&b,
"// auto generated by go tool dist\n"
"\n"
"package runtime\n"
"\n"
"const theGoos = `%s`\n", goos));
writefile(&out, file, 0);
bfree(&b);
bfree(&out);
}
static struct {
char *goarch;
char *goos;
char *hdr;
} zasmhdr[] = {
{"386", "windows",
"#define get_tls(r) MOVL 0x14(FS), r\n"
"#define g(r) 0(r)\n"
"#define m(r) 4(r)\n"
},
{"386", "plan9",
"// Plan 9 does not have per-process segment descriptors with\n"
"// which to do thread-local storage. Instead, we will use a\n"
"// fixed offset from the per-process TOS struct address for\n"
"// the local storage. Since the process ID is contained in the\n"
"// TOS struct, we specify an offset for that here as well.\n"
"#define get_tls(r) MOVL _tos(SB), r \n"
"#define g(r) -8(r)\n"
"#define m(r) -4(r)\n"
"#define procid(r) 48(r)\n"
},
{"386", "linux",
"// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n"
"// turn into %gs:-8 and %gs:-4 (using gcc syntax to denote\n"
"// what the machine sees as opposed to 8l input).\n"
"// 8l rewrites 0(GS) and 4(GS) into these.\n"
"//\n"
"// On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4\n"
"// directly. Instead, we have to store %gs:0 into a temporary\n"
"// register and then use -8(%reg) and -4(%reg). This kind\n"
"// of addressing is correct even when not running Xen.\n"
"//\n"
"// 8l can rewrite MOVL 0(GS), CX into the appropriate pair\n"
"// of mov instructions, using CX as the intermediate register\n"
"// (safe because CX is about to be written to anyway).\n"
"// But 8l cannot handle other instructions, like storing into 0(GS),\n"
"// which is where these macros come into play.\n"
"// get_tls sets up the temporary and then g and r use it.\n"
"//\n"
"// Another wrinkle is that get_tls needs to read from %gs:0,\n"
"// but in 8l input it's called 8(GS), because 8l is going to\n"
"// subtract 8 from all the offsets, as described above.\n"
"//\n"
"// The final wrinkle is that when generating an ELF .o file for\n"
"// external linking mode, we need to be able to relocate the\n"
"// -8(r) and -4(r) instructions. Tag them with an extra (GS*1)\n"
"// that is ignored by the linker except for that identification.\n"
"#define get_tls(r) MOVL 8(GS), r\n"
"#define g(r) -8(r)(GS*1)\n"
"#define m(r) -4(r)(GS*1)\n"
},
{"386", "",
"#define get_tls(r)\n"
"#define g(r) 0(GS)\n"
"#define m(r) 4(GS)\n"
},
{"amd64", "windows",
"#define get_tls(r) MOVQ 0x28(GS), r\n"
"#define g(r) 0(r)\n"
"#define m(r) 8(r)\n"
},
{"amd64", "plan9",
"#define get_tls(r)\n"
"#define g(r) 0(GS)\n"
"#define m(r) 8(GS)\n"
"#define procid(r) 16(GS)\n"
},
{"amd64", "solaris",
"#define get_tls(r) MOVQ 0(FS), r\n"
"#define g(r) -16(r)(FS*1)\n"
"#define m(r) -8(r)(FS*1)\n"
},
runtime.cmd/ld: Add ARM external linking and implement -shared in terms of external linking This CL is an aggregate of 10271047, 10499043, 9733044. Descriptions of each follow: 10499043 runtime,cmd/ld: Merge TLS symbols and teach 5l about ARM TLS This CL prepares for external linking support to ARM. The pseudo-symbols runtime.g and runtime.m are merged into a single runtime.tlsgm symbol. When external linking, the offset of a thread local variable is stored at a memory location instead of being embedded into a offset of a ldr instruction. With a single runtime.tlsgm symbol for both g and m, only one such offset is needed. The larger part of this CL moves TLS code from gcc compiled to internally compiled. The TLS code now uses the modern MRC instruction, and 5l is taught about TLS fallbacks in case the instruction is not available or appropriate. 10271047 This CL adds support for -linkmode external to 5l. For 5l itself, use addrel to allow for D_CALL relocations to be handled by the host linker. Of the cases listed in rsc's comment in issue 4069, only case 5 and 63 needed an update. One of the TODO: addrel cases was since replaced, and the rest of the cases are either covered by indirection through addpool (cases with LTO or LFROM flags) or stubs (case 74). The addpool cases are covered because addpool emits AWORD instructions, which in turn are handled by case 11. In the runtime, change the argv argument in the rt0* functions slightly to be a pointer to the argv list, instead of relying on a particular location of argv. 9733044 The -shared flag to 6l outputs a shared library, implemented in Go and callable from non-Go programs such as C. The main part of this CL change the thread local storage model. Go uses the fastest and least general mode, local exec. TLS data in shared libraries normally requires at least the local dynamic mode, however, this CL instead opts for using the initial exec mode. Initial exec mode is faster than local dynamic mode and can be used in linux since the linker has reserved a limited amount of TLS space for performance sensitive TLS code. Initial exec mode requires an extra load from the GOT table to determine the TLS offset. This penalty will not be paid if ld is not in -shared mode, since TLS accesses will be reduced to local exec. The elf sections .init_array and .rela.init_array are added to register the Go runtime entry with cgo at library load time. The "hidden" attribute is added to Cgo functions called from Go, since Go does not generate call through the GOT table, and adding non-GOT relocations for a global function is not supported by gcc. Cgo symbols don't need to be global and avoiding the GOT table is also faster. The changes to 8l are only removes code relevant to the old -shared mode where internal linking was used. This CL only address the low level linker work. It can be submitted by itself, but to be useful, the runtime changes in CL 9738047 is also needed. Design discussion at https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/zmjXkGrEx6Q Fixes #5590. R=rsc CC=golang-dev https://golang.org/cl/12871044
2013-08-14 15:38:54 +00:00
// The TLS accessors here are defined here to use initial exec model.
// If the linker is not outputting a shared library, it will reduce
// the TLS accessors to the local exec model, effectively removing
// get_tls().
{"amd64", "linux",
runtime.cmd/ld: Add ARM external linking and implement -shared in terms of external linking This CL is an aggregate of 10271047, 10499043, 9733044. Descriptions of each follow: 10499043 runtime,cmd/ld: Merge TLS symbols and teach 5l about ARM TLS This CL prepares for external linking support to ARM. The pseudo-symbols runtime.g and runtime.m are merged into a single runtime.tlsgm symbol. When external linking, the offset of a thread local variable is stored at a memory location instead of being embedded into a offset of a ldr instruction. With a single runtime.tlsgm symbol for both g and m, only one such offset is needed. The larger part of this CL moves TLS code from gcc compiled to internally compiled. The TLS code now uses the modern MRC instruction, and 5l is taught about TLS fallbacks in case the instruction is not available or appropriate. 10271047 This CL adds support for -linkmode external to 5l. For 5l itself, use addrel to allow for D_CALL relocations to be handled by the host linker. Of the cases listed in rsc's comment in issue 4069, only case 5 and 63 needed an update. One of the TODO: addrel cases was since replaced, and the rest of the cases are either covered by indirection through addpool (cases with LTO or LFROM flags) or stubs (case 74). The addpool cases are covered because addpool emits AWORD instructions, which in turn are handled by case 11. In the runtime, change the argv argument in the rt0* functions slightly to be a pointer to the argv list, instead of relying on a particular location of argv. 9733044 The -shared flag to 6l outputs a shared library, implemented in Go and callable from non-Go programs such as C. The main part of this CL change the thread local storage model. Go uses the fastest and least general mode, local exec. TLS data in shared libraries normally requires at least the local dynamic mode, however, this CL instead opts for using the initial exec mode. Initial exec mode is faster than local dynamic mode and can be used in linux since the linker has reserved a limited amount of TLS space for performance sensitive TLS code. Initial exec mode requires an extra load from the GOT table to determine the TLS offset. This penalty will not be paid if ld is not in -shared mode, since TLS accesses will be reduced to local exec. The elf sections .init_array and .rela.init_array are added to register the Go runtime entry with cgo at library load time. The "hidden" attribute is added to Cgo functions called from Go, since Go does not generate call through the GOT table, and adding non-GOT relocations for a global function is not supported by gcc. Cgo symbols don't need to be global and avoiding the GOT table is also faster. The changes to 8l are only removes code relevant to the old -shared mode where internal linking was used. This CL only address the low level linker work. It can be submitted by itself, but to be useful, the runtime changes in CL 9738047 is also needed. Design discussion at https://groups.google.com/forum/?fromgroups#!topic/golang-nuts/zmjXkGrEx6Q Fixes #5590. R=rsc CC=golang-dev https://golang.org/cl/12871044
2013-08-14 15:38:54 +00:00
"#define get_tls(r) MOVQ runtime·tlsgm(SB), r\n"
"#define g(r) 0(r)(GS*1)\n"
"#define m(r) 8(r)(GS*1)\n"
},
{"amd64", "",
"#define get_tls(r)\n"
"#define g(r) 0(GS)\n"
"#define m(r) 8(GS)\n"
},
{"arm", "",
"#define LR R14\n"
},
};
#define MAXWINCB 2000 /* maximum number of windows callbacks allowed */
// mkzasm writes zasm_$GOOS_$GOARCH.h,
// which contains struct offsets for use by
// assembly files. It also writes a copy to the work space
// under the name zasm_GOOS_GOARCH.h (no expansion).
//
void
mkzasm(char *dir, char *file)
{
int i, n;
char *aggr, *p;
Buf in, b, out, exp;
Vec argv, lines, fields;
binit(&in);
binit(&b);
binit(&out);
binit(&exp);
vinit(&argv);
vinit(&lines);
vinit(&fields);
bwritestr(&out, "// auto generated by go tool dist\n\n");
for(i=0; i<nelem(zasmhdr); i++) {
if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) {
bwritestr(&out, zasmhdr[i].hdr);
goto ok;
}
}
fatal("unknown $GOOS/$GOARCH in mkzasm");
ok:
// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c
// to get acid [sic] output.
vreset(&argv);
vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));
vadd(&argv, "-D");
vadd(&argv, bprintf(&b, "GOOS_%s", goos));
vadd(&argv, "-D");
vadd(&argv, bprintf(&b, "GOARCH_%s", goarch));
vadd(&argv, "-I");
vadd(&argv, bprintf(&b, "%s", workdir));
vadd(&argv, "-a");
vadd(&argv, "-n");
vadd(&argv, "-o");
vadd(&argv, bpathf(&b, "%s/proc.acid", workdir));
vadd(&argv, "proc.c");
runv(nil, dir, CheckExit, &argv);
readfile(&in, bpathf(&b, "%s/proc.acid", workdir));
// Convert input like
// aggr G
// {
// Gobuf 24 sched;
// 'Y' 48 stack0;
// }
// StackMin = 128;
// into output like
// #define g_sched 24
// #define g_stack0 48
// #define const_StackMin 128
aggr = nil;
splitlines(&lines, bstr(&in));
for(i=0; i<lines.len; i++) {
splitfields(&fields, lines.p[i]);
if(fields.len == 2 && streq(fields.p[0], "aggr")) {
if(streq(fields.p[1], "G"))
aggr = "g";
else if(streq(fields.p[1], "M"))
aggr = "m";
else if(streq(fields.p[1], "P"))
aggr = "p";
else if(streq(fields.p[1], "Gobuf"))
aggr = "gobuf";
else if(streq(fields.p[1], "LibCall"))
aggr = "libcall";
else if(streq(fields.p[1], "WinCallbackContext"))
aggr = "cbctxt";
else if(streq(fields.p[1], "SEH"))
aggr = "seh";
}
if(hasprefix(lines.p[i], "}"))
aggr = nil;
if(aggr && hasprefix(lines.p[i], "\t") && fields.len >= 2) {
n = fields.len;
p = fields.p[n-1];
if(p[xstrlen(p)-1] == ';')
p[xstrlen(p)-1] = '\0';
bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
}
if(fields.len == 3 && streq(fields.p[1], "=")) { // generated from enumerated constants
p = fields.p[2];
if(p[xstrlen(p)-1] == ';')
p[xstrlen(p)-1] = '\0';
bwritestr(&out, bprintf(&b, "#define const_%s %s\n", fields.p[0], p));
}
}
// Some #defines that are used for .c files.
if(streq(goos, "windows")) {
bwritestr(&out, bprintf(&b, "#define cb_max %d\n", MAXWINCB));
}
xgetenv(&exp, "GOEXPERIMENT");
bwritestr(&out, bprintf(&b, "#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
// Write both to file and to workdir/zasm_GOOS_GOARCH.h.
writefile(&out, file, 0);
writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), 0);
bfree(&in);
bfree(&b);
bfree(&out);
bfree(&exp);
vfree(&argv);
vfree(&lines);
vfree(&fields);
}
// mkzsys writes zsys_$GOOS_$GOARCH.h,
// which contains arch or os specific asm code.
//
void
mkzsys(char *dir, char *file)
{
int i;
Buf out;
USED(dir);
binit(&out);
bwritestr(&out, "// auto generated by go tool dist\n\n");
if(streq(goos, "windows")) {
bwritef(&out,
"// runtime·callbackasm is called by external code to\n"
"// execute Go implemented callback function. It is not\n"
"// called from the start, instead runtime·compilecallback\n"
"// always returns address into runtime·callbackasm offset\n"
"// appropriately so different callbacks start with different\n"
"// CALL instruction in runtime·callbackasm. This determines\n"
"// which Go callback function is executed later on.\n"
"TEXT runtime·callbackasm(SB),7,$0\n");
for(i=0; i<MAXWINCB; i++) {
bwritef(&out, "\tCALL\truntime·callbackasm1(SB)\n");
}
bwritef(&out, "\tRET\n");
}
writefile(&out, file, 0);
bfree(&out);
}
static char *runtimedefs[] = {
runtime: use goc2c as much as possible Package runtime's C functions written to be called from Go started out written in C using carefully constructed argument lists and the FLUSH macro to write a result back to memory. For some functions, the appropriate parameter list ended up being architecture-dependent due to differences in alignment, so we added 'goc2c', which takes a .goc file containing Go func declarations but C bodies, rewrites the Go func declaration to equivalent C declarations for the target architecture, adds the needed FLUSH statements, and writes out an equivalent C file. That C file is compiled as part of package runtime. Native Client's x86-64 support introduces the most complex alignment rules yet, breaking many functions that could until now be portably written in C. Using goc2c for those avoids the breakage. Separately, Keith's work on emitting stack information from the C compiler would require the hand-written functions to add #pragmas specifying how many arguments are result parameters. Using goc2c for those avoids maintaining #pragmas. For both reasons, use goc2c for as many Go-called C functions as possible. This CL is a replay of the bulk of CL 15400047 and CL 15790043, both of which were reviewed as part of the NaCl port and are checked in to the NaCl branch. This CL is part of bringing the NaCl code into the main tree. No new code here, just reformatting and occasional movement into .h files. LGTM=r R=dave, alex.brainman, r CC=golang-codereviews https://golang.org/cl/65220044
2014-02-20 15:58:47 -05:00
"defs.c",
"proc.c",
"parfor.c",
};
// mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h,
// which contains Go struct definitions equivalent to the C ones.
// Mostly we just write the output of 6c -q to the file.
// However, we run it on multiple files, so we have to delete
// the duplicated definitions, and we don't care about the funcs
// and consts, so we delete those too.
//
void
mkzruntimedefs(char *dir, char *file)
{
int i, skip;
char *p;
Buf in, b, b1, out;
Vec argv, lines, fields, seen;
binit(&in);
binit(&b);
binit(&b1);
binit(&out);
vinit(&argv);
vinit(&lines);
vinit(&fields);
vinit(&seen);
bwritestr(&out, "// auto generated by go tool dist\n"
"\n"
"package runtime\n"
"import \"unsafe\"\n"
"var _ unsafe.Pointer\n"
"\n"
);
// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs
// on each of the runtimedefs C files.
vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));
vadd(&argv, "-D");
vadd(&argv, bprintf(&b, "GOOS_%s", goos));
vadd(&argv, "-D");
vadd(&argv, bprintf(&b, "GOARCH_%s", goarch));
vadd(&argv, "-I");
vadd(&argv, bprintf(&b, "%s", workdir));
vadd(&argv, "-q");
vadd(&argv, "-n");
vadd(&argv, "-o");
vadd(&argv, bpathf(&b, "%s/runtimedefs", workdir));
vadd(&argv, "");
p = argv.p[argv.len-1];
for(i=0; i<nelem(runtimedefs); i++) {
argv.p[argv.len-1] = runtimedefs[i];
runv(nil, dir, CheckExit, &argv);
readfile(&b, bpathf(&b1, "%s/runtimedefs", workdir));
bwriteb(&in, &b);
}
argv.p[argv.len-1] = p;
// Process the aggregate output.
skip = 0;
splitlines(&lines, bstr(&in));
for(i=0; i<lines.len; i++) {
p = lines.p[i];
// Drop comment, func, and const lines.
if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func"))
continue;
// Note beginning of type or var decl, which can be multiline.
// Remove duplicates. The linear check of seen here makes the
// whole processing quadratic in aggregate, but there are only
// about 100 declarations, so this is okay (and simple).
if(hasprefix(p, "type ") || hasprefix(p, "var ")) {
splitfields(&fields, p);
if(fields.len < 2)
continue;
if(find(fields.p[1], seen.p, seen.len) >= 0) {
if(streq(fields.p[fields.len-1], "{"))
skip = 1; // skip until }
continue;
}
vadd(&seen, fields.p[1]);
}
if(skip) {
if(hasprefix(p, "}"))
skip = 0;
continue;
}
bwritestr(&out, p);
}
writefile(&out, file, 0);
bfree(&in);
bfree(&b);
bfree(&b1);
bfree(&out);
vfree(&argv);
vfree(&lines);
vfree(&fields);
vfree(&seen);
}