Remove libc dependency for the generated code

This commit is contained in:
Eugen Wissner 2024-05-23 01:13:16 +02:00
parent 189b2e6054
commit 8ea09ff3b4
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
14 changed files with 362 additions and 417 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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"

View File

@ -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)

View File

@ -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);
} }

View File

@ -1,2 +0,0 @@
writei(3 + 4 + 5 + 1 + 2 + 4 + 3)
.

View File

@ -1 +0,0 @@
22

View File

@ -1 +1 @@
10 8

View File

@ -1 +1 @@
24 H

View File

@ -1,2 +1,2 @@
writei(5 * 2) writei(4 * 2)
. .

View File

@ -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"
}; };

View File

@ -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
View 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;
}

View File

@ -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;
}