From 6ab17c98e9bc826a963cee6330da50816d9013e1 Mon Sep 17 00:00:00 2001 From: Christoph Urlacher Date: Mon, 23 Feb 2026 19:58:18 +0100 Subject: [PATCH] Add i368-elf-gcc v5.4 crosscompiler --- examples/arch/bochs.mk | 126 ++++++++++++++++++++++++----------------- examples/arch/linux.mk | 30 +++++----- examples/lib.c | 2 + examples/linker.ld | 14 ++++- examples/syscalls.c | 35 ++++++++++++ wasm-base.dockerfile | 79 +++++++++++++++++++++++++- 6 files changed, 212 insertions(+), 74 deletions(-) create mode 100644 examples/syscalls.c diff --git a/examples/arch/bochs.mk b/examples/arch/bochs.mk index f905708..900ec33 100644 --- a/examples/arch/bochs.mk +++ b/examples/arch/bochs.mk @@ -1,33 +1,51 @@ # C -> WASM -WASI_ROOT := /opt/wasi-sdk -WASI_CC := ${WASI_ROOT}/bin/clang -WASI_CFLAGS := --target=wasm32 \ - --sysroot=${WASI_ROOT}/share/wasi-sysroot \ - -z stack-size=4096 \ - -O0 -nostdlib \ - -Wl,--no-entry \ - -Wl,--initial-memory=65536 \ - -Wl,--export-all \ - -Wl,--export=__heap_base \ - -Wl,--export=__data_end +WASI_ROOT := /opt/wasi-sdk +WASI_CC := ${WASI_ROOT}/bin/clang +WASI_CFLAGS := \ + --target=wasm32 \ + --sysroot=${WASI_ROOT}/share/wasi-sysroot \ + -z stack-size=4096 \ + -O0 \ + -nostdlib \ + -Wl,--no-entry \ + -Wl,--export-all \ + -Wl,--no-gc-sections \ + -Wl,--initial-memory=65536 \ + -Wl,--export=__heap_base \ + -Wl,--export=__data_end # WASM -> Baremetal -WAMR := /opt/wamr -IWASM_LIB := /opt/wamr-libiwasm -CC := gcc +WAMR := /opt/wamr +IWASM_LIB := /opt/wamr-libiwasm +CC := /opt/crosscompiler/bin/i686-elf-gcc # NOTE: When compiling I get "error: bp cannot be used in ‘asm’ here" # I could remove "ebp" from the clobber list (ARCH_ASM_CLOBBER_ALL) or # use the -fomit-frame-pointer flag to tell gcc it shouldn't rely on ebp for enter/leave... -CFLAGS := -I. -O0 -m32 -ffunction-sections -std=c11 -fomit-frame-pointer -LDFLAGS = -Wl,-T linker.ld $^ -Wl,--build-id=none -static -nostdlib -m32 \ - -Wl,-rpath,${IWASM_LIB} -L${IWASM_LIB} -liwasm -lgcc -INCL := -I${WAMR}/core/iwasm/include \ - -I${WAMR}/core/shared/utils \ - -I${WAMR}/core/shared/platform/baremetal -WAMRC := /opt/wamr-wamrc/wamrc -WAMRCFLAGS := --target=i386 --format=object -XXD := busybox xxd - +CFLAGS := \ + -I. \ + -O0 \ + -m32 \ + -ffunction-sections \ + -fomit-frame-pointer +LDFLAGS = \ + -Wl,-T linker.ld \ + $^ \ + -Wl,--build-id=none \ + -static \ + -nostdlib \ + -m32 \ + -Wl,-rpath,${IWASM_LIB} \ + -L${IWASM_LIB} \ + -liwasm \ + -lc \ + -lgcc +INCL := \ + -I${WAMR}/core/iwasm/include \ + -I${WAMR}/core/shared/utils \ + -I${WAMR}/core/shared/platform/baremetal \ + -I/opt/crosscompiler/i686-elf/include +WAMRC := /opt/wamr-wamrc/wamrc +XXD := busybox xxd ################################################################ # C -> WASM @@ -35,11 +53,10 @@ ${BUILD_DIR}/%/module.wasm: %.c mkdir -p $(shell dirname $@) ${WASI_CC} ${WASI_CFLAGS} $< -o $@ - ################################################################ # WASM -> Native Object File # ${BUILD_DIR}/%/system.o: ${BUILD_DIR}/%/module.wasm -# ${WAMRC} ${WAMRCFLAGS} -o ${BUILD_DIR}/$*/system.o ${BUILD_DIR}/$*/module.wasm +# ${WAMRC} --target=i386 --format=object -o ${BUILD_DIR}/$*/system.o ${BUILD_DIR}/$*/module.wasm # # ${BUILD_DIR}/startup.o: arch/bochs/startup.s # ${CC} $< ${CFLAGS} -c -o $@ @@ -47,7 +64,6 @@ ${BUILD_DIR}/%/module.wasm: %.c # ${BUILD_DIR}/%/system.elf: ${BUILD_DIR}/%/system.o ${BUILD_DIR}/startup.o # ${CC} ${LDFLAGS} -o $@ - ################################################################ # WASM -> AOT -> Loaded by Runtime ${BUILD_DIR}/%/module.aot: ${BUILD_DIR}/%/module.wasm @@ -62,13 +78,16 @@ ${BUILD_DIR}/%/system.o: ${BUILD_DIR}/%/module_wasm.c -e "s/__WASM_ARRAY_FILE__/module_wasm.c/g" \ -e "s/__WASM_ARRAY__/build_bochs_$*_module_aot/g" \ -e "s/__WASM_ARRAY_LEN__/build_bochs_$*_module_aot_len/g" \ - ${BUILD_DIR}/$*/module_host.c + ${BUILD_DIR}/$*/module_host.c ${CC} ${CFLAGS} ${INCL} -c ${BUILD_DIR}/$*/module_host.c -o $@ ${BUILD_DIR}/startup.o: arch/bochs/startup.s ${CC} $< ${CFLAGS} -c -o $@ -${BUILD_DIR}/%/system.elf: ${BUILD_DIR}/%/system.o ${BUILD_DIR}/startup.o +${BUILD_DIR}/syscalls.o: syscalls.c + ${CC} $< ${CFLAGS} -c -o $@ + +${BUILD_DIR}/%/system.elf: ${BUILD_DIR}/%/system.o ${BUILD_DIR}/syscalls.o ${BUILD_DIR}/startup.o ${CC} ${LDFLAGS} -o $@ ################################################################ @@ -80,51 +99,50 @@ ${BUILD_DIR}/%/system.iso: ${BUILD_DIR}/%/system.elf cp $< $(shell dirname $<)/grub/boot/system.elf grub-mkrescue -o $@ $(shell dirname $<)/grub - BOCHS_RUNNER_ARGS = \ - -V arch/bochs/vgabios.bin \ - -b arch/bochs/BIOS-bochs-latest \ + -V arch/bochs/vgabios.bin \ + -b arch/bochs/BIOS-bochs-latest \ +# Don't depend on system.iso so we can run the trace target from the fail docker image that does not have any wamr/wasi stuff # ${BUILD_DIR}/%/trace.pb: ${BUILD_DIR}/%/system.iso ${BUILD_DIR}/%/trace.pb: - ${BOCHS_RUNNER} ${BOCHS_RUNNER_ARGS} -1 \ - -f ${FAIL_TRACE} \ - -e $(shell dirname $<)/$*/system.elf \ - -i $(shell dirname $<)/$*/system.iso \ + ${BOCHS_RUNNER} ${BOCHS_RUNNER_ARGS} -1 \ + -f ${FAIL_TRACE} \ + -e ${BUILD_DIR}/$*/system.elf \ + -i ${BUILD_DIR}/$*/system.iso \ -- \ - -Wf,--state-file=$(shell dirname $<)/state \ - -Wf,--trace-file=$(shell dirname $<)/trace.pb \ - -Wf,--start-symbol=start_trace \ - -Wf,--end-symbol=stop_trace \ + -Wf,--state-file=${BUILD_DIR}/state \ + -Wf,--trace-file=${BUILD_DIR}/trace.pb \ + -Wf,--start-symbol=start_trace \ + -Wf,--end-symbol=stop_trace \ -Wf,--check-bounds - client-%: - ${BOCHS_RUNNER} ${BOCHS_RUNNER_ARGS} \ - -f ${FAIL_INJECT} \ + ${BOCHS_RUNNER} ${BOCHS_RUNNER_ARGS} \ + -f ${FAIL_INJECT} \ -e ${BUILD_DIR}/$(subst client-,,$@)/system.elf \ -i ${BUILD_DIR}/$(subst client-,,$@)/system.iso \ -j $(shell getconf _NPROCESSORS_ONLN) \ -- \ -Wf,--state-dir=${BUILD_DIR}/$(subst client-,,$@)/state \ - -Wf,--trap \ - -Wf,--timeout=10 \ - -Wf,--ok-marker=ok_marker \ + -Wf,--trap \ + -Wf,--timeout=10 \ + -Wf,--ok-marker=ok_marker \ -Wf,--fail-marker=fail_marker \ -Wf,--catch-write-textsegment \ - -Wf,--catch-outerspace \ + -Wf,--catch-outerspace \ 2>/dev/null | grep -B 2 -A 8 'INJECT' inject-%: - ${BOCHS_RUNNER} ${BOCHS_RUNNER_ARGS} -1 \ - -f ${FAIL_INJECT} \ + ${BOCHS_RUNNER} ${BOCHS_RUNNER_ARGS} -1 \ + -f ${FAIL_INJECT} \ -e ${BUILD_DIR}/$(subst inject-,,$@)/system.elf \ -i ${BUILD_DIR}/$(subst inject-,,$@)/system.iso \ -j 1 -- \ -Wf,--state-dir=${BUILD_DIR}/$(subst inject-,,$@)/state \ - -Wf,--trap \ - -Wf,--timeout=10 \ - -Wf,--ok-marker=ok_marker \ + -Wf,--trap \ + -Wf,--timeout=10 \ + -Wf,--ok-marker=ok_marker \ -Wf,--fail-marker=fail_marker \ -Wf,--catch-write-textsegment \ -Wf,--catch-outerspace @@ -142,11 +160,13 @@ import-arch-%: ${BUILD_DIR}/%/trace.pb ${HOME}/.my.cnf ${FAIL_IMPORT} -v ${ARCH}/$(patsubst import-arch-%,%,$@) -b ip -t $< -e $(shell dirname $<)/system.elf -i RegisterImporter --no-gp --ip ${FAIL_PRUNE} -v ${ARCH}/$(patsubst import-arch-%,%,$@) -b %% --overwrite - define arch-make-targets build-$1: ${BUILD_DIR}/$1/system.iso trace-$1: ${BUILD_DIR}/$1/trace.pb +objdump-$1: + objdump --disassemble --disassembler-options intel --disassembler-color=on --source ${BUILD_DIR}/$1/system.elf + endef diff --git a/examples/arch/linux.mk b/examples/arch/linux.mk index 1a7a573..3c1c027 100644 --- a/examples/arch/linux.mk +++ b/examples/arch/linux.mk @@ -1,7 +1,7 @@ # C -> WASM -WASI_ROOT := /opt/wasi-sdk -WASI_CC := ${WASI_ROOT}/bin/clang -WASI_CFLAGS := --target=wasm64 \ +WASI_ROOT := /opt/wasi-sdk +WASI_CC := ${WASI_ROOT}/bin/clang +WASI_CFLAGS := --target=wasm64 \ --sysroot=${WASI_ROOT}/share/wasi-sysroot \ -z stack-size=4096 \ -O0 -g -nostdlib \ @@ -12,22 +12,21 @@ WASI_CFLAGS := --target=wasm64 \ -Wl,--export=__data_end # WASM -> Baremetal -WAMR := /opt/wamr -IWASM_LIB := /opt/wamr-libiwasm-64 -CC := gcc +WAMR := /opt/wamr +IWASM_LIB := /opt/wamr-libiwasm-64 +CC := gcc # NOTE: When compiling I get "error: bp cannot be used in ‘asm’ here" # I could remove "ebp" from the clobber list (ARCH_ASM_CLOBBER_ALL) or # use the -fomit-frame-pointer flag to tell gcc it shouldn't rely on ebp for enter/leave... -CFLAGS := -I. -O0 -g -ffunction-sections -std=c11 -fomit-frame-pointer -LDFLAGS = $^ -Wl,--build-id=none -static -nostdlib \ +CFLAGS := -I. -O0 -g -ffunction-sections -std=c11 -fomit-frame-pointer +LDFLAGS = $^ -Wl,--build-id=none -static -nostdlib \ -Wl,-rpath,${IWASM_LIB} -L${IWASM_LIB} -liwasm -lgcc -INCL := -I${WAMR}/core/iwasm/include \ +INCL := -I${WAMR}/core/iwasm/include \ -I${WAMR}/core/shared/utils \ -I${WAMR}/core/shared/platform/baremetal -WAMRC := /opt/wamr-wamrc/wamrc -WAMRCFLAGS := --target=i386 --format=object -XXD := busybox xxd - +WAMRC := /opt/wamr-wamrc/wamrc +WAMRCFLAGS := --target=i386 --format=object +XXD := busybox xxd ################################################################ # C -> WASM @@ -35,7 +34,6 @@ ${BUILD_DIR}/%/module.wasm: %.c mkdir -p $(shell dirname $@) ${WASI_CC} ${WASI_CFLAGS} $< -o $@ - ################################################################ # WASM -> Native Object File # ${BUILD_DIR}/%/system.o: ${BUILD_DIR}/%/module.wasm @@ -47,7 +45,6 @@ ${BUILD_DIR}/%/module.wasm: %.c # ${BUILD_DIR}/%/system.elf: ${BUILD_DIR}/%/system.o ${BUILD_DIR}/startup.o # ${CC} ${LDFLAGS} -o $@ - ################################################################ # WASM -> AOT -> Loaded by Runtime ${BUILD_DIR}/%/module.aot: ${BUILD_DIR}/%/module.wasm @@ -62,7 +59,7 @@ ${BUILD_DIR}/%/system.o: ${BUILD_DIR}/%/module_wasm.c -e "s/__WASM_ARRAY_FILE__/module_wasm.c/g" \ -e "s/__WASM_ARRAY__/build_linux_$*_module_aot/g" \ -e "s/__WASM_ARRAY_LEN__/build_linux_$*_module_aot_len/g" \ - ${BUILD_DIR}/$*/module_host.c + ${BUILD_DIR}/$*/module_host.c ${CC} ${CFLAGS} ${INCL} -c ${BUILD_DIR}/$*/module_host.c -o $@ ${BUILD_DIR}/startup.o: arch/bochs/startup.s @@ -71,7 +68,6 @@ ${BUILD_DIR}/startup.o: arch/bochs/startup.s ${BUILD_DIR}/%/system.elf: ${BUILD_DIR}/%/system.o ${BUILD_DIR}/startup.o ${CC} ${LDFLAGS} -o $@ - define arch-make-targets build-$1: ${BUILD_DIR}/$1/system.elf diff --git a/examples/lib.c b/examples/lib.c index daabf67..0d78721 100644 --- a/examples/lib.c +++ b/examples/lib.c @@ -7,6 +7,8 @@ #define QUOTE(x) __QUOTE(x) #ifndef ARCH_ASM_CLOBBER_ALL +// TODO: Is my gcc to new? It won't let me clobber ebp unless I specify +// -fomit-frame-pointer #define ARCH_ASM_CLOBBER_ALL "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" #endif diff --git a/examples/linker.ld b/examples/linker.ld index 3d8e8c3..6ce8d07 100644 --- a/examples/linker.ld +++ b/examples/linker.ld @@ -18,6 +18,7 @@ SECTIONS { /* Code and readonly data */ .text : { /* fill gaps with int3 opcode to detect invalid jumps */ + /* TODO: This crashes bochs */ FILL(0xcc) /* multiboot header */ @@ -55,9 +56,20 @@ SECTIONS { KEEP (*(".startup_stack")) KEEP (*(".kernel_stack")) *(".data*") - *(COMMON); } + /* Uninitialized data */ + .bss : { + _sbss = .; + *(.bss*) + *(COMMON) + _ebss = .; + } + + /* Align and mark end of all sections — heap starts here */ + . = ALIGN(4096); + _end = .; + /* Memory-mapped I/O APIC */ _sioapic = 0xFEC00000; ioapic = 0xFEC00000; diff --git a/examples/syscalls.c b/examples/syscalls.c new file mode 100644 index 0000000..cf3bb48 --- /dev/null +++ b/examples/syscalls.c @@ -0,0 +1,35 @@ +#include +#include + +extern char _end; /* provided by linker script */ +static char *heap_ptr = &_end; + +void *sbrk(int incr) { + char *prev = heap_ptr; + heap_ptr += incr; + return prev; +} + +int write(int fd, const char *buf, int len) { return len; } + +int read(int fd, char *buf, int len) { return 0; } +int close(int fd) { return -1; } +int fstat(int fd, struct stat *st) { + st->st_mode = S_IFCHR; + return 0; +} +int isatty(int fd) { return 1; } +int lseek(int fd, int offset, int whence) { return 0; } +void _exit(int status) { + while (1) + ; +} +void exit(int status) { + while (1) + ; +} +int kill(int pid, int sig) { + errno = EINVAL; + return -1; +} +int getpid(void) { return 1; } diff --git a/wasm-base.dockerfile b/wasm-base.dockerfile index 8c01c22..c0719a6 100644 --- a/wasm-base.dockerfile +++ b/wasm-base.dockerfile @@ -1,7 +1,73 @@ +# We need an old ubuntu version with an old enough gcc to build an old gcc... +# This gcc can't bootstrap itself because it's target is another platform. +FROM ubuntu:bionic AS compiler-builder + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive TZ=Europe/Berlin apt-get install -y --no-install-recommends \ + build-essential \ + git \ + ca-certificates \ + cmake \ + bison \ + flex \ + libgmp3-dev \ + libmpc-dev \ + libmpfr-dev \ + texinfo \ + libisl-dev \ + wget \ + && apt-get clean + +ARG GCCVERSION=5.4.0 +ARG BINUTILSVERSION=2.26.1 +ARG CROSSPREFIX=/opt/crosscompiler +ARG CROSSTARGET=i686-elf + +# Build Binutils for cross-compilation +WORKDIR / +RUN wget https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILSVERSION.tar.gz \ + && tar xf binutils-$BINUTILSVERSION.tar.gz +RUN mkdir binutils-build && cd binutils-build \ + && /binutils-$BINUTILSVERSION/configure --target=$CROSSTARGET --prefix="$CROSSPREFIX" --with-sysroot --disable-nls --disable-werror \ + && make -j$(nproc) \ + && make install + +# Build GCC for cross-compilation +WORKDIR / +RUN wget https://ftp.gnu.org/gnu/gcc/gcc-$GCCVERSION/gcc-$GCCVERSION.tar.gz \ + && tar xf gcc-$GCCVERSION.tar.gz \ + && cd gcc-$GCCVERSION \ + && contrib/download_prerequisites +RUN mkdir gcc-build && cd gcc-build \ + && PATH="$CROSSPREFIX/bin:$PATH" /gcc-$GCCVERSION/configure --target=$CROSSTARGET --prefix="$CROSSPREFIX" --disable-nls --enable-languages=c,c++ --without-headers --disable-hosted-libstdcxx --disable-libsanitizer --disable-threads \ + && PATH="$CROSSPREFIX/bin:$PATH" make all-gcc -j$(nproc) \ + && PATH="$CROSSPREFIX/bin:$PATH" make all-target-libgcc -j$(nproc) \ + && PATH="$CROSSPREFIX/bin:$PATH" make install-gcc \ + && PATH="$CROSSPREFIX/bin:$PATH" make install-target-libgcc + # && PATH="$CROSSPREFIX/bin:$PATH" make all-target-libstdc++-v3 -j$(nproc) \ + # && PATH="$CROSSPREFIX/bin:$PATH" make install-target-libstdc++-v3 + +# Build newlib - it seems like because I use the same gcc prefix +# I don't even need to specify it as an include when building? +ARG NEWLIBVERSION=4.1.0 +WORKDIR / +RUN wget ftp://sourceware.org/pub/newlib/newlib-$NEWLIBVERSION.tar.gz \ + && tar xf newlib-$NEWLIBVERSION.tar.gz +RUN mkdir newlib-build && cd newlib-build \ + && PATH="$CROSSPREFIX/bin:$PATH" /newlib-$NEWLIBVERSION/configure \ + --target=$CROSSTARGET \ + --prefix="$CROSSPREFIX" \ + --disable-nls \ + --disable-newlib-supplied-syscalls \ + && PATH="$CROSSPREFIX/bin:$PATH" make -j$(nproc) \ + && PATH="$CROSSPREFIX/bin:$PATH" make install + +# ============================================================================= + FROM ubuntu:noble AS wamr-builder RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive TZ=Europe/Berline apt-get install -y --no-install-recommends \ + && DEBIAN_FRONTEND=noninteractive TZ=Europe/Berlin apt-get install -y --no-install-recommends \ build-essential \ gcc-multilib \ g++-multilib \ @@ -16,6 +82,7 @@ RUN apt-get update \ python3-minimal \ python3-pip \ ninja-build \ + wget \ && apt-get clean # TODO: Can't find pthread on X86_32 @@ -28,9 +95,9 @@ RUN python3 -m pip config set global.break-system-packages true RUN git clone https://gitea.vps.chriphost.de/christoph/wamr \ && cd wamr \ && git checkout WAMR-2.4.4 -WORKDIR /wamr # Build WAMR iwasm (vmcore standalone interpreter) +WORKDIR /wamr RUN cd product-mini/platforms/linux \ && mkdir build_iwasm && cd build_iwasm \ && cmake \ @@ -48,6 +115,7 @@ RUN cd product-mini/platforms/linux \ && make -j$(nproc) # Build WAMR wamrc (standalone compiler) +WORKDIR /wamr RUN cd wamr-compiler \ && ./build_llvm.sh \ && mkdir build_wamrc && cd build_wamrc \ @@ -74,9 +142,11 @@ WORKDIR / RUN git clone https://gitea.vps.chriphost.de/christoph/wamr wamrlib \ && cd wamrlib \ && git checkout WAMR-2.4.4 -WORKDIR /wamrlib + +# TODO: Should this stuff not be compiled with the crosscompiler? # Build WAMR libvmlib (compiler runtime - to compile wasm modules embedded in a native application) +# WORKDIR /wamrlib # RUN cd wamr-compiler \ # && ./build_llvm.sh \ # # && ./build_llvm.sh --extra-cmake-flags "-DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_EXE_LINKER_FLAGS=-m32 -lstdc++" \ @@ -97,6 +167,7 @@ WORKDIR /wamrlib # && make -j$(nproc) # Build WAMR libiwasm (vmcore interpreter runtime - to run wasm modules embedded in a native application) +WORKDIR /wamrlib RUN mkdir build_libiwasm && cd build_libiwasm \ && cmake \ -DWAMR_BUILD_PLATFORM=baremetal \ @@ -114,6 +185,7 @@ RUN mkdir build_libiwasm && cd build_libiwasm \ .. \ && make -j$(nproc) +WORKDIR /wamrlib RUN mkdir build_libiwasm_64 && cd build_libiwasm_64 \ && cmake \ -DWAMR_BUILD_PLATFORM=baremetal \ @@ -155,6 +227,7 @@ RUN apt-get update \ bochs \ && apt-get clean +COPY --from=compiler-builder /opt/crosscompiler /opt/crosscompiler COPY --from=wamr-builder /wamrlib /opt/wamr COPY --from=wamr-builder /wamr/wamr-compiler/build_wamrc /opt/wamr/wamr-compiler/build_wamrc COPY --from=wamr-builder /wamr/product-mini/platforms/linux/build_iwasm /opt/wamr/product-mini/platforms/linux/build_iwasm