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

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

unix.superglobalmegacorp.com

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