diff options
| -rw-r--r-- | .gitignore | 15 | ||||
| -rw-r--r-- | NOTICE | 22 | ||||
| -rw-r--r-- | PATCHES.md | 58 | ||||
| -rw-r--r-- | README.md | 75 | ||||
| -rwxr-xr-x | bin/5c | bin | 0 -> 1006072 bytes | |||
| -rwxr-xr-x | bin/6a | bin | 0 -> 408832 bytes | |||
| -rwxr-xr-x | bin/6c | bin | 0 -> 1094136 bytes | |||
| -rwxr-xr-x | bin/6l | bin | 0 -> 546056 bytes | |||
| -rwxr-xr-x | bin/8c | bin | 0 -> 1135776 bytes | |||
| -rwxr-xr-x | bin/kc | bin | 0 -> 979568 bytes | |||
| -rwxr-xr-x | bin/qc | bin | 0 -> 1053832 bytes | |||
| -rwxr-xr-x | bin/vc | bin | 0 -> 981056 bytes | |||
| -rw-r--r-- | efi/README.md | 35 | ||||
| -rw-r--r-- | efi/efi_build.sh | 32 | ||||
| -rw-r--r-- | efi/fetch_ref.sh | 24 | ||||
| -rw-r--r-- | efi/hello.c | 52 | ||||
| -rw-r--r-- | efi/mkesp.py | 108 | ||||
| -rw-r--r-- | scripts/build_al.sh | 57 | ||||
| -rw-r--r-- | scripts/build_all.sh | 19 | ||||
| -rw-r--r-- | scripts/build_arch.sh | 68 | ||||
| -rw-r--r-- | scripts/setup.sh | 30 |
21 files changed, 595 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65f10ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# зависимости, которые тянутся скриптами +deps/ +efi/nboot.diff +efi/pe64.s +efi/pe64min.s +efi/efi.h +efi/mem.h +# артефакты сборки +build/ +*.o +*.6 *.5 *.8 *.v *.q *.k +*.out +efi/bootx64.efi +efi/*.img +y.tab.* @@ -0,0 +1,22 @@ +plan9-kencc-linux — авторство и лицензии +======================================== + +Наш код (сборочные скрипты, efi/hello.c, efi/efi_build.sh, efi/mkesp.py, +efi/fetch_ref.sh, документация) — свободно используйте. + +Собирается из стороннего кода, который НЕ входит в репозиторий, а тянется +скриптами: + +* Компиляторы/ассемблеры/линкеры Plan 9 (kencc) и системные заголовки — + из дерева исходников Plan 9 (plan9foundation / совместимого). + Оригинальные лицензии Plan 9 (MIT-style, ранее Lucent Public License). + +* Тулчейн-обвязка lib9/libbio/libmach и 9c/9l/9ar/yacc — plan9port + (https://github.com/9fans/plan9port), лицензии plan9port. + +* Референсный EFI-загрузчик (pe64.s, efi.h, mem.h) — 9legacy, nboot/efi + (http://9legacy.org/9legacy/patch/nboot.diff). Тянется efi/fetch_ref.sh, + в репозитории НЕ хранится. Лицензия — как у соответствующего кода Plan 9. + +Готовые бинари в bin/ собраны из вышеуказанных исходников нашими скриптами +и наследуют их лицензии. diff --git a/PATCHES.md b/PATCHES.md new file mode 100644 index 0000000..f8b20aa --- /dev/null +++ b/PATCHES.md @@ -0,0 +1,58 @@ +# Патчи под сборку kencc на Linux-хосте + +Все правки автоматически накладываются `scripts/build_arch.sh` и `build_al.sh` +на свежие копии исходников (оригинальное дерево не трогается). Ниже — что и зачем. + +## 1. `round` / `log2` → `p9round` / `p9log2` +`cc/` определяет свои `round()` и `log2()`, конфликтующие по типу с прототипами +из musl `math.h`. Переименовываем определения и вызовы во всех файлах (и в +сгенерённом `y.tab.c`). Файлы: `cc/dcl.c`, `cc/pgen.c`, `cc/sub.c`, `cc/cc.h`, +`Xc/swt.c`, `Xc/cgen.c`. + +## 2. Порядок и единичность `#include "y.tab.h"` +`y.tab.h` (генерит yacc) использует тип `Node`, объявленный в `cc.h`, и не имеет +include-guard. Значит: включать строго ПОСЛЕ `cc.h` и ровно один раз (иначе +конфликт переопределения `YYSTYPE`/`yylval`). В свежем `lex.c` порядок уже +верный — правка нужна была только при ручной возне. + +## 3. Плоская раскладка include +Исходники ссылаются на соседние каталоги: `#include "../cc/cc.h"`, +`"../6c/6.out.h"`, `"../8l/elf.h"`, extension-less `"compat"`, `"macbody"`, +`"lexbody"`. Складываем всё в один каталог сборки и срезаем `../*/` из include. +Extension-less файлы копируем явно. + +## 4. `Runemask` +Плановская libc-константа `Runemask` (маска 21-битной руны) есть в родной Plan9, +но НЕ в plan9port. Добавляем `#define Runemask 0x1FFFFF` в `lex.c`. + +## 5. Прототип `swcmp` → `const` +`swcmp` — компаратор для `qsort`. У musl сигнатура `int(const void*, const void*)`, +а плановский прототип в `gc.h` — `int(void*, void*)`. Приводим прототип к `const`. + +## 6. `-fcommon` +Старый код держит tentative-дефиниции глобалов (напр. `hintabsize` в нескольких +`.c` через общий `gc.h`). Современный gcc по умолчанию `-fno-common` и ругается на +multiple definition. Компилируем с `-fcommon` (как раньше). + +## 7. `mysbrk`: `sbrk()` → `mmap()` ← ключевой +Hunk-аллокатор kencc берёт память через `mysbrk` → `sbrk()`. На musl `sbrk()` +возвращает `-1` (не поддерживается совместно с malloc), из-за чего `gethunk()` +падал в «out of memory» → `exits`, причём диагностика уходила в неинициализированный +Bio-буфер и терялась (компилятор молча выходил с кодом 1). Заменяем тело `mysbrk` +на анонимный `mmap`. Без этого патча компиляторы линкуются, но не компилируют. + +## 8. Дубликат `machcap` / `omachcap` +`cc/omachcap.c` (старый machcap) и `Xc/machcap.c` (новый) оба определяют `machcap`. +В родной сборке `omachcap.o` дремлет в архиве `cc.a` и не тянется. У нас — линкуем +плоско, поэтому: если у арки есть свой `machcap.o` (6c/8c/qc) — выкидываем +`omachcap.o`; если нет (5c/vc/kc) — `omachcap.o` и предоставляет `machcap`. + +## 9. Плановские рантайм-файлы вон +Файлы вроде `Xc/sys.c` тянут `#include "/sys/src/libc/9syscall/sys.h"` — это +рантайм ОС, не часть компилятора, и в `OFILES` mkfile их нет. Выкидываем любые +`.c` с include по абсолютному `/sys/...`. + +## 10. Авторитетность `cc/` при конфликте имён (kc) +У `kc` завалялся собственный устаревший `bits.c`, использующий уже вырезанный из +plan9port форматный тип `Fconv`. Копируем `cc/`-файлы ПОСЛЕ арочных, чтобы общий +`cc/bits.c` (новый fmt API) побеждал. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba80bb4 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# plan9-kencc-linux + +Кросс-сборка **компиляторов / ассемблеров / линкеров Plan 9 (kencc)** для всех +архитектур **с Linux-хоста**, плюс болваночное **UEFI-приложение** (PE32+), +собранное этим же тулчейном. Ничего юниксового в цепочке сборки самих `.efi`. + +Собрано и проверено на Linux/musl. Пилилось телеграм-ботом (Claude) по заказу +чата ancap.in.ua. + +## Что внутри +``` +bin/ готовые бинари: 8c 6c 5c vc qc kc (компиляторы) + 6a 6l +scripts/ + setup.sh клонирует plan9foundation + plan9port, собирает plan9port + build_arch.sh <X> собирает C-компилятор арки X (5|6|8|v|q|k) + build_al.sh <Xa|Xl> собирает ассемблер/линкер + build_all.sh собирает весь кросс-кит +efi/ + hello.c сам EFI-апп (наш код) + fetch_ref.sh тянет pe64.s/efi.h/mem.h из 9legacy (nboot/efi) + efi_build.sh 6a+6c+6l -> dd -> bootx64.efi (PE32+) + mkesp.py заворачивает .efi в загрузочный ESP (FAT16) +patches/ патчи по каждой правке (документация) +PATCHES.md почему каждый патч нужен +``` + +## Идея +Фронтенд-компилятора Plan 9 (`cc/`) + кодогенераторы арок (`5c`,`6c`,`8c`,`vc`,`qc`,`kc`) +пишутся на Plan 9 C и рассчитаны на родную сборку в Plan 9. Мы собираем их на Linux, +компилируя **plan9port**-овским `9c`/`9l` (они дают `lib9`/`libbio`/`libmach` и +дружат с хостовым gcc/musl). Всё, что мешает, лечится небольшими патчами (см. `PATCHES.md`). + +| арка | комп | ассемблер | линкер | +|------|------|-----------|--------| +| 386 | 8c | 8a | 8l | +| amd64 | 6c | 6a | 6l | +| arm | 5c | 5a | 5l | +| mips | vc | va | vl | +| power | qc | qa | ql | +| sparc | kc | ka | kl | + +## Быстрый старт +```sh +# 1. зависимости (plan9foundation + plan9port) +bash scripts/setup.sh +export PLAN9SRC=$PWD/deps/plan9foundation +export PLAN9PORT=$PWD/deps/plan9port +# (опц. musl: export MUSL_ENV=/path/to/env.sh) + +# 2. собрать всё +bash scripts/build_all.sh # -> build/*/{6c,6a,6l,...} + +# 3. один компилятор +bash scripts/build_arch.sh 6 # -> build/6c/6c + +# 4. проверить +echo 'int f(void){return 42;}' > t.c +build/6c/6c t.c && ls t.6 # объектник Plan9 .6 +``` + +## EFI-болванка +```sh +cd efi +./fetch_ref.sh # pe64.s/efi.h/mem.h из 9legacy +PLAN9SRC=$PWD/../deps/plan9foundation ./efi_build.sh +# -> bootx64.efi : PE32+ x64 EFI_APPLICATION +python3 mkesp.py bootx64.efi esp.img # -> загрузочный ESP (/EFI/BOOT/BOOTX64.EFI) +# запуск: qemu-system-x86_64 -bios OVMF.fd -drive format=raw,file=esp.img +``` + +## Готовые бинари +В `bin/` лежат уже собранные компиляторы/асм/линкер (Linux/musl x86_64). +Под свой хост пересобери скриптами — байт-в-байт воспроизводимо. + +Лицензии и авторство исходников — см. `NOTICE`. Binary files differBinary files differBinary files differBinary files differBinary files differBinary files differBinary files differBinary files differdiff --git a/efi/README.md b/efi/README.md new file mode 100644 index 0000000..fa1cdd1 --- /dev/null +++ b/efi/README.md @@ -0,0 +1,35 @@ +# efi — болваночный UEFI-апп на портированном тулчейне Plan 9 + +Минимальное `bootx64.efi` (amd64, формат **PE32+**), собранное **только** +портированными Plan 9 kencc `6a`/`6c`/`6l` — без юниксовых компиляторов/линкеров. + +## Цепочка +| шаг | утилита | что делает | +|-----|---------|-----------| +| 1 | **6a** | ассемблирует `pe64min.s` — DOS/PE32+ заголовок + entry + `eficall` | +| 2 | **6c** | компилирует `hello.c` (плановские `<u.h>` и т.д.) | +| 3 | **6l** | линкует свободностоящий образ по `-T0x8000` | +| 4 | `dd skip=40` | срезает 40-байтный plan9 a.out заголовок → обнажает PE32+ | + +`pe64.s` кладёт DOS+PE32+ заголовки первыми в текст-сегмент; линкер раскладывает +образ от `IMAGEBASE`, а `dd` срезает плановский заголовок — остаётся чистый PE, +который прошивка грузит из `/EFI/BOOT/BOOTX64.EFI`. + +## Сборка +```sh +./fetch_ref.sh # pe64.s/efi.h/mem.h из 9legacy +PLAN9SRC=.../plan9foundation ./efi_build.sh # -> bootx64.efi +python3 mkesp.py bootx64.efi esp.img # -> загрузочный ESP (FAT16) +qemu-system-x86_64 -bios OVMF.fd -drive format=raw,file=esp.img +``` + +## Что своё, что референс +* `hello.c`, `efi_build.sh`, `mkesp.py`, `fetch_ref.sh` — наш код. +* `pe64.s`, `efi.h`, `mem.h` — из 9legacy `nboot/efi` (тянутся, не хранятся). + Для болванки от `pe64.s` берём только заголовки+entry+`eficall` (`head -162`); + машинерию загрузки ядра (`jump`/`_warp32`/`_gdt`) отбрасываем — ей тут нечего делать. + +## Проверка +`MZ` + `PE\0\0` + Machine `0x8664` + Magic `0x20B` (PE32+) + Subsystem `10` +(EFI_APPLICATION). `mkesp.py` дополнительно сам обходит FAT обратно и сверяет +`/EFI/BOOT/BOOTX64.EFI` байт-в-байт с исходным `.efi`. diff --git a/efi/efi_build.sh b/efi/efi_build.sh new file mode 100644 index 0000000..51f0ff9 --- /dev/null +++ b/efi/efi_build.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Собирает болваночный bootx64.efi (PE32+) ПОРТИРОВАННЫМ тулчейном 6a/6c/6l. +# Предварительно: ./fetch_ref.sh (достаёт pe64.s/efi.h/mem.h из 9legacy). +# Требует: BINDIR с 6a/6c/6l (по умолч. ../bin) и PLAN9SRC (плановские include). +set -e +cd "$(dirname "$0")" +BINDIR="${BINDIR:-$PWD/../bin}" +: "${PLAN9SRC:?export PLAN9SRC — нужны плановские include (amd64/include, sys/include)}" +IMAGEBASE=0x8000 +export objtype=amd64 + +[ -f pe64.s ] || { echo "нет pe64.s — сначала ./fetch_ref.sh"; exit 1; } +# минимальный pe64: MZ+PE заголовки + entry + eficall (без загрузчика ядра) +head -162 pe64.s > pe64min.s + +rm -f *.6 *.out bootx64.efi +echo "1) 6a pe64min.s"; "$BINDIR/6a" -DIMAGEBASE=$IMAGEBASE pe64min.s +echo "2) 6c hello.c"; "$BINDIR/6c" -I"$PLAN9SRC/amd64/include" -I"$PLAN9SRC/sys/include" hello.c +echo "3) 6l link"; "$BINDIR/6l" -l -s -R1 -T$IMAGEBASE -o bootx64.out pe64min.6 hello.6 +echo "4) dd strip 40b (plan9 a.out header) -> PE32+" +dd if=bootx64.out bs=1 skip=40 of=bootx64.efi 2>/dev/null + +echo "bootx64.efi: $(ls -la bootx64.efi | awk '{print $5}') байт" +python3 - <<'PY' +d=open('bootx64.efi','rb').read(); po=int.from_bytes(d[0x3c:0x40],'little'); o=po+24 +ok = d[:2]==b'MZ' and d[po:po+4]==b'PE\x00\x00' \ + and int.from_bytes(d[po+4:po+6],'little')==0x8664 \ + and int.from_bytes(d[o:o+2],'little')==0x20b \ + and int.from_bytes(d[o+68:o+70],'little')==10 +print("PE32+ x64 EFI_APPLICATION:", "OK" if ok else "BAD") +PY +echo "ESP-образ: python3 mkesp.py bootx64.efi esp.img" diff --git a/efi/fetch_ref.sh b/efi/fetch_ref.sh new file mode 100644 index 0000000..67b726b --- /dev/null +++ b/efi/fetch_ref.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Тянет из 9legacy референсный EFI-загрузчик (nboot/efi) и достаёт из него +# pe64.s (PE32+ обёртка), efi.h и mem.h. Это НЕ наш код — это Plan9/9legacy, +# поэтому качаем, а не храним в репе. Наш код — hello.c/efi_build.sh/mkesp.py. +set -e +cd "$(dirname "$0")" +echo ">> качаю 9legacy nboot.diff ..." +python3 -c "import urllib.request; urllib.request.urlretrieve('http://9legacy.org/9legacy/patch/nboot.diff','nboot.diff')" +python3 - <<'PY' +import re +d=open('nboot.diff').read().splitlines() +want={'pe64.s','efi.h','mem.h'}; cur=None; buf=[]; out={} +for l in d: + m=re.match(r'^\+\+\+ .*/nboot/efi/(\S+)', l) + if m: + if cur: out[cur]='\n'.join(buf) + cur=m.group(1) if m.group(1) in want else None; buf=[]; continue + if cur is not None: + if l.startswith('--- '): out[cur]='\n'.join(buf); cur=None; continue + if l.startswith('+'): buf.append(l[1:]) +if cur: out[cur]='\n'.join(buf) +for k,v in sorted(out.items()): open(k,'w').write(v); print(' извлёк', k, f'({len(v.splitlines())} строк)') +PY +echo ">> готово: pe64.s efi.h mem.h (референс 9legacy)" diff --git a/efi/hello.c b/efi/hello.c new file mode 100644 index 0000000..35cd86d --- /dev/null +++ b/efi/hello.c @@ -0,0 +1,52 @@ +/* + * Болваночный EFI-апп для amd64 (bootx64.efi), PE32+. + * Собирается ПОРТИРОВАННЫМ тулчейном Plan9 (6c/6a/6l) на Linux/musl: + * PE-заголовок и entry -> pe64.s (ассемблер 6a), из 9legacy nboot/efi. + * Печать -> UEFI SIMPLE_TEXT_OUTPUT_PROTOCOL (efi.h из 9legacy). + * Контракт входа задаёт pe64.s: reloc кладёт SystemTable, прыгает на efimain(ih, st). + */ +#include <u.h> +#include "efi.h" + +/* глобалы, которых ждёт контракт (как в референсном efi.c) */ +EFI_HANDLE IH; +EFI_SYSTEM_TABLE *ST; + +/* вызов EFI-функции по MS-x64 ABI — реализован в pe64.s */ +extern uintptr eficall(void *fn, ...); + +/* печать ASCII-строки через ConOut (конверт в UTF-16, \n -> \r\n) */ +static void +eputs(char *s) +{ + CHAR16 w[2]; + + w[1] = 0; + for(; *s; s++){ + if(*s == '\n'){ + w[0] = '\r'; + eficall(ST->ConOut->OutputString, ST->ConOut, w); + } + w[0] = (uchar)*s; + eficall(ST->ConOut->OutputString, ST->ConOut, w); + } +} + +void +efimain(EFI_HANDLE ih, EFI_SYSTEM_TABLE *st) +{ + IH = ih; + ST = st; + + eputs("\n"); + eputs("+--------------------------------------------------+\n"); + eputs("| Hello from a Plan 9 kencc EFI application |\n"); + eputs("| compiler/assembler/linker: 6c / 6a / 6l |\n"); + eputs("| cross-built on Linux/musl, wrapped as PE32+ |\n"); + eputs("+--------------------------------------------------+\n"); + eputs("\nefimain reached OK. Idling via BootServices->Stall.\n"); + + /* болванка: просто крутимся, чтобы прошивка не считала нас упавшими */ + for(;;) + eficall(ST->BootServices->Stall, (UINTN)1000000); +} diff --git a/efi/mkesp.py b/efi/mkesp.py new file mode 100644 index 0000000..d9ab581 --- /dev/null +++ b/efi/mkesp.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# Собирает загрузочный ESP-образ (FAT16) c /EFI/BOOT/BOOTX64.EFI. +# Без mtools/mkfs — вручную по спецификации FAT, плюс самопроверка (обход обратно). +import struct, sys, os, math + +EFI = sys.argv[1] if len(sys.argv)>1 else "/data/work/chat_-1002129270612/plan9/build/efi/bootx64.efi" +OUT = sys.argv[2] if len(sys.argv)>2 else "/data/work/chat_-1002129270612/plan9/build/efi/esp.img" +data = open(EFI,"rb").read() + +BPS=512; SPC=1; RSVD=1; NFAT=2; ROOTENT=512; TOT=8192 +rootsecs=(ROOTENT*32+BPS-1)//BPS +tmp1=TOT-(RSVD+rootsecs); tmp2=256*SPC+NFAT +FATSZ=(tmp1+tmp2-1)//tmp2 +first_data=RSVD+NFAT*FATSZ+rootsecs +nclus=(TOT-first_data)//SPC +assert nclus>4085, f"стало бы FAT12 ({nclus} кластеров)" + +img=bytearray(TOT*BPS) +# --- boot sector / BPB --- +bs=img +bs[0:3]=b'\xEB\x3C\x90'; bs[3:11]=b'MSWIN4.1' +struct.pack_into('<H',bs,11,BPS); bs[13]=SPC +struct.pack_into('<H',bs,14,RSVD); bs[16]=NFAT +struct.pack_into('<H',bs,17,ROOTENT); struct.pack_into('<H',bs,19,TOT) +bs[21]=0xF8; struct.pack_into('<H',bs,22,FATSZ) +struct.pack_into('<H',bs,24,32); struct.pack_into('<H',bs,26,8) +bs[36]=0x80; bs[38]=0x29; struct.pack_into('<I',bs,39,0x12345678) +bs[43:54]=b'PLAN9 EFI '; bs[54:62]=b'FAT16 ' +bs[510]=0x55; bs[511]=0xAA + +# --- распределение кластеров --- +def nclus_for(n): return max(1,(n+BPS*SPC-1)//(BPS*SPC)) +# EFI(dir)=cl2, BOOT(dir)=cl3, файл=cl4.. +efi_cl=2; boot_cl=3; file_cl=4 +file_ncl=nclus_for(len(data)) +chain={efi_cl:[0xFFFF], boot_cl:[0xFFFF]} +file_clusters=list(range(file_cl,file_cl+file_ncl)) + +FAT=[0]*(nclus+2) +FAT[0]=0xFFF8; FAT[1]=0xFFFF +FAT[efi_cl]=0xFFFF; FAT[boot_cl]=0xFFFF +for i,c in enumerate(file_clusters): + FAT[c]=0xFFFF if i==len(file_clusters)-1 else file_clusters[i+1] + +def wfat(): + raw=bytearray(FATSZ*BPS) + for i,v in enumerate(FAT): + if i*2+2<=len(raw): struct.pack_into('<H',raw,i*2,v&0xFFFF) + for k in range(NFAT): + off=(RSVD+k*FATSZ)*BPS; img[off:off+len(raw)]=raw +wfat() + +def dirent(name,ext,attr,clus,size): + e=bytearray(32) + e[0:11]=(name.ljust(8)[:8]+ext.ljust(3)[:3]).encode('ascii').upper() + e[11]=attr + struct.pack_into('<H',e,26,clus&0xFFFF) + struct.pack_into('<I',e,28,size) + return e + +def clus_off(c): return (first_data+(c-2)*SPC)*BPS + +# root dir: EFI (dir -> cl2) +root_off=(RSVD+NFAT*FATSZ)*BPS +img[root_off:root_off+32]=dirent("EFI","",0x10,efi_cl,0) +# EFI dir (cl2): '.', '..', BOOT(dir->cl3) +o=clus_off(efi_cl) +img[o:o+32]=dirent(".","",0x10,efi_cl,0) +img[o+32:o+64]=dirent("..","",0x10,0,0) +img[o+64:o+96]=dirent("BOOT","",0x10,boot_cl,0) +# BOOT dir (cl3): '.', '..', BOOTX64.EFI(file->cl4) +o=clus_off(boot_cl) +img[o:o+32]=dirent(".","",0x10,boot_cl,0) +img[o+32:o+64]=dirent("..","",0x10,efi_cl,0) +img[o+64:o+96]=dirent("BOOTX64","EFI",0x20,file_cl,len(data)) +# данные файла +for i,c in enumerate(file_clusters): + chunk=data[i*BPS*SPC:(i+1)*BPS*SPC] + off=clus_off(c); img[off:off+len(chunk)]=chunk + +open(OUT,"wb").write(img) + +# --- САМОПРОВЕРКА: обойти FAT и достать /EFI/BOOT/BOOTX64.EFI --- +im=open(OUT,"rb").read() +def rd(a,n): return im[a:a+n] +def fat(c): return struct.unpack_from('<H',im,(RSVD*BPS)+c*2)[0] +def readdir(off): + ents={} + for i in range(0,BPS,32): + e=im[off+i:off+i+32] + if e[0] in (0,0xE5): continue + nm=e[0:8].decode().strip(); ex=e[8:11].decode().strip() + cl=struct.unpack_from('<H',e,26)[0]; sz=struct.unpack_from('<I',e,28)[0] + key=nm+('.'+ex if ex else '') + ents[key]=(cl,sz,e[11]) + return ents +root=readdir((RSVD+NFAT*FATSZ)*BPS) +efi=readdir(clus_off(root['EFI'][0])) +boot=readdir(clus_off(efi['BOOT'][0])) +cl,sz,_=boot['BOOTX64.EFI'] +out=bytearray() +while cl<0xFFF8: + out+=im[clus_off(cl):clus_off(cl)+BPS*SPC]; cl=fat(cl) +out=out[:sz] +ok = bytes(out)==data +print(f"ESP: {OUT} {len(im)} байт (FAT16, {nclus} кластеров, FATSz={FATSZ})") +print(f"путь /EFI/BOOT/BOOTX64.EFI: {sz} байт") +print("самопроверка (обход FAT == оригинал):", "OK" if ok else "FAIL") diff --git a/scripts/build_al.sh b/scripts/build_al.sh new file mode 100644 index 0000000..750f9e0 --- /dev/null +++ b/scripts/build_al.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Собирает Plan9 ассемблер (Xa) или линкер (Xl) на Linux-хосте. +# Использование: PLAN9SRC=... PLAN9PORT=... build_al.sh 6a | build_al.sh 6l +set -e +T="$1"; [ -z "$T" ] && { echo "usage: $0 <напр. 6a|6l|8a|8l>"; exit 1; } +X="${T:0:1}"; K="${T:1:1}" + +: "${PLAN9SRC:?укажи PLAN9SRC}"; : "${PLAN9PORT:?укажи PLAN9PORT}" +[ -n "$MUSL_ENV" ] && . "$MUSL_ENV" +export PLAN9="$PLAN9PORT"; export PATH="$PLAN9/bin:$PATH" +SRC="$PLAN9SRC/sys/src/cmd" +OUT="${OUT:-$PWD/build}"; B="$OUT/$T" + +rm -rf "$B"; mkdir -p "$B"; cd "$B" +cp "$SRC/$T"/* "$B"/ 2>/dev/null || true +cp "$SRC/${X}c"/*.h "$B"/ 2>/dev/null || true # X.out.h +cp "$SRC"/cc/compat "$SRC"/cc/macbody "$SRC"/cc/lexbody "$SRC"/cc/cc.h "$B"/ 2>/dev/null || true +if [ "$K" = "l" ]; then # линкеру: имена опкодов + общий ELF-вывод + cp "$SRC/${X}c"/enam.c "$B"/ 2>/dev/null || true + cp "$SRC"/8l/elf.c "$SRC"/8l/elf.h "$B"/ 2>/dev/null || true +fi + +# ---- патчи (те же, что и для компиляторов) ---- +for c in *.c; do grep -qE '#include[ \t]*[<"]/sys/' "$c" 2>/dev/null && { echo " пропуск: $c"; rm -f "$c"; }; done +sed -i 's|"\.\./[a-zA-Z0-9]*/|"|g' *.c *.h 2>/dev/null || true +sed -i -E 's/\bround\b/p9round/g; s/\blog2\b/p9log2/g' *.c *.h compat macbody lexbody 2>/dev/null || true +python3 - <<'PY' +import glob,re +pat=re.compile(r'\{\s*return\s+sbrk\s*\(\s*(\w+)\s*\)\s*;\s*\}') +mm='''{ +\tvoid *p; +\tp = mmap(0, %s, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +\tif(p == MAP_FAILED) return (void*)-1; +\treturn p; +}''' +for f in glob.glob('*')+['compat']: + try: s=open(f).read() + except: continue + if 'sbrk(' in s and 'mmap' not in s: + m=pat.search(s) + if m: + s2=pat.sub(mm%m.group(1), s, count=1) + if '#include <sys/mman.h>' not in s2: s2='#include <sys/mman.h>\n'+s2 + open(f,'w').write(s2); print(" mmap-патч:",f) +PY +[ -f a.y ] && { yacc -d a.y; sed -i -E 's/\bround\b/p9round/g; s/\blog2\b/p9log2/g' y.tab.c 2>/dev/null || true; } + +ok=0; fail=0; errf=""; rm -f *.o +for f in *.c; do + if 9c -fcommon -I. "$f" >/tmp/al_$$ 2>&1; then ok=$((ok+1)); else fail=$((fail+1)); [ -z "$errf" ] && errf="$f" && cp /tmp/al_$$ /tmp/alf_$$; fi +done +echo "[$T] компиляция: OK=$ok FAIL=$fail" +[ "$fail" != 0 ] && { echo "первая ошибка ($errf):"; head -10 /tmp/alf_$$; exit 2; } + +if [ "$K" = "l" ]; then EXTRA="-lmach"; else EXTRA=""; fi # линкеру нужен libmach +9l -o "$T" *.o $EXTRA +echo "[$T] бинарь: $(ls -la $T | awk '{print $5}') байт -> $B/$T" diff --git a/scripts/build_all.sh b/scripts/build_all.sh new file mode 100644 index 0000000..7f058aa --- /dev/null +++ b/scripts/build_all.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Собирает ВЕСЬ кросс-кит: C-компиляторы + ассемблеры + линкеры всех арок. +# Требует экспортированных PLAN9SRC и PLAN9PORT (см. setup.sh). +set -e +here="$(cd "$(dirname "$0")" && pwd)" +: "${PLAN9SRC:?export PLAN9SRC (см. scripts/setup.sh)}" +: "${PLAN9PORT:?export PLAN9PORT (см. scripts/setup.sh)}" +export OUT="${OUT:-$PWD/build}" + +echo "=== C-компиляторы ===" +for X in 5 6 8 v q k; do bash "$here/build_arch.sh" $X; done +echo "=== ассемблеры ===" +for X in 5 6 8 v q k; do bash "$here/build_al.sh" ${X}a || echo " (${X}a — пропущен/ошибка)"; done +echo "=== линкеры ===" +for X in 5 6 8 v q k; do bash "$here/build_al.sh" ${X}l || echo " (${X}l — пропущен/ошибка)"; done + +echo +echo "=== собранные бинари ($OUT) ===" +find "$OUT" -maxdepth 2 -type f -perm -u+x \( -name '[0-9kvq][cal]' \) -printf '%f %s байт\n' 2>/dev/null | sort diff --git a/scripts/build_arch.sh b/scripts/build_arch.sh new file mode 100644 index 0000000..3cdc4fb --- /dev/null +++ b/scripts/build_arch.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Собирает Plan9 kencc C-компилятор для арки X (5,6,8,v,q,k) на Linux-хосте. +# Использование: PLAN9SRC=.../plan9foundation PLAN9PORT=.../plan9port build_arch.sh 6 +# Опц.: MUSL_ENV=.../env.sh (активатор musl-тулчейна), OUT=каталог сборки. +set -e +X="$1"; [ -z "$X" ] && { echo "usage: $0 <arch: 5|6|8|v|q|k>"; exit 1; } +A="${X}c" + +: "${PLAN9SRC:?укажи PLAN9SRC — дерево исходников plan9 (plan9foundation)}" +: "${PLAN9PORT:?укажи PLAN9PORT — собранный plan9port (bin/9c,9l,9ar,yacc + lib/)}" +[ -n "$MUSL_ENV" ] && . "$MUSL_ENV" +export PLAN9="$PLAN9PORT"; export PATH="$PLAN9/bin:$PATH" +SRC="$PLAN9SRC/sys/src/cmd" +OUT="${OUT:-$PWD/build}" +B="$OUT/$A" + +rm -rf "$B"; mkdir -p "$B"; cd "$B" +# кодоген арки СНАЧАЛА, потом фронтенд cc/ — cc/ авторитетен при совпадении имён +# (у kc свой устаревший bits.c с Fconv — берём cc/bits.c) +cp "$SRC/$A"/*.c "$SRC/$A"/*.h "$B"/ +cp "$SRC"/cc/*.c "$SRC"/cc/*.h "$B"/ +cp "$SRC"/cc/compat "$SRC"/cc/macbody "$SRC"/cc/lexbody "$B"/ 2>/dev/null || true + +# ---- ПАТЧИ под линукс-хост ---- +# 0) выкинуть плановские рантайм-файлы (тянут /sys/... хедеры, не часть компилятора) +for c in *.c; do grep -qE '#include[ \t]*[<"]/sys/' "$c" 2>/dev/null && { echo " пропуск рантайм: $c"; rm -f "$c"; }; done +# 1) плоская раскладка: убрать относительные ../cc/ ../Xc/ +sed -i "s|\"\.\./cc/|\"|g; s|\"\.\./${A}/|\"|g" *.c *.h 2>/dev/null || true +# 2) round/log2 конфликтуют с musl math.h -> p9round/p9log2 +sed -i -E 's/\bround\b/p9round/g; s/\blog2\b/p9log2/g' *.c *.h compat macbody lexbody 2>/dev/null || true +# 3) Runemask нет в plan9port +grep -q 'define Runemask' lex.c || sed -i '/#include[ \t]*"y.tab.h"/a #ifndef Runemask\n#define Runemask 0x1FFFFF\n#endif' lex.c +# 3b) прототип swcmp -> const (musl qsort-компаратор) +sed -i 's/swcmp(void\*, *void\*)/swcmp(const void*, const void*)/' gc.h *.h 2>/dev/null || true +# 4) mysbrk: sbrk() на musl отдаёт -1 -> mmap +python3 - "$B/compat" <<'PY' +import sys +p=sys.argv[1]; s=open(p).read() +old='void*\nmysbrk(ulong size)\n{\n\treturn sbrk(size);\n}' +new='''#include <sys/mman.h> +void* +mysbrk(ulong size) +{ +\tvoid *p; +\tp = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +\tif(p == MAP_FAILED) return (void*)-1; +\treturn p; +}''' +if old in s: open(p,'w').write(s.replace(old,new)); print(" mmap-патч: ок") +else: print(" mmap-патч: паттерн не найден") +PY + +# ---- yacc: cc.y -> y.tab.c / y.tab.h ---- +yacc -d cc.y 2>/dev/null || yacc -d "$SRC/cc/cc.y" +sed -i -E 's/\bround\b/p9round/g; s/\blog2\b/p9log2/g' y.tab.c 2>/dev/null || true + +# ---- компиляция (-fcommon: tentative-глобалы под gcc -fno-common) ---- +ok=0; fail=0; errf=""; rm -f *.o +for f in *.c; do + if 9c -fcommon -I. "$f" >/tmp/ba_$$ 2>&1; then ok=$((ok+1)); else fail=$((fail+1)); [ -z "$errf" ] && errf="$f" && cp /tmp/ba_$$ /tmp/baf_$$; fi +done +echo "[$A] компиляция: OK=$ok FAIL=$fail" +[ "$fail" != 0 ] && { echo "первая ошибка ($errf):"; head -8 /tmp/baf_$$; exit 2; } + +# ---- линковка: свой machcap.o -> выкинуть omachcap.o; иначе omachcap и есть machcap ---- +if [ -f machcap.o ]; then LINK=$(ls *.o | grep -v '^omachcap.o$'); else LINK=$(ls *.o); fi +9l -o "$A" $LINK +echo "[$A] бинарь: $(ls -la $A | awk '{print $5}') байт -> $B/$A" diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100644 index 0000000..2d693f0 --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Готовит зависимости для сборки тулчейна: исходники plan9 + собранный plan9port. +# Требуется: git, gcc/clang, make, yacc (или plan9port-овский). Каталоги кладутся +# рядом со скриптом (или в $DEPS). После — экспортни PLAN9SRC/PLAN9PORT (печатает в конце). +set -e +DEPS="${DEPS:-$PWD/deps}" +mkdir -p "$DEPS"; cd "$DEPS" + +# 1) дерево исходников plan9 (plan9foundation — тут живут cc/ и Xc/Xa/Xl) +if [ ! -d plan9foundation ]; then + echo ">> клонирую plan9foundation ..." + git clone --depth 1 https://github.com/plan9foundation/plan9.git plan9foundation \ + || git clone --depth 1 https://git.9front.org/plan9front/plan9front plan9foundation +fi + +# 2) plan9port (даёт 9c/9l/9ar/yacc + lib9/libbio/libmach — ими и собираем kencc) +if [ ! -d plan9port ]; then + echo ">> клонирую plan9port ..." + git clone --depth 1 https://github.com/9fans/plan9port.git plan9port +fi +if [ ! -x plan9port/bin/9c ]; then + echo ">> собираю plan9port (может занять пару минут) ..." + ( cd plan9port && ./INSTALL -b ) # -b: только собрать, без установки в систему +fi + +echo +echo "Готово. Экспортни:" +echo " export PLAN9SRC=$DEPS/plan9foundation" +echo " export PLAN9PORT=$DEPS/plan9port" +echo " # если используешь musl-тулчейн: export MUSL_ENV=/path/to/env.sh" |
