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

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

unix.superglobalmegacorp.com

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