Remove libc dependency for the generated code
This commit is contained in:
		| @@ -116,12 +116,10 @@ namespace elna::riscv | |||||||
|     static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer, |     static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer, | ||||||
|             std::vector<reference>& references) |             std::vector<reference>& references) | ||||||
|     { |     { | ||||||
|         writer->sink("printf"); |  | ||||||
|         { |         { | ||||||
|             std::vector<instruction> instructions; |             std::vector<instruction> instructions; | ||||||
|             auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%c\n\0"), 4); |  | ||||||
|  |  | ||||||
|             prologue(instructions); |             prologue(instructions); | ||||||
|  |  | ||||||
|             instructions.push_back(instruction(base_opcode::opImm) |             instructions.push_back(instruction(base_opcode::opImm) | ||||||
|                     .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); |                     .i(x_register::a1, funct3_t::addi, x_register::zero, 't')); | ||||||
|             instructions.push_back(instruction(base_opcode::branch) |             instructions.push_back(instruction(base_opcode::branch) | ||||||
| @@ -129,42 +127,68 @@ namespace elna::riscv | |||||||
|             instructions.push_back(instruction(base_opcode::opImm) |             instructions.push_back(instruction(base_opcode::opImm) | ||||||
|                     .i(x_register::a1, funct3_t::addi, x_register::zero, 'f')); |                     .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) |             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::opImm) | ||||||
|             instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); |                     .i(x_register::t1, funct3_t::addi, x_register::zero, '\n')); | ||||||
|             instructions.push_back(instruction(base_opcode::jalr) |             instructions.push_back(instruction(base_opcode::store) | ||||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); |                     .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<const std::byte *>(instructions.data()), |             writer->sink("writeb", reinterpret_cast<const std::byte *>(instructions.data()), | ||||||
|                     instructions.size() * sizeof(instruction)); |                     instructions.size() * sizeof(instruction)); | ||||||
|         } |         } | ||||||
|         { |         { | ||||||
|             std::vector<instruction> instructions; |             std::vector<instruction> instructions; | ||||||
|             auto format_string = writer->sink(reinterpret_cast<const std::byte *>("%d\n\0"), 4); |  | ||||||
|  |  | ||||||
|             prologue(instructions); |             prologue(instructions); | ||||||
|  |  | ||||||
|             instructions.push_back(instruction(base_opcode::opImm) |             instructions.push_back(instruction(base_opcode::opImm) | ||||||
|                     .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); |                     .i(x_register::t0, 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); |  | ||||||
|             instructions.push_back(instruction(base_opcode::opImm) |             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::opImm) | ||||||
|             instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); |                     .i(x_register::t1, funct3_t::addi, x_register::zero, '\n')); | ||||||
|             instructions.push_back(instruction(base_opcode::jalr) |             instructions.push_back(instruction(base_opcode::store) | ||||||
|                     .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); |                     .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<const std::byte *>(instructions.data()), |             writer->sink("writei", reinterpret_cast<const std::byte *>(instructions.data()), | ||||||
|                     instructions.size() * sizeof(instruction)); |                     instructions.size() * sizeof(instruction)); | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| #include <string> |  | ||||||
| #include <filesystem> | #include <filesystem> | ||||||
|  |  | ||||||
| #define BOOST_PROCESS_USE_STD_FS | #define BOOST_PROCESS_USE_STD_FS | ||||||
| @@ -39,7 +38,6 @@ namespace elna | |||||||
|             boost::asio::readable_pipe& read_pipe); |             boost::asio::readable_pipe& read_pipe); | ||||||
|     int run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number, |     int run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number, | ||||||
|             const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments); |             const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments); | ||||||
|     std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object); |  | ||||||
|     test_status build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry); |     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 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); |     void print_result(const std::filesystem::path& test_entry, const test_status& result); | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|   "main": "tools/index.js", |   "main": "tools/index.js", | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "start": "node tools/index.js" |     "cross": "node tools/cross.js" | ||||||
|   }, |   }, | ||||||
|   "author": "Eugen Wissner <belka@caraus.de>", |   "author": "Eugen Wissner <belka@caraus.de>", | ||||||
|   "license": "MPL-2.0" |   "license": "MPL-2.0" | ||||||
|   | |||||||
| @@ -165,7 +165,7 @@ namespace elna::source | |||||||
|         this->current.emplace_back(quadruple_operator::start); |         this->current.emplace_back(quadruple_operator::start); | ||||||
|         program->body().accept(this); |         program->body().accept(this); | ||||||
|         this->current.emplace_back(quadruple_operator::stop); |         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) |     void intermediate_code_generator::visit(call_statement *statement) | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ namespace elna::source | |||||||
|     void name_analysis_visitor::visit(program *program) |     void name_analysis_visitor::visit(program *program) | ||||||
|     { |     { | ||||||
|         class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size }; |         class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size }; | ||||||
|         this->table->enter("main", std::make_shared<procedure_info>(main_type, this->table)); |         this->table->enter("_start", std::make_shared<procedure_info>(main_type, this->table)); | ||||||
|         empty_visitor::visit(program); |         empty_visitor::visit(program); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -105,7 +105,7 @@ namespace elna::source | |||||||
|         this->argument_offset = 0; |         this->argument_offset = 0; | ||||||
|  |  | ||||||
|         empty_visitor::visit(program); |         empty_visitor::visit(program); | ||||||
|         std::dynamic_pointer_cast<procedure_info>(table->lookup("main"))->local_stack_size = |         std::dynamic_pointer_cast<procedure_info>(table->lookup("_start"))->local_stack_size = | ||||||
|             std::abs(this->local_offset); |             std::abs(this->local_offset); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,2 +0,0 @@ | |||||||
| writei(3 + 4 + 5 + 1 + 2 + 4 + 3) |  | ||||||
| . |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| 22 |  | ||||||
| @@ -1 +1 @@ | |||||||
| 10 | 8 | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| 24 | H | ||||||
|   | |||||||
| @@ -1,2 +1,2 @@ | |||||||
| writei(5 * 2) | writei(4 * 2) | ||||||
| . | . | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
| #include <filesystem> | #include <filesystem> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <algorithm> |  | ||||||
| #include <boost/process/env.hpp> | #include <boost/process/env.hpp> | ||||||
|  |  | ||||||
| namespace elna | namespace elna | ||||||
| @@ -105,22 +104,6 @@ namespace elna | |||||||
|         return elna_child.wait(); |         return elna_child.wait(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::string find_object(const std::vector<std::filesystem::path>& 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) |     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(); |         const std::filesystem::path test_filename = test_entry.path().filename(); | ||||||
| @@ -145,17 +128,7 @@ namespace elna | |||||||
|         std::vector<std::filesystem::path> environment; |         std::vector<std::filesystem::path> environment; | ||||||
|         std::vector<std::string> linker_arguments = { "-o", test_binary.string() }; |         std::vector<std::string> 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.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"), |         boost::process::v2::execute(boost::process::v2::process(context, boost::process::search_path("ld"), | ||||||
|                     linker_arguments |                     linker_arguments | ||||||
| @@ -248,7 +221,7 @@ namespace elna | |||||||
|             "-nographic", |             "-nographic", | ||||||
|             "-M", "virt", |             "-M", "virt", | ||||||
|             "-bios", "default", |             "-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", |             "-append", "quiet", | ||||||
|             "-initrd", "build/root.cpio" |             "-initrd", "build/root.cpio" | ||||||
|         }; |         }; | ||||||
|   | |||||||
| @@ -7,45 +7,57 @@ import { Buffer } from 'node:buffer' | |||||||
| 
 | 
 | ||||||
| // Define constants.
 | // Define constants.
 | ||||||
| const tmp = path.resolve('./build/tools') | const tmp = path.resolve('./build/tools') | ||||||
| const target = 'riscv32-unknown-linux-gnu' |  | ||||||
| const baseDirectory = path.resolve('./tools') | const baseDirectory = path.resolve('./tools') | ||||||
| 
 | 
 | ||||||
| const busyboxVersion = '1.36.1' | const kernelVersion = '5.15.159' | ||||||
| const kernelVersion = '5.15.158' |  | ||||||
| const gccVersion = '14.1.0' | const gccVersion = '14.1.0' | ||||||
| const binutilsVersion = '2.42' | const binutilsVersion = '2.42' | ||||||
| const glibcVersion = '2.39' | const glibcVersion = '2.39' | ||||||
| 
 | 
 | ||||||
| function findBuildTarget () { | async function gccVerbose (gccBinary) { | ||||||
|   const env = { |   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 = [] |   const buffers = [] | ||||||
| 
 | 
 | ||||||
|   gccV.stderr.on('data', function (data) { |   gccV.stderr.on('data', function (data) { | ||||||
|     buffers.push(data) |     buffers.push(data) | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   return new Promise(function (resolve, reject) { |   return new Promise(function (resolve, reject) { | ||||||
|     gccV.on('exit', function () { |     gccV.on('exit', function () { | ||||||
|       const [_, target] = Buffer.concat(buffers) |       resolve(Buffer.concat(buffers).toString()) | ||||||
|         .toString() |  | ||||||
|         .split('\n') |  | ||||||
|         .find(line => line.startsWith('Target: ')) |  | ||||||
|         .split(' ') |  | ||||||
| 
 |  | ||||||
|       resolve(target) |  | ||||||
|     }) |     }) | ||||||
|   }) |   }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createImage (rootfs) { | async function findBuildTarget (gccVersion) { | ||||||
|   const rootExt4 = path.join(tmp, 'rootfs.ext4') |   let gccBinary = 'gcc' | ||||||
|  |   let output = await gccVerbose(gccBinary) | ||||||
| 
 | 
 | ||||||
|   childProcess.execFileSync('dd', ['if=/dev/zero', `of=${rootExt4}`, 'bs=1M', 'count=1024'], { stdio: 'inherit' }) |   if (output.startsWith('Apple clang')) { | ||||||
|   childProcess.execFileSync('/sbin/mkfs.ext4', ['-d', rootfs, rootExt4], { stdio: 'inherit' }) |     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) { | async function downloadAndUnarchive (url) { | ||||||
| @@ -89,70 +101,70 @@ async function downloadAndUnarchive (url) { | |||||||
|   }) |   }) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function buildBusyBox (sysroot, rootfs) { | async function copyGlibcHeaders () { | ||||||
|   const cwd = await downloadAndUnarchive(new URL(`https://busybox.net/downloads/busybox-${busyboxVersion}.tar.bz2`)) |   const sourceDirectory = await downloadAndUnarchive( | ||||||
|   const env = { |     new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`) | ||||||
|     ...process.env, |   ) | ||||||
|     CROSS_COMPILE: path.join(sysroot, 'bin', `${target}-`) |   const includeDirectory = path.join(tmp, 'include') | ||||||
|   } | 
 | ||||||
|   const configuration = [ |   await fs.mkdir(includeDirectory) | ||||||
|     `CONFIG_PREFIX=${rootfs}`, |   await fs.cp(path.join(sourceDirectory, 'elf/elf.h'), path.join(includeDirectory, 'elf.h')) | ||||||
|     `CONFIG_SYSROOT=${rootfs}` | 
 | ||||||
|   ] |   return sourceDirectory | ||||||
|   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 buildKernel (sysroot, rootfs) { | async function buildKernel (rootfs, options) { | ||||||
|   const cwd = await downloadAndUnarchive( |   const cwd = await downloadAndUnarchive( | ||||||
|     new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`) |     new URL(`https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${kernelVersion}.tar.xz`) | ||||||
|   ) |   ) | ||||||
|   const env = { |   const env = { | ||||||
|     ...process.env, |     ...process.env, | ||||||
|     CROSS_COMPILE: `${target}-`, |     CROSS_COMPILE: `${options.target}-`, | ||||||
|     ARCH: 'riscv', |     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', ['rv32_defconfig'], { stdio: 'inherit', env, cwd }) | ||||||
|   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) |   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, cwd }) | ||||||
|   childProcess.execFileSync('make', ['headers'], { 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 }) |   await fs.cp(path.join(cwd, 'usr/include'), path.join(userDirectory, 'include'), { recursive: true }) | ||||||
| 
 | 
 | ||||||
|   return path.join(cwd, 'arch/riscv/boot/Image') |   return path.join(cwd, 'arch/riscv/boot/Image') | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function buildInit (sysroot) { | async function buildInit (rootfs, options) { | ||||||
|   const env = { |   const env = { | ||||||
|     ...process.env, |     ...process.env, | ||||||
|     PATH: `${path.join(sysroot, 'bin')}:${process.env['PATH']}` |     PATH: `${path.join(rootfs, 'bin')}:${process.env['PATH']}` | ||||||
|   } |   } | ||||||
|   const compilerArguments = [ |   const compilerArguments = [ | ||||||
|     '-ffreestanding', '-static', |     '-ffreestanding', '-static', | ||||||
|     '-o', path.join(tmp, 'init'), |     '-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) { | async function buildGlibc (sourceDirectory, rootfs, options) { | ||||||
|   const sourceDirectory = await downloadAndUnarchive( |  | ||||||
|     new URL(`https://ftp.gnu.org/gnu/glibc/glibc-${glibcVersion}.tar.xz`) |  | ||||||
|   ) |  | ||||||
|   const configureOptions = [ |   const configureOptions = [ | ||||||
|     '--prefix=/usr', |     '--prefix=/usr', | ||||||
|     '--libdir=/usr/lib', |     `--host=${options.target}`, | ||||||
|     `--host=${target}`, |     `--target=${options.target}`, | ||||||
|     `--build=${options.build}`, |     `--build=${options.build}`, | ||||||
|     `--enable-kernel=${kernelVersion}`, |     `--enable-kernel=${kernelVersion}`, | ||||||
|     `--with-headers=${path.join(rootfs, 'usr/include')}`, |     `--with-headers=${path.join(options.sysroot, 'usr/include')}`, | ||||||
|     '--disable-nscd' |     '--disable-nscd', | ||||||
|  |     '--disable-libquadmath', | ||||||
|  |     '--disable-libitm', | ||||||
|  |     '--disable-werror', | ||||||
|  |     'libc_cv_forced_unwind=yes' | ||||||
|   ] |   ] | ||||||
|  |   const bin = path.join(rootfs, 'bin') | ||||||
|   const env = { |   const env = { | ||||||
|     ...process.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'); |   const cwd = path.join(path.dirname(sourceDirectory), 'build-glibc'); | ||||||
|   await fs.mkdir(cwd) |   await fs.mkdir(cwd) | ||||||
| @@ -163,35 +175,42 @@ async function buildGlibc (sysroot, rootfs, options) { | |||||||
|     cwd |     cwd | ||||||
|   }) |   }) | ||||||
|   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', env, 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( |   const sourceDirectory = await downloadAndUnarchive( | ||||||
|     new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`) |     new URL(`https://ftp.gnu.org/gnu/binutils/binutils-${binutilsVersion}.tar.xz`) | ||||||
|   ) |   ) | ||||||
|   const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils'); |   const cwd = path.join(path.dirname(sourceDirectory), 'build-binutils'); | ||||||
|   await fs.mkdir(cwd) |   await fs.mkdir(cwd) | ||||||
| 
 | 
 | ||||||
|  |   const env = { | ||||||
|  |     ...process.env, | ||||||
|  |     CC: options.gcc, | ||||||
|  |     CXX: options.gxx | ||||||
|  |   } | ||||||
|   const configureOptions = [ |   const configureOptions = [ | ||||||
|     `--prefix=${sysroot}`, |     `--prefix=${rootfs}`, | ||||||
|     `--target=${target}`, |     `--target=${options.target}`, | ||||||
|     '--disable-nls', |     '--disable-nls', | ||||||
|     '--enable-gprofng=no', |     '--enable-gprofng=no', | ||||||
|     '--disable-werror', |     '--disable-werror', | ||||||
|     '--enable-default-hash-style=gnu' |     '--enable-default-hash-style=gnu', | ||||||
|  |     '--disable-libquadmath' | ||||||
|   ] |   ] | ||||||
|   childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { |   childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { | ||||||
|     stdio: 'inherit', |     stdio: 'inherit', | ||||||
|     cwd |     cwd, | ||||||
|  |     env | ||||||
|   }) |   }) | ||||||
|   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd }) |   childProcess.execFileSync('make', ['-j', os.availableParallelism()], { stdio: 'inherit', cwd, env }) | ||||||
|   childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd }) |   childProcess.execFileSync('make', ['install'], { stdio: 'inherit', cwd, env }) | ||||||
| 
 | 
 | ||||||
|   return sourceDirectory |   return sourceDirectory | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function buildGCC1 (sysroot, rootfs, options) { | async function buildGCC1 (rootfs, options) { | ||||||
|   const sourceDirectory = await downloadAndUnarchive( |   const sourceDirectory = await downloadAndUnarchive( | ||||||
|     new URL(`https://gcc.gnu.org/pub/gcc/releases/gcc-${gccVersion}/gcc-${gccVersion}.tar.xz`) |     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 |     cwd: sourceDirectory | ||||||
|   }) |   }) | ||||||
|   const configureOptions = [ |   const configureOptions = [ | ||||||
|     `--prefix=${sysroot}`, |     `--prefix=${rootfs}`, | ||||||
|     `--with-sysroot=${rootfs}`, |     `--with-sysroot=${options.sysroot}`, | ||||||
|     '--enable-languages=c,c++', |     '--enable-languages=c,c++', | ||||||
|     '--disable-shared', |     '--disable-shared', | ||||||
|     '--with-arch=rv32imafdc', |     '--with-arch=rv32imafdc', | ||||||
| @@ -225,16 +244,18 @@ async function buildGCC1 (sysroot, rootfs, options) { | |||||||
|     '--disable-nls', |     '--disable-nls', | ||||||
|     '--with-newlib', |     '--with-newlib', | ||||||
|     '--without-headers', |     '--without-headers', | ||||||
|     `--target=${target}`, |     `--target=${options.target}`, | ||||||
|     `--build=${options.build}`, |     `--build=${options.build}`, | ||||||
|     `--host=${options.build}` |     `--host=${options.build}` | ||||||
|   ] |   ] | ||||||
|   const flags = '-O2 -fPIC' |   const flags = '-O2 -fPIC' | ||||||
|   const env = { |   const env = { | ||||||
|     ...process.env, |     ...process.env, | ||||||
|  |     CC: options.gcc, | ||||||
|  |     CXX: options.gxx, | ||||||
|     CFLAGS: flags, |     CFLAGS: flags, | ||||||
|     CXXFLAGS: 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, { |   childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { | ||||||
|     stdio: 'inherit', |     stdio: 'inherit', | ||||||
| @@ -247,45 +268,14 @@ async function buildGCC1 (sysroot, rootfs, options) { | |||||||
|   return sourceDirectory |   return sourceDirectory | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function buildBinutils (sourceDirectory, sysroot, rootfs, options) { | async function buildGCC2 (sourceDirectory, 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) { |  | ||||||
|   const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc'); |   const cwd = path.join(path.dirname(sourceDirectory), 'build-gcc'); | ||||||
|   await fs.rm(cwd, { recursive: true, force: true }) |   await fs.rm(cwd, { recursive: true, force: true }) | ||||||
|   await fs.mkdir(cwd) |   await fs.mkdir(cwd) | ||||||
| 
 | 
 | ||||||
|   const configureOptions = [ |   const configureOptions = [ | ||||||
|     `--prefix=${sysroot}`, |     `--prefix=${rootfs}`, | ||||||
|     `--with-sysroot=${rootfs}`, |     `--with-sysroot=${options.sysroot}`, | ||||||
|     '--enable-languages=c,c++,lto', |     '--enable-languages=c,c++,lto', | ||||||
|     '--enable-lto', |     '--enable-lto', | ||||||
|     '--enable-shared', |     '--enable-shared', | ||||||
| @@ -297,10 +287,11 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { | |||||||
|     '--disable-multilib', |     '--disable-multilib', | ||||||
|     '--enable-checking=release', |     '--enable-checking=release', | ||||||
|     '--disable-libssp', |     '--disable-libssp', | ||||||
|  |     '--disable-libquadmath', | ||||||
|     '--enable-threads=posix', |     '--enable-threads=posix', | ||||||
|     '--with-default-libstdcxx-abi=new', |     '--with-default-libstdcxx-abi=new', | ||||||
|     '--disable-nls', |     '--disable-nls', | ||||||
|     `--target=${target}`, |     `--target=${options.target}`, | ||||||
|     `--build=${options.build}`, |     `--build=${options.build}`, | ||||||
|     `--host=${options.build}` |     `--host=${options.build}` | ||||||
|   ] |   ] | ||||||
| @@ -309,7 +300,7 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { | |||||||
|     ...process.env, |     ...process.env, | ||||||
|     CFLAGS: flags, |     CFLAGS: flags, | ||||||
|     CXXFLAGS: 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, { |   childProcess.execFileSync(path.join(path.relative(cwd, sourceDirectory), 'configure'), configureOptions, { | ||||||
|     stdio: 'inherit', |     stdio: 'inherit', | ||||||
| @@ -320,83 +311,25 @@ async function buildGCC2 (sourceDirectory, sysroot, rootfs, options) { | |||||||
|   childProcess.execFileSync('make', ['install'], { stdio: 'inherit', env, cwd }) |   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 rootfs = path.join(tmp, 'rootfs') | ||||||
| 
 | 
 | ||||||
| const options = { | const options = await findBuildTarget(gccVersion) | ||||||
|   build: await findBuildTarget() | 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.rm(targetDirectory, { recursive: true, force: true }) | ||||||
|   await fs.mkdir(targetDirectory) |   await fs.mkdir(targetDirectory) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const binutilsSource = await buildCrossBinutils(sysroot) | const binutilsSource = await buildCrossBinutils(rootfs, options) | ||||||
| const gccSource = await buildGCC1(sysroot, rootfs, options) | const gccSource = await buildGCC1(rootfs, options) | ||||||
| 
 | 
 | ||||||
| const kernelImage = await buildKernel(sysroot, rootfs) | const glibcSource = await copyGlibcHeaders() | ||||||
| await buildGlibc(sysroot, rootfs, options) | const kernelImage = await buildKernel(rootfs, options) | ||||||
| await buildBinutils(binutilsSource, sysroot, rootfs, options) | await buildGlibc(glibcSource, rootfs, options) | ||||||
| await buildGCC2(gccSource, sysroot, rootfs, options) | await buildGCC2(gccSource, rootfs, options) | ||||||
| buildInit (sysroot) | buildInit (rootfs, options) | ||||||
| 
 | 
 | ||||||
| await buildBusyBox(sysroot, rootfs) |  | ||||||
| await createRoot(rootfs) |  | ||||||
| 
 |  | ||||||
| await buildGCC3(gccSource, sysroot, rootfs, options) |  | ||||||
| createImage(rootfs) |  | ||||||
| console.log(kernelImage) | console.log(kernelImage) | ||||||
							
								
								
									
										205
									
								
								tools/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								tools/init.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include <dirent.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include <sys/reboot.h> | ||||||
|  |  | ||||||
|  | #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; | ||||||
|  | } | ||||||
							
								
								
									
										185
									
								
								tools/init.cpp
									
									
									
									
									
								
							
							
						
						
									
										185
									
								
								tools/init.cpp
									
									
									
									
									
								
							| @@ -1,185 +0,0 @@ | |||||||
| #include <algorithm> |  | ||||||
| #include <memory> |  | ||||||
| #include <cstdlib> |  | ||||||
| #include <cstdio> |  | ||||||
| #include <cstring> |  | ||||||
| #include <dirent.h> |  | ||||||
| #include <fcntl.h> |  | ||||||
| #include <unistd.h> |  | ||||||
| #include <sys/stat.h> |  | ||||||
| #include <sys/wait.h> |  | ||||||
| #include <sys/reboot.h> |  | ||||||
|  |  | ||||||
| template<std::size_t size> |  | ||||||
| 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<std::size_t size> |  | ||||||
| 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<sizeof(filename)>(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<sizeof(command_buffer)>(pipe_ends[0], command_buffer); |  | ||||||
|         close(pipe_ends[0]); |  | ||||||
|  |  | ||||||
|         int wait_status{ 0 }; |  | ||||||
|         wait(&wait_status); |  | ||||||
|  |  | ||||||
|         make_path<sizeof(filename)>(filename, "./expectations/", file_entry_name, ".txt"); |  | ||||||
|  |  | ||||||
|         std::unique_ptr<FILE, int(*)(FILE *)> 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<std::ptrdiff_t>(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; |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user