Annotation of nono/util/runx/runx.cpp, revision 1.1.1.1

1.1       root        1: // vi:set ts=4:
                      2: 
                      3: //
                      4: // nono
                      5: // Copyright (C) 2024 nono project
                      6: // Licensed under nono-license.txt
                      7: //
                      8: 
                      9: //
                     10: // Human68k の実行ファイルを NetBSD/m68k 上で実行する。
                     11: //
                     12: 
                     13: // メモリマップ
                     14: // 0000'0000 ベクタ
                     15: // 0000'1000 (4KB/ページの時の mmap 開始アドレス)
                     16: // 0000'2000 (8KB/ページの時の mmap 開始アドレス)
                     17: // 0000'2000 初期 A7 保存 (func_t から戻るため)
                     18: // 0000'2200 エントリポイント
                     19: // 0000'c000 コマンドライン引数
                     20: // 0001'ff00 SSP 初期値
                     21: // 0001'ff00 PSP (プロセスエントリ)
                     22: // 0002'0000 ロードアドレス
                     23: 
                     24: #include "runx.h"
                     25: #include <fcntl.h>
                     26: #include <inttypes.h>
                     27: #include <libgen.h>
                     28: #include <limits.h>
                     29: #include <signal.h>
                     30: #include <stdlib.h>
                     31: #include <string.h>
                     32: #include <sys/mman.h>
                     33: #include <sys/stat.h>
                     34: #include <sys/wait.h>
                     35: 
                     36: // ぽかよけ
                     37: #if !defined(__m68k__)
                     38: #error "This file must be compiled on m68k!"
                     39: #endif
                     40: 
                     41: #define LOAD_ADDR  (0x20000)
                     42: 
                     43: typedef int (*func_t)();
                     44: 
                     45: // .X ファイルのヘッダ構造
                     46: struct XFileHeader {
                     47:    uint8  magic[2];        // +00 識別子 'HU'
                     48:    uint8  unused1;
                     49:    uint8  loadmode;        // +03 ロードモード
                     50:    uint32 base_addr;       // +04 ベースアドレス
                     51:    uint32 exec_addr;       // +08 実行開始アドレス
                     52:    uint32 text_size;       // +0c テキストサイズ
                     53:    uint32 data_size;       // +10 データサイズ
                     54:    uint32 bss_size;        // +14 BSS サイズ
                     55:    uint32 reloc_size;      // +18 再配置テーブルサイズ
                     56:    uint32 symbol_size;     // +1c シンボルサイズ
                     57:    uint32 scdline_size;    // +20 SCD 行番号テーブルサイズ
                     58:    uint32 scdsym_size;     // +24 SCD シンボルテーブルサイズ
                     59:    uint32 scdstr_size;     // +28 SCD 文字列テーブルサイズ
                     60:    uint32 unused2[4];
                     61:    uint32 module_offset;   // +3c モジュールリストの位置
                     62: } __packed;
                     63: 
                     64: static void usage();
                     65: static void init_mem();
                     66: static uint32 loadfile();
                     67: static uint32 loadfileX();
                     68: static uint32 loadfileR();
                     69: static void dumpwait(int);
                     70: static void dumpreg(const struct reg *);
                     71: static int  run(uint32);
                     72: 
                     73: int opt_debug;
                     74: int opt_trace;
                     75: struct reg_emul emul;
                     76: 
                     77: static const char *filename;
                     78: static uint32 last_addr;
                     79: static uint8 *mem;
                     80: static uint8 *page0;
                     81: static uint32 pgsize;
                     82: static uint32 ret_addr;
                     83: static char human68k_arg[255];
                     84: 
                     85: int
                     86: main(int ac, char *av[])
                     87: {
                     88:    int c;
                     89: 
                     90:    while ((c = getopt(ac, av, "dt")) != -1) {
                     91:        switch (c) {
                     92:         case 'd':
                     93:            opt_debug++;
                     94:            break;
                     95:         case 't':
                     96:            opt_trace = 1;
                     97:            break;
                     98:         default:
                     99:            usage();
                    100:        }
                    101:    }
                    102:    ac -= optind;
                    103:    av += optind;
                    104: 
                    105:    if (ac < 1) {
                    106:        usage();
                    107:    }
                    108: 
                    109:    // Human68k コマンド名と引数。
                    110:    filename = av[0];
                    111:    human68k_arg[0] = '\0';
                    112:    for (int i = 1; i < ac; i++) {
                    113:        if (i != 1) {
                    114:            strlcat(human68k_arg, " ", sizeof(human68k_arg));
                    115:        }
                    116:        strlcat(human68k_arg, av[i], sizeof(human68k_arg));
                    117:    }
                    118:    DEBUG(1, "human68k_arg=\"%s\"", human68k_arg);
                    119: 
                    120:    // 0番地を含む1ページは mmap(2) とかでも割り当て出来ないようなので
                    121:    // 仕方ないので自前で用意する。
                    122:    pgsize = getpgsize();
                    123:    page0 = (uint8 *)calloc(1, pgsize);
                    124:    if (page0 == NULL) {
                    125:        err(1, "calloc failed");
                    126:    }
                    127: 
                    128:    // mem は pgsize 番地以降 12MB までを確保しておく。
                    129:    // このためにこの実行ファイル自身は 16MB 以降にロードしてある。
                    130:    mem = (uint8 *)mmap((void *)pgsize, 12*1024*1024 - pgsize,
                    131:        PROT_READ | PROT_WRITE,
                    132:        MAP_FIXED | MAP_ANON | MAP_SHARED | MAP_INHERIT, -1, 0);
                    133:    if (mem == MAP_FAILED) {
                    134:        err(1, "mmap");
                    135:    }
                    136:    DEBUG(2, "mem=%p", mem);
                    137:    init_mem();
                    138:    init_doscall();
                    139: 
                    140:    // ロード。
                    141:    uint32 exec_addr = loadfile();
                    142:    if (exec_addr == (uint32)-1) {
                    143:        exit(1);
                    144:    }
                    145: 
                    146:    // 実行。
                    147:    if (run(exec_addr) < 0) {
                    148:        exit(1);
                    149:    }
                    150: 
                    151:    return 0;
                    152: }
                    153: 
                    154: static void
                    155: usage()
                    156: {
                    157:    fprintf(stderr, "%s <Human68k executable> [<argument...>]\n",
                    158:        getprogname());
                    159:    exit(1);
                    160: }
                    161: 
                    162: // メモリの初期化。何をどこまでやるか。
                    163: static void
                    164: init_mem()
                    165: {
                    166:    writemem1(0xcbc, 3);    // とりあえず
                    167:    writemem1(0xcbd, 0xff);
                    168: }
                    169: 
                    170: // 実行ファイルをロードする。
                    171: static uint32
                    172: loadfile()
                    173: {
                    174:    const char *e;
                    175: 
                    176:    // Support only .X and .R executable for now.
                    177:    e = strrchr(filename, '.');
                    178:    if (e != NULL && strcasecmp(e, ".x") == 0) {
                    179:        return loadfileX();
                    180:    } else {
                    181:        return loadfileR();
                    182:    }
                    183: }
                    184: 
                    185: // .X ファイルを読み込んで、リロケートまで行う。
                    186: static uint32
                    187: loadfileX()
                    188: {
                    189:    struct stat st;
                    190:    struct XFileHeader *xhdr;
                    191:    uint8 *file;
                    192:    int fd;
                    193: 
                    194:    fd = open(filename, O_RDONLY);
                    195:    if (fd < 0) {
                    196:        err(1, "%s", filename);
                    197:    }
                    198: 
                    199:    if (fstat(fd, &st) < 0) {
                    200:        err(1, "%s: stat failed", filename);
                    201:    }
                    202:    DEBUG(1, "loading %s %u bytes", filename, (uint)st.st_size);
                    203: 
                    204:    file = (uint8 *)mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
                    205:        fd, 0);
                    206:    if (file == MAP_FAILED) {
                    207:        err(1, "%s: mmap failed", filename);
                    208:    }
                    209: 
                    210:    xhdr = (struct XFileHeader *)file;
                    211:    if (xhdr->magic[0] != 'H' || xhdr->magic[1] != 'U') {
                    212:        errx(1, "%s: is not .X file", filename);
                    213:    }
                    214: 
                    215:    uint32 base_addr = be32toh(xhdr->base_addr);
                    216:    uint32 exec_addr = be32toh(xhdr->exec_addr);
                    217:    uint32 text_size = be32toh(xhdr->text_size);
                    218:    uint32 data_size = be32toh(xhdr->data_size);
                    219:    uint32 bss_size  = be32toh(xhdr->bss_size);
                    220:    DEBUG(1, "base=$%x exec=$%x (text=$%x data=$%x bss=$%x)",
                    221:        base_addr, exec_addr, text_size, data_size, bss_size);
                    222: 
                    223:    uint32 reloc_size = be32toh(xhdr->reloc_size);
                    224: 
                    225:    uint32 load_addr = LOAD_ADDR;
                    226:    uint32 load_size = text_size + data_size;
                    227:    last_addr = load_addr + load_size + bss_size;
                    228: 
                    229:    if (load_addr < pgsize) {
                    230:        for (int i = 0; i < load_size; i++) {
                    231:            writemem1(load_addr + i, file[sizeof(XFileHeader) + i]);
                    232:        }
                    233:    } else {
                    234:        memcpy((void *)load_addr, &file[sizeof(XFileHeader)], load_size);
                    235:    }
                    236: 
                    237:    // 再配置テーブルの file での位置。
                    238:    uint32 reloc_pos = sizeof(XFileHeader) + load_size;
                    239: 
                    240:    // 再配置。
                    241:    uint32 offset = load_addr - base_addr;
                    242:    uint32 reloc_end = reloc_pos + reloc_size;
                    243:    uint32 A = load_addr;
                    244:    uint32 B = base_addr;
                    245:    uint32 C = A - B;
                    246:    while (reloc_pos < reloc_end) {
                    247:        uint32 D;
                    248:        D = be16toh(*(const uint16 *)&file[reloc_pos]);
                    249:        DEBUG(2, "D=%x", D);
                    250:        reloc_pos += 2;
                    251:        if (D == 1) {
                    252:            D = be32toh(*(const uint32 *)&file[reloc_pos]);
                    253:            DEBUG(2, " odd, D=%x", D);
                    254:            reloc_pos += 4;
                    255:        }
                    256:        if ((D & 1) == 0) {
                    257:            A += D;
                    258:            uint32 old = readmem4(A);
                    259:            writemem4(A, old + C);
                    260:            DEBUG(2, " Write_L A=%x, old=%x new=%x", A, old, old + C);
                    261:        } else {
                    262:            A += D - 1;
                    263:            uint32 old = readmem2(A);
                    264:            writemem2(A, old + C);
                    265:            DEBUG(2, " Write_W A=%x, old=%x new=%x", A, old, old + C);
                    266:        }
                    267:    }
                    268:    munmap(file, st.st_size);
                    269:    close(fd);
                    270: 
                    271:    exec_addr += offset;
                    272:    DEBUG(1, ".x format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr);
                    273:    return exec_addr;
                    274: }
                    275: 
                    276: // .R ファイルを読み込む。
                    277: static uint32
                    278: loadfileR()
                    279: {
                    280:    struct stat st;
                    281:    int fd;
                    282: 
                    283:    fd = open(filename, O_RDONLY);
                    284:    if (fd < 0) {
                    285:        err(1, "%s", filename);
                    286:    }
                    287: 
                    288:    if (fstat(fd, &st) < 0) {
                    289:        err(1, "%s: stat failed", filename);
                    290:    }
                    291:    DEBUG(1, "loading %s %u bytes", filename, (uint)st.st_size);
                    292: 
                    293:    // XXX TODO
                    294:    uint32 load_addr = LOAD_ADDR;
                    295:    int n = read(fd, mem + load_addr, st.st_size);
                    296:    if (n < 0) {
                    297:        err(1, "%s", filename);
                    298:    }
                    299:    if (n < st.st_size) {
                    300:        errx(1, "%s: read too short", filename);
                    301:    }
                    302:    close(fd);
                    303: 
                    304:    DEBUG(1, ".r format, exec_addr=%08x", load_addr);
                    305:    return load_addr;
                    306: }
                    307: 
                    308: uint32
                    309: readmem1(uint32 addr)
                    310: {
                    311:    if (addr < pgsize) {
                    312:        return page0[addr];
                    313:    } else {
                    314:        return mem[addr - pgsize];
                    315:    }
                    316: }
                    317: 
                    318: uint32
                    319: readmem2(uint32 addr)
                    320: {
                    321:    uint32 data = (readmem1(addr) << 8) | readmem1(addr + 1);
                    322:    return data;
                    323: }
                    324: 
                    325: uint32
                    326: readmem4(uint32 addr)
                    327: {
                    328:    uint32 data = (readmem2(addr) << 16) | readmem2(addr + 2);
                    329:    return data;
                    330: }
                    331: 
                    332: void
                    333: writemem1(uint32 addr, uint32 data)
                    334: {
                    335:    if (addr < pgsize) {
                    336:        page0[addr] = data;
                    337:    } else {
                    338:        mem[addr - pgsize] = data;
                    339:    }
                    340: }
                    341: 
                    342: void
                    343: writemem2(uint32 addr, uint32 data)
                    344: {
                    345:    writemem1(addr,     data >> 8);
                    346:    writemem1(addr + 1, data);
                    347: }
                    348: 
                    349: void
                    350: writemem4(uint32 addr, uint32 data)
                    351: {
                    352:    writemem2(addr,     data >> 16);
                    353:    writemem2(addr + 2, data);
                    354: }
                    355: 
                    356: // wait(2) status のダンプ表示。
                    357: static void
                    358: dumpwait(int status)
                    359: {
                    360:    printf("wait status=0x%x", status);
                    361:    if (WIFEXITED(status)) {
                    362:        printf(" EXITED status=0x%x\n", WEXITSTATUS(status));
                    363:    }
                    364:    if (WIFSIGNALED(status)) {
                    365:        printf(" SIGNAL %d", WTERMSIG(status));
                    366:        if (WCOREDUMP(status)) {
                    367:            printf(" coredump");
                    368:        }
                    369:        printf("\n");
                    370:    }
                    371:    if (WIFSTOPPED(status)) {
                    372:        int signo = WSTOPSIG(status);
                    373:        printf(" STOPPED by %s\n", signame(signo));
                    374:    }
                    375: }
                    376: 
                    377: static void
                    378: dumpreg(const struct reg *reg)
                    379: {
                    380:    printf("D0:%08x D4:%08x  A0:%08x A4:%08x  SR:%04x\n",
                    381:        RegD(0), RegD(4), RegA(0), RegA(4), GetSR);
                    382:    printf("D1:%08x D5:%08x  A1:%08x A5:%08x\n",
                    383:        RegD(1), RegD(5), RegA(1), RegA(5));
                    384:    printf("D2:%08x D6:%08x  A2:%08x A6:%08x\n",
                    385:        RegD(2), RegD(6), RegA(2), RegA(6));
                    386:    printf("D3:%08x D7:%08x  A3:%08x A7:%08x\n",
                    387:        RegD(3), RegD(7), RegA(3), RegA(7));
                    388: }
                    389: 
                    390: static int
                    391: run(uint32 exec_addr)
                    392: {
                    393:    int fd[2];
                    394:    int r;
                    395:    pid_t pid;
                    396:    uint32 addr;
                    397: 
                    398: #define Write2(d)  do {    \
                    399:    writemem2(addr, (d));   \
                    400:    addr += 2;  \
                    401: } while (0)
                    402: #define Write4(d)  do {    \
                    403:    writemem4(addr, (d));   \
                    404:    addr += 4;  \
                    405: } while (0)
                    406: 
                    407:    // コマンドライン文字列を書き込む。LASCII 形式。
                    408:    addr = 0xc001;
                    409:    for (int i = 0; i < 255; i++) {
                    410:        uint8 c = (uint8)human68k_arg[i];
                    411:        writemem1(addr++, c);
                    412:        if (c == '\0') {
                    413:            writemem1(0xc000, i);
                    414:            break;
                    415:        }
                    416:    }
                    417: 
                    418:    // PSP (Process Entry) を書き込む。
                    419:    // PSP はロードアドレス-256 の位置でなければならないようだ。
                    420:    uint32 psp_base = LOAD_ADDR - 0x100;
                    421:    uint32 ram_size = 10 * 1024 * 1024;
                    422:    uint32 sp_backup = 0x2000;
                    423:    uint32 boot_addr = 0x2200;
                    424:    addr = psp_base;
                    425:    Write4(-1); // 1つ前のメモリ管理ポインタ
                    426:    Write4(-1); // このメモリを確保したプロセスのメモリ管理ポインタ
                    427:    Write4(ram_size - 4 + 1);   // このメモリブロックの終わり+1のアドレス
                    428:    Write4(-1); // 次のメモリ管理ポインタ
                    429: 
                    430:    addr = psp_base + 0x20;
                    431:    Write4(0xc000); // コマンドライン
                    432: 
                    433:    addr = boot_addr;
                    434:    Write2(0x23cf);     // move.l   a7,sp_backup    // スタックを保存
                    435:    Write4(sp_backup);
                    436:    Write2(0x2e7c);     // move.l   psp_base,a7
                    437:    Write4(psp_base);
                    438:    Write2(0x207c);     // move.l   psp_base,a0
                    439:    Write4(psp_base);
                    440:    Write2(0x227c);     // move.l   last_addr,a1
                    441:    Write4(last_addr);
                    442:    Write2(0x247c);     // move.l   #$c000,a2
                    443:    Write4(0xc000);
                    444:    Write2(0x267c);     // move.l   #$e000,a3
                    445:    Write4(0xe000);
                    446:    Write2(0x287c);     // move.l   exec_addr,a4
                    447:    Write4(exec_addr);
                    448:    Write2(0x2c49);     // move.l   a1,a6
                    449:    Write2(0x4eb9);     // jsr.l    exec_addr
                    450:    Write4(exec_addr);
                    451:    ret_addr = addr;
                    452:    Write2(0x2e79);     // movea.l  sp_backup,a7    // スタックを戻して
                    453:    Write4(sp_backup);
                    454:    Write2(0x4e75);     // rts
                    455: 
                    456:    // READY 通知用パイプ。
                    457:    if (pipe2(fd, O_NOSIGPIPE) < 0) {
                    458:        err(1, "pipe");
                    459:    }
                    460: 
                    461:    pid = fork();
                    462:    if (pid < 0) {
                    463:        err(1, "fork");
                    464:    }
                    465:    if (pid == 0) {
                    466:        /* child */
                    467: 
                    468:        // 親の準備が出来るのを待つ。
                    469:        char dummy[1];
                    470:        if (read(fd[0], dummy, 1) < 0) {
                    471:            err(1, "child: read failed");
                    472:        }
                    473: 
                    474:        func_t p = (func_t)boot_addr;
                    475:        r = p();
                    476:        exit(r);
                    477:    } else {
                    478:        /* parent */
                    479:        int status;
                    480:        uint32 nextpc;
                    481: 
                    482:        DEBUG(1, "child pid=%u", pid);
                    483: 
                    484:        if (ptrace(PT_ATTACH, pid, NULL, 0) < 0) {
                    485:            err(1, "ptrace(PT_ATTACH)");
                    486:        }
                    487: 
                    488:        // nextpc==1 は STOP した直後から再開、の意。
                    489:        nextpc = 1;
                    490: 
                    491:        for (;;) {
                    492:            r = wait(&status);
                    493:            if (opt_debug >= 2) {
                    494:                dumpwait(status);
                    495:            }
                    496:            if (WIFEXITED(status)) {
                    497:                break;
                    498:            }
                    499: 
                    500:            if (WIFSTOPPED(status)) {
                    501:                struct reg regbuf;
                    502:                struct reg *reg = &regbuf;
                    503: 
                    504:                if (ptrace(PT_GETREGS, pid, reg, 0) < 0) {
                    505:                    err(1, "ptrace(PT_GETREGS)");
                    506:                }
                    507: 
                    508:                int signo = WSTOPSIG(status);
                    509: 
                    510:                // PT_ATTACH が効くと SIGSTOP が来る。
                    511:                if (signo == SIGSTOP) {
                    512:                    // 準備が出来たので通知 (同期)。
                    513:                    write(fd[1], "", 1);
                    514:                    DEBUG(1, "STOP -> Continue");
                    515:                } else if (signo == SIGILL || signo == SIGSEGV) {
                    516:                    // 特権違反命令や未実装命令は SIGILL、
                    517:                    // メモリアクセス違反は SIGSEGV が飛んでくるが、
                    518:                    // どちらも命令コードを調べて再実行するところは同じ。
                    519:                    uint32 inst32;
                    520:                    errno = 0;
                    521:                    inst32 = ptrace(PT_READ_I, pid, (void *)RegPC, 0);
                    522:                    if (errno != 0) {
                    523:                        err(1, "ptrace(PT_READ_I)");
                    524:                    }
                    525:                    if (opt_debug >= 2) {
                    526:                        dumpreg(reg);
                    527:                    }
                    528:                    DEBUG(2, "pc=%08x inst32=%08x", RegPC, inst32);
                    529:                    uint16 inst = inst32 >> 16;
                    530: 
                    531:                    if ((inst & 0xff00) == 0xff00) {
                    532:                        r = doscall(pid, (inst & 0xff), reg);
                    533:                        if (r == 1) {
                    534:                            nextpc = ret_addr;
                    535:                            goto next;
                    536:                        }
                    537:                        nextpc = RegPC + 2;
                    538:                    } else {
                    539:                        nextpc = exec_op(pid, reg, signo, inst);
                    540:                        if (nextpc == (uint32)-1) {
                    541:                            break;
                    542:                        }
                    543:                    }
                    544: 
                    545:                } else if (signo == SIGTRAP || signo == SIGFPE) {
                    546:                    // TRAP15 は SIGTRAP、CHK* 命令やゼロ除算は SIGFPE、と
                    547:                    // 本来別のシグナルが飛んで来るが
                    548:                    // どちらも同じ形式で処理できるのでまとめてある。
                    549:                    struct ptrace_siginfo psi;
                    550:                    if (ptrace(PT_GET_SIGINFO, pid, &psi, sizeof(psi)) < 0) {
                    551:                        err(1, "ptrace(PT_GET_SIGINFO)");
                    552:                    }
                    553:                    if (signo == SIGTRAP
                    554:                     && psi.psi_siginfo.si_code == TRAP_CHLD) {
                    555:                        // 子プロセス起動時にこちらが来る場合がある。
                    556:                        // see kern_sig.c eventswitch()
                    557:                        DEBUG(1, "TRAP_CHLD");
                    558:                        goto next;
                    559:                    }
                    560:                    switch (psi.psi_siginfo.si_trap) {
                    561:                     case T_ZERODIV:
                    562:                        TRACE(RegPC, "previous op issued T_ZERODIV");
                    563:                        nextpc = exception_format2(reg, 5);
                    564:                        break;
                    565:                     case T_CHKINST:
                    566:                        TRACE(RegPC, "previous op issued T_CHKINST");
                    567:                        nextpc = exception_format2(reg, 6);
                    568:                        break;
                    569:                     case T_TRAPVINST:
                    570:                        TRACE(RegPC, "previous op issued T_TRAPVINST");
                    571:                        nextpc = exception_format2(reg, 7);
                    572:                        break;
                    573:                     case T_TRAP15:
                    574:                        iocscall(pid, reg);
                    575:                        // nextpc は更新されている。
                    576:                        break;
                    577:                     default:
                    578:                        errx(1, "%06x: Not implemented si_trap %d",
                    579:                            RegPC, psi.psi_siginfo.si_trap);
                    580:                    }
                    581: 
                    582:                } else {
                    583:                    errx(1, "%06x: Unknown signal %d", RegPC, signo);
                    584:                }
                    585: 
                    586:  next:
                    587:                if (ptrace(PT_SETREGS, pid, reg, 0) < 0) {
                    588:                    err(1, "ptrace(PT_SETREGS)");
                    589:                }
                    590: 
                    591:                // 実行を再開し、次に何か起きるまで待つ。
                    592:                if (ptrace(PT_CONTINUE, pid, (void *)nextpc, 0) < 0) {
                    593:                    err(1, "PT_CONTINUE");
                    594:                }
                    595:                nextpc = 1;
                    596:            }
                    597:        }
                    598:    }
                    599: 
                    600:    kill(pid, SIGKILL);
                    601:    return 0;
                    602: }

unix.superglobalmegacorp.com

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