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