summaryrefslogtreecommitdiff
path: root/src/cmd/dist/buildruntime.c
blob: e6e309d92a4b059227474099da55855aeb0e1071 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
// 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"
	},
	// 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",
		"#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], "WinCall"))
				aggr = "wincall";
			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[] = {
	"proc.c",
	"iface.c",
	"hashmap.c",
	"chan.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);
}