Annotation of coherent/b/bin/ld386/ld.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * One pass Coff Loader.
        !             3:  * Acts as ld and drvld. Will be mkshrlb when the specs are done.
        !             4:  *
        !             5:  * By Charles Fiterman for Mark Williams 3/30/92.
        !             6:  */
        !             7: #include "ld.h"
        !             8: #include <stdlib.h>
        !             9: #include <signal.h>
        !            10: #include <ctype.h>
        !            11: #include <path.h>
        !            12: 
        !            13: flag_t reloc,  /* Combine input into a new .o not an executable */
        !            14:        nosym,  /* No symbol table out. */
        !            15:        watch,  /* Produce a trace */
        !            16:        noilcl, /* Discard C local symbols beginning .L */
        !            17:        nolcl,  /* Discard all local symbols */
        !            18:        qflag,  /* No warn on commons of different length */
        !            19:        Qflag,  /* Absolute silence on everything */
        !            20:        debflg, /* Create debug data */
        !            21:        drvld,  /* Called as drvld */
        !            22:        fource = -1;    /* halt on error */
        !            23: 
        !            24: int    errCount;
        !            25: int    nundef;
        !            26: mod_t  *head,                  /* head of list of modules to load */
        !            27:        *tail,                  /* tail or list of modules */
        !            28:        *xhead,                 /* head of noload modules */
        !            29:        *xtail;                 /* tail of noload modules */
        !            30: ren_t  *rhead;                 /* rename list */
        !            31: 
        !            32: char *ofname = "a.out";                /* output file name */
        !            33: long comnb, comns, comnl;      /* common lengths */
        !            34: char *entrys;                  /* entry string */
        !            35: sym_t  *symtable[NHASH];       /* hashed symbol table */
        !            36: 
        !            37: unsigned short osegs = NLSEG;  /* the number of output segments */
        !            38: unsigned short segMap[MAXSEG] = /* All segments map to the first four */
        !            39:        { S_TEXT, S_DATA, S_BSSD, S_COMM };
        !            40: 
        !            41: FILEHDR fileh;
        !            42: AOUTHDR aouth;
        !            43: SCNHDR  *secth;                /* output segments */
        !            44: 
        !            45: static long str_length;
        !            46: static int ofd;
        !            47: static long *file_value = NULL, file_first;
        !            48: char *argv0;
        !            49: 
        !            50: /*
        !            51:  * Write or die.
        !            52:  */
        !            53: void
        !            54: xwrite(loc, size)
        !            55: char *loc;
        !            56: unsigned int size;
        !            57: {
        !            58:        if (size != write(ofd, loc, size))
        !            59:                fatal("write error"); /**/
        !            60: }
        !            61: 
        !            62: /*
        !            63:  * Init segment descriptors.
        !            64:  */
        !            65: void
        !            66: initSegs()
        !            67: {
        !            68:        secth = alloc(sizeof(*secth) * MAXSEG);
        !            69:        strcpy(secth[S_TEXT].s_name, _TEXT);
        !            70:        secth[S_TEXT].s_flags = STYP_TEXT;
        !            71:        strcpy(secth[S_DATA].s_name, _DATA);
        !            72:        secth[S_DATA].s_flags = STYP_DATA;
        !            73:        strcpy(secth[S_BSSD].s_name, _BSS);
        !            74:        secth[S_BSSD].s_flags = STYP_BSS;
        !            75:        strcpy(secth[S_COMM].s_name, _COMMENT);
        !            76:        secth[S_COMM].s_flags = STYP_INFO;
        !            77: }
        !            78: 
        !            79: /*
        !            80:  * Come here on interrupt.
        !            81:  */
        !            82: byebye()
        !            83: {
        !            84:        driver_fail(&aouth);
        !            85:        exit(1);
        !            86: }
        !            87: 
        !            88: /*
        !            89:  * We are drvld not ld. Ask the system where to put the stuff.
        !            90:  */
        !            91: getsys()
        !            92: {
        !            93:        register int i;
        !            94:        long bsz, daddr;
        !            95: 
        !            96:        time(&fileh.f_timdat);
        !            97:        fileh.f_nscns = osegs = NLSEG;
        !            98: 
        !            99:        daddr = sizeof(fileh) + (sizeof(* secth) * osegs) +
        !           100:                (fileh.f_opthdr = sizeof(aouth));
        !           101:        
        !           102:        for (i = 0; i < S_BSSD; i++) {  
        !           103:                secth[i].s_scnptr = daddr;
        !           104:                daddr += secth[i].s_size;
        !           105:        }
        !           106:        bsz = secth[S_BSSD].s_size;
        !           107:        aouth.tsize = secth[S_TEXT].s_size;
        !           108:        aouth.dsize = secth[S_DATA].s_size + bsz;
        !           109:        signal(SIGKILL, byebye);
        !           110:        if (!driver_alloc(&aouth))
        !           111:                fatal("kernel interface failed");
        !           112:                /* This will become more elaborite when the kernel is done */
        !           113:        secth[S_TEXT].s_vaddr = secth[S_TEXT].s_paddr = aouth.text_start;
        !           114:        secth[S_DATA].s_vaddr = secth[S_DATA].s_paddr = aouth.data_start;       
        !           115:        secth[S_BSSD].s_vaddr = secth[S_BSSD].s_paddr = aouth.data_start + bsz;
        !           116:        aouth.dsize -= aouth.bsize = bsz;
        !           117: 
        !           118:        if (!nosym)
        !           119:                fileh.f_symptr = daddr;
        !           120: }
        !           121: 
        !           122: /*
        !           123:  * Set virtual bases in array of segments.
        !           124:  */
        !           125: static
        !           126: baseall()
        !           127: {
        !           128:        register SCNHDR  *sh;
        !           129:        int i;
        !           130:        unsigned long daddr, vaddr, size;
        !           131: 
        !           132:        time(&fileh.f_timdat);
        !           133: 
        !           134:        /* Add extra segments to real segments */
        !           135:        for (i = NLSEG; i < osegs; i++) {
        !           136:                SCNHDR *oh;
        !           137: 
        !           138:                oh = secth + i;
        !           139:                sh = secth + segMap[i];
        !           140: 
        !           141:                sh->s_size   += oh->s_size;
        !           142:                sh->s_nreloc += oh->s_nreloc;
        !           143:                sh->s_nlnno  += oh->s_nlnno;
        !           144:        }
        !           145: 
        !           146:        /* only output S_COMM if it has something */
        !           147:        fileh.f_nscns = secth[S_COMM].s_size ? NLSEG : S_COMM;
        !           148: 
        !           149:        size = 0;
        !           150:        daddr = sizeof(fileh) + (sizeof(* secth) * fileh.f_nscns);
        !           151:        if (reloc)
        !           152:                vaddr = 0;
        !           153:        else {
        !           154:                daddr += fileh.f_opthdr = sizeof(aouth);
        !           155:                if (fileh.f_flags & F_KER)      /* kernel */
        !           156:                        vaddr = KERBASE;
        !           157:                else                            /* executable */
        !           158:                        vaddr = daddr;
        !           159:        }
        !           160: 
        !           161:        /* set s_vaddr, s_paddr, s_size, and s_scnptr for all segments */
        !           162:        for (i = 0; i < NLSEG; i++) {
        !           163:                sh = secth + i;
        !           164:                switch (i) {
        !           165:                case S_TEXT:
        !           166:                        aouth.text_start = sh->s_vaddr = vaddr;
        !           167:                        aouth.tsize = size = sh->s_size;                
        !           168:                        break;
        !           169: 
        !           170:                case S_DATA:    /* data adjustment */
        !           171:                        if (!reloc) {
        !           172:                                if (fileh.f_flags & F_KER)
        !           173:                                        vaddr = (vaddr + 0x0fff) & ~0x0fffL;
        !           174:                                else
        !           175:                                        vaddr = (vaddr & 0x0fff) + DATABASE;
        !           176:                        }
        !           177: 
        !           178:                        aouth.data_start = sh->s_vaddr = vaddr;
        !           179:                        aouth.dsize = size = sh->s_size;                
        !           180:                        break;
        !           181: 
        !           182:                case S_BSSD:
        !           183:                        sh->s_paddr = sh->s_vaddr = vaddr;
        !           184:                        vaddr += aouth.bsize = sh->s_size;
        !           185:                        sh->s_scnptr = 0;
        !           186:                        continue;
        !           187: 
        !           188:                default:
        !           189:                        sh->s_vaddr = vaddr;
        !           190:                        size = sh->s_size;
        !           191:                }
        !           192: 
        !           193:                sh->s_paddr = sh->s_vaddr;
        !           194:                sh->s_scnptr = daddr;
        !           195:                daddr += size;
        !           196:                vaddr += size;
        !           197:        }
        !           198: 
        !           199:        /* fix new segments to point to right places */
        !           200:        for (i = 0; i < NLSEG; i++) {
        !           201:                int j;
        !           202:                long vaddr, daddr;
        !           203: 
        !           204:                sh = secth + i;
        !           205:                vaddr = sh->s_vaddr + sh->s_size;       /* mark the end */
        !           206:                daddr = sh->s_scnptr + sh->s_size;
        !           207: 
        !           208:                for (j = osegs; --j >= NLSEG;) {
        !           209:                        SCNHDR *oh;
        !           210: 
        !           211:                        if (segMap[j] != i)
        !           212:                                continue;
        !           213:                        
        !           214:                        oh = secth + j;
        !           215:                        oh->s_vaddr  = vaddr -= oh->s_size;
        !           216:                        oh->s_scnptr = daddr -= oh->s_size;
        !           217:                        w_message("set extra %.8s at %x %x",
        !           218:                                  oh->s_name, vaddr, daddr);
        !           219:                }
        !           220:        }
        !           221: 
        !           222:        /* set relocations for all segments */
        !           223:        for (i = 0; i < NLSEG; i++) {
        !           224:                sh = secth + i;
        !           225:                if (!reloc || (S_BSSD == i) || !sh->s_nreloc) {
        !           226:                        sh->s_relptr = 0;
        !           227:                        continue;
        !           228:                }
        !           229:                sh->s_relptr = daddr;
        !           230:                daddr += sh->s_nreloc * RELSZ;
        !           231:        }
        !           232: 
        !           233:        /* set line numbers for all segments */
        !           234:        for (i = 0; i < NLSEG; i++) {
        !           235:                sh = secth + i;
        !           236:                if (!debflg || !sh->s_nlnno) {
        !           237:                        sh->s_nlnno = sh->s_lnnoptr = 0;
        !           238:                        continue;
        !           239:                }
        !           240:                sh->s_lnnoptr = daddr;
        !           241:                daddr += sh->s_nlnno * LINESZ;
        !           242:        }
        !           243: 
        !           244:        if (!nosym)
        !           245:                fileh.f_symptr = daddr;
        !           246: }
        !           247: 
        !           248: /*
        !           249:  * return entry point
        !           250:  */
        !           251: static long
        !           252: lentry(str)
        !           253: char   *str;
        !           254: {
        !           255:        if (NULL == str)        /* not user specified */
        !           256:                return(aouth.text_start);
        !           257: 
        !           258:        /* If an octal number use that */
        !           259:        {
        !           260:                register unsigned char  c, *s;
        !           261:                long    oaddr = 0;
        !           262: 
        !           263:                /* Try to scan whole string as octal. */
        !           264:                for (s = str; c = *s++;) {
        !           265:                        if (('0' > c) || (c > '7'))
        !           266:                                break;
        !           267:                        oaddr = (oaddr * 8) + (c - '0');
        !           268:                }
        !           269: 
        !           270:                if (!c)
        !           271:                        return (oaddr);
        !           272:        }
        !           273: 
        !           274:        /* Search for string in symbol table. */
        !           275:        {
        !           276:                register sym_t  *sp;
        !           277:                char work[SYMNMLEN + 1];
        !           278: 
        !           279:                for (sp = symtable[crc16(str) % NHASH];
        !           280:                     sp != NULL;
        !           281:                     sp = sp->next) {
        !           282:                        if ((sp->sym.n_sclass == C_EXT) &&
        !           283:                            !strcmp(symName(&(sp->sym), work), str)) {
        !           284:                                if (sp->sym.n_scnum != (S_TEXT + 1))
        !           285:                                   message("entry point '%s' not in .text");
        !           286:                                return(sp->sym.n_value);
        !           287:                        }
        !           288:                }
        !           289: 
        !           290:                message("entry point '%s' undefined", str); /**/
        !           291:                return (aouth.text_start);
        !           292:        }
        !           293: }
        !           294: 
        !           295: /*
        !           296:  * Define referenced special symbols.
        !           297:  */
        !           298: void
        !           299: symBind(sn, ldrv, name, loc)
        !           300: int    sn, ldrv;
        !           301: char   *name;
        !           302: long   loc;
        !           303: {
        !           304:        register SYMENT *sym;
        !           305:        register sym_t *sp;
        !           306:        char  work[SYMNMLEN + 1];
        !           307: 
        !           308:        for (sp = symtable[crc16(name) % NHASH]; sp != NULL; sp = sp->next) {
        !           309:                sym = &(sp->sym);
        !           310:                if ((sym->n_sclass != C_EXT) ||
        !           311:                     strcmp(symName(sym, work), name))
        !           312:                        continue;
        !           313: 
        !           314:                if (undefined(sym)) {
        !           315:                        nundef--;
        !           316:                        sym->n_scnum = sn + 1;
        !           317:                        sym->n_value = loc;
        !           318:                        sp->mod = NULL; /* not defined in any mod */
        !           319:                        return;
        !           320:                }
        !           321: 
        !           322:                if (ldrv)
        !           323:                        return;
        !           324:                else
        !           325:                        spwarn(sp, "redefines builtin symbol");
        !           326:                        /* Some symbols such as __end and __end_text
        !           327:                         * are special to the linker. In general symbols
        !           328:                         * beginning __ are reserved to implementors and
        !           329:                         * should be avoided by users.
        !           330:                         * Your definition has been used. */
        !           331:        }
        !           332: }
        !           333: 
        !           334: /*
        !           335:  * Add reference to symbol table
        !           336:  */
        !           337: void
        !           338: undef(s)
        !           339: char *s;
        !           340: {
        !           341:        SYMENT  lds;
        !           342: 
        !           343:        memset(&lds, '\0', sizeof(lds));
        !           344:        lds.n_offset = (long)s;         /* point symbol at our stuff */
        !           345:        lds.n_sclass = C_EXTDEF;        /* mark undefined */
        !           346:        addsym(&lds, NULL);             /* connect to no module */
        !           347: }
        !           348: 
        !           349: /*
        !           350:  * Show undefined symbols.
        !           351:  */
        !           352: void
        !           353: showUndef(sp, sym)
        !           354: register sym_t *sp;
        !           355: register SYMENT *sym;
        !           356: {
        !           357:        if (undefined(sym))
        !           358:                spmsg(sp, ""); /* NODOC */
        !           359: }
        !           360: 
        !           361: /*
        !           362:  * Pass all symbols through a function.
        !           363:  * In a really elaborite order.
        !           364:  */
        !           365: void
        !           366: allSym(fun)
        !           367: int (*fun)();
        !           368: {
        !           369:        register SYMENT *sym;
        !           370:        register sym_t *sp;
        !           371:        register mod_t *mp;
        !           372:        register i;
        !           373:        SYMENT *symEnd;
        !           374: 
        !           375:        if (reloc) {    /* We are producing another .o */
        !           376:                /*
        !           377:                 * Do symbols connected to modules in module order.
        !           378:                 * Only process a global symbol for it's owner.
        !           379:                 */
        !           380:                for (mp = head; mp != (mod_t *)NULL; mp = mp->next) {
        !           381:                        sym = ((SYMENT *)(mp->f->f_symptr));
        !           382:                        symEnd = sym + mp->f->f_nsyms;
        !           383:                        for (; sym < symEnd; sym += sym->n_numaux + 1) {
        !           384:                                if (1 == sym->n_zeroes) { /* pointer to original */
        !           385:                                        sp = (sym_t *)sym->n_offset;
        !           386:                                        if (sp->mod == mp)
        !           387:                                                (*fun)(sp, &(sp->sym), mp, sym);
        !           388:                                }
        !           389:                                else
        !           390:                                        (*fun)(NULL, sym, mp, sym);
        !           391:                        }
        !           392:                }
        !           393:        }
        !           394:        else {
        !           395:                /*
        !           396:                 * Producing an executable.
        !           397:                 */
        !           398:                for (mp = head; mp != (mod_t *)NULL; mp = mp->next) {
        !           399:                        char showSym;
        !           400: 
        !           401:                        sym = ((SYMENT *)(mp->f->f_symptr));
        !           402:                        symEnd = sym + mp->f->f_nsyms;
        !           403: 
        !           404:                        /*
        !           405:                         * If first entry is not C_FILE supress 
        !           406:                         * all non C_EXT entries for debug, they make
        !           407:                         * Orignon barf. If not debug mode and the
        !           408:                         * user wants a symbol table give him everything.
        !           409:                         */
        !           410:                        showSym = !debflg || C_FILE == sym->n_sclass;
        !           411: 
        !           412:                        for (; sym < symEnd; sym += sym->n_numaux + 1) {
        !           413:                                if ((C_EXT == sym->n_sclass) && !sym->n_numaux)
        !           414:                                        continue;       /* do later */
        !           415: 
        !           416:                                /* pointer to original */
        !           417:                                if (1 == sym->n_zeroes) {
        !           418:                                        sp = (sym_t *)sym->n_offset;
        !           419:                                        if (sp->mod == mp)
        !           420:                                                (*fun)(showSym ? sp : NULL,
        !           421:                                                         &(sp->sym), mp, sym);
        !           422:                                }
        !           423:                                else
        !           424:                                        (*fun)(NULL, sym, mp, sym);
        !           425:                        }
        !           426:                }
        !           427: 
        !           428:                /* All C_EXT entries go at the end. */
        !           429:                for (mp = head; mp != (mod_t *)NULL; mp = mp->next) {
        !           430:                        sym = ((SYMENT *)(mp->f->f_symptr));
        !           431:                        symEnd = sym + mp->f->f_nsyms;
        !           432:                        for (; sym < symEnd; sym += sym->n_numaux + 1) {
        !           433:                                if (!((C_EXT == sym->n_sclass) &&
        !           434:                                    !sym->n_numaux))
        !           435:                                        continue;
        !           436: 
        !           437:                                 /* pointer to original */
        !           438:                                if (1 == sym->n_zeroes) {
        !           439:                                        sp = (sym_t *)sym->n_offset;
        !           440:                                        if (sp->mod == mp)
        !           441:                                                (*fun)(sp, &(sp->sym), mp, sym);
        !           442:                                }
        !           443:                                else
        !           444:                                        (*fun)(NULL, sym, mp, sym);
        !           445:                        }
        !           446:                }
        !           447:        }
        !           448: 
        !           449:        /*
        !           450:         * Do symbols not connected to a module.
        !           451:         * These are end symbols and symbols defined with -u option
        !           452:         */
        !           453:        for (i = 0; i < NHASH; i++)
        !           454:                for (sp = symtable[i]; NULL != sp; sp = sp->next)
        !           455:                        if (NULL == sp->mod)
        !           456:                                (*fun)(sp, &(sp->sym), sp->mod, NULL);
        !           457: }
        !           458: 
        !           459: /*
        !           460:  * Fixup a symbol between passes.
        !           461:  */
        !           462: void
        !           463: symFix(sp, sym, mp, auxp)
        !           464: sym_t *sp;
        !           465: register SYMENT *sym, *auxp;
        !           466: mod_t *mp;
        !           467: {
        !           468:        int segn, len;
        !           469: 
        !           470:        segn = sym->n_scnum;
        !           471:        if (debflg && NULL != auxp && C_FILE == sym->n_sclass) {
        !           472:                if (NULL != file_value) /* files point in a circle */
        !           473:                        *file_value = fileh.f_nsyms;
        !           474:                else
        !           475:                        file_first = fileh.f_nsyms;
        !           476:                file_value = &(sym->n_value);
        !           477:                fileh.f_nsyms += sym->n_numaux + 1;
        !           478:                return;
        !           479:        }
        !           480:        if (!reloc && common(sym)) {
        !           481:                switch ((len = sym->n_value) & 3) {
        !           482:                case 2: /* 2 byte aligned */
        !           483:                        sym->n_value = comns;
        !           484:                        comns += len;
        !           485:                        break;
        !           486:                case 0: /* 4 byte aligned */
        !           487:                        sym->n_value = comnl;
        !           488:                        comnl += len;
        !           489:                        break;
        !           490:                default: /* unaligned */
        !           491:                        sym->n_value = comnb;
        !           492:                        comnb += len;
        !           493:                }
        !           494:                sym->n_scnum  = S_BSSD + 1;
        !           495:        }
        !           496:        else if (segn > 0)
        !           497:                /*
        !           498:                 * Correct the value to the output sector header.
        !           499:                 * If mp == NULL then segn this is a constructed symbol
        !           500:                 * and segn is relative to the output sector headers.
        !           501:                 * Otherwise it is relative to the input sector headers
        !           502:                 * but the connection to the output sector headers was
        !           503:                 * placed in s_paddr by pass1.c/object().
        !           504:                 */
        !           505:                sym->n_value += secth[(NULL == mp) ? segn - 1 :
        !           506:                                        mp->s[segn - 1].s_paddr].s_vaddr;
        !           507: 
        !           508:        if (NULL != sp && !nosym) {
        !           509:                sp->symno = fileh.f_nsyms++;
        !           510:                if (debflg)
        !           511:                        fileh.f_nsyms += sym->n_numaux;
        !           512:        }
        !           513: }
        !           514: 
        !           515: /*
        !           516:  * Do work between passes.
        !           517:  */
        !           518: void
        !           519: betweenPass()
        !           520: {
        !           521:        if (reloc)
        !           522:                fileh.f_flags |= F_AR32WR;
        !           523:        else {
        !           524:                fileh.f_flags |= F_RELFLG | F_EXEC | F_AR32WR;
        !           525:                comnb += (4 - ((comnb + comns) & 3)) & 3;
        !           526:                secth[S_BSSD].s_size += comnb + comns + comnl;
        !           527:        }
        !           528:        if (nosym)
        !           529:                fileh.f_flags |= F_LSYMS;
        !           530: 
        !           531:        if (!debflg)
        !           532:                fileh.f_flags |= F_LNNO;
        !           533: 
        !           534:        if (drvld)
        !           535:                getsys();       /* get segment base information from system */
        !           536:        else
        !           537:                baseall();      /* compute segment base information */
        !           538: 
        !           539:        if (!reloc) {
        !           540:                int i;
        !           541: 
        !           542:                /* define referenced end of segment symbols */
        !           543:                for (i = 0; i < NLSEG; i++) {
        !           544:                        char end_name[20], c, *p;
        !           545: 
        !           546:                        sprintf(end_name, "__end%.8s", secth[i].s_name);
        !           547:                        for (p = end_name; '\0' != (c = *p); p++)
        !           548:                                if (!isalnum(c))
        !           549:                                        *p = '_';
        !           550: 
        !           551:                        symBind(i, drvld, end_name, secth[i].s_size);
        !           552:                }
        !           553:                /* define absolute end symbol */
        !           554:                symBind(S_BSSD, drvld, "__end", secth[S_BSSD].s_size);
        !           555: 
        !           556:                /* get starting addresses for 1, 2 and 4 alligned commons */
        !           557:                comnb = secth[S_BSSD].s_vaddr + secth[S_BSSD].s_size - comnb;
        !           558:                comns = comnb - comns;
        !           559:                comnl = comns - comnl;
        !           560:        }
        !           561: 
        !           562:        if (nundef && !reloc) {
        !           563:                message("the following symbols are undefined");
        !           564:                errCount--;
        !           565:                allSym(showUndef);
        !           566:        }
        !           567: 
        !           568:        switch (errCount & fource) {
        !           569:        case 0:
        !           570:                break;
        !           571:        case 1:
        !           572:                fatal("pass 1, 1 error"); /* NODOC */
        !           573:        default:
        !           574:                fatal("pass 1, %d errors", errCount);
        !           575:                /* At the end of pass 1 there were \fIn\fB errors detected.
        !           576:                 * The link stopped here. */
        !           577:        }
        !           578: 
        !           579:        /* Run through symbol table doing fixups */
        !           580:        fileh.f_nsyms = 0;
        !           581:        allSym(symFix);
        !           582: 
        !           583:        if (NULL != file_value)
        !           584:                *file_value = file_first;
        !           585: 
        !           586:        aouth.entry = lentry(entrys);
        !           587: }
        !           588: 
        !           589: /*
        !           590:  * output symbol table.
        !           591:  */
        !           592: static void
        !           593: outputSym(s, sm, mp, auxp)
        !           594: register sym_t *s;
        !           595: register SYMENT *sm;
        !           596: mod_t *mp;
        !           597: SYMENT *auxp;
        !           598: {
        !           599:        int i;
        !           600:        char *name, work[SYMNMLEN + 1];
        !           601:        SCNHDR *scn;
        !           602:        SYMENT sym;
        !           603:  
        !           604:        if (NULL == (char *)s)
        !           605:                return;
        !           606: 
        !           607:        /* build writeable copy */
        !           608:        memcpy(&sym, sm, sizeof(sym));
        !           609:        name = symName(sm, work);
        !           610: 
        !           611:        if (SYMNMLEN < (i = strlen(name))) {
        !           612:                sym.n_offset = str_length;
        !           613:                str_length += i + 1;
        !           614:        }
        !           615:        else
        !           616:                memcpy(sym.n_name, name, SYMNMLEN);
        !           617: 
        !           618:        if (!debflg || NULL == auxp)
        !           619:                sym.n_numaux = 0;
        !           620:                
        !           621:        if ((sym.n_scnum > 0) && (NULL != mp))
        !           622:                sym.n_scnum = segMap[mp->s[sym.n_scnum - 1].s_paddr] + 1;
        !           623: 
        !           624:        xwrite(&sym, SYMESZ);
        !           625: 
        !           626:        if (sym.n_numaux) {
        !           627:                int aux = 1;
        !           628:                int has_fcn = 0;
        !           629: 
        !           630:                switch(sym.n_sclass) {
        !           631:                case C_STRTAG:
        !           632:                case C_UNTAG:
        !           633:                case C_ENTAG:
        !           634:                case C_BLOCK:
        !           635:                        has_fcn = 1;
        !           636:                        break;
        !           637:                case C_STAT:
        !           638:                case C_FILE:
        !           639:                        aux = sym.n_numaux + 1; /* dont scan for ixs */
        !           640:                        break;
        !           641:                default:
        !           642:                        if (ISFCN(sym.n_type))
        !           643:                                has_fcn = 1;
        !           644:                }
        !           645: 
        !           646:                for (; aux <= sym.n_numaux; aux++) {
        !           647:                        SYMENT *s1, *s2, *s3;
        !           648:                        AUXENT *a = auxp + aux;
        !           649: 
        !           650:                        /*
        !           651:                         * Fix indices to new value.
        !           652:                         */
        !           653:                        if (a->ae_tagndx) {
        !           654:                                s1 = ((SYMENT *)(mp->f->f_symptr)) +
        !           655:                                        a->ae_tagndx;
        !           656:                                s = (sym_t *)s1->n_offset;
        !           657:                                a->ae_tagndx = s->symno;
        !           658:                        }
        !           659:                        /* ae_endndx points past an entry */
        !           660:                        if (has_fcn && a->ae_endndx) {
        !           661:                                s2 = ((SYMENT *)(mp->f->f_symptr));
        !           662:                                /* find the entry it points past */
        !           663:                                for (s1 = s2 + a->ae_endndx;
        !           664:                                     (s3 = (s2 + s2->n_numaux + 1)) < s1;
        !           665:                                     s2 = s3)
        !           666:                                        ;
        !           667:                                s = (sym_t *)s2->n_offset;
        !           668:                                a->ae_endndx = s->symno + s2->n_numaux + 1;
        !           669:                        }
        !           670:                }
        !           671:                xwrite(auxp + 1, SYMESZ * sym.n_numaux);
        !           672:        }
        !           673: }
        !           674: 
        !           675: /*
        !           676:  * output long symbols.
        !           677:  */
        !           678: static void
        !           679: longSym(s, sm)
        !           680: register sym_t *s;
        !           681: register SYMENT *sm;
        !           682: {
        !           683:        int i;
        !           684:        char *name, work[SYMNMLEN + 1];
        !           685: 
        !           686:        if (NULL == s)
        !           687:                return;
        !           688: 
        !           689:        name = symName(sm, work);
        !           690:        if (SYMNMLEN < (i = strlen(name)))
        !           691:                xwrite(name, i + 1);
        !           692: }
        !           693: 
        !           694: 
        !           695: /*
        !           696:  * Scan this modules sections for a name match.
        !           697:  */
        !           698: SCNHDR *
        !           699: findSeg(mp, segn)
        !           700: register mod_t *mp;
        !           701: {
        !           702:        int i;
        !           703:        register SCNHDR *orsp, *isgp;
        !           704: 
        !           705:        orsp = secth + segn;
        !           706:        for (i = 0; i < mp->f->f_nscns; i++) {
        !           707:                isgp = mp->s + i;
        !           708:                if (strncmp(isgp->s_name, orsp->s_name, 8))
        !           709:                        isgp = NULL;
        !           710:                else
        !           711:                        break;
        !           712:        }
        !           713:        return isgp;
        !           714: }
        !           715: 
        !           716: /*
        !           717:  * Do relocations
        !           718:  */
        !           719: static void
        !           720: relocations(mp, osegn, isegn)
        !           721: mod_t *  mp;
        !           722: {
        !           723:        register RELOC *rel;
        !           724:        char    *t;     /* actual text */
        !           725:        SCNHDR  *isgp, *orsp;
        !           726:        unsigned i;
        !           727:        long    size, told, fixr;
        !           728: 
        !           729:        if ((NULL == (isgp = findSeg(mp, isegn))) || !(size = isgp->s_size))
        !           730:                return;
        !           731: 
        !           732:        orsp = secth + osegn;
        !           733:        fixr = isgp->s_vaddr - orsp->s_vaddr;
        !           734:        t = (char *)isgp->s_scnptr;
        !           735: 
        !           736:        if (watch) {
        !           737:                errCount--;
        !           738:                mpmsg(mp, "relocating seg#%.8s[%06lx]@%06lx to %06lx r %ld",
        !           739:                        isgp->s_name,
        !           740:                        size,
        !           741:                        isgp->s_vaddr,
        !           742:                        orsp->s_vaddr,
        !           743:                        isgp->s_nreloc); /* NODOC */
        !           744:                told = lseek(ofd, 0, 2);
        !           745:        }
        !           746:        
        !           747:        for (i = 0; i < isgp->s_nreloc; i++) {
        !           748:                char *ptr;
        !           749:                char *mtype;
        !           750:                sym_t *sp;
        !           751:                SYMENT *s, *sym;
        !           752:                long relf, w, at;
        !           753:                int   undef;
        !           754:                char work[SYMNMLEN + 1], *name;
        !           755:                static char *pcrel = "pcrel";
        !           756: 
        !           757:                /* get reloc record */
        !           758:                rel = ((RELOC *)isgp->s_relptr) + i;
        !           759: 
        !           760:                w = rel->r_vaddr - isgp->s_vaddr;
        !           761:                if ((w < 0) || (w > size))
        !           762:                        corrupt(mp);
        !           763:                ptr = t + w;
        !           764: 
        !           765:                if ((rel->r_symndx < 0) || (rel->r_symndx > mp->f->f_nsyms))
        !           766:                        corrupt(mp);
        !           767:                
        !           768:                s = (SYMENT *)mp->f->f_symptr + rel->r_symndx;
        !           769:                if (1 == s->n_zeroes) { /* fixed elsewhere */ 
        !           770:                        sp = (sym_t *)s->n_offset;
        !           771:                        sym = &(sp->sym);
        !           772:                } else {
        !           773:                        sp = NULL;
        !           774:                        sym = s;
        !           775:                }
        !           776:                relf = sym->n_value;
        !           777:                mtype = "rel";
        !           778:                undef = undefined(sym);                         
        !           779: 
        !           780:                if (watch) {
        !           781:                        at = told + w;
        !           782:                        name = symName(sym, work);
        !           783:                }
        !           784:                /*
        !           785:                 * This wierdness is to deal with a coff wierdness.
        !           786:                 * The address of a common is incremented by the
        !           787:                 * length of the common as seen in that module.
        !           788:                 */
        !           789:                if (common(s))
        !           790:                        relf -= s->n_value;
        !           791:                /*
        !           792:                 * If the symbol is native to this module
        !           793:                 * the reference already has this modules
        !           794:                 * segment address. Subtract it.
        !           795:                 */
        !           796:                else if (!undef && ((NULL == sp) || (sp->mod == mp)))
        !           797:                        relf -= mp->s[s->n_scnum - 1].s_vaddr;
        !           798: 
        !           799: /* relocate what the pointer is aimed at and leave a record */
        !           800: #define relocate(type) *(type *)ptr = (w = *(type *)ptr) + relf
        !           801: 
        !           802:                if (reloc) {
        !           803:                        rel->r_vaddr = w + orsp->s_vaddr;
        !           804:                        rel->r_symndx = sp->symno;
        !           805: 
        !           806:                        switch (rel->r_type) {
        !           807:                        case R_PCRBYTE:
        !           808:                                mtype = pcrel;
        !           809:                                relf = fixr;
        !           810: 
        !           811:                        case R_DIR8:
        !           812:                        case R_RELBYTE:
        !           813:                                relocate(char);
        !           814:                                break;
        !           815: 
        !           816:                        case R_PCRWORD:
        !           817:                                mtype = pcrel;
        !           818:                                relf = fixr;
        !           819: 
        !           820:                        case R_DIR16:
        !           821:                        case R_RELWORD:
        !           822:                                relocate(short);
        !           823:                                break;
        !           824: 
        !           825:                        case R_PCRLONG:
        !           826:                                mtype = pcrel;
        !           827:                                relf = fixr;
        !           828: 
        !           829:                        case R_DIR32:
        !           830:                        case R_RELLONG:
        !           831:                                relocate(long);
        !           832:                                break;
        !           833: 
        !           834:                        case R_NONREL:
        !           835:                                mtype = "nonrel";
        !           836:                                w = *(long *)ptr;
        !           837:                                relf = 0;
        !           838:                                break;
        !           839: 
        !           840:                        default:
        !           841:                        mpmsg(mp, "unknown r_type %d in segment %.8s record %d",
        !           842:                                rel->r_type, isgp->s_name, i);
        !           843:                        /* Unknown type on COFF relocation record. */
        !           844:                        }
        !           845: 
        !           846:                        if ((sym->n_scnum > 0) && (mtype != pcrel))
        !           847:                                rel->r_symndx = sym->n_scnum - 1;
        !           848: 
        !           849:                        if (!undef && (sym->n_scnum < 0))
        !           850:                                rel->r_type = R_NONREL;
        !           851: 
        !           852:                        w_message("%lx '%s'(%d %lx %lx) %lx = '%s'(%lx) + %lx",
        !           853:                                at,
        !           854:                                mtype,
        !           855:                                rel->r_type,
        !           856:                                rel->r_vaddr,
        !           857:                                rel->r_symndx,
        !           858:                                relf + w,
        !           859:                                name,
        !           860:                                sym->n_value,
        !           861:                                w);
        !           862:                }
        !           863:                else {
        !           864:                        switch (rel->r_type) {
        !           865:                        case R_PCRBYTE:
        !           866:                                mtype = pcrel;
        !           867:                                relf -= orsp->s_vaddr;
        !           868:                        case R_DIR8:
        !           869:                        case R_RELBYTE:
        !           870:                                relocate(char);
        !           871:                                break;
        !           872:                        case R_PCRWORD:
        !           873:                                mtype = pcrel;
        !           874:                                relf -= orsp->s_vaddr;
        !           875:                        case R_RELWORD:
        !           876:                        case R_DIR16:
        !           877:                                relocate(short);
        !           878:                                break;
        !           879:                        case R_PCRLONG:
        !           880:                                mtype = pcrel;
        !           881:                                relf -= orsp->s_vaddr;
        !           882:                        case R_RELLONG:
        !           883:                        case R_DIR32:
        !           884:                                relocate(long);
        !           885:                                break;
        !           886:                        case R_NONREL:
        !           887:                                mtype = "nonrel";
        !           888:                                w = *(long *)ptr;
        !           889:                                relf = 0;
        !           890:                                break;
        !           891:                        default:
        !           892:                        mpmsg(mp, "unknown r_type %d in segment %.8s record %d",
        !           893:                                rel->r_type, isgp->s_name, i);
        !           894:                        /* NODOC */
        !           895:                        }
        !           896:                        w_message("%lx '%s'(%d) %lx = '%s'(%lx) + %lx",
        !           897:                                at,
        !           898:                                mtype,
        !           899:                                rel->r_type,
        !           900:                                relf + w,
        !           901:                                name,
        !           902:                                sym->n_value,
        !           903:                                w);
        !           904:                }
        !           905:        }
        !           906: #undef relocate
        !           907: 
        !           908:        orsp->s_vaddr += size;
        !           909:        xwrite(t, (int)size);
        !           910: }
        !           911: 
        !           912: /*
        !           913:  * Write line records for a segment.
        !           914:  */
        !           915: writeLine(mp, segn)
        !           916: mod_t *mp;
        !           917: {
        !           918:        int j;
        !           919:        long fptr;
        !           920:        AUXENT *a;
        !           921:        SYMENT *sym;
        !           922:        LINENO *l;
        !           923:        SCNHDR *scn;
        !           924: 
        !           925:        if (NULL == (scn = findSeg(mp, segn)) || (!scn->s_nlnno))
        !           926:                return;
        !           927: 
        !           928:        /*
        !           929:         * set up n_lnnoptr in symbol table to point
        !           930:         * to line records
        !           931:         */
        !           932:        l = (LINENO *)scn->s_lnnoptr;
        !           933:        fptr = lseek(ofd, 0L, 1);       /* where I am */
        !           934:        for (j = 0; j < scn->s_nlnno; j++, l++) {
        !           935:                if (!l->l_lnno) {
        !           936:                        sym = ((SYMENT *)(mp->f->f_symptr)) +
        !           937:                                l->l_addr.l_symndx;
        !           938:                        if (sym->n_numaux) {
        !           939:                                a = (AUXENT *)sym + 1;
        !           940:                                a->ae_lnnoptr = fptr;
        !           941:                                if (1 == sym->n_zeroes) {
        !           942:                                    sym_t *sp;
        !           943:                                    sp = (sym_t *)sym->n_offset;
        !           944:                                    l->l_addr.l_symndx = sp->symno;
        !           945:                                }
        !           946:                        }
        !           947:                }
        !           948:                fptr += LINESZ;
        !           949:        }
        !           950:        xwrite(scn->s_lnnoptr, LINESZ * scn->s_nlnno);
        !           951: }
        !           952: 
        !           953: /*
        !           954:  * Output all data.
        !           955:  */
        !           956: outputAll()
        !           957: {
        !           958:        register mod_t *mp;
        !           959:        register SCNHDR *scn;
        !           960:        int i, j;
        !           961:        struct stat statbuf;
        !           962: 
        !           963:        /* Open output file */
        !           964:        ofd = qopen(ofname, 1);
        !           965: 
        !           966:        /* write header */
        !           967:        xwrite(&fileh, sizeof(fileh));
        !           968: 
        !           969:        if (!reloc) {   /* if executable set bits and write opt header */
        !           970:                stat(ofname, &statbuf);
        !           971:                chmod(ofname,
        !           972:                        statbuf.st_mode | S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
        !           973: 
        !           974:                aouth.magic = Z_MAGIC;
        !           975:                xwrite(&aouth, sizeof(aouth));
        !           976:        }
        !           977: 
        !           978:        /* write sector headers */
        !           979:        xwrite(secth, sizeof(* secth) * fileh.f_nscns);
        !           980: 
        !           981:        /* write corrected text segments */
        !           982:        for (i = 0; i < NLSEG; i++) {
        !           983:                if (i == S_BSSD)
        !           984:                        continue;
        !           985:                for (j = 0; j < osegs; j++) {
        !           986:                        if (segMap[j] == i)
        !           987:                                for (mp = head; mp != NULL; mp = mp->next)
        !           988:                                        relocations(mp, i, j);
        !           989:                }
        !           990:        }
        !           991: 
        !           992:        /* write relocation if required */
        !           993:        for (i = (reloc ? 0 : S_BSSD); i < S_BSSD; i++) {
        !           994:                for (j = 0; j < osegs; j++) {
        !           995:                        if (segMap[j] != i)
        !           996:                                continue;
        !           997: 
        !           998:                        for (mp = head; mp != NULL; mp = mp->next) {
        !           999:                                if ((NULL == (scn = findSeg(mp, j))) ||
        !          1000:                                     !scn->s_nreloc)
        !          1001:                                        continue;
        !          1002:                                xwrite(scn->s_relptr,
        !          1003:                                        RELSZ * (int)scn->s_nreloc);
        !          1004:                        }
        !          1005:                }
        !          1006:        }
        !          1007: 
        !          1008:        /* write lines if required */
        !          1009:        for (i = (debflg ? 0 : NLSEG); i < NLSEG; i++) {
        !          1010:                for (j = 0; j < osegs; j++) {
        !          1011:                        if (segMap[j] != i)
        !          1012:                                continue;
        !          1013: 
        !          1014:                        for (mp = head; mp != NULL; mp = mp->next)
        !          1015:                                writeLine(mp, j);
        !          1016:                }
        !          1017:        }
        !          1018: 
        !          1019:        if (!nosym) {
        !          1020:                str_length = 4;
        !          1021: 
        !          1022:                allSym(outputSym);
        !          1023:                if (4 != str_length) {
        !          1024:                        xwrite(&str_length, sizeof(str_length));
        !          1025:                        allSym(longSym);
        !          1026:                }
        !          1027:        }
        !          1028: 
        !          1029:        close(ofd);
        !          1030: }
        !          1031: 
        !          1032: /*
        !          1033:  * Process arguements, and call other work.
        !          1034:  */
        !          1035: main(argc, argv)
        !          1036: char *argv[];
        !          1037: {
        !          1038:        int     c;
        !          1039:        char    *specialList = NULL;
        !          1040:        char    *argString = "?ge:finKl:L:o:A:O:rsu:wXxZ:qQ";
        !          1041:        char    *env;
        !          1042: 
        !          1043:        /* find program name */
        !          1044:        if (NULL == (argv0 = strrchr(argv[0], '/')))
        !          1045:                argv0 = argv[0];
        !          1046:        else
        !          1047:                argv0++;
        !          1048: 
        !          1049:        env = getenv("LIBPATH");
        !          1050:        /*
        !          1051:         * drvld is an alternative name for ld.
        !          1052:         * In this mode ld will load the kernel's symbol table, so
        !          1053:         * that the loadable driver can link directly to kernel services.
        !          1054:         * After pass1 we call driver_alloc() to tell the kernel the
        !          1055:         * sizes of the driver segments, the kernel replys with locations
        !          1056:         * for them. Then we output the driver to a tmp file and execl()
        !          1057:         */
        !          1058:        if (!strcmp(argv0, "drvld")) {
        !          1059: #if 0
        !          1060:                ofname = tmpnam(NULL);
        !          1061: #endif
        !          1062:                argString = "?ge:l:L:u:wdq";
        !          1063:                fileh.f_flags |= F_KER;
        !          1064:                drvld = 1;
        !          1065:                /* read kernel for symbol table but don't load */
        !          1066:                readFile(kernelName(), 0, NULL);
        !          1067:                _addargs("DRVLD", &argc, &argv);        
        !          1068:        }
        !          1069:        else
        !          1070:                _addargs("LD", &argc, &argv);   
        !          1071: 
        !          1072:        initSegs();
        !          1073: 
        !          1074:        while (EOF != (c = getargs(argc, argv, argString))) {
        !          1075:                switch (c) {
        !          1076:                case 0:         /* Not an option, read a file for load */
        !          1077:                        readFile(optarg, 1, NULL);
        !          1078:                        continue;
        !          1079: 
        !          1080:                case 'A': /* Read a file as an archive regardless of name */
        !          1081:                        readFile(optarg, 1, ".a");
        !          1082:                        continue;
        !          1083: 
        !          1084:                case 'O': /* Read a file as an object regardless of name */
        !          1085:                        readFile(optarg, 1, ".o");
        !          1086:                        continue;
        !          1087: 
        !          1088:                case 'f':       /* attempt link even if errors */
        !          1089:                        fource = 0;
        !          1090:                        continue;
        !          1091: 
        !          1092:                case 'g':       /* save extra segments and aux symbols */
        !          1093:                        debflg ^= 1;
        !          1094:                        continue;
        !          1095: 
        !          1096:                case 'e':
        !          1097:                        entrys = optarg;
        !          1098: 
        !          1099:                case 'Z':       /* use and erase after */
        !          1100:                case 'i':       /* obselete options */
        !          1101:                case 'n':
        !          1102:                        continue;
        !          1103: 
        !          1104:                case 'K':       /* recompile of Kernel */
        !          1105:                        fileh.f_flags |= F_KER;
        !          1106:                        continue;
        !          1107: 
        !          1108:                case 'L':
        !          1109:                        /*
        !          1110:                         * Special filelist for lookup.
        !          1111:                         */
        !          1112:                        {
        !          1113:                                char *new;
        !          1114: 
        !          1115:                                if (NULL == specialList) {
        !          1116:                                        new = alloc(strlen(optarg) + 2);
        !          1117:                                        sprintf(new, "%c%s", LISTSEP, optarg);
        !          1118:                                }
        !          1119:                                else {
        !          1120:                                        new = alloc(
        !          1121:                                         strlen(specialList)+strlen(optarg)+2);
        !          1122:                                        sprintf(new, "%s%c%s",
        !          1123:                                         specialList, LISTSEP, optarg);
        !          1124:                                        free(specialList);
        !          1125:                                }
        !          1126:                                specialList = new;
        !          1127:                                continue;
        !          1128:                        }
        !          1129: 
        !          1130:                case 'l':
        !          1131:                        /* -l<lib>: use standard lib */
        !          1132:                        {
        !          1133:                                char *xp, *lp;
        !          1134: 
        !          1135:                                xp = alloc(strlen(optarg) + 6);
        !          1136:                                sprintf(xp, "lib%s.a", optarg);
        !          1137:                                lp = NULL;
        !          1138:                                if (NULL != specialList)
        !          1139:                                        lp = path(specialList, xp, AREAD);
        !          1140:                                if (NULL == lp && NULL != env)
        !          1141:                                        lp = path(env, xp, AREAD);
        !          1142:                                if (NULL == lp)
        !          1143:                                        lp = path(DEFLIBPATH, xp, AREAD);
        !          1144:                                if (NULL == lp)
        !          1145:                                   fatal("can't find '%s'", xp);
        !          1146:                                   /* Can't locate requested library. */
        !          1147: 
        !          1148:                                readFile(lp, 1, NULL);
        !          1149:                                free(xp);
        !          1150:                                continue;
        !          1151:                        }
        !          1152: 
        !          1153:                case 'o':
        !          1154:                        ofname = optarg;
        !          1155:                        continue;
        !          1156: 
        !          1157:                case 'r':
        !          1158:                        reloc ^= 1;     /* retain relocation information */
        !          1159:                        continue;
        !          1160: 
        !          1161:                case 's':
        !          1162:                        nosym ^= 1;
        !          1163:                        continue;
        !          1164: 
        !          1165:                case 'u':
        !          1166:                        undef(optarg);
        !          1167:                        continue;
        !          1168: 
        !          1169:                case 'Q':
        !          1170:                        Qflag = 1;
        !          1171: 
        !          1172:                case 'q':
        !          1173:                        qflag = 1;
        !          1174:                        continue;
        !          1175: 
        !          1176:                case 'w':
        !          1177:                        watch ^= 1;
        !          1178:                        continue;
        !          1179: 
        !          1180:                case 'X':
        !          1181:                        noilcl ^= 1;
        !          1182:                        continue;
        !          1183: 
        !          1184:                case 'x':
        !          1185:                        nolcl ^= 1;
        !          1186:                        continue;
        !          1187: 
        !          1188:                case '?':
        !          1189:                        help();         /* non returning */
        !          1190: 
        !          1191:                default:
        !          1192:                        usage();        /* non returning */
        !          1193:                }
        !          1194:        }
        !          1195:        if (nosym)
        !          1196:                nolcl = 1;
        !          1197: 
        !          1198:        if (reloc || debflg)
        !          1199:                nosym = nolcl = noilcl = 0;
        !          1200: 
        !          1201:        if (!fileh.f_magic)
        !          1202:                fatal("No work");
        !          1203:        /* There were no object files loaded. */
        !          1204: 
        !          1205:        betweenPass();  /* between passes */
        !          1206:        outputAll();    /* output all data */
        !          1207: 
        !          1208:        /* repass argument list to erase any -Z files */
        !          1209:        for (optix = 1; EOF != (c = getargs(argc, argv, argString));)
        !          1210:                if ('Z' == c)
        !          1211:                        unlink(optarg);
        !          1212: 
        !          1213: #if 0
        !          1214:        if (drvld) {
        !          1215:                execl(ofname, ofname, NULL); /* should never return */
        !          1216:                fatal("Cannot execute loadable driver '%s'.", ofname); /**/
        !          1217:        }
        !          1218: #endif
        !          1219:        return (0);
        !          1220: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.