1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <bootexec.h> 5 #include <mach.h> 6 #include "elf.h" 7 8 /* 9 * All a.out header types. The dummy entry allows canonical 10 * processing of the union as a sequence of longs 11 */ 12 13 typedef struct { 14 union{ 15 struct { 16 Exec; /* a.out.h */ 17 uvlong hdr[1]; 18 }; 19 Ehdr; /* elf.h */ 20 struct mipsexec; /* bootexec.h */ 21 struct mips4kexec; /* bootexec.h */ 22 struct sparcexec; /* bootexec.h */ 23 struct nextexec; /* bootexec.h */ 24 } e; 25 long dummy; /* padding to ensure extra long */ 26 } ExecHdr; 27 28 static int nextboot(int, Fhdr*, ExecHdr*); 29 static int sparcboot(int, Fhdr*, ExecHdr*); 30 static int mipsboot(int, Fhdr*, ExecHdr*); 31 static int mips4kboot(int, Fhdr*, ExecHdr*); 32 static int common(int, Fhdr*, ExecHdr*); 33 static int commonllp64(int, Fhdr*, ExecHdr*); 34 static int adotout(int, Fhdr*, ExecHdr*); 35 static int elfdotout(int, Fhdr*, ExecHdr*); 36 static int armdotout(int, Fhdr*, ExecHdr*); 37 static void setsym(Fhdr*, long, long, long, vlong); 38 static void setdata(Fhdr*, uvlong, long, vlong, long); 39 static void settext(Fhdr*, uvlong, uvlong, long, vlong); 40 static void hswal(void*, int, ulong(*)(ulong)); 41 static uvlong _round(uvlong, ulong); 42 43 /* 44 * definition of per-executable file type structures 45 */ 46 47 typedef struct Exectable{ 48 long magic; /* big-endian magic number of file */ 49 char *name; /* executable identifier */ 50 char *dlmname; /* dynamically loadable module identifier */ 51 uchar type; /* Internal code */ 52 uchar _magic; /* _MAGIC() magic */ 53 Mach *mach; /* Per-machine data */ 54 long hsize; /* header size */ 55 ulong (*swal)(ulong); /* beswal or leswal */ 56 int (*hparse)(int, Fhdr*, ExecHdr*); 57 } ExecTable; 58 59 extern Mach mmips; 60 extern Mach mmips2le; 61 extern Mach mmips2be; 62 extern Mach msparc; 63 extern Mach msparc64; 64 extern Mach m68020; 65 extern Mach mi386; 66 extern Mach mamd64; 67 extern Mach marm; 68 extern Mach mpower; 69 extern Mach malpha; 70 71 ExecTable exectab[] = 72 { 73 { V_MAGIC, /* Mips v.out */ 74 "mips plan 9 executable", 75 "mips plan 9 dlm", 76 FMIPS, 77 1, 78 &mmips, 79 sizeof(Exec), 80 beswal, 81 adotout }, 82 { M_MAGIC, /* Mips 4.out */ 83 "mips 4k plan 9 executable BE", 84 "mips 4k plan 9 dlm BE", 85 FMIPS2BE, 86 1, 87 &mmips2be, 88 sizeof(Exec), 89 beswal, 90 adotout }, 91 { N_MAGIC, /* Mips 0.out */ 92 "mips 4k plan 9 executable LE", 93 "mips 4k plan 9 dlm LE", 94 FMIPS2LE, 95 1, 96 &mmips2le, 97 sizeof(Exec), 98 beswal, 99 adotout }, 100 { 0x160<<16, /* Mips boot image */ 101 "mips plan 9 boot image", 102 nil, 103 FMIPSB, 104 0, 105 &mmips, 106 sizeof(struct mipsexec), 107 beswal, 108 mipsboot }, 109 { (0x160<<16)|3, /* Mips boot image */ 110 "mips 4k plan 9 boot image", 111 nil, 112 FMIPSB, 113 0, 114 &mmips2be, 115 sizeof(struct mips4kexec), 116 beswal, 117 mips4kboot }, 118 { K_MAGIC, /* Sparc k.out */ 119 "sparc plan 9 executable", 120 "sparc plan 9 dlm", 121 FSPARC, 122 1, 123 &msparc, 124 sizeof(Exec), 125 beswal, 126 adotout }, 127 { 0x01030107, /* Sparc boot image */ 128 "sparc plan 9 boot image", 129 nil, 130 FSPARCB, 131 0, 132 &msparc, 133 sizeof(struct sparcexec), 134 beswal, 135 sparcboot }, 136 { U_MAGIC, /* Sparc64 u.out */ 137 "sparc64 plan 9 executable", 138 "sparc64 plan 9 dlm", 139 FSPARC64, 140 1, 141 &msparc64, 142 sizeof(Exec), 143 beswal, 144 adotout }, 145 { A_MAGIC, /* 68020 2.out & boot image */ 146 "68020 plan 9 executable", 147 "68020 plan 9 dlm", 148 F68020, 149 1, 150 &m68020, 151 sizeof(Exec), 152 beswal, 153 common }, 154 { 0xFEEDFACE, /* Next boot image */ 155 "next plan 9 boot image", 156 nil, 157 FNEXTB, 158 0, 159 &m68020, 160 sizeof(struct nextexec), 161 beswal, 162 nextboot }, 163 { I_MAGIC, /* I386 8.out & boot image */ 164 "386 plan 9 executable", 165 "386 plan 9 dlm", 166 FI386, 167 1, 168 &mi386, 169 sizeof(Exec), 170 beswal, 171 common }, 172 { S_MAGIC, /* amd64 6.out & boot image */ 173 "amd64 plan 9 executable", 174 "amd64 plan 9 dlm", 175 FAMD64, 176 1, 177 &mamd64, 178 sizeof(Exec)+8, 179 nil, 180 commonllp64 }, 181 { Q_MAGIC, /* PowerPC q.out & boot image */ 182 "power plan 9 executable", 183 "power plan 9 dlm", 184 FPOWER, 185 1, 186 &mpower, 187 sizeof(Exec), 188 beswal, 189 common }, 190 { ELF_MAG, /* any elf32 */ 191 "elf executable", 192 nil, 193 FNONE, 194 0, 195 &mi386, 196 sizeof(Ehdr), 197 nil, 198 elfdotout }, 199 { E_MAGIC, /* Arm 5.out */ 200 "arm plan 9 executable", 201 "arm plan 9 dlm", 202 FARM, 203 1, 204 &marm, 205 sizeof(Exec), 206 beswal, 207 common }, 208 { (143<<16)|0413, /* (Free|Net)BSD Arm */ 209 "arm *bsd executable", 210 nil, 211 FARM, 212 0, 213 &marm, 214 sizeof(Exec), 215 leswal, 216 armdotout }, 217 { L_MAGIC, /* alpha 7.out */ 218 "alpha plan 9 executable", 219 "alpha plan 9 dlm", 220 FALPHA, 221 1, 222 &malpha, 223 sizeof(Exec), 224 beswal, 225 common }, 226 { 0x0700e0c3, /* alpha boot image */ 227 "alpha plan 9 boot image", 228 nil, 229 FALPHA, 230 0, 231 &malpha, 232 sizeof(Exec), 233 beswal, 234 common }, 235 { 0 }, 236 }; 237 238 Mach *mach = &mi386; /* Global current machine table */ 239 240 static ExecTable* 241 couldbe4k(ExecTable *mp) 242 { 243 Dir *d; 244 ExecTable *f; 245 246 if((d=dirstat("/proc/1/regs")) == nil) 247 return mp; 248 if(d->length < 32*8){ /* R3000 */ 249 free(d); 250 return mp; 251 } 252 free(d); 253 for (f = exectab; f->magic; f++) 254 if(f->magic == M_MAGIC) { 255 f->name = "mips plan 9 executable on mips2 kernel"; 256 return f; 257 } 258 return mp; 259 } 260 261 int 262 crackhdr(int fd, Fhdr *fp) 263 { 264 ExecTable *mp; 265 ExecHdr d; 266 int nb, ret; 267 ulong magic; 268 269 fp->type = FNONE; 270 nb = read(fd, (char *)&d.e, sizeof(d.e)); 271 if (nb <= 0) 272 return 0; 273 274 ret = 0; 275 magic = beswal(d.e.magic); /* big-endian */ 276 for (mp = exectab; mp->magic; mp++) { 277 if (nb < mp->hsize) 278 continue; 279 280 /* 281 * The magic number has morphed into something 282 * with fields (the straw was DYN_MAGIC) so now 283 * a flag is needed in Fhdr to distinguish _MAGIC() 284 * magic numbers from foreign magic numbers. 285 * 286 * This code is creaking a bit and if it has to 287 * be modified/extended much more it's probably 288 * time to step back and redo it all. 289 */ 290 if(mp->_magic){ 291 if(mp->magic != (magic & ~DYN_MAGIC)) 292 continue; 293 294 if(mp->magic == V_MAGIC) 295 mp = couldbe4k(mp); 296 297 if ((magic & DYN_MAGIC) && mp->dlmname != nil) 298 fp->name = mp->dlmname; 299 else 300 fp->name = mp->name; 301 } 302 else{ 303 if(mp->magic != magic) 304 continue; 305 fp->name = mp->name; 306 } 307 fp->type = mp->type; 308 fp->hdrsz = mp->hsize; /* will be zero on bootables */ 309 fp->_magic = mp->_magic; 310 fp->magic = magic; 311 312 mach = mp->mach; 313 if(mp->swal != nil) 314 hswal(&d, sizeof(d.e)/sizeof(ulong), mp->swal); 315 ret = mp->hparse(fd, fp, &d); 316 seek(fd, mp->hsize, 0); /* seek to end of header */ 317 break; 318 } 319 if(mp->magic == 0) 320 werrstr("unknown header type"); 321 return ret; 322 } 323 324 /* 325 * Convert header to canonical form 326 */ 327 static void 328 hswal(void *v, int n, ulong (*swap)(ulong)) 329 { 330 ulong *ulp; 331 332 for(ulp = v; n--; ulp++) 333 *ulp = (*swap)(*ulp); 334 } 335 336 /* 337 * Crack a normal a.out-type header 338 */ 339 static int 340 adotout(int fd, Fhdr *fp, ExecHdr *hp) 341 { 342 long pgsize; 343 344 USED(fd); 345 pgsize = mach->pgsize; 346 settext(fp, hp->e.entry, pgsize+sizeof(Exec), 347 hp->e.text, sizeof(Exec)); 348 setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize), 349 hp->e.data, fp->txtsz+sizeof(Exec), hp->e.bss); 350 setsym(fp, hp->e.syms, hp->e.spsz, hp->e.pcsz, fp->datoff+fp->datsz); 351 return 1; 352 } 353 354 static void 355 commonboot(Fhdr *fp) 356 { 357 if (!(fp->entry & mach->ktmask)) 358 return; 359 360 switch(fp->type) { /* boot image */ 361 case F68020: 362 fp->type = F68020B; 363 fp->name = "68020 plan 9 boot image"; 364 break; 365 case FI386: 366 fp->type = FI386B; 367 fp->txtaddr = (u32int)fp->entry; 368 fp->name = "386 plan 9 boot image"; 369 fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize); 370 break; 371 case FARM: 372 fp->txtaddr = mach->kbase+0x8010; 373 fp->name = "ARM plan 9 boot image"; 374 fp->dataddr = fp->txtaddr+fp->txtsz; 375 return; 376 case FALPHA: 377 fp->type = FALPHAB; 378 fp->txtaddr = (u32int)fp->entry; 379 fp->name = "alpha plan 9 boot image"; 380 fp->dataddr = fp->txtaddr+fp->txtsz; 381 break; 382 case FPOWER: 383 fp->type = FPOWERB; 384 fp->txtaddr = (u32int)fp->entry; 385 fp->name = "power plan 9 boot image"; 386 fp->dataddr = fp->txtaddr+fp->txtsz; 387 break; 388 case FAMD64: 389 fp->type = FAMD64B; 390 fp->txtaddr = fp->entry; 391 fp->name = "amd64 plan 9 boot image"; 392 fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize); 393 break; 394 default: 395 return; 396 } 397 fp->hdrsz = 0; /* header stripped */ 398 } 399 400 /* 401 * _MAGIC() style headers and 402 * alpha plan9-style bootable images for axp "headerless" boot 403 * 404 */ 405 static int 406 common(int fd, Fhdr *fp, ExecHdr *hp) 407 { 408 adotout(fd, fp, hp); 409 if(hp->e.magic & DYN_MAGIC) { 410 fp->txtaddr = 0; 411 fp->dataddr = fp->txtsz; 412 return 1; 413 } 414 commonboot(fp); 415 return 1; 416 } 417 418 static int 419 commonllp64(int, Fhdr *fp, ExecHdr *hp) 420 { 421 long pgsize; 422 uvlong entry; 423 424 hswal(&hp->e, sizeof(Exec)/sizeof(long), beswal); 425 if(!(hp->e.magic & HDR_MAGIC)) 426 return 0; 427 428 /* 429 * There can be more magic here if the 430 * header ever needs more expansion. 431 * For now just catch use of any of the 432 * unused bits. 433 */ 434 if((hp->e.magic & ~DYN_MAGIC)>>16) 435 return 0; 436 entry = beswav(hp->e.hdr[0]); 437 438 pgsize = mach->pgsize; 439 settext(fp, entry, pgsize+fp->hdrsz, hp->e.text, fp->hdrsz); 440 setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize), 441 hp->e.data, fp->txtsz+fp->hdrsz, hp->e.bss); 442 setsym(fp, hp->e.syms, hp->e.spsz, hp->e.pcsz, fp->datoff+fp->datsz); 443 444 if(hp->e.magic & DYN_MAGIC) { 445 fp->txtaddr = 0; 446 fp->dataddr = fp->txtsz; 447 return 1; 448 } 449 commonboot(fp); 450 return 1; 451 } 452 453 /* 454 * mips bootable image. 455 */ 456 static int 457 mipsboot(int fd, Fhdr *fp, ExecHdr *hp) 458 { 459 USED(fd); 460 fp->type = FMIPSB; 461 switch(hp->e.amagic) { 462 default: 463 case 0407: /* some kind of mips */ 464 settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start, 465 hp->e.tsize, sizeof(struct mipsexec)+4); 466 setdata(fp, (u32int)hp->e.data_start, hp->e.dsize, 467 fp->txtoff+hp->e.tsize, hp->e.bsize); 468 break; 469 case 0413: /* some kind of mips */ 470 settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start, 471 hp->e.tsize, 0); 472 setdata(fp, (u32int)hp->e.data_start, hp->e.dsize, 473 hp->e.tsize, hp->e.bsize); 474 break; 475 } 476 setsym(fp, hp->e.nsyms, 0, hp->e.pcsize, hp->e.symptr); 477 fp->hdrsz = 0; /* header stripped */ 478 return 1; 479 } 480 481 /* 482 * mips4k bootable image. 483 */ 484 static int 485 mips4kboot(int fd, Fhdr *fp, ExecHdr *hp) 486 { 487 USED(fd); 488 fp->type = FMIPSB; 489 switch(hp->e.h.amagic) { 490 default: 491 case 0407: /* some kind of mips */ 492 settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start, 493 hp->e.h.tsize, sizeof(struct mips4kexec)); 494 setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize, 495 fp->txtoff+hp->e.h.tsize, hp->e.h.bsize); 496 break; 497 case 0413: /* some kind of mips */ 498 settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start, 499 hp->e.h.tsize, 0); 500 setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize, 501 hp->e.h.tsize, hp->e.h.bsize); 502 break; 503 } 504 setsym(fp, hp->e.h.nsyms, 0, hp->e.h.pcsize, hp->e.h.symptr); 505 fp->hdrsz = 0; /* header stripped */ 506 return 1; 507 } 508 509 /* 510 * sparc bootable image 511 */ 512 static int 513 sparcboot(int fd, Fhdr *fp, ExecHdr *hp) 514 { 515 USED(fd); 516 fp->type = FSPARCB; 517 settext(fp, hp->e.sentry, hp->e.sentry, hp->e.stext, 518 sizeof(struct sparcexec)); 519 setdata(fp, hp->e.sentry+hp->e.stext, hp->e.sdata, 520 fp->txtoff+hp->e.stext, hp->e.sbss); 521 setsym(fp, hp->e.ssyms, 0, hp->e.sdrsize, fp->datoff+hp->e.sdata); 522 fp->hdrsz = 0; /* header stripped */ 523 return 1; 524 } 525 526 /* 527 * next bootable image 528 */ 529 static int 530 nextboot(int fd, Fhdr *fp, ExecHdr *hp) 531 { 532 USED(fd); 533 fp->type = FNEXTB; 534 settext(fp, hp->e.textc.vmaddr, hp->e.textc.vmaddr, 535 hp->e.texts.size, hp->e.texts.offset); 536 setdata(fp, hp->e.datac.vmaddr, hp->e.datas.size, 537 hp->e.datas.offset, hp->e.bsss.size); 538 setsym(fp, hp->e.symc.nsyms, hp->e.symc.spoff, hp->e.symc.pcoff, 539 hp->e.symc.symoff); 540 fp->hdrsz = 0; /* header stripped */ 541 return 1; 542 } 543 544 /* 545 * Elf32 binaries. 546 */ 547 static int 548 elfdotout(int fd, Fhdr *fp, ExecHdr *hp) 549 { 550 551 ulong (*swal)(ulong); 552 ushort (*swab)(ushort); 553 Ehdr *ep; 554 Phdr *ph; 555 int i, it, id, is, phsz; 556 557 /* bitswap the header according to the DATA format */ 558 ep = &hp->e; 559 if(ep->ident[CLASS] != ELFCLASS32) { 560 werrstr("bad ELF class - not 32 bit"); 561 return 0; 562 } 563 if(ep->ident[DATA] == ELFDATA2LSB) { 564 swab = leswab; 565 swal = leswal; 566 } else if(ep->ident[DATA] == ELFDATA2MSB) { 567 swab = beswab; 568 swal = beswal; 569 } else { 570 werrstr("bad ELF encoding - not big or little endian"); 571 return 0; 572 } 573 574 ep->type = swab(ep->type); 575 ep->machine = swab(ep->machine); 576 ep->version = swal(ep->version); 577 ep->elfentry = swal(ep->elfentry); 578 ep->phoff = swal(ep->phoff); 579 ep->shoff = swal(ep->shoff); 580 ep->flags = swal(ep->flags); 581 ep->ehsize = swab(ep->ehsize); 582 ep->phentsize = swab(ep->phentsize); 583 ep->phnum = swab(ep->phnum); 584 ep->shentsize = swab(ep->shentsize); 585 ep->shnum = swab(ep->shnum); 586 ep->shstrndx = swab(ep->shstrndx); 587 if(ep->type != EXEC || ep->version != CURRENT) 588 return 0; 589 590 /* we could definitely support a lot more machines here */ 591 fp->magic = ELF_MAG; 592 fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15; 593 switch(ep->machine) { 594 case I386: 595 mach = &mi386; 596 fp->type = FI386; 597 break; 598 case MIPS: 599 mach = &mmips; 600 fp->type = FMIPS; 601 break; 602 case SPARC64: 603 mach = &msparc64; 604 fp->type = FSPARC64; 605 break; 606 case POWER: 607 mach = &mpower; 608 fp->type = FPOWER; 609 break; 610 case AMD64: 611 mach = &mamd64; 612 fp->type = FAMD64; 613 break; 614 default: 615 return 0; 616 } 617 618 if(ep->phentsize != sizeof(Phdr)) { 619 werrstr("bad ELF header size"); 620 return 0; 621 } 622 phsz = sizeof(Phdr)*ep->phnum; 623 ph = malloc(phsz); 624 if(!ph) 625 return 0; 626 seek(fd, ep->phoff, 0); 627 if(read(fd, ph, phsz) < 0) { 628 free(ph); 629 return 0; 630 } 631 hswal(ph, phsz/sizeof(ulong), swal); 632 633 /* find text, data and symbols and install them */ 634 it = id = is = -1; 635 for(i = 0; i < ep->phnum; i++) { 636 if(ph[i].type == LOAD 637 && (ph[i].flags & (R|X)) == (R|X) && it == -1) 638 it = i; 639 else if(ph[i].type == LOAD 640 && (ph[i].flags & (R|W)) == (R|W) && id == -1) 641 id = i; 642 else if(ph[i].type == NOPTYPE && is == -1) 643 is = i; 644 } 645 if(it == -1 || id == -1) { 646 /* 647 * The SPARC64 boot image is something of an ELF hack. 648 * Text+Data+BSS are represented by ph[0]. Symbols 649 * are represented by ph[1]: 650 * 651 * filesz, memsz, vaddr, paddr, off 652 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff 653 * ph[1] : symsz, lcsz, 0, 0, symoff 654 */ 655 if(ep->machine == SPARC64 && ep->phnum == 2) { 656 ulong txtaddr, txtsz, dataddr, bsssz; 657 658 txtaddr = ph[0].vaddr | 0x80000000; 659 txtsz = ph[0].filesz - ph[0].paddr; 660 dataddr = txtaddr + txtsz; 661 bsssz = ph[0].memsz - ph[0].filesz; 662 settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset); 663 setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz); 664 setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset); 665 free(ph); 666 return 1; 667 } 668 669 werrstr("No TEXT or DATA sections"); 670 free(ph); 671 return 0; 672 } 673 674 settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset); 675 setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz); 676 if(is != -1) 677 setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset); 678 free(ph); 679 return 1; 680 } 681 682 /* 683 * (Free|Net)BSD ARM header. 684 */ 685 static int 686 armdotout(int fd, Fhdr *fp, ExecHdr *hp) 687 { 688 uvlong kbase; 689 690 USED(fd); 691 settext(fp, hp->e.entry, sizeof(Exec), hp->e.text, sizeof(Exec)); 692 setdata(fp, fp->txtsz, hp->e.data, fp->txtsz, hp->e.bss); 693 setsym(fp, hp->e.syms, hp->e.spsz, hp->e.pcsz, fp->datoff+fp->datsz); 694 695 kbase = 0xF0000000; 696 if ((fp->entry & kbase) == kbase) { /* Boot image */ 697 fp->txtaddr = kbase+sizeof(Exec); 698 fp->name = "ARM *BSD boot image"; 699 fp->hdrsz = 0; /* header stripped */ 700 fp->dataddr = kbase+fp->txtsz; 701 } 702 return 1; 703 } 704 705 static void 706 settext(Fhdr *fp, uvlong e, uvlong a, long s, vlong off) 707 { 708 fp->txtaddr = a; 709 fp->entry = e; 710 fp->txtsz = s; 711 fp->txtoff = off; 712 } 713 714 static void 715 setdata(Fhdr *fp, uvlong a, long s, vlong off, long bss) 716 { 717 fp->dataddr = a; 718 fp->datsz = s; 719 fp->datoff = off; 720 fp->bsssz = bss; 721 } 722 723 static void 724 setsym(Fhdr *fp, long symsz, long sppcsz, long lnpcsz, vlong symoff) 725 { 726 fp->symsz = symsz; 727 fp->symoff = symoff; 728 fp->sppcsz = sppcsz; 729 fp->sppcoff = fp->symoff+fp->symsz; 730 fp->lnpcsz = lnpcsz; 731 fp->lnpcoff = fp->sppcoff+fp->sppcsz; 732 } 733 734 735 static uvlong 736 _round(uvlong a, ulong b) 737 { 738 uvlong w; 739 740 w = (a/b)*b; 741 if (a!=w) 742 w += b; 743 return(w); 744 } 745