github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/src/cmd/dist/unix.c (about)

     1  // Copyright 2012 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // These #ifdefs are being used as a substitute for
     6  // build configuration, so that on any system, this
     7  // tool can be built with the local equivalent of
     8  //	cc *.c
     9  //
    10  #ifndef WIN32
    11  #ifndef PLAN9
    12  
    13  #include "a.h"
    14  #include <unistd.h>
    15  #include <dirent.h>
    16  #include <sys/stat.h>
    17  #include <sys/wait.h>
    18  #include <sys/param.h>
    19  #include <sys/utsname.h>
    20  #include <fcntl.h>
    21  #include <string.h>
    22  #include <stdio.h>
    23  #include <stdlib.h>
    24  #include <errno.h>
    25  #include <stdarg.h>
    26  #include <setjmp.h>
    27  #include <signal.h>
    28  
    29  // bprintf replaces the buffer with the result of the printf formatting
    30  // and returns a pointer to the NUL-terminated buffer contents.
    31  char*
    32  bprintf(Buf *b, char *fmt, ...)
    33  {
    34  	va_list arg;
    35  	char buf[4096];
    36  	
    37  	breset(b);
    38  	va_start(arg, fmt);
    39  	vsnprintf(buf, sizeof buf, fmt, arg);
    40  	va_end(arg);
    41  	bwritestr(b, buf);
    42  	return bstr(b);
    43  }
    44  
    45  // bpathf is the same as bprintf (on windows it turns / into \ after the printf).
    46  // It returns a pointer to the NUL-terminated buffer contents.
    47  char*
    48  bpathf(Buf *b, char *fmt, ...)
    49  {
    50  	va_list arg;
    51  	char buf[4096];
    52  	
    53  	breset(b);
    54  	va_start(arg, fmt);
    55  	vsnprintf(buf, sizeof buf, fmt, arg);
    56  	va_end(arg);
    57  	bwritestr(b, buf);
    58  	return bstr(b);
    59  }
    60  
    61  // bwritef is like bprintf but does not reset the buffer
    62  // and does not return the NUL-terminated string.
    63  void
    64  bwritef(Buf *b, char *fmt, ...)
    65  {
    66  	va_list arg;
    67  	char buf[4096];
    68  	
    69  	va_start(arg, fmt);
    70  	vsnprintf(buf, sizeof buf, fmt, arg);
    71  	va_end(arg);
    72  	bwritestr(b, buf);
    73  }
    74  
    75  // breadfrom appends to b all the data that can be read from fd.
    76  static void
    77  breadfrom(Buf *b, int fd)
    78  {
    79  	int n;
    80  
    81  	for(;;) {
    82  		bgrow(b, 4096);
    83  		n = read(fd, b->p+b->len, 4096);
    84  		if(n < 0)
    85  			fatal("read: %s", strerror(errno));
    86  		if(n == 0)
    87  			break;
    88  		b->len += n;
    89  	}
    90  }
    91  
    92  // xgetenv replaces b with the value of the named environment variable.
    93  void
    94  xgetenv(Buf *b, char *name)
    95  {
    96  	char *p;
    97  	
    98  	breset(b);
    99  	p = getenv(name);
   100  	if(p != NULL)
   101  		bwritestr(b, p);
   102  }
   103  
   104  static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg);
   105  
   106  // run runs the command named by cmd.
   107  // If b is not nil, run replaces b with the output of the command.
   108  // If dir is not nil, run runs the command in that directory.
   109  // If mode is CheckExit, run calls fatal if the command is not successful.
   110  void
   111  run(Buf *b, char *dir, int mode, char *cmd, ...)
   112  {
   113  	va_list arg;
   114  	Vec argv;
   115  	char *p;
   116  	
   117  	vinit(&argv);
   118  	vadd(&argv, cmd);
   119  	va_start(arg, cmd);
   120  	while((p = va_arg(arg, char*)) != nil)
   121  		vadd(&argv, p);
   122  	va_end(arg);
   123  	
   124  	runv(b, dir, mode, &argv);
   125  	
   126  	vfree(&argv);
   127  }
   128  
   129  // runv is like run but takes a vector.
   130  void
   131  runv(Buf *b, char *dir, int mode, Vec *argv)
   132  {
   133  	genrun(b, dir, mode, argv, 1);
   134  }
   135  
   136  // bgrunv is like run but runs the command in the background.
   137  // bgwait waits for pending bgrunv to finish.
   138  void
   139  bgrunv(char *dir, int mode, Vec *argv)
   140  {
   141  	genrun(nil, dir, mode, argv, 0);
   142  }
   143  
   144  #define MAXBG 4 /* maximum number of jobs to run at once */
   145  
   146  static struct {
   147  	int pid;
   148  	int mode;
   149  	char *cmd;
   150  	Buf *b;
   151  } bg[MAXBG];
   152  static int nbg;
   153  static int maxnbg = nelem(bg);
   154  
   155  static void bgwait1(void);
   156  
   157  // genrun is the generic run implementation.
   158  static void
   159  genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
   160  {
   161  	int i, p[2], pid;
   162  	Buf cmd;
   163  	char *q;
   164  
   165  	while(nbg >= maxnbg)
   166  		bgwait1();
   167  
   168  	// Generate a copy of the command to show in a log.
   169  	// Substitute $WORK for the work directory.
   170  	binit(&cmd);
   171  	for(i=0; i<argv->len; i++) {
   172  		if(i > 0)
   173  			bwritestr(&cmd, " ");
   174  		q = argv->p[i];
   175  		if(workdir != nil && hasprefix(q, workdir)) {
   176  			bwritestr(&cmd, "$WORK");
   177  			q += strlen(workdir);
   178  		}
   179  		bwritestr(&cmd, q);
   180  	}
   181  	if(vflag > 1)
   182  		errprintf("%s\n", bstr(&cmd));
   183  
   184  	if(b != nil) {
   185  		breset(b);
   186  		if(pipe(p) < 0)
   187  			fatal("pipe: %s", strerror(errno));
   188  	}
   189  
   190  	switch(pid = fork()) {
   191  	case -1:
   192  		fatal("fork: %s", strerror(errno));
   193  	case 0:
   194  		if(b != nil) {
   195  			close(0);
   196  			close(p[0]);
   197  			dup2(p[1], 1);
   198  			dup2(p[1], 2);
   199  			if(p[1] > 2)
   200  				close(p[1]);
   201  		}
   202  		if(dir != nil) {
   203  			if(chdir(dir) < 0) {
   204  				fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
   205  				_exit(1);
   206  			}
   207  		}
   208  		vadd(argv, nil);
   209  		execvp(argv->p[0], argv->p);
   210  		fprintf(stderr, "%s\n", bstr(&cmd));
   211  		fprintf(stderr, "exec %s: %s\n", argv->p[0], strerror(errno));
   212  		_exit(1);
   213  	}
   214  	if(b != nil) {
   215  		close(p[1]);
   216  		breadfrom(b, p[0]);
   217  		close(p[0]);
   218  	}
   219  
   220  	if(nbg < 0)
   221  		fatal("bad bookkeeping");
   222  	bg[nbg].pid = pid;
   223  	bg[nbg].mode = mode;
   224  	bg[nbg].cmd = btake(&cmd);
   225  	bg[nbg].b = b;
   226  	nbg++;
   227  	
   228  	if(wait)
   229  		bgwait();
   230  
   231  	bfree(&cmd);
   232  }
   233  
   234  // bgwait1 waits for a single background job.
   235  static void
   236  bgwait1(void)
   237  {
   238  	int i, pid, status, mode;
   239  	char *cmd;
   240  	Buf *b;
   241  
   242  	errno = 0;
   243  	while((pid = wait(&status)) < 0) {
   244  		if(errno != EINTR)
   245  			fatal("waitpid: %s", strerror(errno));
   246  	}
   247  	for(i=0; i<nbg; i++)
   248  		if(bg[i].pid == pid)
   249  			goto ok;
   250  	fatal("waitpid: unexpected pid");
   251  
   252  ok:
   253  	cmd = bg[i].cmd;
   254  	mode = bg[i].mode;
   255  	bg[i].pid = 0;
   256  	b = bg[i].b;
   257  	bg[i].b = nil;
   258  	bg[i] = bg[--nbg];
   259  	
   260  	if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
   261  		if(b != nil)
   262  			xprintf("%s\n", bstr(b));
   263  		fatal("FAILED: %s", cmd);
   264  	}
   265  	xfree(cmd);
   266  }
   267  
   268  // bgwait waits for all the background jobs.
   269  void
   270  bgwait(void)
   271  {
   272  	while(nbg > 0)
   273  		bgwait1();
   274  }
   275  
   276  // xgetwd replaces b with the current directory.
   277  void
   278  xgetwd(Buf *b)
   279  {
   280  	char buf[MAXPATHLEN];
   281  	
   282  	breset(b);
   283  	if(getcwd(buf, MAXPATHLEN) == nil)
   284  		fatal("getcwd: %s", strerror(errno));
   285  	bwritestr(b, buf);	
   286  }
   287  
   288  // xrealwd replaces b with the 'real' name for the given path.
   289  // real is defined as what getcwd returns in that directory.
   290  void
   291  xrealwd(Buf *b, char *path)
   292  {
   293  	int fd;
   294  	
   295  	fd = open(".", 0);
   296  	if(fd < 0)
   297  		fatal("open .: %s", strerror(errno));
   298  	if(chdir(path) < 0)
   299  		fatal("chdir %s: %s", path, strerror(errno));
   300  	xgetwd(b);
   301  	if(fchdir(fd) < 0)
   302  		fatal("fchdir: %s", strerror(errno));
   303  	close(fd);
   304  }
   305  
   306  // isdir reports whether p names an existing directory.
   307  bool
   308  isdir(char *p)
   309  {
   310  	struct stat st;
   311  	
   312  	return stat(p, &st) >= 0 && S_ISDIR(st.st_mode);
   313  }
   314  
   315  // isfile reports whether p names an existing file.
   316  bool
   317  isfile(char *p)
   318  {
   319  	struct stat st;
   320  	
   321  	return stat(p, &st) >= 0 && S_ISREG(st.st_mode);
   322  }
   323  
   324  // mtime returns the modification time of the file p.
   325  Time
   326  mtime(char *p)
   327  {
   328  	struct stat st;
   329  	
   330  	if(stat(p, &st) < 0)
   331  		return 0;
   332  	return (Time)st.st_mtime*1000000000LL;
   333  }
   334  
   335  // isabs reports whether p is an absolute path.
   336  bool
   337  isabs(char *p)
   338  {
   339  	return hasprefix(p, "/");
   340  }
   341  
   342  // readfile replaces b with the content of the named file.
   343  void
   344  readfile(Buf *b, char *file)
   345  {
   346  	int fd;
   347  	
   348  	breset(b);
   349  	fd = open(file, 0);
   350  	if(fd < 0)
   351  		fatal("open %s: %s", file, strerror(errno));
   352  	breadfrom(b, fd);
   353  	close(fd);
   354  }
   355  
   356  // writefile writes b to the named file, creating it if needed.  if
   357  // exec is non-zero, marks the file as executable.
   358  void
   359  writefile(Buf *b, char *file, int exec)
   360  {
   361  	int fd;
   362  	
   363  	fd = creat(file, 0666);
   364  	if(fd < 0)
   365  		fatal("create %s: %s", file, strerror(errno));
   366  	if(write(fd, b->p, b->len) != b->len)
   367  		fatal("short write: %s", strerror(errno));
   368  	if(exec)
   369  		fchmod(fd, 0755);
   370  	close(fd);
   371  }
   372  
   373  // xmkdir creates the directory p.
   374  void
   375  xmkdir(char *p)
   376  {
   377  	if(mkdir(p, 0777) < 0)
   378  		fatal("mkdir %s: %s", p, strerror(errno));
   379  }
   380  
   381  // xmkdirall creates the directory p and its parents, as needed.
   382  void
   383  xmkdirall(char *p)
   384  {
   385  	char *q;
   386  
   387  	if(isdir(p))
   388  		return;
   389  	q = strrchr(p, '/');
   390  	if(q != nil) {
   391  		*q = '\0';
   392  		xmkdirall(p);
   393  		*q = '/';
   394  	}
   395  	xmkdir(p);
   396  }
   397  
   398  // xremove removes the file p.
   399  void
   400  xremove(char *p)
   401  {
   402  	if(vflag > 2)
   403  		errprintf("rm %s\n", p);
   404  	unlink(p);
   405  }
   406  
   407  // xremoveall removes the file or directory tree rooted at p.
   408  void
   409  xremoveall(char *p)
   410  {
   411  	int i;
   412  	Buf b;
   413  	Vec dir;
   414  
   415  	binit(&b);
   416  	vinit(&dir);
   417  
   418  	if(isdir(p)) {
   419  		xreaddir(&dir, p);
   420  		for(i=0; i<dir.len; i++) {
   421  			bprintf(&b, "%s/%s", p, dir.p[i]);
   422  			xremoveall(bstr(&b));
   423  		}
   424  		if(vflag > 2)
   425  			errprintf("rm %s\n", p);
   426  		rmdir(p);
   427  	} else {
   428  		if(vflag > 2)
   429  			errprintf("rm %s\n", p);
   430  		unlink(p);
   431  	}
   432  	
   433  	bfree(&b);
   434  	vfree(&dir);
   435  }
   436  
   437  // xreaddir replaces dst with a list of the names of the files in dir.
   438  // The names are relative to dir; they are not full paths.
   439  void
   440  xreaddir(Vec *dst, char *dir)
   441  {
   442  	DIR *d;
   443  	struct dirent *dp;
   444  
   445  	vreset(dst);
   446  	d = opendir(dir);
   447  	if(d == nil)
   448  		fatal("opendir %s: %s", dir, strerror(errno));
   449  	while((dp = readdir(d)) != nil) {
   450  		if(streq(dp->d_name, ".") || streq(dp->d_name, ".."))
   451  			continue;
   452  		vadd(dst, dp->d_name);
   453  	}
   454  	closedir(d);
   455  }
   456  
   457  // xworkdir creates a new temporary directory to hold object files
   458  // and returns the name of that directory.
   459  char*
   460  xworkdir(void)
   461  {
   462  	Buf b;
   463  	char *p;
   464  
   465  	binit(&b);
   466  
   467  	xgetenv(&b, "TMPDIR");
   468  	if(b.len == 0)
   469  		bwritestr(&b, "/var/tmp");
   470  	if(b.p[b.len-1] != '/')
   471  		bwrite(&b, "/", 1);
   472  	bwritestr(&b, "go-cbuild-XXXXXX");
   473  	p = bstr(&b);
   474  	if(mkdtemp(p) == nil)
   475  		fatal("mkdtemp(%s): %s", p, strerror(errno));
   476  	p = btake(&b);
   477  
   478  	bfree(&b);
   479  
   480  	return p;
   481  }
   482  
   483  // fatal prints an error message to standard error and exits.
   484  void
   485  fatal(char *msg, ...)
   486  {
   487  	va_list arg;
   488  	
   489  	fflush(stdout);
   490  	fprintf(stderr, "go tool dist: ");
   491  	va_start(arg, msg);
   492  	vfprintf(stderr, msg, arg);
   493  	va_end(arg);
   494  	fprintf(stderr, "\n");
   495  	
   496  	bgwait();
   497  	exit(1);
   498  }
   499  
   500  // xmalloc returns a newly allocated zeroed block of n bytes of memory.
   501  // It calls fatal if it runs out of memory.
   502  void*
   503  xmalloc(int n)
   504  {
   505  	void *p;
   506  	
   507  	p = malloc(n);
   508  	if(p == nil)
   509  		fatal("out of memory");
   510  	memset(p, 0, n);
   511  	return p;
   512  }
   513  
   514  // xstrdup returns a newly allocated copy of p.
   515  // It calls fatal if it runs out of memory.
   516  char*
   517  xstrdup(char *p)
   518  {
   519  	p = strdup(p);
   520  	if(p == nil)
   521  		fatal("out of memory");
   522  	return p;
   523  }
   524  
   525  // xrealloc grows the allocation p to n bytes and
   526  // returns the new (possibly moved) pointer.
   527  // It calls fatal if it runs out of memory.
   528  void*
   529  xrealloc(void *p, int n)
   530  {
   531  	p = realloc(p, n);
   532  	if(p == nil)
   533  		fatal("out of memory");
   534  	return p;
   535  }
   536  
   537  // xfree frees the result returned by xmalloc, xstrdup, or xrealloc.
   538  void
   539  xfree(void *p)
   540  {
   541  	free(p);
   542  }
   543  
   544  // hassuffix reports whether p ends with suffix.
   545  bool
   546  hassuffix(char *p, char *suffix)
   547  {
   548  	int np, ns;
   549  
   550  	np = strlen(p);
   551  	ns = strlen(suffix);
   552  	return np >= ns && streq(p+np-ns, suffix);
   553  }
   554  
   555  // hasprefix reports whether p begins with prefix.
   556  bool
   557  hasprefix(char *p, char *prefix)
   558  {
   559  	return strncmp(p, prefix, strlen(prefix)) == 0;
   560  }
   561  
   562  // contains reports whether sep appears in p.
   563  bool
   564  contains(char *p, char *sep)
   565  {
   566  	return strstr(p, sep) != nil;
   567  }
   568  
   569  // streq reports whether p and q are the same string.
   570  bool
   571  streq(char *p, char *q)
   572  {
   573  	return strcmp(p, q) == 0;
   574  }
   575  
   576  // lastelem returns the final path element in p.
   577  char*
   578  lastelem(char *p)
   579  {
   580  	char *out;
   581  
   582  	out = p;
   583  	for(; *p; p++)
   584  		if(*p == '/')
   585  			out = p+1;
   586  	return out;
   587  }
   588  
   589  // xmemmove copies n bytes from src to dst.
   590  void
   591  xmemmove(void *dst, void *src, int n)
   592  {
   593  	memmove(dst, src, n);
   594  }
   595  
   596  // xmemcmp compares the n-byte regions starting at a and at b.
   597  int
   598  xmemcmp(void *a, void *b, int n)
   599  {
   600  	return memcmp(a, b, n);
   601  }
   602  
   603  // xstrlen returns the length of the NUL-terminated string at p.
   604  int
   605  xstrlen(char *p)
   606  {
   607  	return strlen(p);
   608  }
   609  
   610  // xexit exits the process with return code n.
   611  void
   612  xexit(int n)
   613  {
   614  	exit(n);
   615  }
   616  
   617  // xatexit schedules the exit-handler f to be run when the program exits.
   618  void
   619  xatexit(void (*f)(void))
   620  {
   621  	atexit(f);
   622  }
   623  
   624  // xprintf prints a message to standard output.
   625  void
   626  xprintf(char *fmt, ...)
   627  {
   628  	va_list arg;
   629  	
   630  	va_start(arg, fmt);
   631  	vprintf(fmt, arg);
   632  	va_end(arg);
   633  }
   634  
   635  // errprintf prints a message to standard output.
   636  void
   637  errprintf(char *fmt, ...)
   638  {
   639  	va_list arg;
   640  	
   641  	va_start(arg, fmt);
   642  	vfprintf(stderr, fmt, arg);
   643  	va_end(arg);
   644  }
   645  
   646  // xsetenv sets the environment variable $name to the given value.
   647  void
   648  xsetenv(char *name, char *value)
   649  {
   650  	setenv(name, value, 1);
   651  }
   652  
   653  // main takes care of OS-specific startup and dispatches to xmain.
   654  int
   655  main(int argc, char **argv)
   656  {
   657  	Buf b;
   658  	int osx;
   659  	struct utsname u;
   660  
   661  	setvbuf(stdout, nil, _IOLBF, 0);
   662  	setvbuf(stderr, nil, _IOLBF, 0);
   663  
   664  	setenv("TERM", "dumb", 1); // disable escape codes in clang errors
   665  
   666  	binit(&b);
   667  	
   668  	slash = "/";
   669  
   670  #if defined(__APPLE__)
   671  	gohostos = "darwin";
   672  	// Even on 64-bit platform, darwin uname -m prints i386.
   673  	run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil);
   674  	if(contains(bstr(&b), "EM64T"))
   675  		gohostarch = "amd64";
   676  #elif defined(__linux__)
   677  	gohostos = "linux";
   678  #elif defined(__DragonFly__)
   679  	gohostos = "dragonfly";
   680  #elif defined(__FreeBSD__)
   681  	gohostos = "freebsd";
   682  #elif defined(__FreeBSD_kernel__)
   683  	// detect debian/kFreeBSD. 
   684  	// http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F
   685  	gohostos = "freebsd";	
   686  #elif defined(__OpenBSD__)
   687  	gohostos = "openbsd";
   688  #elif defined(__NetBSD__)
   689  	gohostos = "netbsd";
   690  #elif defined(__sun) && defined(__SVR4)
   691  	gohostos = "solaris";
   692  	// Even on 64-bit platform, solaris uname -m prints i86pc.
   693  	run(&b, nil, 0, "isainfo", "-n", nil);
   694  	if(contains(bstr(&b), "amd64"))
   695  		gohostarch = "amd64";
   696  	if(contains(bstr(&b), "i386"))
   697  		gohostarch = "386";
   698  #else
   699  	fatal("unknown operating system");
   700  #endif
   701  
   702  	if(gohostarch == nil) {
   703  		if(uname(&u) < 0)
   704  			fatal("uname: %s", strerror(errno));
   705  		if(contains(u.machine, "x86_64") || contains(u.machine, "amd64"))
   706  			gohostarch = "amd64";
   707  		else if(hassuffix(u.machine, "86"))
   708  			gohostarch = "386";
   709  		else if(contains(u.machine, "arm"))
   710  			gohostarch = "arm";
   711  		else if(contains(u.machine, "ppc64le"))
   712  			gohostarch = "ppc64le";
   713  		else if(contains(u.machine, "ppc64"))
   714  			gohostarch = "ppc64";
   715  		else
   716  			fatal("unknown architecture: %s", u.machine);
   717  	}
   718  
   719  	if(streq(gohostarch, "arm"))
   720  		maxnbg = 1;
   721  
   722  	// The OS X 10.6 linker does not support external linking mode.
   723  	// See golang.org/issue/5130.
   724  	//
   725  	// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
   726  	// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
   727  	// See golang.org/issue/5822.
   728  	//
   729  	// Roughly, OS X 10.N shows up as uname release (N+4),
   730  	// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
   731  	if(streq(gohostos, "darwin")) {
   732  		if(uname(&u) < 0)
   733  			fatal("uname: %s", strerror(errno));
   734  		osx = atoi(u.release) - 4;
   735  		if(osx <= 6)
   736  			goextlinkenabled = "0";
   737  		if(osx >= 8)
   738  			defaultclang = 1;
   739  	}
   740  
   741  	init();
   742  	xmain(argc, argv);
   743  	bfree(&b);
   744  	return 0;
   745  }
   746  
   747  // xqsort is a wrapper for the C standard qsort.
   748  void
   749  xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
   750  {
   751  	qsort(data, n, elemsize, cmp);
   752  }
   753  
   754  // xstrcmp compares the NUL-terminated strings a and b.
   755  int
   756  xstrcmp(char *a, char *b)
   757  {
   758  	return strcmp(a, b);
   759  }
   760  
   761  // xstrstr returns a pointer to the first occurrence of b in a.
   762  char*
   763  xstrstr(char *a, char *b)
   764  {
   765  	return strstr(a, b);
   766  }
   767  
   768  // xstrrchr returns a pointer to the final occurrence of c in p.
   769  char*
   770  xstrrchr(char *p, int c)
   771  {
   772  	return strrchr(p, c);
   773  }
   774  
   775  // xsamefile reports whether f1 and f2 are the same file (or dir)
   776  int
   777  xsamefile(char *f1, char *f2)
   778  {
   779  	return streq(f1, f2); // suffice for now
   780  }
   781  
   782  sigjmp_buf sigill_jmpbuf;
   783  static void sigillhand(int);
   784  
   785  // xtryexecfunc tries to execute function f, if any illegal instruction
   786  // signal received in the course of executing that function, it will
   787  // return 0, otherwise it will return 1.
   788  // Some systems (notably NetBSD) will spin and spin when executing VFPv3
   789  // instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering
   790  // SIGILL, so we set a 1-second alarm to catch that case.
   791  int
   792  xtryexecfunc(void (*f)(void))
   793  {
   794  	int r;
   795  	r = 0;
   796  	signal(SIGILL, sigillhand);
   797  	signal(SIGALRM, sigillhand);
   798  	alarm(1);
   799  	if(sigsetjmp(sigill_jmpbuf, 1) == 0) {
   800  		f();
   801  		r = 1;
   802  	}
   803  	signal(SIGILL, SIG_DFL);
   804  	alarm(0);
   805  	signal(SIGALRM, SIG_DFL);
   806  	return r;
   807  }
   808  
   809  // SIGILL handler helper
   810  static void
   811  sigillhand(int signum)
   812  {
   813  	USED(signum);
   814  	siglongjmp(sigill_jmpbuf, 1);
   815  }
   816  
   817  static void
   818  __cpuid(int dst[4], int ax)
   819  {
   820  #ifdef __i386__
   821  	// we need to avoid ebx on i386 (esp. when -fPIC).
   822  	asm volatile(
   823  		"mov %%ebx, %%edi\n\t"
   824  		"cpuid\n\t"
   825  		"xchgl %%ebx, %%edi"
   826  		: "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
   827  		: "0" (ax));
   828  #elif defined(__x86_64__)
   829  	asm volatile("cpuid"
   830  		: "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
   831  		: "0" (ax));
   832  #else
   833  	dst[0] = dst[1] = dst[2] = dst[3] = 0;
   834  #endif
   835  }
   836  
   837  bool
   838  cansse2(void)
   839  {
   840  	int info[4];
   841  	
   842  	__cpuid(info, 1);
   843  	return (info[3] & (1<<26)) != 0;	// SSE2
   844  }
   845  
   846  #endif // PLAN9
   847  #endif // __WINDOWS__