|
|
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(¬e, 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, ¬e.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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.