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