github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/cmd/ld/go.c (about) 1 // Copyright 2009 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 // go-specific code shared across loaders (5l, 6l, 8l). 6 7 #include "l.h" 8 #include "../ld/lib.h" 9 10 // accumulate all type information from .6 files. 11 // check for inconsistencies. 12 13 // TODO: 14 // generate debugging section in binary. 15 // once the dust settles, try to move some code to 16 // libmach, so that other linkers and ar can share. 17 18 /* 19 * package import data 20 */ 21 typedef struct Import Import; 22 struct Import 23 { 24 Import *hash; // next in hash table 25 char *prefix; // "type", "var", "func", "const" 26 char *name; 27 char *def; 28 char *file; 29 }; 30 enum { 31 NIHASH = 1024 32 }; 33 static Import *ihash[NIHASH]; 34 static int nimport; 35 static void imported(char *pkg, char *import); 36 37 static int 38 hashstr(char *name) 39 { 40 uint32 h; 41 char *cp; 42 43 h = 0; 44 for(cp = name; *cp; h += *cp++) 45 h *= 1119; 46 h &= 0xffffff; 47 return h; 48 } 49 50 static Import * 51 ilookup(char *name) 52 { 53 int h; 54 Import *x; 55 56 h = hashstr(name) % NIHASH; 57 for(x=ihash[h]; x; x=x->hash) 58 if(x->name[0] == name[0] && strcmp(x->name, name) == 0) 59 return x; 60 x = mal(sizeof *x); 61 x->name = estrdup(name); 62 x->hash = ihash[h]; 63 ihash[h] = x; 64 nimport++; 65 return x; 66 } 67 68 static void loadpkgdata(char*, char*, char*, int); 69 static void loadcgo(char*, char*, char*, int); 70 static int parsemethod(char**, char*, char**); 71 static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); 72 73 void 74 ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) 75 { 76 char *data, *p0, *p1, *name; 77 78 if(debug['g']) 79 return; 80 81 if((int)len != len) { 82 fprint(2, "%s: too much pkg data in %s\n", argv0, filename); 83 if(debug['u']) 84 errorexit(); 85 return; 86 } 87 data = mal(len+1); 88 if(Bread(f, data, len) != len) { 89 fprint(2, "%s: short pkg read %s\n", argv0, filename); 90 if(debug['u']) 91 errorexit(); 92 return; 93 } 94 data[len] = '\0'; 95 96 // first \n$$ marks beginning of exports - skip rest of line 97 p0 = strstr(data, "\n$$"); 98 if(p0 == nil) { 99 if(debug['u'] && whence != ArchiveObj) { 100 fprint(2, "%s: cannot find export data in %s\n", argv0, filename); 101 errorexit(); 102 } 103 return; 104 } 105 p0 += 3; 106 while(*p0 != '\n' && *p0 != '\0') 107 p0++; 108 109 // second marks end of exports / beginning of local data 110 p1 = strstr(p0, "\n$$"); 111 if(p1 == nil) { 112 fprint(2, "%s: cannot find end of exports in %s\n", argv0, filename); 113 if(debug['u']) 114 errorexit(); 115 return; 116 } 117 while(p0 < p1 && (*p0 == ' ' || *p0 == '\t' || *p0 == '\n')) 118 p0++; 119 if(p0 < p1) { 120 if(strncmp(p0, "package ", 8) != 0) { 121 fprint(2, "%s: bad package section in %s - %s\n", argv0, filename, p0); 122 if(debug['u']) 123 errorexit(); 124 return; 125 } 126 p0 += 8; 127 while(p0 < p1 && (*p0 == ' ' || *p0 == '\t' || *p0 == '\n')) 128 p0++; 129 name = p0; 130 while(p0 < p1 && *p0 != ' ' && *p0 != '\t' && *p0 != '\n') 131 p0++; 132 if(debug['u'] && whence != ArchiveObj && 133 (p0+6 > p1 || memcmp(p0, " safe\n", 6) != 0)) { 134 fprint(2, "%s: load of unsafe package %s\n", argv0, filename); 135 nerrors++; 136 errorexit(); 137 } 138 if(p0 < p1) { 139 if(*p0 == '\n') 140 *p0++ = '\0'; 141 else { 142 *p0++ = '\0'; 143 while(p0 < p1 && *p0++ != '\n') 144 ; 145 } 146 } 147 if(strcmp(pkg, "main") == 0 && strcmp(name, "main") != 0) { 148 fprint(2, "%s: %s: not package main (package %s)\n", argv0, filename, name); 149 nerrors++; 150 errorexit(); 151 } 152 loadpkgdata(filename, pkg, p0, p1 - p0); 153 } 154 155 // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. 156 if(whence == Pkgdef) 157 return; 158 159 // look for cgo section 160 p0 = strstr(p1, "\n$$ // cgo"); 161 if(p0 != nil) { 162 p0 = strchr(p0+1, '\n'); 163 if(p0 == nil) { 164 fprint(2, "%s: found $$ // cgo but no newline in %s\n", argv0, filename); 165 if(debug['u']) 166 errorexit(); 167 return; 168 } 169 p1 = strstr(p0, "\n$$"); 170 if(p1 == nil) 171 p1 = strstr(p0, "\n!\n"); 172 if(p1 == nil) { 173 fprint(2, "%s: cannot find end of // cgo section in %s\n", argv0, filename); 174 if(debug['u']) 175 errorexit(); 176 return; 177 } 178 loadcgo(filename, pkg, p0 + 1, p1 - (p0+1)); 179 } 180 } 181 182 static void 183 loadpkgdata(char *file, char *pkg, char *data, int len) 184 { 185 char *p, *ep, *prefix, *name, *def; 186 Import *x; 187 188 file = estrdup(file); 189 p = data; 190 ep = data + len; 191 while(parsepkgdata(file, pkg, &p, ep, &prefix, &name, &def) > 0) { 192 x = ilookup(name); 193 if(x->prefix == nil) { 194 x->prefix = prefix; 195 x->def = estrdup(def); 196 x->file = file; 197 } else if(strcmp(x->prefix, prefix) != 0) { 198 fprint(2, "%s: conflicting definitions for %s\n", argv0, name); 199 fprint(2, "%s:\t%s %s ...\n", x->file, x->prefix, name); 200 fprint(2, "%s:\t%s %s ...\n", file, prefix, name); 201 nerrors++; 202 } else if(strcmp(x->def, def) != 0) { 203 fprint(2, "%s: conflicting definitions for %s\n", argv0, name); 204 fprint(2, "%s:\t%s %s %s\n", x->file, x->prefix, name, x->def); 205 fprint(2, "%s:\t%s %s %s\n", file, prefix, name, def); 206 nerrors++; 207 } 208 free(name); 209 free(def); 210 } 211 free(file); 212 } 213 214 static int 215 parsepkgdata(char *file, char *pkg, char **pp, char *ep, char **prefixp, char **namep, char **defp) 216 { 217 char *p, *prefix, *name, *def, *edef, *meth; 218 int n, inquote; 219 220 // skip white space 221 p = *pp; 222 loop: 223 while(p < ep && (*p == ' ' || *p == '\t' || *p == '\n')) 224 p++; 225 if(p == ep || strncmp(p, "$$\n", 3) == 0) 226 return 0; 227 228 // prefix: (var|type|func|const) 229 prefix = p; 230 if(p + 7 > ep) 231 return -1; 232 if(strncmp(p, "var ", 4) == 0) 233 p += 4; 234 else if(strncmp(p, "type ", 5) == 0) 235 p += 5; 236 else if(strncmp(p, "func ", 5) == 0) 237 p += 5; 238 else if(strncmp(p, "const ", 6) == 0) 239 p += 6; 240 else if(strncmp(p, "import ", 7) == 0) { 241 p += 7; 242 while(p < ep && *p != ' ') 243 p++; 244 p++; 245 name = p; 246 while(p < ep && *p != '\n') 247 p++; 248 if(p >= ep) { 249 fprint(2, "%s: %s: confused in import line\n", argv0, file); 250 nerrors++; 251 return -1; 252 } 253 *p++ = '\0'; 254 imported(pkg, name); 255 goto loop; 256 } 257 else { 258 fprint(2, "%s: %s: confused in pkg data near <<%.40s>>\n", argv0, file, prefix); 259 nerrors++; 260 return -1; 261 } 262 p[-1] = '\0'; 263 264 // name: a.b followed by space 265 name = p; 266 inquote = 0; 267 while(p < ep) { 268 if (*p == ' ' && !inquote) 269 break; 270 271 if(*p == '\\') 272 p++; 273 else if(*p == '"') 274 inquote = !inquote; 275 276 p++; 277 } 278 279 if(p >= ep) 280 return -1; 281 *p++ = '\0'; 282 283 // def: free form to new line 284 def = p; 285 while(p < ep && *p != '\n') 286 p++; 287 if(p >= ep) 288 return -1; 289 edef = p; 290 *p++ = '\0'; 291 292 // include methods on successive lines in def of named type 293 while(parsemethod(&p, ep, &meth) > 0) { 294 *edef++ = '\n'; // overwrites '\0' 295 if(edef+1 > meth) { 296 // We want to indent methods with a single \t. 297 // 6g puts at least one char of indent before all method defs, 298 // so there will be room for the \t. If the method def wasn't 299 // indented we could do something more complicated, 300 // but for now just diagnose the problem and assume 301 // 6g will keep indenting for us. 302 fprint(2, "%s: %s: expected methods to be indented %p %p %.10s\n", argv0, 303 file, edef, meth, meth); 304 nerrors++; 305 return -1; 306 } 307 *edef++ = '\t'; 308 n = strlen(meth); 309 memmove(edef, meth, n); 310 edef += n; 311 } 312 313 name = expandpkg(name, pkg); 314 def = expandpkg(def, pkg); 315 316 // done 317 *pp = p; 318 *prefixp = prefix; 319 *namep = name; 320 *defp = def; 321 return 1; 322 } 323 324 static int 325 parsemethod(char **pp, char *ep, char **methp) 326 { 327 char *p; 328 329 // skip white space 330 p = *pp; 331 while(p < ep && (*p == ' ' || *p == '\t')) 332 p++; 333 if(p == ep) 334 return 0; 335 336 // might be a comment about the method 337 if(p + 2 < ep && strncmp(p, "//", 2) == 0) 338 goto useline; 339 340 // if it says "func (", it's a method 341 if(p + 6 < ep && strncmp(p, "func (", 6) == 0) 342 goto useline; 343 return 0; 344 345 useline: 346 // definition to end of line 347 *methp = p; 348 while(p < ep && *p != '\n') 349 p++; 350 if(p >= ep) { 351 fprint(2, "%s: lost end of line in method definition\n", argv0); 352 *pp = ep; 353 return -1; 354 } 355 *p++ = '\0'; 356 *pp = p; 357 return 1; 358 } 359 360 static void 361 loadcgo(char *file, char *pkg, char *p, int n) 362 { 363 char *pend, *next, *p0, *q; 364 char *f[10], *local, *remote, *lib; 365 int nf; 366 LSym *s; 367 368 USED(file); 369 pend = p + n; 370 p0 = nil; 371 for(; p<pend; p=next) { 372 next = strchr(p, '\n'); 373 if(next == nil) 374 next = ""; 375 else 376 *next++ = '\0'; 377 378 free(p0); 379 p0 = estrdup(p); // save for error message 380 nf = tokenize(p, f, nelem(f)); 381 382 if(strcmp(f[0], "cgo_import_dynamic") == 0) { 383 if(nf < 2 || nf > 4) 384 goto err; 385 386 local = f[1]; 387 remote = local; 388 if(nf > 2) 389 remote = f[2]; 390 lib = ""; 391 if(nf > 3) 392 lib = f[3]; 393 394 if(debug['d']) { 395 fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file); 396 nerrors++; 397 return; 398 } 399 400 if(strcmp(local, "_") == 0 && strcmp(remote, "_") == 0) { 401 // allow #pragma dynimport _ _ "foo.so" 402 // to force a link of foo.so. 403 havedynamic = 1; 404 adddynlib(lib); 405 continue; 406 } 407 408 local = expandpkg(local, pkg); 409 q = strchr(remote, '#'); 410 if(q) 411 *q++ = '\0'; 412 s = linklookup(ctxt, local, 0); 413 if(local != f[1]) 414 free(local); 415 if(s->type == 0 || s->type == SXREF || s->type == SHOSTOBJ) { 416 s->dynimplib = lib; 417 s->extname = remote; 418 s->dynimpvers = q; 419 if(s->type != SHOSTOBJ) 420 s->type = SDYNIMPORT; 421 havedynamic = 1; 422 } 423 continue; 424 } 425 426 if(strcmp(f[0], "cgo_import_static") == 0) { 427 if(nf != 2) 428 goto err; 429 local = f[1]; 430 s = linklookup(ctxt, local, 0); 431 s->type = SHOSTOBJ; 432 s->size = 0; 433 continue; 434 } 435 436 if(strcmp(f[0], "cgo_export_static") == 0 || strcmp(f[0], "cgo_export_dynamic") == 0) { 437 // TODO: Remove once we know Windows is okay. 438 if(strcmp(f[0], "cgo_export_static") == 0 && HEADTYPE == Hwindows) 439 continue; 440 441 if(nf < 2 || nf > 3) 442 goto err; 443 local = f[1]; 444 if(nf > 2) 445 remote = f[2]; 446 else 447 remote = local; 448 local = expandpkg(local, pkg); 449 s = linklookup(ctxt, local, 0); 450 451 if(flag_shared && s == linklookup(ctxt, "main", 0)) 452 continue; 453 454 // export overrides import, for openbsd/cgo. 455 // see issue 4878. 456 if(s->dynimplib != nil) { 457 s->dynimplib = nil; 458 s->extname = nil; 459 s->dynimpvers = nil; 460 s->type = 0; 461 } 462 463 if(s->cgoexport == 0) { 464 s->extname = remote; 465 if(ndynexp%32 == 0) 466 dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); 467 dynexp[ndynexp++] = s; 468 } else if(strcmp(s->extname, remote) != 0) { 469 fprint(2, "%s: conflicting cgo_export directives: %s as %s and %s\n", argv0, s->name, s->extname, remote); 470 nerrors++; 471 return; 472 } 473 if(strcmp(f[0], "cgo_export_static") == 0) 474 s->cgoexport |= CgoExportStatic; 475 else 476 s->cgoexport |= CgoExportDynamic; 477 if(local != f[1]) 478 free(local); 479 continue; 480 } 481 482 if(strcmp(f[0], "cgo_dynamic_linker") == 0) { 483 if(nf != 2) 484 goto err; 485 486 if(!debug['I']) { // not overridden by command line 487 if(interpreter != nil && strcmp(interpreter, f[1]) != 0) { 488 fprint(2, "%s: conflict dynlinker: %s and %s\n", argv0, interpreter, f[1]); 489 nerrors++; 490 return; 491 } 492 free(interpreter); 493 interpreter = estrdup(f[1]); 494 } 495 continue; 496 } 497 498 if(strcmp(f[0], "cgo_ldflag") == 0) { 499 if(nf != 2) 500 goto err; 501 if(nldflag%32 == 0) 502 ldflag = erealloc(ldflag, (nldflag+32)*sizeof ldflag[0]); 503 ldflag[nldflag++] = estrdup(f[1]); 504 continue; 505 } 506 } 507 free(p0); 508 return; 509 510 err: 511 fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); 512 nerrors++; 513 } 514 515 static LSym *markq; 516 static LSym *emarkq; 517 518 static void 519 mark1(LSym *s, LSym *parent) 520 { 521 if(s == S || s->reachable) 522 return; 523 if(strncmp(s->name, "go.weak.", 8) == 0) 524 return; 525 s->reachable = 1; 526 s->reachparent = parent; 527 if(markq == nil) 528 markq = s; 529 else 530 emarkq->queue = s; 531 emarkq = s; 532 } 533 534 void 535 mark(LSym *s) 536 { 537 mark1(s, nil); 538 } 539 540 static void 541 markflood(void) 542 { 543 Auto *a; 544 LSym *s; 545 int i; 546 547 for(s=markq; s!=S; s=s->queue) { 548 if(s->type == STEXT) { 549 if(debug['v'] > 1) 550 Bprint(&bso, "marktext %s\n", s->name); 551 for(a=s->autom; a; a=a->link) 552 mark1(a->gotype, s); 553 } 554 for(i=0; i<s->nr; i++) 555 mark1(s->r[i].sym, s); 556 if(s->pcln) { 557 for(i=0; i<s->pcln->nfuncdata; i++) 558 mark1(s->pcln->funcdata[i], s); 559 } 560 mark1(s->gotype, s); 561 mark1(s->sub, s); 562 mark1(s->outer, s); 563 } 564 } 565 566 static char* 567 markextra[] = 568 { 569 "runtime.morestack", 570 "runtime.morestackx", 571 572 "runtime.morestack00", 573 "runtime.morestack10", 574 "runtime.morestack01", 575 "runtime.morestack11", 576 577 "runtime.morestack8", 578 "runtime.morestack16", 579 "runtime.morestack24", 580 "runtime.morestack32", 581 "runtime.morestack40", 582 "runtime.morestack48", 583 584 // on arm, lock in the div/mod helpers too 585 "_div", 586 "_divu", 587 "_mod", 588 "_modu", 589 }; 590 591 void 592 deadcode(void) 593 { 594 int i; 595 LSym *s, *last, *p; 596 Fmt fmt; 597 598 if(debug['v']) 599 Bprint(&bso, "%5.2f deadcode\n", cputime()); 600 601 mark(linklookup(ctxt, INITENTRY, 0)); 602 for(i=0; i<nelem(markextra); i++) 603 mark(linklookup(ctxt, markextra[i], 0)); 604 605 for(i=0; i<ndynexp; i++) 606 mark(dynexp[i]); 607 608 markflood(); 609 610 // keep each beginning with 'typelink.' if the symbol it points at is being kept. 611 for(s = ctxt->allsym; s != S; s = s->allsym) { 612 if(strncmp(s->name, "go.typelink.", 12) == 0) 613 s->reachable = s->nr==1 && s->r[0].sym->reachable; 614 } 615 616 // remove dead text but keep file information (z symbols). 617 last = nil; 618 for(s = ctxt->textp; s != nil; s = s->next) { 619 if(!s->reachable) 620 continue; 621 // NOTE: Removing s from old textp and adding to new, shorter textp. 622 if(last == nil) 623 ctxt->textp = s; 624 else 625 last->next = s; 626 last = s; 627 } 628 if(last == nil) 629 ctxt->textp = nil; 630 else 631 last->next = nil; 632 633 for(s = ctxt->allsym; s != S; s = s->allsym) 634 if(strncmp(s->name, "go.weak.", 8) == 0) { 635 s->special = 1; // do not lay out in data segment 636 s->reachable = 1; 637 s->hide = 1; 638 } 639 640 // record field tracking references 641 fmtstrinit(&fmt); 642 for(s = ctxt->allsym; s != S; s = s->allsym) { 643 if(strncmp(s->name, "go.track.", 9) == 0) { 644 s->special = 1; // do not lay out in data segment 645 s->hide = 1; 646 if(s->reachable) { 647 fmtprint(&fmt, "%s", s->name+9); 648 for(p=s->reachparent; p; p=p->reachparent) 649 fmtprint(&fmt, "\t%s", p->name); 650 fmtprint(&fmt, "\n"); 651 } 652 s->type = SCONST; 653 s->value = 0; 654 } 655 } 656 if(tracksym == nil) 657 return; 658 s = linklookup(ctxt, tracksym, 0); 659 if(!s->reachable) 660 return; 661 addstrdata(tracksym, fmtstrflush(&fmt)); 662 } 663 664 void 665 doweak(void) 666 { 667 LSym *s, *t; 668 669 // resolve weak references only if 670 // target symbol will be in binary anyway. 671 for(s = ctxt->allsym; s != S; s = s->allsym) { 672 if(strncmp(s->name, "go.weak.", 8) == 0) { 673 t = linkrlookup(ctxt, s->name+8, s->version); 674 if(t && t->type != 0 && t->reachable) { 675 s->value = t->value; 676 s->type = t->type; 677 s->outer = t; 678 } else { 679 s->type = SCONST; 680 s->value = 0; 681 } 682 continue; 683 } 684 } 685 } 686 687 void 688 addexport(void) 689 { 690 int i; 691 692 if(HEADTYPE == Hdarwin) 693 return; 694 695 for(i=0; i<ndynexp; i++) 696 adddynsym(ctxt, dynexp[i]); 697 } 698 699 /* %Z from gc, for quoting import paths */ 700 int 701 Zconv(Fmt *fp) 702 { 703 Rune r; 704 char *s, *se; 705 int n; 706 707 s = va_arg(fp->args, char*); 708 if(s == nil) 709 return fmtstrcpy(fp, "<nil>"); 710 711 se = s + strlen(s); 712 713 // NOTE: Keep in sync with ../gc/go.c:/^Zconv. 714 while(s < se) { 715 n = chartorune(&r, s); 716 s += n; 717 switch(r) { 718 case Runeerror: 719 if(n == 1) { 720 fmtprint(fp, "\\x%02x", (uchar)*(s-1)); 721 break; 722 } 723 // fall through 724 default: 725 if(r < ' ') { 726 fmtprint(fp, "\\x%02x", r); 727 break; 728 } 729 fmtrune(fp, r); 730 break; 731 case '\t': 732 fmtstrcpy(fp, "\\t"); 733 break; 734 case '\n': 735 fmtstrcpy(fp, "\\n"); 736 break; 737 case '\"': 738 case '\\': 739 fmtrune(fp, '\\'); 740 fmtrune(fp, r); 741 break; 742 case 0xFEFF: // BOM, basically disallowed in source code 743 fmtstrcpy(fp, "\\uFEFF"); 744 break; 745 } 746 } 747 return 0; 748 } 749 750 751 typedef struct Pkg Pkg; 752 struct Pkg 753 { 754 uchar mark; 755 uchar checked; 756 Pkg *next; 757 char *path; 758 Pkg **impby; 759 int nimpby; 760 int mimpby; 761 Pkg *all; 762 }; 763 764 static Pkg *phash[1024]; 765 static Pkg *pkgall; 766 767 static Pkg* 768 getpkg(char *path) 769 { 770 Pkg *p; 771 int h; 772 773 h = hashstr(path) % nelem(phash); 774 for(p=phash[h]; p; p=p->next) 775 if(strcmp(p->path, path) == 0) 776 return p; 777 p = mal(sizeof *p); 778 p->path = estrdup(path); 779 p->next = phash[h]; 780 phash[h] = p; 781 p->all = pkgall; 782 pkgall = p; 783 return p; 784 } 785 786 static void 787 imported(char *pkg, char *import) 788 { 789 Pkg *p, *i; 790 791 // everyone imports runtime, even runtime. 792 if(strcmp(import, "\"runtime\"") == 0) 793 return; 794 795 pkg = smprint("\"%Z\"", pkg); // turn pkg path into quoted form, freed below 796 p = getpkg(pkg); 797 i = getpkg(import); 798 if(i->nimpby >= i->mimpby) { 799 i->mimpby *= 2; 800 if(i->mimpby == 0) 801 i->mimpby = 16; 802 i->impby = erealloc(i->impby, i->mimpby*sizeof i->impby[0]); 803 } 804 i->impby[i->nimpby++] = p; 805 free(pkg); 806 } 807 808 static Pkg* 809 cycle(Pkg *p) 810 { 811 int i; 812 Pkg *bad; 813 814 if(p->checked) 815 return 0; 816 817 if(p->mark) { 818 nerrors++; 819 print("import cycle:\n"); 820 print("\t%s\n", p->path); 821 return p; 822 } 823 p->mark = 1; 824 for(i=0; i<p->nimpby; i++) { 825 if((bad = cycle(p->impby[i])) != nil) { 826 p->mark = 0; 827 p->checked = 1; 828 print("\timports %s\n", p->path); 829 if(bad == p) 830 return nil; 831 return bad; 832 } 833 } 834 p->checked = 1; 835 p->mark = 0; 836 return 0; 837 } 838 839 void 840 importcycles(void) 841 { 842 Pkg *p; 843 844 for(p=pkgall; p; p=p->all) 845 cycle(p); 846 } 847 848 void 849 setlinkmode(char *arg) 850 { 851 if(strcmp(arg, "internal") == 0) 852 linkmode = LinkInternal; 853 else if(strcmp(arg, "external") == 0) 854 linkmode = LinkExternal; 855 else if(strcmp(arg, "auto") == 0) 856 linkmode = LinkAuto; 857 else { 858 fprint(2, "unknown link mode -linkmode %s\n", arg); 859 errorexit(); 860 } 861 }