From 8ea09ff3b4c72e13dfdfe553052e0caa4518c815 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 23 May 2024 01:13:16 +0200 Subject: [PATCH] Remove libc dependency for the generated code --- backend/riscv.cpp | 74 +++++--- include/elna/tester.hpp | 2 - package.json | 2 +- source/optimizer.cpp | 2 +- source/semantic.cpp | 4 +- tests/7_member_sum.eln | 2 - tests/expectations/7_member_sum.txt | 1 - tests/expectations/multiply.txt | 2 +- tests/expectations/multiply_3.txt | 2 +- tests/multiply.eln | 2 +- tests/tester.cpp | 29 +-- tools/{index.js => cross.js} | 267 +++++++++++----------------- tools/init.c | 205 +++++++++++++++++++++ tools/init.cpp | 185 ------------------- 14 files changed, 362 insertions(+), 417 deletions(-) delete mode 100644 tests/7_member_sum.eln delete mode 100644 tests/expectations/7_member_sum.txt rename tools/{index.js => cross.js} (51%) create mode 100644 tools/init.c delete mode 100644 tools/init.cpp diff --git a/backend/riscv.cpp b/backend/riscv.cpp index e6584cb..6ce7526 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -116,12 +116,10 @@ namespace elna::riscv static void generate_intrinsics(std::shared_ptr> writer, std::vector& references) { - writer->sink("printf"); { std::vector instructions; - auto format_string = writer->sink(reinterpret_cast("%c\n\0"), 4); - prologue(instructions); + instructions.push_back(instruction(base_opcode::opImm) .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); instructions.push_back(instruction(base_opcode::branch) @@ -129,42 +127,68 @@ namespace elna::riscv instructions.push_back(instruction(base_opcode::opImm) .i(x_register::a1, funct3_t::addi, x_register::zero, 'f')); - relocate(format_string, address_t::high20, references, instructions, writer); - instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); - relocate(format_string, address_t::lower12i, references, instructions, writer); instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); + .i(x_register::t0, funct3_t::addi, x_register::a0, 0)); + instructions.push_back(instruction(base_opcode::store) + .s(0, funct3_t::sw, x_register::s0, x_register::a1)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::zero, 1)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::s0, 0)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a2, funct3_t::addi, x_register::zero, 1)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a7, funct3_t::addi, x_register::zero, 64)); + instructions.push_back(instruction(base_opcode::system) + .i(x_register::zero, funct3_t::priv, x_register::zero, 0)); - relocate("printf", address_t::text, references, instructions ,writer); - instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); - instructions.push_back(instruction(base_opcode::jalr) - .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::t1, funct3_t::addi, x_register::zero, '\n')); + instructions.push_back(instruction(base_opcode::store) + .s(0, funct3_t::sw, x_register::s0, x_register::t1)); + instructions.push_back(instruction(base_opcode::system) + .i(x_register::zero, funct3_t::priv, x_register::zero, 0)); - epilogue(8, instructions); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::t0, 0)); + + epilogue(12, instructions); writer->sink("writeb", reinterpret_cast(instructions.data()), instructions.size() * sizeof(instruction)); } { std::vector instructions; - auto format_string = writer->sink(reinterpret_cast("%d\n\0"), 4); - prologue(instructions); + instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); - - relocate(format_string, address_t::high20, references, instructions, writer); - instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); - relocate(format_string, address_t::lower12i, references, instructions, writer); + .i(x_register::t0, funct3_t::addi, x_register::a0, 0)); instructions.push_back(instruction(base_opcode::opImm) - .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); + .i(x_register::a0, funct3_t::addi, x_register::a0, '0')); + instructions.push_back(instruction(base_opcode::store) + .s(0, funct3_t::sw, x_register::s0, x_register::a0)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::zero, 1)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::s0, 0)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a2, funct3_t::addi, x_register::zero, 1)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a7, funct3_t::addi, x_register::zero, 64)); + instructions.push_back(instruction(base_opcode::system) + .i(x_register::zero, funct3_t::priv, x_register::zero, 0)); - relocate("printf", address_t::text, references, instructions, writer); - instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); - instructions.push_back(instruction(base_opcode::jalr) - .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::t1, funct3_t::addi, x_register::zero, '\n')); + instructions.push_back(instruction(base_opcode::store) + .s(0, funct3_t::sw, x_register::s0, x_register::t1)); + instructions.push_back(instruction(base_opcode::system) + .i(x_register::zero, funct3_t::priv, x_register::zero, 0)); - epilogue(8, instructions); + instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::t0, 0)); + + epilogue(12, instructions); writer->sink("writei", reinterpret_cast(instructions.data()), instructions.size() * sizeof(instruction)); diff --git a/include/elna/tester.hpp b/include/elna/tester.hpp index 2a81a6a..fb1e6a1 100644 --- a/include/elna/tester.hpp +++ b/include/elna/tester.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #define BOOST_PROCESS_USE_STD_FS @@ -39,7 +38,6 @@ namespace elna boost::asio::readable_pipe& read_pipe); int run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number, const std::filesystem::path& binary, std::initializer_list arguments); - std::string find_object(const std::vector& environment, const std::string& object); test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry); void run_test(boost::asio::io_context& context, const std::filesystem::path& test_entry); void print_result(const std::filesystem::path& test_entry, const test_status& result); diff --git a/package.json b/package.json index a09256f..4c4f3f0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "tools/index.js", "type": "module", "scripts": { - "start": "node tools/index.js" + "cross": "node tools/cross.js" }, "author": "Eugen Wissner ", "license": "MPL-2.0" diff --git a/source/optimizer.cpp b/source/optimizer.cpp index 1463d4d..be1b436 100644 --- a/source/optimizer.cpp +++ b/source/optimizer.cpp @@ -165,7 +165,7 @@ namespace elna::source this->current.emplace_back(quadruple_operator::start); program->body().accept(this); this->current.emplace_back(quadruple_operator::stop); - code["main"] = std::move(this->current); + code["_start"] = std::move(this->current); } void intermediate_code_generator::visit(call_statement *statement) diff --git a/source/semantic.cpp b/source/semantic.cpp index 108303a..ec1e383 100644 --- a/source/semantic.cpp +++ b/source/semantic.cpp @@ -44,7 +44,7 @@ namespace elna::source void name_analysis_visitor::visit(program *program) { class procedure_type main_type{ std::vector>(), this->pointer_size }; - this->table->enter("main", std::make_shared(main_type, this->table)); + this->table->enter("_start", std::make_shared(main_type, this->table)); empty_visitor::visit(program); } @@ -105,7 +105,7 @@ namespace elna::source this->argument_offset = 0; empty_visitor::visit(program); - std::dynamic_pointer_cast(table->lookup("main"))->local_stack_size = + std::dynamic_pointer_cast(table->lookup("_start"))->local_stack_size = std::abs(this->local_offset); } diff --git a/tests/7_member_sum.eln b/tests/7_member_sum.eln deleted file mode 100644 index 8c56a1b..0000000 --- a/tests/7_member_sum.eln +++ /dev/null @@ -1,2 +0,0 @@ -writei(3 + 4 + 5 + 1 + 2 + 4 + 3) -. diff --git a/tests/expectations/7_member_sum.txt b/tests/expectations/7_member_sum.txt deleted file mode 100644 index 2bd5a0a..0000000 --- a/tests/expectations/7_member_sum.txt +++ /dev/null @@ -1 +0,0 @@ -22 diff --git a/tests/expectations/multiply.txt b/tests/expectations/multiply.txt index f599e28..45a4fb7 100644 --- a/tests/expectations/multiply.txt +++ b/tests/expectations/multiply.txt @@ -1 +1 @@ -10 +8 diff --git a/tests/expectations/multiply_3.txt b/tests/expectations/multiply_3.txt index a45fd52..a9edc74 100644 --- a/tests/expectations/multiply_3.txt +++ b/tests/expectations/multiply_3.txt @@ -1 +1 @@ -24 +H diff --git a/tests/multiply.eln b/tests/multiply.eln index 08ff32b..51ca3d1 100644 --- a/tests/multiply.eln +++ b/tests/multiply.eln @@ -1,2 +1,2 @@ -writei(5 * 2) +writei(4 * 2) . diff --git a/tests/tester.cpp b/tests/tester.cpp index 477289b..6707751 100755 --- a/tests/tester.cpp +++ b/tests/tester.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include namespace elna @@ -105,22 +104,6 @@ namespace elna return elna_child.wait(); } - std::string find_object(const std::vector& environment, const std::string& object) - { - auto variable = std::find(environment.cbegin(), environment.cend(), object); - - for (const auto& variable : environment) - { - auto full_path = variable / object; - - if (std::filesystem::exists(full_path)) - { - return full_path.native(); - } - } - return object; - } - test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry) { const std::filesystem::path test_filename = test_entry.path().filename(); @@ -145,17 +128,7 @@ namespace elna std::vector environment; std::vector linker_arguments = { "-o", test_binary.string() }; - for (const auto segment : boost::this_process::environment()["LIBRARY_PATH"].to_vector()) - { - linker_arguments.push_back("-L" + segment); - environment.push_back(std::filesystem::path(segment)); - } - linker_arguments.push_back(find_object(environment, "crt0.o")); - linker_arguments.push_back(find_object(environment, "crtbegin.o")); linker_arguments.push_back(test_object.string()); - linker_arguments.insert(linker_arguments.cend(), - { "--start-group", "-lgcc", "-lc", "-lgloss", "--end-group" }); - linker_arguments.push_back(find_object(environment, "crtend.o")); boost::process::v2::execute(boost::process::v2::process(context, boost::process::search_path("ld"), linker_arguments @@ -248,7 +221,7 @@ namespace elna "-nographic", "-M", "virt", "-bios", "default", - "-kernel", "build/tools/linux-5.15.158/arch/riscv/boot/Image", + "-kernel", "build/tools/linux-5.15.159/arch/riscv/boot/Image", "-append", "quiet", "-initrd", "build/root.cpio" }; diff --git a/tools/index.js b/tools/cross.js similarity index 51% rename from tools/index.js rename to tools/cross.js index ee8c673..e440b30 100644 --- a/tools/index.js +++ b/tools/cross.js @@ -7,45 +7,57 @@ import { Buffer } from 'node:buffer' // Define constants. const tmp = path.resolve('./build/tools') -const target = 'riscv32-unknown-linux-gnu' const baseDirectory = path.resolve('./tools') -const busyboxVersion = '1.36.1' -const kernelVersion = '5.15.158' +const kernelVersion = '5.15.159' const gccVersion = '14.1.0' const binutilsVersion = '2.42' const glibcVersion = '2.39' -function findBuildTarget () { +async function gccVerbose (gccBinary) { const env = { - LANG: 'en_US.UTF-8' + ...process.env, + LANG: 'C' } - const gccV = childProcess.spawn('gcc', ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env }) + const gccV = childProcess.spawn(gccBinary, ['--verbose'], { stdio: ['ignore', 'ignore', 'pipe'], env }) const buffers = [] gccV.stderr.on('data', function (data) { buffers.push(data) }) - return new Promise(function (resolve, reject) { gccV.on('exit', function () { - const [_, target] = Buffer.concat(buffers) - .toString() - .split('\n') - .find(line => line.startsWith('Target: ')) - .split(' ') - - resolve(target) + resolve(Buffer.concat(buffers).toString()) }) }) } -function createImage (rootfs) { - const rootExt4 = path.join(tmp, 'rootfs.ext4') +async function findBuildTarget (gccVersion) { + let gccBinary = 'gcc' + let output = await gccVerbose(gccBinary) - childProcess.execFileSync('dd', ['if=/dev/zero', `of=${rootExt4}`, 'bs=1M', 'count=1024'], { stdio: 'inherit' }) - childProcess.execFileSync('/sbin/mkfs.ext4', ['-d', rootfs, rootExt4], { stdio: 'inherit' }) + if (output.startsWith('Apple clang')) { + gccBinary = `gcc-${gccVersion.split('.')[0]}` + output = await gccVerbose(gccBinary) + } + return output + .split('\n') + .reduce(function (accumulator, line) { + if (line.startsWith('Target: ')) { + return { + ...accumulator, + build: line.split(' ')[1] + } + } else if (line.startsWith('COLLECT_GCC')) { + return { + ...accumulator, + gcc: line.split('=')[1] + } + } else { + return accumulator + } + }, {}) } async function downloadAndUnarchive (url) { @@ -89,70 +101,70 @@ async function downloadAndUnarchive (url) { }) } -async function buildBusyBox (sysroot, rootfs) { - const cwd = await downloadAndUnarchive(new URL(`https://busybox.net/downloads/busybox-${busyboxVersion}.tar.bz2`)) - const env = { - ...process.env, - CROSS_COMPILE: path.join(sysroot, 'bin', `${target}-`) - } - const configuration = [ - `CONFIG_PREFIX=${rootfs}`, - `CONFIG_SYSROOT=${rootfs}` - ] - childProcess.execFileSync('make', ['defconfig'], { stdio: 'inherit', env, cwd }) - childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) - childProcess.execFileSync('make', [...configuration, 'install'], { stdio: 'inherit', env, cwd }) +async function copyGlibcHeaders () { + const sourceDirectory = await downloadAndUnarchive( + new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`) + ) + const includeDirectory = path.join(tmp, 'include') + + await fs.mkdir(includeDirectory) + await fs.cp(path.join(sourceDirectory, 'elf/elf.h'), path.join(includeDirectory, 'elf.h')) + + return sourceDirectory } -async function buildKernel (sysroot, rootfs) { +async function buildKernel (rootfs, options) { const cwd = await downloadAndUnarchive( new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`) ) const env = { ...process.env, - CROSS_COMPILE: `${target}-`, + CROSS_COMPILE: `${options.target}-`, ARCH: 'riscv', - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` + PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}`, + HOSTCFLAGS: `-D_UUID_T -D__GETHOSTUUID_H -I${path.join(tmp, 'include')}` } childProcess.execFileSync('make', ['rv32_defconfig'], { stdio: 'inherit', env, cwd }) childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) childProcess.execFileSync('make', ['headers'], { stdio: 'inherit', env, cwd }) - const userDirectory = path.join(rootfs, 'usr') + const userDirectory = path.join(options.sysroot, 'usr') await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true }) return path.join(cwd, 'arch/riscv/boot/Image') } -async function buildInit (sysroot) { +async function buildInit (rootfs, options) { const env = { ...process.env, - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` + PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}` } const compilerArguments = [ '-ffreestanding', '-static', '-o', path.join(tmp, 'init'), - path.join(baseDirectory, 'init.cpp') + path.join(baseDirectory, 'init.c') ] - childProcess.execFileSync('riscv32-unknown-linux-gnu-g++', compilerArguments, { stdio: 'inherit', env }) + childProcess.execFileSync('riscv32-unknown-linux-gnu-gcc', compilerArguments, { stdio: 'inherit', env }) } -async function buildGlibc (sysroot, rootfs, options) { - const sourceDirectory = await downloadAndUnarchive( - new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`) - ) +async function buildGlibc (sourceDirectory, rootfs, options) { const configureOptions = [ '--prefix=/usr', - '--libdir=/usr/lib', - `--host=${target}`, + `--host=${options.target}`, + `--target=${options.target}`, `--build=${options.build}`, `--enable-kernel=${kernelVersion}`, - `--with-headers=${path.join(rootfs, 'usr/include')}`, - '--disable-nscd' + `--with-headers=${path.join(options.sysroot, 'usr/include')}`, + '--disable-nscd', + '--disable-libquadmath', + '--disable-libitm', + '--disable-werror', + 'libc_cv_forced_unwind=yes' ] + const bin = path.join(rootfs, 'bin') const env = { ...process.env, - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` + PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}` } const cwd = path.join(path.dirname(sourceDirectory), 'build-glibc'); await fs.mkdir(cwd) @@ -163,35 +175,42 @@ async function buildGlibc (sysroot, rootfs, options) { cwd }) childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) - childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd }) + childProcess.execFileSync('make', [`install_root=${options.sysroot}`, 'install'], { stdio: 'inherit', env, cwd }) } -async function buildCrossBinutils (sysroot) { +async function buildCrossBinutils (rootfs, options) { const sourceDirectory = await downloadAndUnarchive( new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`) ) const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils'); await fs.mkdir(cwd) + const env = { + ...process.env, + CC: options.gcc, + CXX: options.gxx + } const configureOptions = [ - `--prefix=${sysroot}`, - `--target=${target}`, + `--prefix=${rootfs}`, + `--target=${options.target}`, '--disable-nls', '--enable-gprofng=no', '--disable-werror', - '--enable-default-hash-style=gnu' + '--enable-default-hash-style=gnu', + '--disable-libquadmath' ] childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { stdio: 'inherit', - cwd + cwd, + env }) - childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd }) - childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd }) + childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd, env }) + childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd, env }) return sourceDirectory } -async function buildGCC1 (sysroot, rootfs, options) { +async function buildGCC1 (rootfs, options) { const sourceDirectory = await downloadAndUnarchive( new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`) ) @@ -203,8 +222,8 @@ async function buildGCC1 (sysroot, rootfs, options) { cwd: sourceDirectory }) const configureOptions = [ - `--prefix=${sysroot}`, - `--with-sysroot=${rootfs}`, + `--prefix=${rootfs}`, + `--with-sysroot=${options.sysroot}`, '--enable-languages=c,c++', '--disable-shared', '--with-arch=rv32imafdc', @@ -225,16 +244,18 @@ async function buildGCC1 (sysroot, rootfs, options) { '--disable-nls', '--with-newlib', '--without-headers', - `--target=${target}`, + `--target=${options.target}`, `--build=${options.build}`, `--host=${options.build}` ] const flags = '-O2 -fPIC' const env = { ...process.env, + CC: options.gcc, + CXX: options.gxx, CFLAGS: flags, CXXFLAGS: flags, - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` + PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}` } childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { stdio: 'inherit', @@ -247,45 +268,14 @@ async function buildGCC1 (sysroot, rootfs, options) { return sourceDirectory } -async function buildBinutils (sourceDirectory, sysroot, rootfs, options) { - const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils'); - await fs.rm(cwd, { recursive: true, force: true }) - await fs.mkdir(cwd) - - const configureOptions = [ - '--prefix=/usr', - `--build=${options.build}`, - `--host=${target}`, - `--with-build-sysroot=${rootfs}`, - '--disable-nls', - '--enable-shared', - '--enable-gprofng=no', - '--disable-werror', - '--enable-default-hash-style=gnu' - ] - const env = { - ...process.env, - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` - } - childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { - stdio: 'inherit', - env, - cwd - }) - childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) - childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd }) - - return sourceDirectory -} - -async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { +async function buildGCC2 (sourceDirectory, rootfs, options) { const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc'); await fs.rm(cwd, { recursive: true, force: true }) await fs.mkdir(cwd) const configureOptions = [ - `--prefix=${sysroot}`, - `--with-sysroot=${rootfs}`, + `--prefix=${rootfs}`, + `--with-sysroot=${options.sysroot}`, '--enable-languages=c,c++,lto', '--enable-lto', '--enable-shared', @@ -297,10 +287,11 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { '--disable-multilib', '--enable-checking=release', '--disable-libssp', + '--disable-libquadmath', '--enable-threads=posix', '--with-default-libstdcxx-abi=new', '--disable-nls', - `--target=${target}`, + `--target=${options.target}`, `--build=${options.build}`, `--host=${options.build}` ] @@ -309,7 +300,7 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { ...process.env, CFLAGS: flags, CXXFLAGS: flags, - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` + PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}` } childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { stdio: 'inherit', @@ -320,83 +311,25 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { childProcess.execFileSync('make', ['install'], { stdio: 'inherit', env, cwd }) } -async function buildGCC3 (sourceDirectory, sysroot, rootfs, options) { - const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc'); - await fs.rm(cwd, { recursive: true, force: true }) - await fs.mkdir(cwd) - - const configureOptions = [ - `--prefix=/usr`, - `--libdir=/usr/lib`, - '--enable-languages=c,c++,lto', - '--enable-lto', - '--enable-shared', - '--with-arch=rv32imafdc', - '--with-abi=ilp32d', - '--with-tune=rocket', - '--with-isa-spec=20191213', - '--disable-bootstrap', - '--disable-multilib', - '--enable-checking=release', - '--disable-libssp', - '--enable-threads=posix', - '--with-default-libstdcxx-abi=new', - '--disable-nls', - `--target=${target}`, - `--build=${options.build}`, - `--host=${target}` - ] - const flags = '-O2 -fPIC' - const env = { - ...process.env, - CFLAGS: flags, - CXXFLAGS: flags, - PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` - } - childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { - stdio: 'inherit', - env, - cwd - }) - childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) - childProcess.execFileSync('make', [`DESTDIR=${rootfs}`, 'install'], { stdio: 'inherit', env, cwd }) - -} - -async function createRoot (rootfs) { - for (const directory of ['proc', 'sys', 'dev', 'etc/init.d', 'usr/local', 'usr/local/bin']) { - await fs.mkdir(path.join(rootfs, directory)) - } - await fs.cp(path.join(baseDirectory, 'files/fstab'), path.join(rootfs, 'etc/fstab')) - await fs.cp(path.join(baseDirectory, 'files/rcS'), path.join(rootfs, 'etc/init.d/rcS')) - await fs.cp(path.join(baseDirectory, 'files/rcK'), path.join(rootfs, 'etc/init.d/rcK')) - await fs.cp(path.join(baseDirectory, 'files/inittab'), path.join(rootfs, 'etc/inittab')) -} - -const sysroot = path.join(tmp, 'sysroot') const rootfs = path.join(tmp, 'rootfs') -const options = { - build: await findBuildTarget() -} +const options = await findBuildTarget(gccVersion) +options.gxx = options.gcc.replaceAll('c', '+') +options.sysroot = path.join(tmp, 'sysroot') +options.target = 'riscv32-unknown-linux-gnu' -for (const targetDirectory of [tmp, sysroot, rootfs]) { +for (const targetDirectory of [tmp, rootfs]) { await fs.rm(targetDirectory, { recursive: true, force: true }) await fs.mkdir(targetDirectory) } -const binutilsSource = await buildCrossBinutils(sysroot) -const gccSource = await buildGCC1(sysroot, rootfs, options) +const binutilsSource = await buildCrossBinutils(rootfs, options) +const gccSource = await buildGCC1(rootfs, options) -const kernelImage = await buildKernel(sysroot, rootfs) -await buildGlibc(sysroot, rootfs, options) -await buildBinutils(binutilsSource, sysroot, rootfs, options) -await buildGCC2(gccSource, sysroot, rootfs, options) -buildInit (sysroot) +const glibcSource = await copyGlibcHeaders() +const kernelImage = await buildKernel(rootfs, options) +await buildGlibc(glibcSource, rootfs, options) +await buildGCC2(gccSource, rootfs, options) +buildInit (rootfs, options) -await buildBusyBox(sysroot, rootfs) -await createRoot(rootfs) - -await buildGCC3(gccSource, sysroot, rootfs, options) -createImage(rootfs) console.log(kernelImage) diff --git a/tools/init.c b/tools/init.c new file mode 100644 index 0000000..cb646bd --- /dev/null +++ b/tools/init.c @@ -0,0 +1,205 @@ +#include +#include +#include +#include +#include +#include +#include + +#define FILENAME_BUFFER_SIZE 256 + +size_t read_command(int descriptor, char *command_buffer) +{ + ssize_t bytes_read = 0; + size_t read_so_far = 0; + + while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0) + { + read_so_far += bytes_read; + if (read_so_far >= FILENAME_BUFFER_SIZE - 1) + { + break; + } + } + command_buffer[read_so_far] = 0; + return read_so_far; +} + +enum status +{ + status_success, + status_failure, + status_warning, + status_fatal +}; + +unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension) +{ + unsigned int i = 0; + + for (; i < FILENAME_BUFFER_SIZE; i++) + { + if (directory[i] == 0) + { + break; + } + destination[i] = directory[i]; + } + for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) + { + if (filename[j] == 0) + { + break; + } + destination[i] = filename[j]; + } + if (extension == NULL) + { + goto done; + } + for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) + { + if (extension[j] == 0) + { + break; + } + destination[i] = extension[j]; + } +done: + destination[i] = 0; + + return i; +} + +enum status run_test(const char *file_entry_name) +{ + printf("Running %s. ", file_entry_name); + + char filename[FILENAME_BUFFER_SIZE]; + char command_buffer[FILENAME_BUFFER_SIZE]; + char file_buffer[256]; + int pipe_ends[2]; + + if (pipe(pipe_ends) == -1) + { + perror("pipe"); + return status_fatal; + } + make_path(filename, "./tests/", file_entry_name, NULL); + + int child_pid = fork(); + if (child_pid == -1) + { + return status_fatal; + } + else if (child_pid == 0) + { + close(STDIN_FILENO); + close(STDERR_FILENO); + close(pipe_ends[0]); // Close the read end. + + if (dup2(pipe_ends[1], STDOUT_FILENO) == -1) + { + perror("dup2"); + } + else + { + execl(filename, filename); + perror("execl"); + } + close(STDOUT_FILENO); + close(pipe_ends[1]); + _exit(1); + } + else + { + close(pipe_ends[1]); // Close the write end. + read_command(pipe_ends[0], command_buffer); + close(pipe_ends[0]); + + int wait_status = 0; + wait(&wait_status); + + make_path(filename, "./expectations/", file_entry_name, ".txt"); + + FILE *expectation_descriptor = fopen(filename, "r"); + + if (expectation_descriptor == NULL) + { + return status_warning; + } + size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor); + fclose(expectation_descriptor); + + file_buffer[read_from_file] = 0; + for (unsigned int i = 0; ; ++i) + { + if (command_buffer[i] == 0 && file_buffer[i] == 0) + { + fwrite("\n", 1, 1, stdout); + return status_success; + } + else if (command_buffer[i] != file_buffer[i]) + { + printf("Failed. Got:\n%s", command_buffer); + return status_failure; + } + } + } +} + +struct summary +{ + size_t total; + size_t failure; + size_t success; +}; + +void walk() +{ + DIR *directory_stream = opendir("./tests"); + struct dirent *file_entry; + + struct summary test_summary = { .total = 0, .failure = 0, .success = 0 }; + + while ((file_entry = readdir(directory_stream)) != NULL) + { + if (file_entry->d_name[0] == '.') + { + continue; + } + ++test_summary.total; + switch (run_test(file_entry->d_name)) + { + case status_failure: + ++test_summary.failure; + break; + case status_success: + ++test_summary.success; + break; + case status_warning: + break; + case status_fatal: + goto end_walk; + } + } + printf("Successful: %lu, Failed: %lu, Total: %lu.\n", + test_summary.success, test_summary.failure, test_summary.total); +end_walk: + closedir(directory_stream); +} + +int main() +{ + int dev_console = open("/dev/console", O_WRONLY); + if (dev_console != -1) + { + dup2(dev_console, STDOUT_FILENO); + walk(); + close(dev_console); + } + sync(); + reboot(RB_POWER_OFF); + + return 1; +} diff --git a/tools/init.cpp b/tools/init.cpp deleted file mode 100644 index 858e4ee..0000000 --- a/tools/init.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -template -std::size_t read_command(int descriptor, char *command_buffer) -{ - std::ptrdiff_t bytes_read{ 0 }; - std::size_t read_so_far{ 0 }; - - while ((bytes_read = read(descriptor, command_buffer + read_so_far, size - read_so_far - 1)) > 0) - { - read_so_far += bytes_read; - if (read_so_far >= size - 1) - { - break; - } - } - command_buffer[read_so_far] = 0; - return read_so_far; -} - -enum class status -{ - success, - failure, - warning, - fatal -}; - -template -void make_path(char *destination, const char *directory, const char *filename, const char *extension) -{ - memcpy(destination, directory, strlen(directory)); - std::size_t remaining_space = size - strlen(directory); - - if (extension != nullptr) - { - remaining_space -= strlen(extension) + 1; - } - strncpy(destination + strlen(directory), filename, remaining_space); - - if (extension != nullptr) - { - strncpy(destination + strlen(destination), extension, strlen(extension)); - } -} - -status run_test(const char *file_entry_name) -{ - printf("Running %s. ", file_entry_name); - - char filename[256]; - char command_buffer[256]; - char file_buffer[256]; - int pipe_ends[2]; - - if (pipe(pipe_ends) == -1) - { - perror("pipe"); - return status::fatal; - } - make_path(filename, "./tests/", file_entry_name, nullptr); - - auto child_pid = fork(); - if (child_pid == -1) - { - return status::fatal; - } - else if (child_pid == 0) - { - close(STDIN_FILENO); - close(STDERR_FILENO); - close(pipe_ends[0]); // Close the read end. - - if (dup2(pipe_ends[1], STDOUT_FILENO) == -1) - { - perror("dup2"); - } - else - { - execl(filename, filename); - perror("execl"); - } - close(STDOUT_FILENO); - close(pipe_ends[1]); - std::exit(1); - } - else - { - close(pipe_ends[1]); // Close the write end. - auto read_from_command = read_command(pipe_ends[0], command_buffer); - close(pipe_ends[0]); - - int wait_status{ 0 }; - wait(&wait_status); - - make_path(filename, "./expectations/", file_entry_name, ".txt"); - - std::unique_ptr expectation_descriptor(fopen(filename, "r"), &fclose); - - if (expectation_descriptor == nullptr) - { - return status::warning; - } - auto read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor.get()); - file_buffer[std::max(0, read_from_file)] = 0; - - if (strcmp(command_buffer, file_buffer) != 0) - { - printf("Failed. Got:\n%s", command_buffer); - - return status::failure; - } - else - { - fwrite("\n", 1, 1, stdout); - return status::success; - } - } -} - -struct summary -{ - std::size_t total{ 0 }; - std::size_t failure{ 0 }; - std::size_t success{ 0 }; -}; - -void walk() -{ - auto directory_stream = opendir("./tests"); - dirent *file_entry; - - summary test_summary; - - while ((file_entry = readdir(directory_stream)) != nullptr) - { - if (file_entry->d_name[0] == '.') - { - continue; - } - ++test_summary.total; - switch (run_test(file_entry->d_name)) - { - case status::failure: - ++test_summary.failure; - break; - case status::success: - ++test_summary.success; - break; - case status::warning: - break; - case status::fatal: - goto end_walk; - } - } - printf("Successful: %lu, Failed: %lu, Total: %lu.\n", - test_summary.success, test_summary.failure, test_summary.total); -end_walk: - closedir(directory_stream); -} - -int main() -{ - auto dev_console = open("/dev/console", O_WRONLY); - if (dev_console != -1) - { - dup2(dev_console, STDOUT_FILENO); - walk(); - close(dev_console); - } - sync(); - reboot(RB_POWER_OFF); - - return 1; -}