Annotation of nono/vm/bootloader.cpp, revision 1.1.1.5

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2022 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // ローダ
                      9: //
                     10: 
                     11: // BootLoader オブジェクト (ログ表示名 "BootLoader") は
                     12: // Device リストにつなぐ常設インスタンスではないため、
                     13: // そのままではコマンドラインオプション等でログレベルを指定する方法がないが
                     14: // その代わり、RAM デバイスのログレベルを引き継いである。
                     15: // よって、使い方としては従来通り -C -Lram で指定できる。
                     16: //
                     17: // Object 派生で putlog() のオーバーライドは用意していないので、
                     18: // ログ出力には putmsg() を使うこと。
                     19: 
                     20: #include "bootloader.h"
                     21: #include "aout.h"
                     22: #include "autofd.h"
                     23: #include "elf.h"
1.1.1.3   root       24: #include "iodevstream.h"
1.1       root       25: #include "mainapp.h"
                     26: #include "mainram.h"
                     27: #include <fcntl.h>
                     28: #include <sys/stat.h>
                     29: #include <sys/mman.h>
                     30: #include <zlib.h>
1.1.1.3   root       31: #include <vector>
1.1       root       32: 
1.1.1.4   root       33: // info で指定された実行ファイルを mainram にロードする。
                     34: /*static*/ bool
                     35: BootLoader::LoadExec(MainRAMDevice *mainram_, LoadInfo *info)
                     36: {
                     37:        BootLoader loader(mainram_);
                     38:        return loader.LoadExec(info);
                     39: }
                     40: 
                     41: // info で指定されたデータファイルを mainram にロードする。
                     42: /*static*/ bool
                     43: BootLoader::LoadData(MainRAMDevice *mainram_, LoadInfo *info)
                     44: {
                     45:        BootLoader loader(mainram_);
                     46:        return loader.LoadData(info);
                     47: }
                     48: 
1.1       root       49: // コンストラクタ
1.1.1.3   root       50: BootLoader::BootLoader(MainRAMDevice *mainram_)
1.1       root       51:        : inherited(OBJ_BOOTLOADER)
                     52: {
1.1.1.3   root       53:        mainram = mainram_;
1.1       root       54: 
                     55:        // RAM デバイスのログレベルを踏襲する。
1.1.1.3   root       56:        loglevel = mainram->loglevel;
1.1       root       57: }
                     58: 
                     59: // デストラクタ
                     60: BootLoader::~BootLoader()
                     61: {
                     62: }
                     63: 
1.1.1.2   root       64: // info で指定されたデータファイルをゲストにロードする。
                     65: // info->data, size が指定されていればバッファから読み込む (現状こちらのみ)。
                     66: // info->start には読み込み先アドレスを指定する (戻り値ではない)。
                     67: // info->end にはロードした最終アドレス(の次のアドレス) が返る (ここは同じ)。
                     68: // 成功すれば true、失敗すれば false を返す。
                     69: bool
                     70: BootLoader::LoadData(LoadInfo *info)
                     71: {
                     72:        assert(info->data != NULL);
                     73:        assert(info->size != 0);
                     74: 
                     75:        // ゲストにコピー。
1.1.1.3   root       76:        if (mainram->WriteMem(info->start, info->data, info->size) == false) {
                     77:                return false;
                     78:        }
1.1.1.2   root       79:        info->end = info->start + info->size;
                     80: 
                     81:        return true;
                     82: }
                     83: 
                     84: // info で指定された実行ファイルをゲストにロードする。
                     85: // info->data, size が指定されていればバッファから読み込む。
                     86: // 指定されてなければホストの info->path から読み込む (その際 data, size は
                     87: // 内部で使用する)。
                     88: // info->path はいずれにしてもエラー表示の際に使用する。
                     89: // 実行ファイルのフォーマットは自動で認識する。
                     90: // 成功すれば true、失敗すれば false を返す。
                     91: bool
                     92: BootLoader::LoadExec(LoadInfo *info)
                     93: {
                     94:        if (info->data == NULL || info->size == 0) {
                     95:                return LoadExecFromFile(info);
                     96:        } else {
                     97:                return LoadExecFromBuf(info);
                     98:        }
                     99: }
                    100: 
                    101: // info->path で指定された実行ファイルをゲストにロードする。
1.1       root      102: // ファイルのフォーマットは自動で認識する。
                    103: // 成功すれば true、失敗すれば false を返す。
                    104: bool
1.1.1.2   root      105: BootLoader::LoadExecFromFile(LoadInfo *info)
1.1       root      106: {
                    107:        uint8 *filedata;
                    108:        size_t filesize;
                    109:        struct stat st;
                    110:        autofd fd;
                    111:        bool rv;
                    112: 
                    113:        fd = open(info->path, O_RDONLY);
                    114:        if (fd == -1) {
1.1.1.5 ! root      115:                warn("%s \"%s\"", __method__, info->path);
1.1       root      116:                return false;
                    117:        }
                    118: 
                    119:        if (fstat(fd, &st) == -1) {
1.1.1.5 ! root      120:                warn("%s \"%s\" fstat failed", __method__, info->path);
1.1       root      121:                return false;
                    122:        }
                    123: 
                    124:        if (!S_ISREG(st.st_mode)) {
1.1.1.5 ! root      125:                warnx("%s \"%s\" not a regular file", __method__, info->path);
1.1       root      126:                return false;
                    127:        }
                    128: 
                    129:        // 今の所マジック判定は最初の4バイトで出来るので
                    130:        if (st.st_size < 4) {
1.1.1.5 ! root      131:                warnx("%s \"%s\" file too short", __method__, info->path);
1.1       root      132:                return false;
                    133:        }
                    134:        filesize = st.st_size;
                    135: 
                    136:        filedata = (uint8 *)mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
                    137:        if (filedata == MAP_FAILED) {
1.1.1.5 ! root      138:                warn("%s \"%s\" mmap failed", __method__, info->path);
1.1       root      139:                return false;
                    140:        }
                    141: 
                    142:        info->data = filedata;
                    143:        info->size = filesize;
                    144: 
                    145:        // 先頭3バイトの gzip マジックを調べる
                    146:        if (filedata[0] == 0x1f && filedata[1] == 0x8b && filedata[2] == 0x08) {
                    147:                // gzip
                    148:                rv = LoadGzFile(info);
                    149:        } else {
                    150:                // 通常ファイル
1.1.1.2   root      151:                rv = LoadExecFromBuf(info);
1.1       root      152:        }
                    153: 
                    154:        munmap(filedata, filesize);
                    155:        return rv;
                    156: }
                    157: 
                    158: // gzip を展開してゲストにロードする。
                    159: // 成功すれば true、失敗すれば false を返す。
                    160: bool
                    161: BootLoader::LoadGzFile(LoadInfo *info)
                    162: {
                    163:        z_stream z {};
                    164:        uint32 decompsize;
                    165:        int rv;
                    166: 
                    167:        // この時点の data, size は展開前のもの
                    168:        const uint8 *infile = info->data;
                    169:        size_t infilesize = info->size;
                    170: 
                    171:        // gzip なら(?) ファイルの末尾4バイトが (LE で?) 展開後サイズ(?)。
                    172:        decompsize = le32toh(*(const uint32 *)&infile[infilesize - 4]);
1.1.1.3   root      173:        std::vector<uint8> dstbuf(decompsize);
1.1       root      174: 
                    175:        // gzip 展開
                    176:        // (uncompress() の方が楽そうに見えるが gzip 形式に対応してないらしい)
                    177:        z.zalloc = Z_NULL;
                    178:        z.zfree = Z_NULL;
                    179:        z.opaque = Z_NULL;
                    180:        rv = inflateInit2(&z, 47);
                    181:        if (rv != Z_OK) {
                    182:                warnx("%s \"%s\" inflateInit2 failed %d", __func__, info->path, rv);
                    183:                return false;
                    184:        }
                    185: 
                    186:        z.next_in = const_cast<Bytef *>(infile);
                    187:        z.avail_in = infilesize - 4;
1.1.1.3   root      188:        z.next_out = dstbuf.data();
1.1       root      189:        z.avail_out = decompsize;
                    190:        rv = inflate(&z, Z_NO_FLUSH);
                    191:        if (rv != Z_OK) {
                    192:                warnx("%s \"%s\" inflate failed %d", __func__, info->path, rv);
                    193:                return false;
                    194:        }
                    195: 
                    196:        inflateEnd(&z);
                    197: 
                    198:        // 展開できたのでロード
1.1.1.3   root      199:        info->data = dstbuf.data();
1.1       root      200:        info->size = decompsize;
1.1.1.2   root      201:        return LoadExecFromBuf(info);
1.1       root      202: }
                    203: 
                    204: // info->data, size で指定されたバッファを実行ファイルとみなしてゲストに
                    205: // ロードする。ファイルのフォーマットは自動で認識する。
                    206: // 成功すれば true、失敗すれば false を返す。
                    207: bool
1.1.1.2   root      208: BootLoader::LoadExecFromBuf(LoadInfo *info)
1.1       root      209: {
                    210:        bool rv;
                    211: 
                    212:        // フォーマットだけ判定して分岐
                    213:        const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)info->data;
                    214:        const aout_header *aout = (const aout_header *)info->data;
                    215:        if (memcmp(ehdr->e_ident, "\177ELF", 4) == 0) {
                    216:                rv = Load_elf32(info);
                    217:        } else if (AOUT_MAGIC(be32toh(aout->magic)) == AOUT_OMAGIC) {
                    218:                rv = Load_aout(info);
                    219:        } else {
                    220:                warnx("%s \"%s\" unknown executable file format", __func__, info->path);
                    221:                return false;
                    222:        }
                    223: 
                    224:        return rv;
                    225: }
                    226: 
                    227: // ホストの a.out をゲストの RAM に読み込む。
                    228: // 成功すれば true、失敗すれば false を返す。
                    229: bool
                    230: BootLoader::Load_aout(LoadInfo *info)
                    231: {
                    232:        aout_header aout;
                    233:        int copylen;
                    234: 
                    235:        if (info->size < sizeof(aout)) {
                    236:                warnx("%s \"%s\" file too short", __func__, info->path);
                    237:                return false;
                    238:        }
                    239: 
                    240:        // ヘッダを読み込む
                    241:        // BigEndian しかターゲットにしてないので最初から全部変換しとく
                    242:        const aout_header *ah = (const aout_header *)info->data;
                    243:        aout.magic       = be32toh(ah->magic);
                    244:        aout.textsize    = be32toh(ah->textsize);
                    245:        aout.datasize    = be32toh(ah->datasize);
                    246:        aout.bsssize     = be32toh(ah->bsssize);
                    247:        aout.symsize     = be32toh(ah->symsize);
                    248:        aout.entry       = be32toh(ah->entry);
                    249:        aout.textrelsize = be32toh(ah->textrelsize);
                    250:        aout.datarelsize = be32toh(ah->datarelsize);
                    251: 
                    252:        if (loglevel >= 1) {
                    253:                const char *mstr;
                    254:                switch (AOUT_MAGIC(aout.magic)) {
                    255:                 case AOUT_OMAGIC:      mstr = "OMAGIC";        break;
                    256:                 case AOUT_NMAGIC:      mstr = "NMAGIC";        break;
                    257:                 case AOUT_ZMAGIC:      mstr = "ZMAGIC";        break;
                    258:                 default:
                    259:                        mstr = "unsupported magic";
                    260:                        break;
                    261:                }
                    262:                putmsgn("%s magic       = %08x (MID=0x%03x, %s)", __func__,
                    263:                        aout.magic, AOUT_MID(aout.magic), mstr);
                    264:                putmsgn("%s textsize    = %08x", __func__, aout.textsize);
                    265:                putmsgn("%s datasize    = %08x", __func__, aout.datasize);
                    266:                putmsgn("%s bsssize     = %08x", __func__, aout.bsssize);
                    267:                putmsgn("%s symsize     = %08x", __func__, aout.symsize);
                    268:                putmsgn("%s entry       = %08x", __func__, aout.entry);
                    269:                putmsgn("%s textrelsize = %08x", __func__, aout.textrelsize);
                    270:                putmsgn("%s datarelsize = %08x", __func__, aout.datarelsize);
                    271:        }
                    272: 
                    273:        // MID はチェックしない。
                    274:        // CPU アーキテクチャが一致するかくらいはチェックしたいところだが、
                    275:        // MID は CPU ごとではなく OS(?) ごとに異なっているのと、MID の
                    276:        // 一元管理された一次情報が存在しないので、正解リストを作るのが困難。
                    277: 
                    278:        // とりあえず再配置は無視
                    279:        if (aout.textrelsize != 0 || aout.datarelsize != 0) {
                    280:                warnx("%s \"%s\" relocation not supported", __func__, info->path);
                    281:                return false;
                    282:        }
                    283: 
                    284:        // OpenBSD の boot は text+data が filesize より大きくて何かおかしいが
                    285:        // OMAGIC の a.out は text+data が連続しているだけなので、とりあえず
                    286:        // 何も考えずにファイルサイズ分ロードしておく。
                    287:        copylen = aout.textsize + aout.datasize;
                    288:        if (copylen >= info->size) {
                    289:                copylen = info->size;
                    290:                warnx("warning: %s \"%s\" corrupted? "
                    291:                      "(text=%u + data=%u, filesize=%d); loading %d bytes",
                    292:                        __func__,
                    293:                        info->path, aout.textsize, aout.datasize, (int)info->size, copylen);
1.1.1.5 ! root      294:                // FALLTHROUGH
1.1       root      295:        }
                    296: 
1.1.1.3   root      297:        if (aout.entry + copylen > mainram->GetSize()) {
1.1       root      298:                warnx("%s \"%s\" out of memory in VM", __func__, info->path);
                    299:                return false;
                    300:        }
                    301: 
                    302:        // 再配置不要なので、text + data を entry に置いて entry に飛ぶだけ
1.1.1.3   root      303:        bool ok = mainram->WriteMem(aout.entry, info->data + sizeof(aout), copylen);
                    304:        if (ok == false) {
                    305:                warnx("%s \"%s\" WriteMem($%x, $%x) failed",
                    306:                        __func__, info->path, aout.entry, copylen);
                    307:                return false;
                    308:        }
1.1       root      309: 
                    310:        // a.out は entry だけでよい
                    311:        info->entry = aout.entry;
                    312:        return true;
                    313: }
                    314: 
                    315: // ELF フォーマットのエンディアンからホストエンディアンに変換
                    316: #define elf16toh(x)    ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
                    317:        be16toh(x) : le16toh(x))
                    318: #define elf32toh(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
                    319:        be32toh(x) : le32toh(x))
                    320: #define htoelf16(x)    ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
                    321:        htobe16(x) : htole16(x))
                    322: #define htoelf32(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
                    323:        htobe32(x) : htole32(x))
                    324: 
                    325: // XXX このへん、そのうちきれいにする
                    326: #define GetElfStr1(array, v)   GetElfStr(array, countof(array), v)
                    327: static const char *
                    328: GetElfStr(const std::pair<uint, const char *> *map, uint mapcount, uint v)
                    329: {
                    330:        for (int i = 0; i < mapcount; i++) {
                    331:                if (map[i].first == v) {
                    332:                        return map[i].second;
                    333:                }
                    334:        }
                    335:        return "?";
                    336: }
                    337: 
                    338: static std::pair<uint, const char *> class_str[] = {
                    339:        { ELFCLASSNONE, "NONE" },
                    340:        { ELFCLASS32,   "32bit" },
                    341:        { ELFCLASS64,   "64bit" },
                    342: };
                    343: #define GetElfClassStr(x)      GetElfStr1(class_str, (x))
                    344: 
                    345: static std::pair<uint, const char *> data_str[] = {
                    346:        { ELFDATANONE,  "NONE" },
                    347:        { ELFDATA2LSB,  "LE" },
                    348:        { ELFDATA2MSB,  "BE" },
                    349: };
                    350: #define GetElfDataStr(x)       GetElfStr1(data_str, (x))
                    351: 
                    352: static std::pair<uint, const char *> etype_str[] = {
                    353:        { ET_NONE,      "ET_NONE" },
                    354:        { ET_REL,       "ET_REL" },
                    355:        { ET_EXEC,      "ET_EXEC" },
                    356:        { ET_DYN,       "ET_DYN" },
                    357:        { ET_CORE,      "ET_CORE" },
                    358: };
                    359: #define GetElfETypeStr(x)      GetElfStr1(etype_str, (x))
                    360: 
                    361: static std::pair<uint, const char *> machine_str[] = {
                    362:        // 多いので関係分のみ
                    363:        { EM_NONE,      "NONE" },
                    364:        { EM_68K,       "Motorola 68000" },
                    365:        { EM_88K,       "Motorola 88000" },
                    366: };
                    367: #define GetElfMachineStr(x)    GetElfStr1(machine_str, (x))
                    368: 
                    369: static std::pair<uint, const char *> ptype_str[] = {
                    370:        { PT_NULL,              "PT_NULL" },
                    371:        { PT_LOAD,              "PT_LOAD" },
                    372:        { PT_DYNAMIC,   "PT_DYNAMIC" },
                    373:        { PT_INTERP,    "PT_INTERP" },
                    374:        { PT_NOTE,              "PT_NOTE" },
                    375:        { PT_SHLIB,             "PT_SHLIB" },
                    376:        { PT_PHDR,              "PT_PHDR" },
                    377:        { PT_GNU_RELRO, "PT_GNU_RELRO" },       // GNU specific
                    378:        { PT_OPENBSD_RANDOMIZE, "PT_OPENBSD_RANDOMIZE" },
                    379:        { PT_OPENBSD_WXNEEDED,  "PT_OPENBSD_WXNEEDED" },
                    380:        { PT_OPENBSD_BOOTDATA,  "PT_OPENBSD_BOOTDATA" },
                    381: };
                    382: #define GetElfPTypeStr(x)      GetElfStr1(ptype_str, (x))
                    383: 
                    384: static std::pair<uint, const char *> shtype_str[] = {
                    385:        { SHT_NULL,             "SHT_NULL" },
                    386:        { SHT_PROGBITS, "SHT_PROGBITS" },
                    387:        { SHT_SYMTAB,   "SHT_SYMTAB" },
                    388:        { SHT_STRTAB,   "SHT_STRTAB" },
                    389:        { SHT_RELA,             "SHT_RELA" },
                    390:        { SHT_NOTE,             "SHT_NOTE" },
                    391:        { SHT_NOBITS,   "SHT_NOBITS" },
                    392:        { SHT_REL,              "SHT_REL" },
                    393: };
                    394: #define GetElfShTypeStr(x)     GetElfStr1(shtype_str, (x))
                    395: 
                    396: // ホストの ELF をゲストの RAM に読み込む。
                    397: // 成功すれば true、失敗すれば false を返す。
                    398: bool
                    399: BootLoader::Load_elf32(LoadInfo *info)
                    400: {
                    401:        uint16 target_elf_mid;
                    402:        bool rv;
                    403: 
                    404:        if (gMainApp.Has(VMCap::M68K)) {
                    405:                target_elf_mid = EM_68K;
                    406:        } else if (gMainApp.Has(VMCap::M88K)) {
                    407:                target_elf_mid = EM_88K;
                    408:        } else {
                    409:                PANIC("Unknown CPU?");
                    410:        }
                    411: 
                    412:        // ヘッダをチェック
                    413:        const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)info->data;
                    414:        if (info->size < sizeof(*ehdr)) {
                    415:                warnx("%s \"%s\" file too short", __func__, info->path);
                    416:                return false;
                    417:        }
                    418:        uint16 e_type    = elf16toh(ehdr->e_type);
                    419:        uint16 e_machine = elf16toh(ehdr->e_machine);
                    420:        uint32 e_entry   = elf32toh(ehdr->e_entry);
                    421:        if (loglevel >= 1) {
                    422:                uint n;
                    423:                n = ehdr->e_ident[EI_CLASS];
                    424:                putmsgn("EI_CLASS    = $%02x (%s)", n, GetElfClassStr(n));
                    425:                n = ehdr->e_ident[EI_DATA];
                    426:                putmsgn("EI_DATA     = $%02x (%s)", n, GetElfDataStr(n));
                    427: 
                    428:                putmsgn("e_type      = $%02x (%s)", e_type, GetElfETypeStr(e_type));
                    429:                putmsgn("e_machine   = $%02x (%s)", e_machine,
                    430:                        GetElfMachineStr(e_machine));
                    431:                putmsgn("e_entry     = $%08x", e_entry);
                    432:        }
                    433: 
                    434:        if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
                    435:                warnx("%s \"%s\" not ELF32 format", __func__, info->path);
                    436:                return false;
                    437:        }
                    438:        if (e_machine != target_elf_mid) {
                    439:                warnx("%s \"%s\" machine type mismatch "
                    440:                        "($%02x expected but $%02x)", __func__,
                    441:                        info->path, target_elf_mid, e_machine);
                    442:                return false;
                    443:        }
                    444: 
                    445:        switch (e_type) {
                    446:         case ET_EXEC:
                    447:                info->entry = e_entry;
                    448:                rv = Load_elf32_exec(info);
                    449:                break;
                    450:         case ET_REL:
                    451:                rv = Load_elf32_rel(info);
                    452:                break;
                    453:         default:
                    454:                warnx("%s \"%s\" unsupported file type", __func__, info->path);
                    455:                return false;
                    456:        }
                    457: 
                    458:        if (loglevel >= 1) {
                    459:                putmsgn("start = $%08x", info->start);
                    460:                putmsgn("sym   = $%08x", info->sym);
                    461:                putmsgn("end   = $%08x", info->end);
                    462:        }
                    463: 
                    464:        return rv;
                    465: }
                    466: 
                    467: // ホストの ELF 実行形式ファイルをゲストの RAM に読み込む。
                    468: // 読み込めたら true、エラーなら false を返す。
                    469: // エントリポイントは呼び出し元が知っているのでこっちでは関与しない。
                    470: bool
                    471: BootLoader::Load_elf32_exec(LoadInfo *info)
                    472: {
                    473:        const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)info->data;
                    474:        std::string bootmsg;
                    475: 
                    476:        // プログラムヘッダに読み込むべき領域が示されている。
                    477:        // - e_phoff がプログラムヘッダのファイル先頭からのオフセット
                    478:        // - e_phentsize がプログラムヘッダ1つの大きさ
                    479:        // - e_phnum がプログラムヘッダの個数
                    480:        uint32 e_phoff     = elf32toh(ehdr->e_phoff);
                    481:        uint16 e_phentsize = elf16toh(ehdr->e_phentsize);
                    482:        uint16 e_phnum     = elf16toh(ehdr->e_phnum);
                    483:        if (loglevel >= 1) {
                    484:                putmsgn("e_phoff     = $%08x", e_phoff);
                    485:                putmsgn("e_phentsize = $%04x", e_phentsize);
                    486:                putmsgn("e_phnum     = %d",    e_phnum);
                    487:        }
                    488:        if (e_phentsize != sizeof(Elf32_Phdr)) {
                    489:                warnx("%s \"%s\" unknown program header size 0x%x", __func__,
                    490:                        info->path, e_phentsize);
                    491:                return false;
                    492:        }
                    493: 
1.1.1.3   root      494:        IODeviceStream ds(mainram);
1.1       root      495: 
                    496:        for (int j = 0; j < e_phnum; j++) {
                    497:                const Elf32_Phdr *phdr =
                    498:                        &((const Elf32_Phdr*)(info->data + e_phoff))[j];
                    499:                // p_type が PT_LOAD なところをロードする。
                    500:                // その際 p_memsz > p_filesz なら差が BSS 領域。
                    501:                uint32 p_type   = elf32toh(phdr->p_type);       // タイプ
                    502:                uint32 p_offset = elf32toh(phdr->p_offset);     // ファイル上のオフセット
                    503:                uint32 p_vaddr  = elf32toh(phdr->p_vaddr);      // 仮想アドレス
                    504:                uint32 p_filesz = elf32toh(phdr->p_filesz);     // 読み込み元ファイルサイズ
                    505:                uint32 p_memsz  = elf32toh(phdr->p_memsz);      // 書き込み先サイズ
                    506:                if (loglevel >= 1) {
                    507:                        putmsgn("[%d] p_type   = $%08x (%s)", j,
                    508:                                p_type, GetElfPTypeStr(p_type));
                    509:                        putmsgn("    p_offset = $%08x", p_offset);
                    510:                        putmsgn("    p_vaddr  = $%08x", p_vaddr);
                    511:                        putmsgn("    p_filesz = $%08x", p_filesz);
                    512:                        putmsgn("    p_memsz  = $%08x", p_memsz);
                    513:                }
                    514: 
                    515:                if (p_type == PT_LOAD) {
1.1.1.3   root      516:                        if (p_vaddr + p_memsz > mainram->GetSize()) {
1.1       root      517:                                warnx("%s \"%s\" out of memory in VM", __func__, info->path);
                    518:                                return false;
                    519:                        }
                    520: 
1.1.1.4   root      521:                        // 読み込み開始位置を一応覚えておく。
                    522:                        // 隙間のある2つ以上のヘッダが来られるとあまり意味はなくなるが。
1.1       root      523:                        if (j == 0) {
                    524:                                info->start = p_vaddr;
                    525:                        } else {
                    526:                                info->start = std::min(info->start, p_vaddr);
                    527:                        }
                    528: 
1.1.1.4   root      529:                        ds.SetAddr(p_vaddr);
1.1.1.3   root      530:                        ds.WriteMem(info->data + p_offset, p_filesz);
1.1       root      531:                        for (int i = p_filesz; i < p_memsz; i++) {
1.1.1.3   root      532:                                ds.Write1(0);
1.1       root      533:                        }
                    534:                        bootmsg += string_format("%d+%d", p_filesz, p_memsz - p_filesz);
                    535:                } else if (p_type == PT_NOTE) {
                    536:                        // ベンダー文字列みたいなのらしいので無視してよい
                    537:                        continue;
                    538:                } else if (p_type == PT_OPENBSD_RANDOMIZE) {
                    539:                        // XXX どうする?
                    540:                        continue;
                    541: 
                    542:                } else {
                    543:                        warnx("%s \"%s\" p_type 0x%x(%s) not supported", __func__,
                    544:                                info->path, p_type, GetElfPTypeStr(p_type));
                    545:                        return false;
                    546:                }
                    547:        }
                    548: 
1.1.1.4   root      549:        info->end = ds.GetAddr();
1.1.1.2   root      550:        info->sym = info->end;
1.1       root      551: 
                    552:        // シンボルテーブルをロード。出来なくても構わないので続行する。
                    553:        // 本当は NetBSD カーネルの時に限るのだが、とりあえず。
1.1.1.2   root      554:        if (info->loadsym) {
                    555:                Load_elf32_sym(info, bootmsg);
                    556:        }
1.1       root      557: 
                    558:        // boot がカーネルを読んだ時と同じ形式のログを出しておく。(検証用)
                    559:        putmsg(1, "%s=0x%x", bootmsg.c_str(), info->end - info->start);
                    560: 
                    561:        return true;
                    562: }
                    563: 
                    564: // NetBSD カーネルのシンボルテーブルを読み込む。
                    565: // NetBSD のブートローダはカーネルを読み込む際 (LOAD_HDR | LOAD_SYM オプション
                    566: // が指定されていればだが、デフォルトでは (LOAD_ALL として) 指定されているので)
                    567: // カーネル本体 (プログラム領域 + BSS) の後ろに ELF ヘッダ、セクションヘッダ、
                    568: // シンボルセクションを配置する。当然配置しただけでは中に書いてあるアドレスが
                    569: // 異なるので、アドレスの再配置も行う。まじか。
                    570: //
                    571: // 他 OS は未調査。本来 NetBSD カーネルのみで行うべきだが、ELF 実行ファイル
                    572: // からは判断しづらいので、とりあえず常に行う。
                    573: // 他 ELF からは、範囲外アクセスすると見えないはずの便利なものが見えてしまう
                    574: // という不具合があることになる。
                    575: //
                    576: // メモリマップ (上が0)
                    577: // :                              :
                    578: // : ここまでが読み込み済みの     :
                    579: // : カーネルの内容(たぶんBSS末尾):
                    580: // +- - - - - - - - - - - - - - - + <-- 呼び出し時点の info->end
                    581: // | 4バイト境界までパディング    |
                    582: // +------------------------------+ <-- elfp (ここが起点)
                    583: // | ELF ヘッダ (52バイト)        |
                    584: // | .e_shoff (セクションヘッダ位置) = (shpp - elfp)
                    585: // |                              |
                    586: // +------------------------------+ <-- shpp
                    587: // | セクションヘッダ(e_shnum個)  |
                    588: // :                              :
                    589: // |                              |
                    590: // +- - - - - - - - - - - - - - - +
                    591: // | 4バイト境界までパディング    |
                    592: // +------------------------------+ ここからシンボル情報
                    593: // | セクション名テーブル         | 一つ目はセクション名テーブル
                    594: // +- - - - - - - - - - - - - - - +
                    595: // | 4バイト境界までパディング    |
                    596: // +------------------------------+
                    597: // | SYMTAB or STRTAB #n          |
                    598: // +- - - - - - - - - - - - - - - +
                    599: // | 4バイト境界までパディング    |
                    600: // +------------------------------+
                    601: // | SYMTAB or STRTAB #m          |
                    602: // +- - - - - - - - - - - - - - - +
                    603: // | 4バイト境界までパディング    |
                    604: // +------------------------------+
                    605: //
                    606: // SYMTAB、STRTAB はあるだけ全部並べるが、おそらく最小セットは次の3つ。
                    607: //
                    608: // 1. セクション名テーブル
                    609: //    セクションタイプ STRTAB のうちセクション番号が ELF ヘッダの e_shstrndx
                    610: //    で指定されているもの。セクション名 (".text" とか) が入っている。
                    611: //    実行には不要だが、セクション名比較が必要なケースがあるので置いてある。
                    612: //
                    613: // 2. シンボルテーブル
                    614: //    セクションタイプが SYMTAB のもの。
                    615: //
                    616: // 3. 文字列テーブル
                    617: //    セクションタイプ STRTAB のうち、前述の SYMTAB セクションの sh_link が
                    618: //    このセクション番号を指しているもの。
                    619: //
                    620: // 各セクション内の情報はセクション内で閉じておりリロケータブル。
                    621: // ELF ヘッダとセクションヘッダは元の ELF バイナリから抜き出したため、
                    622: // オフセットを持っている箇所 (のうち NetBSD がシンボル解決のために参照する
                    623: // もの) は、ここで書き換える必要がある。その時の起点は ELF ヘッダの先頭
                    624: // (elfp)。具体的には、ELF ヘッダにあるセクションヘッダの位置 (e_shoff) と
                    625: // 各セクションヘッダにある自セクションの位置 (sh_offset)。のはず。
                    626: //
                    627: bool
                    628: BootLoader::Load_elf32_sym(LoadInfo *info, std::string& bootmsg)
                    629: {
                    630:        // アラインメントが必要な時は32ビット(4バイト)。
                    631:        // これは ELF の制約ではなく NetBSD のローダの話。
                    632:        const uint ELFROUND = 4;
                    633: 
                    634:        Elf32_Ehdr elf;
                    635:        std::vector<char> shstr;
                    636:        uint32 maxp;
                    637: 
                    638:        // ELF ヘッダをローカルに読み込む。
                    639:        // ここは後で書き換えて RAM に書き出すので、中身は BE のまま。
                    640:        // それとマクロのためどのみち *ehdr は必要。
                    641:        memcpy(&elf, info->data, sizeof(elf));
                    642:        const Elf32_Ehdr *ehdr = &elf;
                    643: 
                    644:        uint16 e_machine  = elf16toh(elf.e_machine);
                    645:        uint32 e_shoff    = elf32toh(elf.e_shoff);
                    646:        uint16 e_shnum    = elf16toh(elf.e_shnum);
                    647:        uint16 e_shstrndx = elf16toh(elf.e_shstrndx);
                    648:        if (loglevel >= 1) {
                    649:                putmsgn("e_shoff     = $%08x", e_shoff);
                    650:                putmsgn("e_shnum     = %d",    e_shnum);
                    651:                putmsgn("e_shstrndx  = %d",    e_shstrndx);
                    652:        }
                    653: 
                    654:        maxp = info->end;
                    655:        maxp = roundup(maxp, ELFROUND);
                    656: 
                    657:        /*
                    658:         * Load the ELF HEADER, SECTION HEADERS and possibly the SYMBOL
                    659:         * SECTIONS.
                    660:         */
                    661:        // ここに ELF ヘッダだが、書き出す内容は後で決まるので、
                    662:        // ここではサイズ分空けておくだけ。
                    663:        uint32 elfp = maxp;
                    664:        maxp += sizeof(Elf32_Ehdr);
                    665: 
                    666:        // セクションヘッダをワークの shp に読み込む。
                    667:        uint32 sz = sizeof(Elf32_Shdr) * e_shnum;
                    668:        std::vector<Elf32_Shdr> shp(e_shnum);
                    669:        memcpy(&shp[0], info->data + e_shoff, sz);
                    670: 
                    671:        // ここを shpp にして、maxp はセクションヘッダ分進める。
                    672:        uint32 shpp = maxp;
                    673:        maxp += roundup(sz, ELFROUND);
                    674: 
                    675:        // section names セクションをローカルの shstr に読み込む。
                    676:        uint32 shstroff = 0;
                    677:        uint32 shstrsz = 0;
                    678:        if (e_shstrndx != SHN_UNDEF) {
                    679:                shstroff = elf32toh(shp[e_shstrndx].sh_offset);
                    680:                shstrsz  = elf32toh(shp[e_shstrndx].sh_size);
                    681: 
                    682:                shstr.resize(shstrsz);
                    683:                memcpy(&shstr[0], info->data + shstroff, shstrsz);
                    684:        }
                    685: 
                    686:        // ここでやっと、この ELF バイナリが NetBSD のものかどうか調べる。
                    687:        // 本当は NetBSD カーネルに限定したいけど、特定方法が分からないので
                    688:        // とりあえず NetBSD 製バイナリというところまで。NetBSD 製バイナリは
                    689:        // ".note.netbsd.ident" という名前の NOTE セクションを持ってるようだ。
                    690:        // 知らんけど。
                    691:        bool is_netbsd = false;
                    692:        for (int i = 0; i < e_shnum; i++) {
                    693:                uint32 sh_name = elf32toh(shp[i].sh_name);      // セクション名
                    694:                uint32 sh_type = elf32toh(shp[i].sh_type);      // タイプ
                    695: 
                    696:                if (sh_type == SHT_NOTE &&
                    697:                        strcmp(&shstr[sh_name], ".note.netbsd.ident") == 0)
                    698:                {
                    699:                        is_netbsd = true;
                    700:                        break;
                    701:                }
                    702:        }
                    703:        // 案の定というか非公式 NetBSD/luna88k はベースが古すぎるせいか
                    704:        // ".note.netbsd.ident" セクションを持っていない。
                    705:        // 仕方ないので M88k の ELF で OpenBSD 用セクションを持ってなければ
                    706:        // NetBSD ということにする…。
                    707:        if (e_machine == EM_88K) {
                    708:                // プログラムヘッダから PT_OPENBSD_* を探すのでもよいが、
                    709:                // この関数内はもうセクションヘッダを触っているので、ここでも
                    710:                // セクションヘッダから名前を引くことにする。
                    711:                bool is_openbsd = false;
                    712:                for (int i = 0; i < e_shnum; i++) {
                    713:                        uint32 sh_name = elf32toh(shp[i].sh_name);      // セクション名
                    714:                        uint32 sh_type = elf32toh(shp[i].sh_type);      // タイプ
                    715: 
                    716:                        if (sh_type == SHT_PROGBITS &&
                    717:                                strcmp(&shstr[sh_name], ".openbsd.randomdata") == 0)
                    718:                        {
                    719:                                is_openbsd = true;
                    720:                                break;
                    721:                        }
                    722:                }
                    723: 
                    724:                if (is_openbsd == false) {
                    725:                        is_netbsd = true;
                    726:                }
                    727:        }
                    728: 
                    729:        if (is_netbsd == false) {
                    730:                putmsg(1, "symbol not loaded (no NetBSD binary)");
                    731:                return true;
                    732:        }
                    733: 
                    734:        /*
                    735:         * First load the section names section. Only useful for CTF.
                    736:         */
                    737:        // まず、さっき読んだ section names セクションを RAM にコピー。
                    738:        if (e_shstrndx != SHN_UNDEF) {
1.1.1.3   root      739:                mainram->WriteMem(maxp, info->data + shstroff, shstrsz);
1.1       root      740: 
                    741:                shp[e_shstrndx].sh_offset = htoelf32(maxp - elfp);
                    742:                maxp += roundup(shstrsz, ELFROUND);
                    743:        }
                    744: 
                    745:        /*
                    746:         * Now load the symbol sections themselves. Make sure the sections are
                    747:         * ELFROUND-aligned. Update sh_offset to be relative to elfp. Set it to
                    748:         * zero when we don't want the sections to be taken care of, the kernel
                    749:         * will properly skip them.
                    750:         */
                    751:        // シンボルセクションを読み込む。セクションは ELFROUND 境界に整列。
                    752:        bool first = true;
                    753:        for (int i = 1; i < e_shnum; i++) {
                    754:                uint32 sh_name   = elf32toh(shp[i].sh_name);    // セクション名
                    755:                uint32 sh_type   = elf32toh(shp[i].sh_type);    // タイプ
                    756:                uint32 sh_addr   = elf32toh(shp[i].sh_addr);    // アドレス
                    757:                uint32 sh_offset = elf32toh(shp[i].sh_offset);  // ファイル先頭から
                    758:                uint32 sh_size   = elf32toh(shp[i].sh_size);    // サイズ
                    759:                if (loglevel >= 1) {
1.1.1.3   root      760:                        putmsgn("[%2u] sh_name   = %s", i, &shstr[sh_name]);
1.1       root      761:                        putmsgn("     sh_type   = $%08x (%s)",
                    762:                                sh_type, GetElfShTypeStr(sh_type));
                    763:                        putmsgn("     sh_addr   = $%08x", sh_addr);
                    764:                        putmsgn("     sh_offset = $%08x", sh_offset);
                    765:                        putmsgn("     sh_size   = $%08x", sh_size);
                    766:                }
                    767: 
                    768:                if (i == e_shstrndx) {
                    769:                        // このセクションはすでに読んでいる
                    770:                        continue;
                    771:                }
                    772: 
                    773:                switch (sh_type) {
                    774:                 case SHT_PROGBITS:
                    775:                        // PROGBITS は通常シンボルではないが、
                    776:                        // ".SUNW_ctf" セクションにはシンボルが入ってるようだ。
                    777:                        if (shstr.empty() == false) {
                    778:                                /* got a CTF section? */
                    779:                                if (strcmp(&shstr[sh_name], ".SUNW_ctf") == 0) {
                    780:                                        goto havesym;
                    781:                                }
                    782:                        }
                    783: 
                    784:                        shp[i].sh_offset = htoelf32(0);
                    785:                        break;
                    786: 
                    787:                 case SHT_STRTAB:
                    788:                        // 文字列テーブルセクションはたぶん2つあって、
                    789:                        // 片方はセクション名テーブルで、これはカーネルには必要ない。
                    790:                        // もう一方の文字列テーブルはシンボルテーブルに対応する文字列
                    791:                        // テーブルなので、こっちが実行時のシンボルとして必要。
                    792:                        // これは SYMTAB の link フィールドから差されているほう。
                    793:                        //
                    794:                        // Sec# Name      Type    Link
                    795:                        // [15] .symtab   SYMTAB    16  <- シンボル情報
                    796:                        // [16] .strtab   STRTAB     0  <- [15] に対応するシンボルテーブル
                    797:                        // [17] .shstrtab STRTAB     0  <- セクション名テーブル
                    798:                        for (int j = 1; j < e_shnum; j++) {
                    799:                                uint32 shj_type = elf32toh(shp[j].sh_type);
                    800:                                uint32 shj_link = elf32toh(shp[j].sh_link);
                    801:                                if (shj_type == SHT_SYMTAB && shj_link == (uint)i) {
                    802:                                        goto havesym;
                    803:                                }
                    804:                        }
                    805:                        /*
                    806:                         * Don't bother with any string table that isn't
                    807:                         * referenced by a symbol table.
                    808:                         */
                    809:                        shp[i].sh_offset = htoelf32(0);
                    810:                        break;
                    811: 
                    812:                 case SHT_SYMTAB:
                    813:                 havesym:
                    814:                        bootmsg += string_format("%s%d", (first ? " [" : "+"), sh_size);
1.1.1.3   root      815:                        mainram->WriteMem(maxp, info->data + sh_offset, sh_size);
1.1       root      816:                        shp[i].sh_offset = htoelf32(maxp - elfp);
                    817:                        maxp += roundup(sh_size, ELFROUND);
                    818:                        first = false;
                    819:                        break;
                    820: 
                    821:                 case SHT_NOTE:
                    822:                 {
                    823:                        struct __packed {
                    824:                                Elf32_Nhdr nh;
                    825:                                uint8_t name[ELF_NOTE_NETBSD_NAMESZ + 1];
                    826:                                uint8_t desc[ELF_NOTE_NETBSD_DESCSZ];
                    827:                        } note;
                    828: 
                    829:                        if (sh_size < sizeof(note)) {
                    830:                                shp[i].sh_offset = htoelf32(0);
                    831:                                break;
                    832:                        }
                    833: 
                    834:                        // 今の所 NOTE に使いみちはないけどとりあえず真似ておく
                    835:                        memcpy(&note, info->data + sh_offset, sizeof(note));
                    836:                        uint32 n_namesz = elf32toh(note.nh.n_namesz);
                    837:                        uint32 n_descsz = elf32toh(note.nh.n_descsz);
                    838:                        uint32 n_type   = elf32toh(note.nh.n_type);
                    839:                        if (n_namesz == ELF_NOTE_NETBSD_NAMESZ &&
                    840:                                n_descsz == ELF_NOTE_NETBSD_DESCSZ &&
                    841:                                n_type   == ELF_NOTE_TYPE_NETBSD_TAG &&
                    842:                                memcmp(note.name, ELF_NOTE_NETBSD_NAME, sizeof(note.name)) == 0)
                    843:                        {
                    844:                                uint32 netbsd_version;
                    845:                                memcpy(&netbsd_version, &note.desc, sizeof(netbsd_version));
                    846:                                putmsg(1, "netbsd_version=%d", elf32toh(netbsd_version));
                    847:                        }
                    848:                        shp[i].sh_offset = htoelf32(0);
                    849:                        break;
                    850:                 }
                    851: 
                    852:                 default:
                    853:                        shp[i].sh_offset = htoelf32(0);
                    854:                        break;
                    855:                }
                    856:        }
                    857:        if (first == false) {
                    858:                bootmsg += ']';
                    859:        }
                    860: 
                    861:        if (loglevel >= 1) {
                    862:                putmsgn("Modified Section Header:");
                    863:                for (int k = 0; k < elf16toh(elf.e_shnum); k++) {
                    864:                        auto& s = shp[k];
1.1.1.3   root      865:                        putmsgn("[%2u] %-8s offset=%08x size=%08x",
1.1       root      866:                                k,
                    867:                                GetElfShTypeStr(elf32toh(s.sh_type)) + 4,
                    868:                                elf32toh(s.sh_offset),
                    869:                                elf32toh(s.sh_size));
                    870:                }
                    871:        }
                    872:        // ローカルの shp を shpp の位置に書き出す。
                    873:        // このために shp はターゲットエンディアンになっている。
1.1.1.3   root      874:        mainram->WriteMem(shpp, &shp[0], sz);
1.1       root      875: 
                    876:        /*
                    877:         * Update the ELF HEADER to give information relative to elfp.
                    878:         */
                    879:        elf.e_phoff     = htoelf32(0);
                    880:        elf.e_shoff     = htoelf32(sizeof(Elf32_Ehdr));
                    881:        elf.e_phentsize = htoelf16(0);
                    882:        elf.e_phnum     = htoelf16(0);
                    883:        if (loglevel >= 1) {
                    884:                putmsgn("Modified ELF Header:");
                    885:                putmsgn(" e_type      = $%04x", elf16toh(elf.e_type));
                    886:                putmsgn(" e_entry     = $%08x", elf32toh(elf.e_entry));
                    887:                putmsgn(" e_phoff     = $%08x", elf32toh(elf.e_phoff));
                    888:                putmsgn(" e_shoff     = $%08x", elf32toh(elf.e_shoff));
                    889:                putmsgn(" e_phentsize = $%04x x %d",
                    890:                        elf16toh(elf.e_phentsize), elf16toh(elf.e_phnum));
                    891:                putmsgn(" e_shentsize = $%04x x %d",
                    892:                        elf16toh(elf.e_shentsize), elf16toh(elf.e_shnum));
                    893:        }
1.1.1.3   root      894:        mainram->WriteMem(elfp, &elf, sizeof(elf));
1.1       root      895: 
                    896:        info->sym = elfp;
                    897:        info->end = maxp;
                    898: 
                    899:        return true;
                    900: }
                    901: 
                    902: // ホストの ELF オブジェクトの text セクションをゲストの RAM に読み込む。
                    903: // .o(オブジェクトファイル) 用。
                    904: // 読み込めたら true、エラーなら false を返す。
                    905: bool
                    906: BootLoader::Load_elf32_rel(LoadInfo *info)
                    907: {
                    908:        const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)info->data;
                    909:        uint32 entry = 0x20000;
                    910: 
                    911:        uint32 e_shoff     = elf32toh(ehdr->e_shoff);
                    912:        uint16 e_shentsize = elf16toh(ehdr->e_shentsize);
                    913:        uint16 e_shnum     = elf16toh(ehdr->e_shnum);
                    914:        if (loglevel >= 1) {
                    915:                putmsgn("e_shoff     = $%08x", e_shoff);
                    916:                putmsgn("e_shentsize = $%04x", e_shentsize);
                    917:                putmsgn("e_shnum     = %d",    e_shnum);
                    918:        }
                    919:        if (e_shentsize != sizeof(Elf32_Shdr)) {
                    920:                warnx("%s \"%s\" unknown section header size 0x%x", __func__,
                    921:                        info->path, e_shentsize);
                    922:                return false;
                    923:        }
                    924: 
1.1.1.3   root      925:        IODeviceStream ds(mainram, entry);
1.1       root      926: 
                    927:        for (int j = 0; j < e_shnum; j++) {
                    928:                const Elf32_Shdr *shdr =
                    929:                        &((const Elf32_Shdr*)(info->data + e_shoff))[j];
                    930:                // sh_type
                    931:                uint32 sh_type   = elf32toh(shdr->sh_type);             // タイプ
                    932:                uint32 sh_addr   = elf32toh(shdr->sh_addr);             // アドレス
                    933:                uint32 sh_offset = elf32toh(shdr->sh_offset);   // ファイル先頭から
                    934:                uint32 sh_size   = elf32toh(shdr->sh_size);             // サイズ
                    935:                if (loglevel >= 1) {
                    936:                        putmsgn("[%d] sh_type   = $%08x (%s)", j,
                    937:                                sh_type, GetElfShTypeStr(sh_type));
                    938:                        putmsgn("    sh_addr   = $%08x", sh_addr);
                    939:                        putmsgn("    sh_offset = $%08x", sh_offset);
                    940:                        putmsgn("    sh_size   = $%08x", sh_size);
                    941:                }
                    942: 
                    943:                if (sh_type == SHT_PROGBITS) {
1.1.1.3   root      944:                        ds.WriteMem(info->data + sh_offset, sh_size);
1.1       root      945:                } else if (sh_type == SHT_RELA || sh_type == SHT_REL) {
                    946:                        // 再配置情報があるのは .R 形式ではない
                    947:                        warnx("%s \"%s\" has relocation section", __func__, info->path);
1.1.1.3   root      948:                        return false;
1.1       root      949:                } else {
                    950:                        // ?
                    951:                }
                    952:        }
                    953: 
                    954:        info->start = entry;
                    955:        info->entry = entry;
1.1.1.4   root      956:        info->end   = ds.GetAddr();
1.1       root      957:        return true;
                    958: }
                    959: 
                    960: 
                    961: //
                    962: // LoadInfo クラス
                    963: //
                    964: 
                    965: LoadInfo::LoadInfo()
                    966: {
1.1.1.2   root      967:        loadsym = true;
1.1       root      968:        entry = -1;
                    969: }
                    970: 
                    971: LoadInfo::LoadInfo(const char *path_)
                    972:        : LoadInfo()
                    973: {
                    974:        path = path_;
                    975: }
                    976: 
                    977: LoadInfo::LoadInfo(const char *path_, const uint8 *data_, size_t size_)
                    978:        : LoadInfo(path_)
                    979: {
                    980:        data = data_;
                    981:        size = size_;
                    982: }

unix.superglobalmegacorp.com

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