1 /* 2 * Copyright (c) 1982 Regents of the University of California 3 */ 4 #ifndef lint 5 static char sccsid[] = "@(#)assyms.c 4.14 08/11/83"; 6 #endif not lint 7 8 #include <stdio.h> 9 #include <ctype.h> 10 #include "as.h" 11 #include "asscan.h" 12 #include "assyms.h" 13 14 /* 15 * Managers for chunks of symbols allocated from calloc() 16 * We maintain a linked list of such chunks. 17 * 18 */ 19 struct allocbox *allochead; /*head of chunk list*/ 20 struct allocbox *alloctail; /*tail*/ 21 struct allocbox *newbox; /*for creating a new chunk*/ 22 struct symtab *nextsym; /*next symbol free*/ 23 int symsleft; /*slots left in current chunk*/ 24 25 struct symtab **symptrs; 26 struct symtab **symdelim[NLOC + NLOC +1]; 27 struct symtab **symptrub; 28 /* 29 * Managers for the dynamically extendable hash table 30 */ 31 struct hashdallop *htab; 32 33 Iptr *itab[NINST]; /*maps opcodes to instructions*/ 34 /* 35 * Counts what went into the symbol table, so that the 36 * size of the symbol table can be computed. 37 */ 38 int nsyms; /* total number in the symbol table */ 39 int njxxx; /* number of jxxx entrys */ 40 int nforgotten; /* number of symbols erroneously entered */ 41 int nlabels; /* number of label entries */ 42 43 /* 44 * Managers of the symbol literal storage. 45 */ 46 struct strpool *strplhead = 0; 47 48 symtabinit() 49 { 50 allochead = 0; 51 alloctail = 0; 52 nextsym = 0; 53 symsleft = 0; 54 strpoolalloc(); /* get the first strpool storage area */ 55 htab = 0; 56 htaballoc(); /* get the first part of the hash table */ 57 } 58 59 /* 60 * Install all known instructions in the symbol table 61 */ 62 syminstall() 63 { 64 register Iptr ip; 65 register struct symtab **hp; 66 register char *p1, *p2; 67 register int i; 68 69 for (i = 0; i < NINST; i++) 70 itab[i] = (Iptr*)BADPOINT; 71 72 for (ip = (Iptr)instab; FETCHNAME(ip)[0]; ip++) { 73 p1 = FETCHNAME(ip); 74 p2 = yytext; 75 while (*p2++ = *p1++); 76 hp = lookup(0); /* 0 => don't install this*/ 77 if (*hp==NULL) { 78 *hp = (struct symtab *)ip; 79 if ( (ip->s_tag!=INSTn) 80 && (ip->s_tag!=INST0) 81 && (ip->s_tag!=0)) 82 continue; /* was pseudo-op */ 83 if (itab[ip->i_eopcode] == (Iptr*)BADPOINT){ 84 itab[ip->i_eopcode] = 85 (Iptr*)ClearCalloc(256, sizeof(Iptr)); 86 for (i = 0; i < 256; i++) 87 itab[ip->i_eopcode][i] = 88 (Iptr)BADPOINT; 89 } 90 itab[ip->i_eopcode][ip->i_popcode] = ip; 91 } 92 } 93 } /*end of syminstall*/ 94 95 #define ISLABEL(sp) \ 96 ( (!savelabels) \ 97 && (sp->s_tag == LABELID) \ 98 && (STRPLACE(sp) & STR_CORE) \ 99 && (FETCHNAME(sp)[0] == 'L')) 100 /* 101 * Assign final values to symbols, 102 * and overwrite the index field with its relative position in 103 * the symbol table we give to the loader. 104 */ 105 extern struct exec hdr; 106 107 freezesymtab() 108 { 109 register struct symtab *sp; 110 long bs; 111 register int relpos = 0; 112 register struct symtab *ubsp; 113 register struct allocbox *allocwalk; 114 115 DECLITERATE(allocwalk, sp, ubsp) 116 { 117 if (sp->s_tag >= IGNOREBOUND) 118 continue; /*totally ignore jxxx entries */ 119 /* 120 * Ignore stabs, but give them a symbol table index 121 */ 122 if (sp->s_type & STABFLAG) 123 goto assignindex; 124 if ((sp->s_type&XTYPE)==XUNDEF) 125 sp->s_type = XXTRN+XUNDEF; 126 else if ((sp->s_type&XTYPE)==XDATA) 127 sp->s_value += usedot[sp->s_index].e_xvalue; 128 else if ((sp->s_type&XTYPE)==XTEXT) 129 sp->s_value += usedot[sp->s_index].e_xvalue; 130 else if ((sp->s_type&XTYPE)==XBSS) { 131 bs = sp->s_value; 132 sp->s_value = hdr.a_bss + datbase; 133 hdr.a_bss += bs; 134 } 135 assignindex: 136 if (!ISLABEL(sp)) 137 sp->s_index = relpos++; 138 } 139 } 140 141 /* 142 * For all of the stabs that had their final value undefined during pass 1 143 * and during pass 2 assign a final value. 144 * We have already given stab entrys a initial approximation 145 * when we constsructed the sorted symbol table. 146 * Iteration order doesn't matter. 147 */ 148 149 stabfix() 150 { 151 register struct symtab *sp, **cosp; 152 register struct symtab *p; 153 154 SYMITERATE(cosp, sp){ 155 if(sp->s_ptype && (sp->s_type & STABFLAG)) { 156 p = sp->s_dest; 157 /* 158 * STABFLOATING indicates that the offset has been saved in s_desc, s_other 159 */ 160 if(sp->s_tag == STABFLOATING) { 161 sp->s_value = ( ( ((unsigned char) sp->s_other) << 16) | ( (unsigned short) sp->s_desc ) ); 162 sp->s_value = sp->s_value + p->s_value; 163 } 164 else sp->s_value = p->s_value; 165 sp->s_index = p->s_index; 166 sp->s_type = p->s_type; 167 168 169 } 170 } 171 } 172 173 char *Calloc(number, size) 174 int number, size; 175 { 176 register char *newstuff; 177 char *sbrk(); 178 newstuff = sbrk(number*size); 179 if ((int)newstuff == -1){ 180 yyerror("Ran out of Memory"); 181 delexit(); 182 } 183 return(newstuff); 184 } 185 186 char *ClearCalloc(number, size) 187 int number, size; 188 { 189 register char *newstuff; /* r11 */ 190 register int length = number * size; /* r10 */ 191 #ifdef lint 192 length = length; 193 #endif length 194 newstuff = Calloc(number, size); 195 asm("movc5 $0, (r0), $0, r10, (r11)"); 196 return(newstuff); 197 } 198 199 struct symtab *symalloc() 200 { 201 if (symsleft == 0){ 202 newbox = (struct allocbox *)ClearCalloc(1,ALLOCQTY); 203 symsleft = SYMDALLOP; 204 nextsym = &newbox->symslots[0]; 205 if (alloctail == 0){ 206 allochead = alloctail = newbox; 207 } else { 208 alloctail->nextalloc = newbox; 209 alloctail = newbox; 210 } 211 } 212 --symsleft; 213 ++nsyms; 214 return(nextsym++); 215 } 216 217 strpoolalloc() 218 { 219 register struct strpool *new; 220 221 new = (struct strpool *)Calloc(1, sizeof (struct strpool)); 222 new->str_nalloc = 0; 223 new->str_next = strplhead; 224 strplhead = new; 225 } 226 227 symcmp(Pptr, Qptr) 228 struct symtab **Pptr, **Qptr; 229 { 230 register struct symtab *p = *Pptr; 231 register struct symtab *q = *Qptr; 232 if (p->s_index < q->s_index) 233 return(-1); 234 if (p->s_index > q->s_index) 235 return(1); 236 if (p->s_value < q->s_value) 237 return(-1); 238 if (p->s_value > q->s_value) 239 return(1); 240 /* 241 * Force jxxx entries to virtually preceed labels defined 242 * to follow the jxxxx instruction, so that bumping the 243 * jxxx instruction correctly fixes up the following labels 244 */ 245 if (p->s_tag >= IGNOREBOUND) /*p points to a jxxx*/ 246 return(-1); 247 if (q->s_tag >= IGNOREBOUND) 248 return(1); 249 /* 250 * both are now just plain labels; the relative order doesn't 251 * matter. Both can't be jxxxes, as they would have different 252 * values. 253 */ 254 return(0); 255 } /*end of symcmp*/ 256 257 /* 258 * We construct the auxiliary table of pointers, symptrs and 259 * symdelim 260 * We also assign preliminary values to stab entries that did not yet 261 * have an absolute value (because they initially referred to 262 * forward references). We don't worry about .stabds, as they 263 * already have an estimated final value 264 */ 265 266 sortsymtab() 267 { 268 register struct symtab *sp; 269 register struct symtab **cowalk; 270 register struct allocbox *allocwalk; 271 struct symtab *ubsp; 272 int segno; 273 int slotno; 274 int symsin; /*number put into symptrs*/ 275 276 symptrs = (struct symtab **)Calloc(nsyms + 2, sizeof *symptrs); 277 /* 278 * Allocate one word at the beginning of the symptr array 279 * so that backwards scans through the symptr array will 280 * work correctly while scanning through the zeroth segment 281 */ 282 *symptrs++ = 0; 283 cowalk = symptrs; 284 symsin = 0; 285 DECLITERATE(allocwalk, sp, ubsp) { 286 if (sp->s_ptype && (sp->s_type &STABFLAG)){ 287 sp->s_value = sp->s_dest->s_value; 288 sp->s_index = sp->s_dest->s_index; 289 } 290 if (symsin >= nsyms) 291 yyerror("INTERNAL ERROR: overfilled symbol table indirection table"); 292 *cowalk++ = sp; 293 symsin++; 294 } 295 if (symsin != nsyms) 296 yyerror("INTERNAL ERROR: installed %d syms, should have installed %d", 297 symsin, nsyms); 298 symptrub = &symptrs[nsyms ]; 299 qsort(symptrs, nsyms, sizeof *symptrs, symcmp); 300 symdelim[0] = symptrs; 301 for (cowalk = symptrs, sp = *cowalk, segno = 0, slotno = 1; 302 segno < NLOC + NLOC; 303 segno++, slotno++){ 304 for (; sp && sp->s_index == segno; sp = *++cowalk); 305 symdelim[slotno] = cowalk; /*forms the ub delimeter*/ 306 } 307 } /*end of sortsymtab*/ 308 309 #ifdef DEBUG 310 dumpsymtab() 311 { 312 register int segno; 313 register struct symtab *sp, **cosp, *ub; 314 char *tagstring(); 315 316 printf("Symbol Table dump:\n"); 317 for (segno = 0; segno < NLOC + NLOC; segno++){ 318 printf("Segment number: %d\n", segno); 319 SEGITERATE(segno, 0, 0, cosp, sp, ub, ++){ 320 printf("\tSeg: %d \"%s\" value: %d index: %d tag %s\n", 321 segno, FETCHNAME(sp), 322 sp->s_value, sp->s_index, 323 tagstring(sp->s_tag)); 324 printf("\t\ttype: %d jxbump %d jxfear: %d\n", 325 sp->s_type, sp->s_jxbump, sp->s_jxfear); 326 } 327 printf("\n\n"); 328 } 329 } 330 331 static char tagbuff[4]; 332 333 char *tagstring(tag) 334 unsigned char tag; 335 { 336 switch(tag){ 337 case JXACTIVE: return("active"); 338 case JXNOTYET: return("notyet"); 339 case JXALIGN: return("align"); 340 case JXQUESTIONABLE: return("jxquestionable"); 341 case JXINACTIVE: return("inactive"); 342 case JXTUNNEL: return("tunnel"); 343 case OBSOLETE: return("obsolete"); 344 case IGNOREBOUND: return("ignorebound"); 345 case STABFLOATING: return("stabfloating"); 346 case STABFIXED: return("stabfixed"); 347 case LABELID: return("labelid"); 348 case OKTOBUMP: return("oktobump"); 349 case ISET: return("iset"); 350 case ILSYM: return("ilsym"); 351 default: sprintf(tagbuff,"%d", tag); 352 return(tagbuff); 353 } 354 } 355 #endif DEBUG 356 357 htaballoc() 358 { 359 register struct hashdallop *new; 360 new = (struct hashdallop *)ClearCalloc(1, sizeof (struct hashdallop)); 361 if (htab == 0) 362 htab = new; 363 else { /* add AFTER the 1st slot */ 364 new->h_next = htab->h_next; 365 htab->h_next = new; 366 } 367 } 368 369 #define HASHCLOGGED (NHASH / 2) 370 371 /* 372 * Lookup a symbol stored in extern yytext. 373 * All strings passed in via extern yytext had better have 374 * a trailing null. Strings are placed in yytext for hashing by 375 * syminstall() and by yylex(); 376 * 377 * We take pains to avoid function calls; this functdion 378 * is called quite frequently, and the calls overhead 379 * in the vax contributes significantly to the overall 380 * execution speed of as. 381 */ 382 struct symtab **lookup(instflg) 383 int instflg; /* 0: don't install */ 384 { 385 static int initialprobe; 386 register struct symtab **hp; 387 register char *from; 388 register char *to; 389 register int len; 390 register int nprobes; 391 static struct hashdallop *hdallop; 392 static struct symtab **emptyslot; 393 static struct hashdallop *emptyhd; 394 static struct symtab **hp_ub; 395 396 emptyslot = 0; 397 for (nprobes = 0, from = yytext; 398 *from; 399 nprobes <<= 2, nprobes += *from++) 400 continue; 401 nprobes += from[-1] << 5; 402 nprobes %= NHASH; 403 if (nprobes < 0) 404 nprobes += NHASH; 405 406 initialprobe = nprobes; 407 for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){ 408 for (hp = &(hdallop->h_htab[initialprobe]), 409 nprobes = 1, 410 hp_ub = &(hdallop->h_htab[NHASH]); 411 (*hp) && (nprobes < NHASH); 412 hp += nprobes, 413 hp -= (hp >= hp_ub) ? NHASH:0, 414 nprobes += 2) 415 { 416 from = yytext; 417 to = FETCHNAME(*hp); 418 while (*from && *to) 419 if (*from++ != *to++) 420 goto nextprobe; 421 if (*to == *from) /*assert both are == 0*/ 422 return(hp); 423 nextprobe: ; 424 } 425 if (*hp == 0 && emptyslot == 0 && 426 hdallop->h_nused < HASHCLOGGED) { 427 emptyslot = hp; 428 emptyhd = hdallop; 429 } 430 } 431 if (emptyslot == 0) { 432 htaballoc(); 433 hdallop = htab->h_next; /* aren't we smart! */ 434 hp = &hdallop->h_htab[initialprobe]; 435 } else { 436 hdallop = emptyhd; 437 hp = emptyslot; 438 } 439 if (instflg) { 440 *hp = symalloc(); 441 hdallop->h_nused++; 442 for (from = yytext, len = 0; *from++; len++) 443 continue; 444 (*hp)->s_name = (char *)savestr(yytext, len + 1, STR_BOTH); 445 } 446 return(hp); 447 } /*end of lookup*/ 448 /* 449 * save a string str with len in the places indicated by place 450 */ 451 struct strdesc *savestr(str, len, place) 452 char *str; 453 int len; 454 int place; 455 { 456 reg struct strdesc *res; 457 int tlen; 458 /* 459 * Compute the total length of the record to live in core 460 */ 461 tlen = sizeof(struct strdesc) - sizeof(res->sd_string); 462 if (place & STR_CORE) 463 tlen += len; 464 /* 465 * See if there is enough space for the record, 466 * and allocate the record. 467 */ 468 if (tlen >= (STRPOOLDALLOP - strplhead->str_nalloc)) 469 strpoolalloc(); 470 res = (struct strdesc *)(strplhead->str_names + strplhead->str_nalloc); 471 /* 472 * Save the string information that is always present 473 */ 474 res->sd_stroff = strfilepos; 475 res->sd_strlen = len; 476 res->sd_place = place; 477 /* 478 * Now, save the string itself. If str is null, then 479 * the characters have already been dumped to the file 480 */ 481 if ((place & STR_CORE) && str) 482 movestr(res[0].sd_string, str, len); 483 if (place & STR_FILE){ 484 if (str){ 485 fwrite(str, 1, len, strfile); 486 } 487 strfilepos += len; 488 } 489 /* 490 * Adjust the in core string pool size 491 */ 492 strplhead->str_nalloc += tlen; 493 return(res); 494 } 495 /* 496 * The relocation information is saved internally in an array of 497 * lists of relocation buffers. The relocation buffers are 498 * exactly the same size as a token buffer; if we use VM for the 499 * temporary file we reclaim this storage, otherwise we create 500 * them by mallocing. 501 */ 502 #define RELBUFLG TOKBUFLG 503 #define NRELOC ((TOKBUFLG - \ 504 (sizeof (int) + sizeof (struct relbufdesc *)) \ 505 ) / (sizeof (struct relocation_info))) 506 507 struct relbufdesc{ 508 int rel_count; 509 struct relbufdesc *rel_next; 510 struct relocation_info rel_reloc[NRELOC]; 511 }; 512 extern struct relbufdesc *tok_free; 513 #define rel_free tok_free 514 static struct relbufdesc *rel_temp; 515 struct relocation_info r_can_1PC; 516 struct relocation_info r_can_0PC; 517 518 initoutrel() 519 { 520 r_can_0PC.r_address = 0; 521 r_can_0PC.r_symbolnum = 0; 522 r_can_0PC.r_pcrel = 0; 523 r_can_0PC.r_length = 0; 524 r_can_0PC.r_extern = 0; 525 526 r_can_1PC = r_can_0PC; 527 r_can_1PC.r_pcrel = 1; 528 } 529 530 outrel(xp, reloc_how) 531 register struct exp *xp; 532 int reloc_how; /* TYPB..TYPH + (possibly)RELOC_PCREL */ 533 { 534 struct relocation_info reloc; 535 register int x_type_mask; 536 int pcrel; 537 538 x_type_mask = xp->e_xtype & ~XFORW; 539 pcrel = reloc_how & RELOC_PCREL; 540 reloc_how &= ~RELOC_PCREL; 541 542 if (bitoff&07) 543 yyerror("Padding error"); 544 if (x_type_mask == XUNDEF) 545 yyerror("Undefined reference"); 546 547 if ( (x_type_mask != XABS) || pcrel ) { 548 if (ty_NORELOC[reloc_how]) 549 yyerror("Illegal Relocation of floating or large int number."); 550 reloc = pcrel ? r_can_1PC : r_can_0PC; 551 reloc.r_address = dotp->e_xvalue - 552 ( (dotp < &usedot[NLOC] || readonlydata) ? 0 : datbase ); 553 reloc.r_length = ty_nlg[reloc_how]; 554 switch(x_type_mask){ 555 case XXTRN | XUNDEF: 556 reloc.r_symbolnum = xp->e_xname->s_index; 557 reloc.r_extern = 1; 558 break; 559 default: 560 if (readonlydata && (x_type_mask&~XXTRN) == XDATA) 561 x_type_mask = XTEXT | (x_type_mask&XXTRN); 562 reloc.r_symbolnum = x_type_mask; 563 break; 564 } 565 if ( (relfil == 0) || (relfil->rel_count >= NRELOC) ){ 566 if (rel_free){ 567 rel_temp = rel_free; 568 rel_free = rel_temp->rel_next; 569 } else { 570 rel_temp = (struct relbufdesc *) 571 Calloc(1,sizeof (struct relbufdesc)); 572 } 573 rel_temp->rel_count = 0; 574 rel_temp->rel_next = relfil; 575 relfil = rusefile[dotp - &usedot[0]] = rel_temp; 576 } 577 relfil->rel_reloc[relfil->rel_count++] = reloc; 578 } 579 /* 580 * write the unrelocated value to the text file 581 */ 582 dotp->e_xvalue += ty_nbyte[reloc_how]; 583 if (pcrel) 584 xp->e_xvalue -= dotp->e_xvalue; 585 switch(reloc_how){ 586 case TYPO: 587 case TYPQ: 588 589 case TYPF: 590 case TYPD: 591 case TYPG: 592 case TYPH: 593 bignumwrite(xp->e_number, reloc_how); 594 break; 595 596 default: 597 bwrite((char *)&(xp->e_xvalue), ty_nbyte[reloc_how], txtfil); 598 break; 599 } 600 } 601 /* 602 * Flush out all of the relocation information. 603 * Note that the individual lists of buffers are in 604 * reverse order, so we must reverse them 605 */ 606 off_t closeoutrel(relocfile) 607 BFILE *relocfile; 608 { 609 int locindex; 610 u_long Closeoutrel(); 611 612 trsize = 0; 613 for (locindex = 0; locindex < NLOC; locindex++){ 614 trsize += Closeoutrel(rusefile[locindex], relocfile); 615 } 616 drsize = 0; 617 for (locindex = 0; locindex < NLOC; locindex++){ 618 drsize += Closeoutrel(rusefile[NLOC + locindex], relocfile); 619 } 620 return(trsize + drsize); 621 } 622 623 u_long Closeoutrel(relfil, relocfile) 624 struct relbufdesc *relfil; 625 BFILE *relocfile; 626 { 627 u_long tail; 628 if (relfil == 0) 629 return(0L); 630 tail = Closeoutrel(relfil->rel_next, relocfile); 631 bwrite((char *)&relfil->rel_reloc[0], 632 relfil->rel_count * sizeof (struct relocation_info), 633 relocfile); 634 return(tail + relfil->rel_count * sizeof (struct relocation_info)); 635 } 636 637 #define NOUTSYMS (nsyms - njxxx - nforgotten - (savelabels ? 0 : nlabels)) 638 int sizesymtab() 639 { 640 return (sizeof (struct nlist) * NOUTSYMS); 641 } 642 /* 643 * Write out n symbols to file f, beginning at p 644 * ignoring symbols that are obsolete, jxxx instructions, and 645 * possibly, labels 646 */ 647 int symwrite(symfile) 648 BFILE *symfile; 649 { 650 int symsout; /*those actually written*/ 651 int symsdesired = NOUTSYMS; 652 reg struct symtab *sp, *ub; 653 char *name; /* temp to save the name */ 654 int totalstr; 655 /* 656 * We use sp->s_index to hold the length of the 657 * name; it isn't used for anything else 658 */ 659 register struct allocbox *allocwalk; 660 661 symsout = 0; 662 totalstr = sizeof(totalstr); 663 DECLITERATE(allocwalk, sp, ub) { 664 if (sp->s_tag >= IGNOREBOUND) 665 continue; 666 if (ISLABEL(sp)) 667 continue; 668 symsout++; 669 name = sp->s_name; /* save pointer */ 670 /* 671 * the length of the symbol table string 672 * always includes the trailing null; 673 * blast the pointer to its a.out value. 674 */ 675 if (sp->s_name && (sp->s_index = STRLEN(sp))){ 676 sp->s_nmx = totalstr; 677 totalstr += sp->s_index; 678 } else { 679 sp->s_nmx = 0; 680 } 681 if (sp->s_ptype != 0) 682 sp->s_type = sp->s_ptype; 683 else 684 sp->s_type = (sp->s_type & (~XFORW)); 685 if (readonlydata && (sp->s_type&~N_EXT) == N_DATA) 686 sp->s_type = N_TEXT | (sp->s_type & N_EXT); 687 bwrite((char *)&sp->s_nm, sizeof (struct nlist), symfile); 688 sp->s_name = name; /* restore pointer */ 689 } 690 if (symsout != symsdesired) 691 yyerror("INTERNAL ERROR: Wrote %d symbols, wanted to write %d symbols\n", 692 symsout, symsdesired); 693 /* 694 * Construct the string pool from the symbols that were written, 695 * possibly fetching from the string file if the string 696 * is not core resident. 697 */ 698 bwrite(&totalstr, sizeof(totalstr), symfile); 699 symsout = 0; 700 DECLITERATE(allocwalk, sp, ub) { 701 if (sp->s_tag >= IGNOREBOUND) 702 continue; 703 if (ISLABEL(sp)) 704 continue; 705 symsout++; 706 if (STRLEN(sp) > 0){ 707 if (STRPLACE(sp) & STR_CORE){ 708 bwrite(FETCHNAME(sp), STRLEN(sp), symfile); 709 } else if (STRPLACE(sp) & STR_FILE){ 710 char rbuf[2048]; 711 int left, nread; 712 fseek(strfile, STROFF(sp), 0); 713 for (left = STRLEN(sp); left > 0; left -= nread){ 714 nread = fread(rbuf, sizeof(char), 715 min(sizeof(rbuf), left), strfile); 716 if (nread == 0) 717 break; 718 bwrite(rbuf, nread, symfile); 719 } 720 } 721 } 722 } 723 if (symsout != symsdesired) 724 yyerror("INTERNAL ERROR: Wrote %d strings, wanted %d\n", 725 symsout, symsdesired); 726 } 727