// Inferno utils/6l/asm.c
// http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c
//
//      Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//      Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//      Portions Copyright © 1997-1999 Vita Nuova Limited
//      Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//      Portions Copyright © 2004,2006 Bruce Ellis
//      Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//      Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//      Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Writing object files.
#include        "l.h"
#include        "../ld/lib.h"
#include        "../ld/elf.h"
#include        "../ld/dwarf.h"
#include        "../ld/macho.h"
#include        "../ld/pe.h"
#define PADDR(a)        ((uint32)(a) & ~0x80000000)
char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2";
char freebsddynld[] = "/libexec/ld-elf.so.1";
char openbsddynld[] = "/usr/libexec/ld.so";
char netbsddynld[] = "/libexec/ld.elf_so";
char dragonflydynld[] = "/usr/libexec/ld-elf.so.2";
char solarisdynld[] = "/lib/amd64/ld.so.1";
char    zeroes[32];
static int
needlib(char *name)
{
        char *p;
        LSym *s;
        if(*name == '\0')
                return 0;
        /* reuse hash code in symbol table */
        p = smprint(".elfload.%s", name);
        s = linklookup(ctxt, p, 0);
        free(p);
        if(s->type == 0) {
                s->type = 100;  // avoid SDATA, etc.
                return 1;
        }
        return 0;
}
int nelfsym = 1;
static void addpltsym(LSym*);
static void addgotsym(LSym*);
void
adddynrela(LSym *rela, LSym *s, Reloc *r)
{
        addaddrplus(ctxt, rela, s, r->off);
        adduint64(ctxt, rela, R_X86_64_RELATIVE);
        addaddrplus(ctxt, rela, r->sym, r->add); // Addend
}
void
adddynrel(LSym *s, Reloc *r)
{
        LSym *targ, *rela, *got;
        
        targ = r->sym;
        ctxt->cursym = s;
        switch(r->type) {
        default:
                if(r->type >= 256) {
                        diag("unexpected relocation type %d", r->type);
                        return;
                }
                break;
        // Handle relocations found in ELF object files.
        case 256 + R_X86_64_PC32:
                if(targ->type == SDYNIMPORT)
                        diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ->name);
                if(targ->type == 0 || targ->type == SXREF)
                        diag("unknown symbol %s in pcrel", targ->name);
                r->type = R_PCREL;
                r->add += 4;
                return;
        
        case 256 + R_X86_64_PLT32:
                r->type = R_PCREL;
                r->add += 4;
                if(targ->type == SDYNIMPORT) {
                        addpltsym(targ);
                        r->sym = linklookup(ctxt, ".plt", 0);
                        r->add += targ->plt;
                }
                return;
        
        case 256 + R_X86_64_GOTPCREL:
                if(targ->type != SDYNIMPORT) {
                        // have symbol
                        if(r->off >= 2 && s->p[r->off-2] == 0x8b) {
                                // turn MOVQ of GOT entry into LEAQ of symbol itself
                                s->p[r->off-2] = 0x8d;
                                r->type = R_PCREL;
                                r->add += 4;
                                return;
                        }
                        // fall back to using GOT and hope for the best (CMOV*)
                        // TODO: just needs relocation, no need to put in .dynsym
                }
                addgotsym(targ);
                r->type = R_PCREL;
                r->sym = linklookup(ctxt, ".got", 0);
                r->add += 4;
                r->add += targ->got;
                return;
        
        case 256 + R_X86_64_64:
                if(targ->type == SDYNIMPORT)
                        diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ->name);
                r->type = R_ADDR;
                return;
        
        // Handle relocations found in Mach-O object files.
        case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 0:
        case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 0:
        case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 0:
                // TODO: What is the difference between all these?
                r->type = R_ADDR;
                if(targ->type == SDYNIMPORT)
                        diag("unexpected reloc for dynamic symbol %s", targ->name);
                return;
        case 512 + MACHO_X86_64_RELOC_BRANCH*2 + 1:
                if(targ->type == SDYNIMPORT) {
                        addpltsym(targ);
                        r->sym = linklookup(ctxt, ".plt", 0);
                        r->add = targ->plt;
                        r->type = R_PCREL;
                        return;
                }
                // fall through
        case 512 + MACHO_X86_64_RELOC_UNSIGNED*2 + 1:
        case 512 + MACHO_X86_64_RELOC_SIGNED*2 + 1:
        case 512 + MACHO_X86_64_RELOC_SIGNED_1*2 + 1:
        case 512 + MACHO_X86_64_RELOC_SIGNED_2*2 + 1:
        case 512 + MACHO_X86_64_RELOC_SIGNED_4*2 + 1:
                r->type = R_PCREL;
                if(targ->type == SDYNIMPORT)
                        diag("unexpected pc-relative reloc for dynamic symbol %s", targ->name);
                return;
        case 512 + MACHO_X86_64_RELOC_GOT_LOAD*2 + 1:
                if(targ->type != SDYNIMPORT) {
                        // have symbol
                        // turn MOVQ of GOT entry into LEAQ of symbol itself
                        if(r->off < 2 || s->p[r->off-2] != 0x8b) {
                                diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ->name);
                                return;
                        }
                        s->p[r->off-2] = 0x8d;
                        r->type = R_PCREL;
                        return;
                }
                // fall through
        case 512 + MACHO_X86_64_RELOC_GOT*2 + 1:
                if(targ->type != SDYNIMPORT)
                        diag("unexpected GOT reloc for non-dynamic symbol %s", targ->name);
                addgotsym(targ);
                r->type = R_PCREL;
                r->sym = linklookup(ctxt, ".got", 0);
                r->add += targ->got;
                return;
        }
        
        // Handle references to ELF symbols from our own object files.
        if(targ->type != SDYNIMPORT)
                return;
        switch(r->type) {
        case R_CALL:
        case R_PCREL:
                addpltsym(targ);
                r->sym = linklookup(ctxt, ".plt", 0);
                r->add = targ->plt;
                return;
        
        case R_ADDR:
                if(s->type == STEXT && iself) {
                        // The code is asking for the address of an external
                        // function.  We provide it with the address of the
                        // correspondent GOT symbol.
                        addgotsym(targ);
                        r->sym = linklookup(ctxt, ".got", 0);
                        r->add += targ->got;
                        return;
                }
                if(s->type != SDATA)
                        break;
                if(iself) {
                        adddynsym(ctxt, targ);
                        rela = linklookup(ctxt, ".rela", 0);
                        addaddrplus(ctxt, rela, s, r->off);
                        if(r->siz == 8)
                                adduint64(ctxt, rela, ELF64_R_INFO(targ->dynid, R_X86_64_64));
                        else
                                adduint64(ctxt, rela, ELF64_R_INFO(targ->dynid, R_X86_64_32));
                        adduint64(ctxt, rela, r->add);
                        r->type = 256;  // ignore during relocsym
                        return;
                }
                if(HEADTYPE == Hdarwin && s->size == PtrSize && r->off == 0) {
                        // Mach-O relocations are a royal pain to lay out.
                        // They use a compact stateful bytecode representation
                        // that is too much bother to deal with.
                        // Instead, interpret the C declaration
                        //      void *_Cvar_stderr = &stderr;
                        // as making _Cvar_stderr the name of a GOT entry
                        // for stderr.  This is separate from the usual GOT entry,
                        // just in case the C code assigns to the variable,
                        // and of course it only works for single pointers,
                        // but we only need to support cgo and that's all it needs.
                        adddynsym(ctxt, targ);
                        got = linklookup(ctxt, ".got", 0);
                        s->type = got->type | SSUB;
                        s->outer = got;
                        s->sub = got->sub;
                        got->sub = s;
                        s->value = got->size;
                        adduint64(ctxt, got, 0);
                        adduint32(ctxt, linklookup(ctxt, ".linkedit.got", 0), targ->dynid);
                        r->type = 256;  // ignore during relocsym
                        return;
                }
                break;
        }
        
        ctxt->cursym = s;
        diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type);
}
int
elfreloc1(Reloc *r, vlong sectoff)
{
        int32 elfsym;
        VPUT(sectoff);
        elfsym = r->xsym->elfsym;
        switch(r->type) {
        default:
                return -1;
        case R_ADDR:
                if(r->siz == 4)
                        VPUT(R_X86_64_32 | (uint64)elfsym<<32);
                else if(r->siz == 8)
                        VPUT(R_X86_64_64 | (uint64)elfsym<<32);
                else
                        return -1;
                break;
        case R_TLS_LE:
                if(r->siz == 4)
                        VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32);
                else
                        return -1;
                break;
                
        case R_CALL:
        case R_PCREL:
                if(r->siz == 4) {
                        if(r->xsym->type == SDYNIMPORT)
                                VPUT(R_X86_64_GOTPCREL | (uint64)elfsym<<32);
                        else
                                VPUT(R_X86_64_PC32 | (uint64)elfsym<<32);
                } else
                        return -1;
                break;
        
        case R_TLS:
                if(r->siz == 4) {
                        if(flag_shared)
                                VPUT(R_X86_64_GOTTPOFF | (uint64)elfsym<<32);
                        else
                                VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32);
                } else
                        return -1;
                break;          
        }
        VPUT(r->xadd);
        return 0;
}
int
machoreloc1(Reloc *r, vlong sectoff)
{
        uint32 v;
        LSym *rs;
        
        rs = r->xsym;
        if(rs->type == SHOSTOBJ) {
                if(rs->dynid < 0) {
                        diag("reloc %d to non-macho symbol %s type=%d", r->type, rs->name, rs->type);
                        return -1;
                }
                v = rs->dynid;                  
                v |= 1<<27; // external relocation
        } else {
                v = rs->sect->extnum;
                if(v == 0) {
                        diag("reloc %d to symbol %s in non-macho section %s type=%d", r->type, rs->name, rs->sect->name, rs->type);
                        return -1;
                }
        }
        switch(r->type) {
        default:
                return -1;
        case R_ADDR:
                v |= MACHO_X86_64_RELOC_UNSIGNED<<28;
                break;
        case R_CALL:
        case R_PCREL:
                v |= 1<<24; // pc-relative bit
                v |= MACHO_X86_64_RELOC_BRANCH<<28;
                break;
        }
        
        switch(r->siz) {
        default:
                return -1;
        case 1:
                v |= 0<<25;
                break;
        case 2:
                v |= 1<<25;
                break;
        case 4:
                v |= 2<<25;
                break;
        case 8:
                v |= 3<<25;
                break;
        }
        LPUT(sectoff);
        LPUT(v);
        return 0;
}
int
archreloc(Reloc *r, LSym *s, vlong *val)
{
        USED(r);
        USED(s);
        USED(val);
        return -1;
}
void
elfsetupplt(void)
{
        LSym *plt, *got;
        plt = linklookup(ctxt, ".plt", 0);
        got = linklookup(ctxt, ".got.plt", 0);
        if(plt->size == 0) {
                // pushq got+8(IP)
                adduint8(ctxt, plt, 0xff);
                adduint8(ctxt, plt, 0x35);
                addpcrelplus(ctxt, plt, got, 8);
                
                // jmpq got+16(IP)
                adduint8(ctxt, plt, 0xff);
                adduint8(ctxt, plt, 0x25);
                addpcrelplus(ctxt, plt, got, 16);
                
                // nopl 0(AX)
                adduint32(ctxt, plt, 0x00401f0f);
                
                // assume got->size == 0 too
                addaddrplus(ctxt, got, linklookup(ctxt, ".dynamic", 0), 0);
                adduint64(ctxt, got, 0);
                adduint64(ctxt, got, 0);
        }
}
static void
addpltsym(LSym *s)
{
        if(s->plt >= 0)
                return;
        
        adddynsym(ctxt, s);
        
        if(iself) {
                LSym *plt, *got, *rela;
                plt = linklookup(ctxt, ".plt", 0);
                got = linklookup(ctxt, ".got.plt", 0);
                rela = linklookup(ctxt, ".rela.plt", 0);
                if(plt->size == 0)
                        elfsetupplt();
                
                // jmpq *got+size(IP)
                adduint8(ctxt, plt, 0xff);
                adduint8(ctxt, plt, 0x25);
                addpcrelplus(ctxt, plt, got, got->size);
        
                // add to got: pointer to current pos in plt
                addaddrplus(ctxt, got, plt, plt->size);
                
                // pushq $x
                adduint8(ctxt, plt, 0x68);
                adduint32(ctxt, plt, (got->size-24-8)/8);
                
                // jmpq .plt
                adduint8(ctxt, plt, 0xe9);
                adduint32(ctxt, plt, -(plt->size+4));
                
                // rela
                addaddrplus(ctxt, rela, got, got->size-8);
                adduint64(ctxt, rela, ELF64_R_INFO(s->dynid, R_X86_64_JMP_SLOT));
                adduint64(ctxt, rela, 0);
                
                s->plt = plt->size - 16;
        } else if(HEADTYPE == Hdarwin) {
                // To do lazy symbol lookup right, we're supposed
                // to tell the dynamic loader which library each 
                // symbol comes from and format the link info
                // section just so.  I'm too lazy (ha!) to do that
                // so for now we'll just use non-lazy pointers,
                // which don't need to be told which library to use.
                //
                // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html
                // has details about what we're avoiding.
                LSym *plt;
                
                addgotsym(s);
                plt = linklookup(ctxt, ".plt", 0);
                adduint32(ctxt, linklookup(ctxt, ".linkedit.plt", 0), s->dynid);
                // jmpq *got+size(IP)
                s->plt = plt->size;
                adduint8(ctxt, plt, 0xff);
                adduint8(ctxt, plt, 0x25);
                addpcrelplus(ctxt, plt, linklookup(ctxt, ".got", 0), s->got);
        } else {
                diag("addpltsym: unsupported binary format");
        }
}
static void
addgotsym(LSym *s)
{
        LSym *got, *rela;
        if(s->got >= 0)
                return;
        adddynsym(ctxt, s);
        got = linklookup(ctxt, ".got", 0);
        s->got = got->size;
        adduint64(ctxt, got, 0);
        if(iself) {
                rela = linklookup(ctxt, ".rela", 0);
                addaddrplus(ctxt, rela, got, s->got);
                adduint64(ctxt, rela, ELF64_R_INFO(s->dynid, R_X86_64_GLOB_DAT));
                adduint64(ctxt, rela, 0);
        } else if(HEADTYPE == Hdarwin) {
                adduint32(ctxt, linklookup(ctxt, ".linkedit.got", 0), s->dynid);
        } else {
                diag("addgotsym: unsupported binary format");
        }
}
void
adddynsym(Link *ctxt, LSym *s)
{
        LSym *d;
        int t;
        char *name;
        if(s->dynid >= 0)
                return;
        if(iself) {
                s->dynid = nelfsym++;
                d = linklookup(ctxt, ".dynsym", 0);
                name = s->extname;
                adduint32(ctxt, d, addstring(linklookup(ctxt, ".dynstr", 0), name));
                /* type */
                t = STB_GLOBAL << 4;
                if(s->cgoexport && (s->type&SMASK) == STEXT)
                        t |= STT_FUNC;
                else
                        t |= STT_OBJECT;
                adduint8(ctxt, d, t);
        
                /* reserved */
                adduint8(ctxt, d, 0);
        
                /* section where symbol is defined */
                if(s->type == SDYNIMPORT)
                        adduint16(ctxt, d, SHN_UNDEF);
                else {
                        switch(s->type) {
                        default:
                        case STEXT:
                                t = 11;
                                break;
                        case SRODATA:
                                t = 12;
                                break;
                        case SDATA:
                                t = 13;
                                break;
                        case SBSS:
                                t = 14;
                                break;
                        }
                        adduint16(ctxt, d, t);
                }
        
                /* value */
                if(s->type == SDYNIMPORT)
                        adduint64(ctxt, d, 0);
                else
                        addaddr(ctxt, d, s);
        
                /* size of object */
                adduint64(ctxt, d, s->size);
        
                if(!(s->cgoexport & CgoExportDynamic) && s->dynimplib && needlib(s->dynimplib)) {
                        elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED,
                                addstring(linklookup(ctxt, ".dynstr", 0), s->dynimplib));
                }
        } else if(HEADTYPE == Hdarwin) {
                diag("adddynsym: missed symbol %s (%s)", s->name, s->extname);
        } else if(HEADTYPE == Hwindows) {
                // already taken care of
        } else {
                diag("adddynsym: unsupported binary format");
        }
}
void
adddynlib(char *lib)
{
        LSym *s;
        
        if(!needlib(lib))
                return;
        
        if(iself) {
                s = linklookup(ctxt, ".dynstr", 0);
                if(s->size == 0)
                        addstring(s, "");
                elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED, addstring(s, lib));
        } else if(HEADTYPE == Hdarwin) {
                machoadddynlib(lib);
        } else {
                diag("adddynlib: unsupported binary format");
        }
}
void
asmb(void)
{
        int32 magic;
        int i;
        vlong vl, symo, dwarfoff, machlink;
        Section *sect;
        LSym *sym;
        if(debug['v'])
                Bprint(&bso, "%5.2f asmb\n", cputime());
        Bflush(&bso);
        if(debug['v'])
                Bprint(&bso, "%5.2f codeblk\n", cputime());
        Bflush(&bso);
        if(iself)
                asmbelfsetup();
        sect = segtext.sect;
        cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
        codeblk(sect->vaddr, sect->len);
        for(sect = sect->next; sect != nil; sect = sect->next) {
                cseek(sect->vaddr - segtext.vaddr + segtext.fileoff);
                datblk(sect->vaddr, sect->len);
        }
        if(segrodata.filelen > 0) {
                if(debug['v'])
                        Bprint(&bso, "%5.2f rodatblk\n", cputime());
                Bflush(&bso);
                cseek(segrodata.fileoff);
                datblk(segrodata.vaddr, segrodata.filelen);
        }
        if(debug['v'])
                Bprint(&bso, "%5.2f datblk\n", cputime());
        Bflush(&bso);
        cseek(segdata.fileoff);
        datblk(segdata.vaddr, segdata.filelen);
        machlink = 0;
        if(HEADTYPE == Hdarwin) {
                if(debug['v'])
                        Bprint(&bso, "%5.2f dwarf\n", cputime());
                dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);
                cseek(dwarfoff);
                segdwarf.fileoff = cpos();
                dwarfemitdebugsections();
                segdwarf.filelen = cpos() - segdwarf.fileoff;
                machlink = domacholink();
        }
        switch(HEADTYPE) {
        default:
                diag("unknown header type %d", HEADTYPE);
        case Hplan9:
        case Helf:
                break;
        case Hdarwin:
                debug['8'] = 1; /* 64-bit addresses */
                break;
        case Hlinux:
        case Hfreebsd:
        case Hnetbsd:
        case Hopenbsd:
        case Hdragonfly:
        case Hsolaris:
                debug['8'] = 1; /* 64-bit addresses */
                break;
        case Hnacl:
        case Hwindows:
                break;
        }
        symsize = 0;
        spsize = 0;
        lcsize = 0;
        symo = 0;
        if(!debug['s']) {
                if(debug['v'])
                        Bprint(&bso, "%5.2f sym\n", cputime());
                Bflush(&bso);
                switch(HEADTYPE) {
                default:
                case Hplan9:
                case Helf:
                        debug['s'] = 1;
                        symo = HEADR+segtext.len+segdata.filelen;
                        break;
                case Hdarwin:
                        symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
                        break;
                case Hlinux:
                case Hfreebsd:
                case Hnetbsd:
                case Hopenbsd:
                case Hdragonfly:
                case Hsolaris:
                case Hnacl:
                        symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen;
                        symo = rnd(symo, INITRND);
                        break;
                case Hwindows:
                        symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen;
                        symo = rnd(symo, PEFILEALIGN);
                        break;
                }
                cseek(symo);
                switch(HEADTYPE) {
                default:
                        if(iself) {
                                cseek(symo);
                                asmelfsym();
                                cflush();
                                cwrite(elfstrdat, elfstrsize);
                                if(debug['v'])
                                       Bprint(&bso, "%5.2f dwarf\n", cputime());
                                dwarfemitdebugsections();
                                
                                if(linkmode == LinkExternal)
                                        elfemitreloc();
                        }
                        break;
                case Hplan9:
                        asmplan9sym();
                        cflush();
                        sym = linklookup(ctxt, "pclntab", 0);
                        if(sym != nil) {
                                lcsize = sym->np;
                                for(i=0; i < lcsize; i++)
                                        cput(sym->p[i]);
                                
                                cflush();
                        }
                        break;
                case Hwindows:
                        if(debug['v'])
                               Bprint(&bso, "%5.2f dwarf\n", cputime());
                        dwarfemitdebugsections();
                        break;
                case Hdarwin:
                        if(linkmode == LinkExternal)
                                machoemitreloc();
                        break;
                }
        }
        if(debug['v'])
                Bprint(&bso, "%5.2f headr\n", cputime());
        Bflush(&bso);
        cseek(0L);
        switch(HEADTYPE) {
        default:
        case Hplan9:    /* plan9 */
                magic = 4*26*26+7;
                magic |= 0x00008000;            /* fat header */
                lputb(magic);                   /* magic */
                lputb(segtext.filelen);                 /* sizes */
                lputb(segdata.filelen);
                lputb(segdata.len - segdata.filelen);
                lputb(symsize);                 /* nsyms */
                vl = entryvalue();
                lputb(PADDR(vl));               /* va of entry */
                lputb(spsize);                  /* sp offsets */
                lputb(lcsize);                  /* line offsets */
                vputb(vl);                      /* va of entry */
                break;
        case Hdarwin:
                asmbmacho();
                break;
        case Hlinux:
        case Hfreebsd:
        case Hnetbsd:
        case Hopenbsd:
        case Hdragonfly:
        case Hsolaris:
        case Hnacl:
                asmbelf(symo);
                break;
        case Hwindows:
                asmbpe();
                break;
        }
        cflush();
}
vlong
rnd(vlong v, vlong r)
{
        vlong c;
        if(r <= 0)
                return v;
        v += r - 1;
        c = v % r;
        if(c < 0)
                c += r;
        v -= c;
        return v;
}