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