From 7878653e8dbd65d94ea8ea8bae6e0afd3c3d0af3 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Tue, 27 May 2025 23:02:19 +0200 Subject: fix: fix SEGFAULT on CxxDrv (AMD64), caused by a stack corruption. refactor: Refactor LibCompiler's codebase, deperecate older C compilers, and fully focusing on C++ now. Signed-off-by: Amlal El Mahrouss --- dev/LibCompiler/src/Backend/Assembler32x0.cc | 49 + dev/LibCompiler/src/Backend/Assembler64x0.cc | 873 ++++++++++++++++ dev/LibCompiler/src/Backend/AssemblerAMD64.cc | 1203 +++++++++++++++++++++++ dev/LibCompiler/src/Backend/AssemblerARM64.cc | 585 +++++++++++ dev/LibCompiler/src/Backend/AssemblerPowerPC.cc | 909 +++++++++++++++++ 5 files changed, 3619 insertions(+) create mode 100644 dev/LibCompiler/src/Backend/Assembler32x0.cc create mode 100644 dev/LibCompiler/src/Backend/Assembler64x0.cc create mode 100644 dev/LibCompiler/src/Backend/AssemblerAMD64.cc create mode 100644 dev/LibCompiler/src/Backend/AssemblerARM64.cc create mode 100644 dev/LibCompiler/src/Backend/AssemblerPowerPC.cc (limited to 'dev/LibCompiler/src/Backend') diff --git a/dev/LibCompiler/src/Backend/Assembler32x0.cc b/dev/LibCompiler/src/Backend/Assembler32x0.cc new file mode 100644 index 0000000..683b866 --- /dev/null +++ b/dev/LibCompiler/src/Backend/Assembler32x0.cc @@ -0,0 +1,49 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @file 32asm.cxx +// @author EL Mahrouss Amlal +// @brief 32x0 Assembler. + +// REMINDER: when dealing with an undefined symbol use (string +// size):LinkerFindSymbol:(string) so that ld will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_32x0__ 1 + +#include +#include +#include +#include + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +#define kStdOut (std::cout << kWhite) +#define kStdErr (std::cout << kRed) + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief 32x0 Assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +LIBCOMPILER_MODULE(NEAssemblerMain32000) { + return 0; +} diff --git a/dev/LibCompiler/src/Backend/Assembler64x0.cc b/dev/LibCompiler/src/Backend/Assembler64x0.cc new file mode 100644 index 0000000..fb96708 --- /dev/null +++ b/dev/LibCompiler/src/Backend/Assembler64x0.cc @@ -0,0 +1,873 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @file Assembler64x0.cxx +// @author EL Mahrouss Amlal +// @brief 64x000 Assembler. + +// REMINDER: when dealing with an undefined symbol use (string +// size):LinkerFindSymbol:(string) so that ld will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_64x0__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +static char kOutputArch = LibCompiler::kPefArch64000; + +/// @note The 64x0 is VLSIW, so we need to jump to 4 bytes. +constexpr auto k64x0IPAlignment = 0x4U; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector> kOriginLabel; + +static std::vector kBytes; + +static LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; +static const std::string kRelocSymbol = ":RuntimeSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string line); + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief 64x0 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +LIBCOMPILER_MODULE(AssemblerMain64x0) { + ::signal(SIGSEGV, Detail::drvi_crash_handler); + + for (size_t i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "-64x0-ver") == 0 || strcmp(argv[i], "-64x0-v") == 0) { + kStdOut + << "Assembler64x0: 64x0 Assembler.\nAssembler64x0: v1.10\nAssembler64x0: Copyright (c) " + "Amlal El Mahrouss\n"; + return 0; + } else if (strcmp(argv[i], "-64x0-h") == 0) { + kStdOut << "Assembler64x0: 64x0 Assembler.\nAssembler64x0: Copyright (c) 2024 Mahrouss " + "Logic.\n"; + kStdOut << "--version: Print program version.\n"; + kStdOut << "--verbose: Print verbose output.\n"; + kStdOut << "--binary: Output as flat binary.\n"; + kStdOut << "--64xxx: Compile for a subset of the X64000.\n"; + + return 0; + } else if (strcmp(argv[i], "-64x0-binary") == 0) { + kOutputAsBinary = true; + continue; + } else if (strcmp(argv[i], "-64x0-verbose") == 0) { + kVerbose = true; + continue; + } + + kStdOut << "Assembler64x0: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) { + kStdOut << "Assembler64x0: can't open: " << argv[i] << std::endl; + goto asm_fail_exit; + } + + std::string object_output(argv[i]); + + for (auto& ext : kAsmFileExts) { + if (object_output.find(ext) != std::string::npos) { + object_output.erase(object_output.find(ext), std::strlen(ext)); + } + } + + object_output += kOutputAsBinary ? kBinaryFileExt : kObjectFileExt; + + std::ifstream file_ptr(argv[i]); + std::ofstream file_ptr_out(object_output, std::ofstream::binary); + + if (file_ptr_out.bad()) { + if (kVerbose) { + kStdOut << "Assembler64x0: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::Encoder64x0 asm64; + + while (std::getline(file_ptr, line)) { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { + Detail::print_error(ln, argv[i]); + continue; + } + + try { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } catch (const std::exception& e) { + if (kVerbose) { + std::string what = e.what(); + Detail::print_warning("exit because of: " + what, "LibCompiler"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) { + if (kVerbose) { + kStdOut << "Assembler64x0: Writing object file...\n"; + } + + // this is the final step, write everything to the file. + + auto pos = file_ptr_out.tellp(); + + hdr.fCount = kRecords.size() + kUndefinedSymbols.size(); + + file_ptr_out << hdr; + + if (kRecords.empty()) { + kStdErr << "Assembler64x0: At least one record is needed to write an object " + "file.\nAssembler64x0: Make one using `public_segment .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return 1; + } + + kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + std::size_t record_count = 0UL; + + for (auto& rec : kRecords) { + if (kVerbose) kStdOut << "Assembler64x0: Wrote record " << rec.fName << " to file...\n"; + + rec.fFlags |= LibCompiler::kKindRelocationAtRuntime; + rec.fOffset = record_count; + ++record_count; + + file_ptr_out << rec; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto& sym : kUndefinedSymbols) { + LibCompiler::AERecordHeader _record_hdr{0}; + + if (kVerbose) kStdOut << "Assembler64x0: Wrote symbol " << sym << " to file...\n"; + + _record_hdr.fKind = kAENullType; + _record_hdr.fSize = sym.size(); + _record_hdr.fOffset = record_count; + + ++record_count; + + memset(_record_hdr.fPad, kAENullType, kAEPad); + memcpy(_record_hdr.fName, sym.c_str(), sym.size()); + + file_ptr_out << _record_hdr; + + ++kCounter; + } + + auto pos_end = file_ptr_out.tellp(); + + file_ptr_out.seekp(pos); + + hdr.fStartCode = pos_end; + hdr.fCodeSize = kBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } else { + if (kVerbose) { + kStdOut << "Assembler64x0: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto& byte : kBytes) { + file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); + } + + if (kVerbose) kStdOut << "Assembler64x0: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) kStdOut << "Assembler64x0: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) kStdOut << "Assembler64x0: Exit failed.\n"; + + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string line) { + // extern_segment is the opposite of public_segment, it signals to the ld + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid extern_segment directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment")); + + /// sanity check to avoid stupid linker errors. + if (name.size() == 0) { + Detail::print_error("Invalid extern_segment", "power-as"); + throw std::runtime_error("invalid_extern_segment"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char& j : name) { + if (j == ' ' || j == ',') j = '$'; + } + + result += name; + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by Assembler64x0 to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (LibCompiler::find_word(line, "public_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid public_segment directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment")); + + std::string name_copy = name; + + for (char& j : name) { + if (j == ' ') j = '$'; + } + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Detail::algorithm { +// \brief authorize a brief set of characters. +static inline bool is_not_alnum_space(char c) { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || (c == ',') || (c == '(') || + (c == ')') || (c == '"') || (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.')); +} + +bool is_valid_64x0(std::string str) { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); +} +} // namespace Detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::Encoder64x0::CheckLine(std::string line, std::string file) { + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || line.find('#') != std::string::npos || + LibCompiler::find_word(line, ";")) { + if (line.find('#') != std::string::npos) { + line.erase(line.find('#')); + } else if (line.find(';') != std::string::npos) { + line.erase(line.find(';')); + } else { + // now check the line for validity + if (!Detail::algorithm::is_valid_64x0(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!Detail::algorithm::is_valid_64x0(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) { + if (line.find(',') + 1 == line.size()) { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } else { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto& ch : substr) { + if (ch != ' ' && ch != '\t') { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + + // these do take an argument. + std::vector operands_inst = {"stw", "ldw", "lda", "sta"}; + + // these don't. + std::vector filter_inst = {"jlr", "jrl", "int"}; + + for (auto& opcode64x0 : kOpcodes64x0) { + if (line.find(opcode64x0.fName) != std::string::npos) { + if (opcode64x0.fFunct7 == kAsmNoArgs) return err_str; + + for (auto& op : operands_inst) { + // if only the instruction was found. + if (line == op) { + err_str += "\nMalformed "; + err_str += op; + err_str += " instruction, here -> "; + err_str += line; + } + } + + // if it is like that -> addr1, 0x0 + if (auto it = std::find(filter_inst.begin(), filter_inst.end(), opcode64x0.fName); + it == filter_inst.cend()) { + if (LibCompiler::find_word(line, opcode64x0.fName)) { + if (!isspace(line[line.find(opcode64x0.fName) + strlen(opcode64x0.fName)])) { + err_str += "\nMissing space between "; + err_str += opcode64x0.fName; + err_str += " and operands.\nhere -> "; + err_str += line; + } + } + } + + return err_str; + } + } + + err_str += "Unrecognized instruction: " + line; + + return err_str; +} + +bool LibCompiler::Encoder64x0::WriteNumber(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex_number"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "Assembler64x0: found a base 16 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "Assembler64x0: found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + kBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "Assembler64x0: found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + kBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "Assembler64x0: found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::Encoder64x0::WriteLine(std::string line, std::string file) { + if (LibCompiler::find_word(line, "public_segment ")) return true; + + for (auto& opcode64x0 : kOpcodes64x0) { + // strict check here + if (LibCompiler::find_word(line, opcode64x0.fName) && Detail::algorithm::is_valid_64x0(line)) { + std::string name(opcode64x0.fName); + std::string jump_label, cpy_jump_label; + + kBytes.emplace_back(opcode64x0.fOpcode); + kBytes.emplace_back(opcode64x0.fFunct3); + kBytes.emplace_back(opcode64x0.fFunct7); + + // check funct7 type. + switch (opcode64x0.fFunct7) { + // reg to reg means register to register transfer operation. + case kAsmRegToReg: + case kAsmImmediate: { + // \brief how many registers we found. + std::size_t found_some = 0UL; + + for (size_t line_index = 0UL; line_index < line.size(); line_index++) { + if (line[line_index] == kAsmRegisterPrefix[0] && isdigit(line[line_index + 1])) { + std::string register_syntax = kAsmRegisterPrefix; + register_syntax += line[line_index + 1]; + + if (isdigit(line[line_index + 2])) register_syntax += line[line_index + 2]; + + std::string reg_str; + reg_str += line[line_index + 1]; + + if (isdigit(line[line_index + 2])) reg_str += line[line_index + 2]; + + // it ranges from r0 to r19 + // something like r190 doesn't exist in the instruction set. + if (kOutputArch == LibCompiler::kPefArch64000) { + if (isdigit(line[line_index + 3]) && isdigit(line[line_index + 2])) { + reg_str += line[line_index + 3]; + Detail::print_error("invalid register index, r" + reg_str + + "\nnote: The 64x0 accepts registers from r0 to r20.", + file); + throw std::runtime_error("invalid_register_index"); + } + } + + // finally cast to a size_t + std::size_t reg_index = strtol(reg_str.c_str(), nullptr, 10); + + if (reg_index > kAsmRegisterLimit) { + Detail::print_error("invalid register index, r" + reg_str, file); + throw std::runtime_error("invalid_register_index"); + } + + kBytes.emplace_back(reg_index); + ++found_some; + + if (kVerbose) { + kStdOut << "Assembler64x0: Register found: " << register_syntax << "\n"; + kStdOut << "Assembler64x0: Register amount in instruction: " << found_some << "\n"; + } + } + } + + // we're not in immediate addressing, reg to reg. + if (opcode64x0.fFunct7 != kAsmImmediate) { + // remember! register to register! + if (found_some == 1) { + Detail::print_error( + "Too few registers.\ntip: each Assembler64x0 register " + "starts with 'r'.\nline: " + + line, + file); + throw std::runtime_error("not_a_register"); + } + } + + if (found_some < 1 && name != "ldw" && name != "lda" && name != "stw") { + Detail::print_error("invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } else if (found_some == 1 && name == "add") { + Detail::print_error("invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } else if (found_some == 1 && name == "sub") { + Detail::print_error("invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } + + if (found_some > 0 && name == "pop") { + Detail::print_error( + "invalid combination for opcode 'pop'.\ntip: it expects " + "nothing.\nline: " + + line, + file); + throw std::runtime_error("invalid_comb_op_pop"); + } + } + default: + break; + } + + // try to fetch a number from the name + if (name == "stw" || name == "ldw" || name == "lda" || name == "sta") { + auto where_string = name; + + // if we load something, we'd need it's symbol/literal + if (name == "stw" || name == "sta" || name == "ldw" || name == "lda" || name == "sta") + where_string = ","; + + jump_label = line; + + auto found_sym = false; + + while (jump_label.find(where_string) != std::string::npos) { + jump_label = jump_label.substr(jump_label.find(where_string) + where_string.size()); + + while (jump_label.find(" ") != std::string::npos) { + jump_label.erase(jump_label.find(" "), 1); + } + + if (jump_label[0] != kAsmRegisterPrefix[0] && !isdigit(jump_label[1])) { + if (found_sym) { + Detail::print_error( + "invalid combination of opcode and operands.\nhere -> " + jump_label, file); + throw std::runtime_error("invalid_comb_op_ops"); + } else { + // death trap installed. + found_sym = true; + } + } + } + + cpy_jump_label = jump_label; + + // replace any spaces with $ + if (jump_label[0] == ' ') { + while (jump_label.find(' ') != std::string::npos) { + if (isalnum(jump_label[0]) || isdigit(jump_label[0])) break; + + jump_label.erase(jump_label.find(' '), 1); + } + } + + if (!this->WriteNumber(0, jump_label)) { + // sta expects this: sta 0x000000, r0 + if (name == "sta") { + Detail::print_error("invalid combination of opcode and operands.\nHere ->" + line, + file); + throw std::runtime_error("invalid_comb_op_ops"); + } + } else { + if (name == "sta" && cpy_jump_label.find("extern_segment ") != std::string::npos) { + Detail::print_error("invalid usage extern_segment on 'sta', here: " + line, file); + throw std::runtime_error("invalid_sta_usage"); + } + } + + goto asm_write_label; + } + + // This is the case where we jump to a label, it is also used as a goto. + if (name == "lda" || name == "sta") { + asm_write_label: + if (cpy_jump_label.find('\n') != std::string::npos) + cpy_jump_label.erase(cpy_jump_label.find('\n'), 1); + + if (cpy_jump_label.find("extern_segment") != std::string::npos) { + cpy_jump_label.erase(cpy_jump_label.find("extern_segment"), strlen("extern_segment")); + + if (name == "sta") { + Detail::print_error("extern_segment is not allowed on a sta operation.", file); + throw std::runtime_error("extern_segment_sta_op"); + } else { + goto asm_end_label_cpy; + } + } + + if (name == "lda" || name == "sta") { + for (auto& label : kOriginLabel) { + if (cpy_jump_label == label.first) { + if (kVerbose) { + kStdOut << "Assembler64x0: Replace label " << cpy_jump_label + << " to address: " << label.second << std::endl; + } + + LibCompiler::NumberCast64 num(label.second); + + for (auto& num : num.number) { + kBytes.push_back(num); + } + + goto asm_end_label_cpy; + } + } + + if (cpy_jump_label[0] == '0') { + switch (cpy_jump_label[1]) { + case 'x': + case 'o': + case 'b': + if (this->WriteNumber(0, cpy_jump_label)) goto asm_end_label_cpy; + + break; + default: + break; + } + + if (isdigit(cpy_jump_label[0])) { + if (this->WriteNumber(0, cpy_jump_label)) goto asm_end_label_cpy; + + break; + } + } + } + + if (cpy_jump_label.size() < 1) { + Detail::print_error("label is empty, can't jump on it.", file); + throw std::runtime_error("label_empty"); + } + + /// don't go any further if: + /// load word (ldw) or store word. (stw) + + if (name == "ldw" || name == "stw") break; + + auto mld_reloc_str = std::to_string(cpy_jump_label.size()); + mld_reloc_str += kUndefinedSymbol; + mld_reloc_str += cpy_jump_label; + + bool ignore_back_slash = false; + + for (auto& reloc_chr : mld_reloc_str) { + if (reloc_chr == '\\') { + ignore_back_slash = true; + continue; + } + + if (ignore_back_slash) { + ignore_back_slash = false; + continue; + } + + kBytes.push_back(reloc_chr); + } + + kBytes.push_back('\0'); + goto asm_end_label_cpy; + } + + asm_end_label_cpy: + kOrigin += k64x0IPAlignment; + + break; + } + } + + return true; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/Backend/AssemblerAMD64.cc b/dev/LibCompiler/src/Backend/AssemblerAMD64.cc new file mode 100644 index 0000000..e9f3ad5 --- /dev/null +++ b/dev/LibCompiler/src/Backend/AssemblerAMD64.cc @@ -0,0 +1,1203 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file AssemblerAMD64.cc +/// @author EL Mahrouss Amlal +/// @brief AMD64 Assembler. +/// REMINDER: when dealing with an undefined symbol use (string +/// size):LinkerFindSymbol:(string) so that ld will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +/// bugs: 0 + +/// feature request: 1 +/// Encode registers in mov, add, xor... + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_AMD64__ 1 + +#define kAssemblerPragmaSymStr "#" +#define kAssemblerPragmaSym '#' + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +static char kOutputArch = LibCompiler::kPefArchAMD64; + +constexpr auto kIPAlignement = 0x1U; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector> kOriginLabel; + +/// @brief keep it simple by default. +static std::int32_t kRegisterBitWidth = 16U; + +static std::vector kAppBytes; + +static LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kDefinedSymbols; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string line); + +#include + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief AMD64 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +LIBCOMPILER_MODULE(AssemblerMainAMD64) { + //////////////// CPU OPCODES BEGIN //////////////// + + ::signal(SIGSEGV, Detail::drvi_crash_handler); + + std::string opcodes_jump[kJumpLimit] = {"ja", "jae", "jb", "jbe", "jc", "je", "jg", "jge", + "jl", "jle", "jna", "jnae", "jnb", "jnbe", "jnc", "jne", + "jng", "jnge", "jnl", "jnle", "jno", "jnp", "jns", "jnz", + "jo", "jp", "jpe", "jpo", "js", "jz"}; + + for (i64_hword_t i = 0; i < kJumpLimit; i++) { + CpuOpcodeAMD64 code{.fName = opcodes_jump[i], + .fOpcode = static_cast(kAsmJumpOpcode + i)}; + kOpcodesAMD64.push_back(code); + } + + CpuOpcodeAMD64 code{.fName = "jcxz", .fOpcode = 0xE3}; + kOpcodesAMD64.push_back(code); + + for (i64_hword_t i = kJumpLimitStandard; i < kJumpLimitStandardLimit; i++) { + CpuOpcodeAMD64 code{.fName = "jmp", .fOpcode = i}; + kOpcodesAMD64.push_back(code); + } + + CpuOpcodeAMD64 lahf{.fName = "lahf", .fOpcode = 0x9F}; + kOpcodesAMD64.push_back(lahf); + + CpuOpcodeAMD64 lds{.fName = "lds", .fOpcode = 0xC5}; + kOpcodesAMD64.push_back(lds); + + CpuOpcodeAMD64 lea{.fName = "lea", .fOpcode = 0x8D}; + kOpcodesAMD64.push_back(lea); + + CpuOpcodeAMD64 nop{.fName = "nop", .fOpcode = 0x90}; + kOpcodesAMD64.push_back(nop); + + //////////////// CPU OPCODES END //////////////// + + for (size_t i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--amd64:ver") == 0 || strcmp(argv[i], "--amd64:v") == 0) { + kStdOut << "AssemblerAMD64: AMD64 Assembler Driver.\nAssemblerAMD64: " + "v1.10\nAssemblerAMD64: Copyright " + "(c) Amlal El Mahrouss\n"; + return 0; + } else if (strcmp(argv[i], "--amd64:h") == 0) { + kStdOut << "AssemblerAMD64: AMD64 Assembler Driver.\nAssemblerAMD64: Copyright (c) 2024 " + "Amlal El Mahrouss\n"; + kStdOut << "--version: Print program version.\n"; + kStdOut << "--verbose: Print verbose output.\n"; + kStdOut << "--binary: Output as flat binary.\n"; + + return 0; + } else if (strcmp(argv[i], "--amd64:binary") == 0) { + kOutputAsBinary = true; + continue; + } else if (strcmp(argv[i], "--amd64:verbose") == 0) { + kVerbose = true; + continue; + } + + kStdOut << "AssemblerAMD64: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) { + kStdOut << "AssemblerAMD64: can't open: " << argv[i] << std::endl; + goto asm_fail_exit; + } + + std::string object_output(argv[i]); + std::string asm_input(argv[i]); + + for (auto& ext : kAsmFileExts) { + if (object_output.ends_with(ext)) { + object_output.erase(object_output.find(ext), std::strlen(ext)); + break; + } + } + + object_output += kOutputAsBinary ? kBinaryFileExt : kObjectFileExt; + + std::ifstream file_ptr(argv[i]); + std::ofstream file_ptr_out(object_output, std::ofstream::binary); + + kStdOut << "AssemblerAMD64: Assembling: " << argv[i] << "\n"; + + if (file_ptr_out.bad()) { + if (kVerbose) { + kStdOut << "AssemblerAMD64: error: " << strerror(errno) << "\n"; + } + + return 1; + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::EncoderAMD64 asm64; + + if (kVerbose) { + kStdOut << "Compiling: " + asm_input << "\n"; + kStdOut << "From: " + line << "\n"; + } + + while (std::getline(file_ptr, line)) { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { + Detail::print_error(ln, argv[i]); + continue; + } + + try { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } catch (const std::exception& e) { + if (kVerbose) { + std::string what = e.what(); + Detail::print_warning("exit because of: " + what, "LibCompiler"); + } + + try { + std::filesystem::remove(object_output); + } catch (...) { + } + + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) { + if (kVerbose) { + kStdOut << "AssemblerAMD64: Writing object file...\n"; + } + + // this is the final step, write everything to the file. + + auto pos = file_ptr_out.tellp(); + + hdr.fCount = kRecords.size() + kUndefinedSymbols.size(); + + file_ptr_out << hdr; + + if (kRecords.empty()) { + kStdErr << "AssemblerAMD64: At least one record is needed to write an object " + "file.\nAssemblerAMD64: Make one using `public_segment .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return 1; + } + + kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); + + std::size_t record_count = 0UL; + + for (auto& rec : kRecords) { + if (kVerbose) kStdOut << "AssemblerAMD64: Wrote record " << rec.fName << " to file...\n"; + + rec.fFlags |= LibCompiler::kKindRelocationAtRuntime; + rec.fOffset = record_count; + ++record_count; + + file_ptr_out << rec; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto& sym : kUndefinedSymbols) { + LibCompiler::AERecordHeader _record_hdr{0}; + + if (kVerbose) kStdOut << "AssemblerAMD64: Wrote symbol " << sym << " to file...\n"; + + _record_hdr.fKind = kAENullType; + _record_hdr.fSize = sym.size(); + _record_hdr.fOffset = record_count; + + ++record_count; + + memset(_record_hdr.fPad, kAENullType, kAEPad); + memcpy(_record_hdr.fName, sym.c_str(), sym.size()); + + file_ptr_out << _record_hdr; + + ++kCounter; + } + + auto pos_end = file_ptr_out.tellp(); + + file_ptr_out.seekp(pos); + + hdr.fStartCode = pos_end; + hdr.fCodeSize = kAppBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } else { + if (kVerbose) { + kStdOut << "AssemblerAMD64: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto& byte : kAppBytes) { + if (byte == 0) continue; + + if (byte == 0xFF) { + byte = 0; + } + + file_ptr_out << reinterpret_cast(&byte)[0]; + } + + if (kVerbose) kStdOut << "AssemblerAMD64: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) kStdOut << "AssemblerAMD64: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) kStdOut << "AssemblerAMD64: Exit failed.\n"; + + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string line) { + // extern_segment is the opposite of public_segment, it signals to the ld + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment") + 1); + + if (name.size() == 0) { + Detail::print_error("Invalid extern_segment", "power-as"); + throw std::runtime_error("invalid_extern_segment"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char& j : name) { + if (j == ' ' || j == ',') j = '$'; + } + + result += name; + + if (name.find(kPefCode64) != std::string::npos) { + // data is treated as code. + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(kPefData64) != std::string::npos) { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(kPefZero64) != std::string::npos) { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by AssemblerAMD64 to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64 and + // .zero64. + else if (LibCompiler::find_word(line, "public_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment") + 1); + + std::string name_copy = name; + + for (char& j : name) { + if (j == ' ') j = '$'; + } + + if (std::find(kDefinedSymbols.begin(), kDefinedSymbols.end(), name) != kDefinedSymbols.end()) { + Detail::print_error("Symbol already defined.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + kDefinedSymbols.push_back(name); + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Detail::algorithm { +// \brief authorize a brief set of characters. +static inline bool is_not_valid(char c) { + if ((isalpha(c) || isdigit(c)) || + ((c == ' ') || (c == '\t') || (c == ',') || (c == '(') || (c == ')') || (c == '"') || + (c == '*') || (c == '\'') || (c == '[') || (c == ']') || (c == '+') || (c == '_') || + (c == ':') || (c == '@') || (c == '.') || (c == '#') || (c == ';'))) + return false; + + return true; +} + +bool is_valid_amd64(std::string str) { + return std::find_if(str.begin(), str.end(), is_not_valid) == str.end(); +} +} // namespace Detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::EncoderAMD64::CheckLine(std::string line, std::string file) { + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || + LibCompiler::find_word(line, kAssemblerPragmaSymStr) || LibCompiler::find_word(line, ";") || + line[0] == kAssemblerPragmaSym) { + if (line.find(';') != std::string::npos) { + line.erase(line.find(';')); + } else { + // now check the line for validity + if (!Detail::algorithm::is_valid_amd64(line)) { + err_str = "Line contains non valid characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) { + if (line.find(',') + 1 == line.size()) { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } else { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto& ch : substr) { + if (ch != ' ' && ch != '\t') { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + for (auto& opcodeAMD64 : kOpcodesAMD64) { + if (LibCompiler::find_word(line, opcodeAMD64.fName)) { + return err_str; + } + } + + err_str += "\nUnrecognized instruction -> " + line; + + return err_str; +} + +bool LibCompiler::EncoderAMD64::WriteNumber(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast64 num = + LibCompiler::NumberCast64(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 16 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num = + LibCompiler::NumberCast64(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num = + LibCompiler::NumberCast64(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + LibCompiler::NumberCast64 num = + LibCompiler::NumberCast64(strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +bool LibCompiler::EncoderAMD64::WriteNumber32(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); + res += kOrigin; + + if (errno != 0) { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 16 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; + } + case 'b': { + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + res += kOrigin; + + if (errno != 0) { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + case 'o': { + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + res += kOrigin; + + if (errno != 0) { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 10); + res += kOrigin; + + if (errno != 0) { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +bool LibCompiler::EncoderAMD64::WriteNumber16(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast16 num = + LibCompiler::NumberCast16(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 16 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast16 num = + LibCompiler::NumberCast16(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast16 num = + LibCompiler::NumberCast16(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + LibCompiler::NumberCast16 num = + LibCompiler::NumberCast16(strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) { + if (i == 0) i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +bool LibCompiler::EncoderAMD64::WriteNumber8(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast8 num = + LibCompiler::NumberCast8(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + kAppBytes.push_back(num.number); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 16 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast8 num = + LibCompiler::NumberCast8(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + kAppBytes.push_back(num.number); + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast8 num = + LibCompiler::NumberCast8(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + kAppBytes.push_back(num.number); + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + LibCompiler::NumberCast8 num = + LibCompiler::NumberCast8(strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + kAppBytes.push_back(num.number); + + if (kVerbose) { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::EncoderAMD64::WriteLine(std::string line, std::string file) { + if (LibCompiler::find_word(line, "public_segment ")) return true; + + struct RegMapAMD64 { + std::string fName; + i64_byte_t fModRM; + }; + + std::vector kRegisterList{ + {.fName = "ax", .fModRM = 0x0}, {.fName = "cx", .fModRM = 1}, + {.fName = "dx", .fModRM = 0x2}, {.fName = "bx", .fModRM = 3}, + {.fName = "sp", .fModRM = 0x4}, {.fName = "bp", .fModRM = 5}, + {.fName = "si", .fModRM = 0x6}, {.fName = "di", .fModRM = 7}, + }; + + BOOL foundInstruction = false; + + for (auto& opcodeAMD64 : kOpcodesAMD64) { + // strict check here + if (LibCompiler::find_word(line, opcodeAMD64.fName) && + Detail::algorithm::is_valid_amd64(line)) { + foundInstruction = true; + std::string name(opcodeAMD64.fName); + + /// Move instruction handler. + if (line.find(name) != std::string::npos) { + if (name == "mov" || name == "xor") { + std::string substr = line.substr(line.find(name) + name.size()); + + uint64_t bits = kRegisterBitWidth; + + if (substr.find(",") == std::string::npos) { + Detail::print_error("Syntax error: missing right operand.", "LibCompiler"); + throw std::runtime_error("syntax_err"); + } + + bool onlyOneReg = true; + + std::vector currentRegList; + + for (auto& reg : kRegisterList) { + std::vector regExt = {'e', 'r'}; + + for (auto& ext : regExt) { + std::string registerName; + + if (bits > 16) registerName.push_back(ext); + + registerName += reg.fName; + + while (line.find(registerName) != std::string::npos) { + line.erase(line.find(registerName), registerName.size()); + + if (bits == 16) { + if (registerName[0] == 'r') { + Detail::print_error("invalid size for register, current bit width is: " + + std::to_string(kRegisterBitWidth), + file); + throw std::runtime_error("invalid_reg_size"); + } + } + + currentRegList.push_back({.fName = registerName, .fModRM = reg.fModRM}); + } + } + } + + if (currentRegList.size() > 1) onlyOneReg = false; + + bool hasRBasedRegs = false; + + if (!onlyOneReg) { + /// very tricky to understand. + /// but this checks for a r8 through r15 register. + if (currentRegList[0].fName[0] == 'r' || currentRegList[1].fName[0] == 'r') { + if (isdigit(currentRegList[0].fName[1]) && isdigit(currentRegList[1].fName[1])) { + kAppBytes.emplace_back(0x4d); + hasRBasedRegs = true; + } else if (isdigit(currentRegList[0].fName[1]) || + isdigit(currentRegList[1].fName[1])) { + kAppBytes.emplace_back(0x4c); + hasRBasedRegs = true; + } + } + } + + if (name == "mov") { + if (bits == 64 || bits == 32) { + if (!hasRBasedRegs && bits >= 32) { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + } + + if (!onlyOneReg) kAppBytes.emplace_back(0x89); + } else if (bits == 16) { + if (hasRBasedRegs) { + Detail::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } else { + kAppBytes.emplace_back(0x66); + kAppBytes.emplace_back(0x89); + } + } + } else { + if (!hasRBasedRegs && bits >= 32) { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + } + + kAppBytes.emplace_back(0x31); + } + + if (onlyOneReg) { + auto num = GetNumber32(line, ","); + + for (auto& num_idx : num.number) { + if (num_idx == 0) num_idx = 0xFF; + } + + auto modrm = (0x3 << 6 | currentRegList[0].fModRM); + + kAppBytes.emplace_back(0xC7); // prefixed before placing the modrm and then the number. + kAppBytes.emplace_back(modrm); + + if (name != "xor") { + kAppBytes.emplace_back(num.number[0]); + kAppBytes.emplace_back(num.number[1]); + kAppBytes.emplace_back(num.number[2]); + kAppBytes.emplace_back(num.number[3]); + } + + break; + } + + if (currentRegList[1].fName[0] == 'r' && currentRegList[0].fName[0] == 'e') { + Detail::print_error("Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[0].fName[0] == 'r' && currentRegList[1].fName[0] == 'e') { + Detail::print_error("Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (bits == 16) { + if (currentRegList[0].fName[0] == 'r' || currentRegList[0].fName[0] == 'e') { + Detail::print_error("Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[1].fName[0] == 'r' || currentRegList[1].fName[0] == 'e') { + Detail::print_error("Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + } else { + if (currentRegList[0].fName[0] != 'r' || currentRegList[0].fName[0] == 'e') { + Detail::print_error("Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[1].fName[0] != 'r' || currentRegList[1].fName[0] == 'e') { + Detail::print_error("Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + } + + /// encode register using the modrm encoding. + + auto modrm = (0x3 << 6 | currentRegList[1].fModRM << 3 | currentRegList[0].fModRM); + + kAppBytes.emplace_back(modrm); + + break; + } + } + + if (name == "int" || name == "into" || name == "intd") { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + this->WriteNumber8(line.find(name) + name.size() + 1, line); + + break; + } else if (name == "jmp" || name == "call") { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + + if (!this->WriteNumber32(line.find(name) + name.size() + 1, line)) { + throw std::runtime_error("BUG: WriteNumber32"); + } + + break; + } else if (name == "syscall") { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + kAppBytes.emplace_back(0x05); + + break; + } else { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + + break; + } + } + } + + if (line[0] == kAssemblerPragmaSym) { + if (foundInstruction) { + Detail::print_error("Syntax error: " + line, file); + throw std::runtime_error("syntax_err"); + } + + if (line.find("bits 64") != std::string::npos) { + kRegisterBitWidth = 64U; + } else if (line.find("bits 32") != std::string::npos) { + kRegisterBitWidth = 32U; + } else if (line.find("bits 16") != std::string::npos) { + kRegisterBitWidth = 16U; + } else if (line.find("org") != std::string::npos) { + size_t base[] = {10, 16, 2, 7}; + + for (size_t i = 0; i < 4; i++) { + if (kOrigin = strtol((line.substr(line.find("org") + strlen("org") + 1)).c_str(), nullptr, + base[i]); + kOrigin) { + if (errno != 0) { + continue; + } else { + if (kVerbose) { + kStdOut << "AssemblerAMD64: Origin Set: " << kOrigin << std::endl; + } + + break; + } + } + } + } + } + /// write a dword + else if (line.find(".dword") != std::string::npos) { + this->WriteNumber32(line.find(".dword") + strlen(".dword") + 1, line); + } + /// write a long + else if (line.find(".long") != std::string::npos) { + this->WriteNumber(line.find(".long") + strlen(".long") + 1, line); + } + /// write a 16-bit number + else if (line.find(".word") != std::string::npos) { + this->WriteNumber16(line.find(".word") + strlen(".word") + 1, line); + } + + kOrigin += kIPAlignement; + + return true; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/Backend/AssemblerARM64.cc b/dev/LibCompiler/src/Backend/AssemblerARM64.cc new file mode 100644 index 0000000..8fad6b5 --- /dev/null +++ b/dev/LibCompiler/src/Backend/AssemblerARM64.cc @@ -0,0 +1,585 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file AssemblerARM64.cxx +/// @author EL Mahrouss Amlal +/// @brief 'ACORN' Assembler. + +/// REMINDER: when dealing with an undefined symbol use (string +/// size):LinkerFindSymbol:(string) so that li will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_ARM64__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +constexpr auto cPowerIPAlignment = 0x1U; + +static CharType kOutputArch = LibCompiler::kPefArchARM64; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector> kOriginLabel; + +static std::vector kBytes; + +static LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; +static const std::string kRelocSymbol = ":RuntimeSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string line); + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief POWER assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +LIBCOMPILER_MODULE(AssemblerMainARM64) { + ::signal(SIGSEGV, Detail::drvi_crash_handler); + + for (size_t i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--ver") == 0 || strcmp(argv[i], "--v") == 0) { + kStdOut << "AssemblerPower: AARCH64 Assembler Driver.\nAssemblerPower: " << kDistVersion + << "\nAssemblerPower: " + "Copyright (c) " + "Amlal El Mahrouss\n"; + return 0; + } else if (strcmp(argv[i], "--h") == 0) { + kStdOut << "AssemblerPower: AARCH64 Assembler Driver.\nAssemblerPower: Copyright (c) 2024 " + "Amlal El Mahrouss\n"; + kStdOut << "--version,/v: print program version.\n"; + kStdOut << "--verbose: print verbose output.\n"; + kStdOut << "--binary: output as flat binary.\n"; + + return 0; + } else if (strcmp(argv[i], "--binary") == 0) { + kOutputAsBinary = true; + continue; + } else if (strcmp(argv[i], "--verbose") == 0) { + kVerbose = true; + continue; + } + + kStdOut << "AssemblerPower: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) { + kStdOut << "AssemblerPower: can't open: " << argv[i] << std::endl; + goto asm_fail_exit; + } + + std::string object_output(argv[i]); + + for (auto& ext : kAsmFileExts) { + if (object_output.find(ext) != std::string::npos) { + object_output.erase(object_output.find(ext), std::strlen(ext)); + } + } + + object_output += kOutputAsBinary ? kBinaryFileExt : kObjectFileExt; + + std::ifstream file_ptr(argv[i]); + std::ofstream file_ptr_out(object_output, std::ofstream::binary); + + if (file_ptr_out.bad()) { + if (kVerbose) { + kStdOut << "AssemblerPower: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::EncoderARM64 asm64; + + while (std::getline(file_ptr, line)) { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { + Detail::print_error(ln, argv[i]); + continue; + } + + try { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } catch (const std::exception& e) { + if (kVerbose) { + std::string what = e.what(); + Detail::print_warning("exit because of: " + what, "LibCompiler"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) { + if (kVerbose) { + kStdOut << "AssemblerARM64: Writing object file...\n"; + } + + // this is the final step, write everything to the file. + + auto pos = file_ptr_out.tellp(); + + hdr.fCount = kRecords.size() + kUndefinedSymbols.size(); + + file_ptr_out << hdr; + + if (kRecords.empty()) { + kStdErr << "AssemblerARM64: At least one record is needed to write an object " + "file.\nAssemblerARM64: Make one using `public_segment .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return 1; + } + + kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + std::size_t record_count = 0UL; + + for (auto& record_hdr : kRecords) { + record_hdr.fFlags |= LibCompiler::kKindRelocationAtRuntime; + record_hdr.fOffset = record_count; + ++record_count; + + file_ptr_out << record_hdr; + + if (kVerbose) kStdOut << "AssemblerARM64: Wrote record " << record_hdr.fName << "...\n"; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto& sym : kUndefinedSymbols) { + LibCompiler::AERecordHeader undefined_sym{0}; + + if (kVerbose) kStdOut << "AssemblerARM64: Wrote symbol " << sym << " to file...\n"; + + undefined_sym.fKind = kAENullType; + undefined_sym.fSize = sym.size(); + undefined_sym.fOffset = record_count; + + ++record_count; + + memset(undefined_sym.fPad, kAENullType, kAEPad); + memcpy(undefined_sym.fName, sym.c_str(), sym.size()); + + file_ptr_out << undefined_sym; + + ++kCounter; + } + + auto pos_end = file_ptr_out.tellp(); + + file_ptr_out.seekp(pos); + + hdr.fStartCode = pos_end; + hdr.fCodeSize = kBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } else { + if (kVerbose) { + kStdOut << "AssemblerARM64: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto& byte : kBytes) { + file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); + } + + if (kVerbose) kStdOut << "AssemblerARM64: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) kStdOut << "AssemblerARM64: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) kStdOut << "AssemblerARM64: Exit failed.\n"; + + return LIBCOMPILER_EXEC_ERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string line) { + // extern_segment is the opposite of public_segment, it signals to the li + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid extern_segment directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment") + 1); + + if (name.size() == 0) { + Detail::print_error("Invalid extern_segment", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char& j : name) { + if (j == ' ' || j == ',') j = '$'; + } + + result += name; + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by Assembler to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (LibCompiler::find_word(line, "public_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid public_segment directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment")); + + std::string name_copy = name; + + for (char& j : name) { + if (j == ' ') j = '$'; + } + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Detail::algorithm { +// \brief authorize a brief set of characters. +static inline bool is_not_alnum_space(char c) { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || (c == ',') || (c == '(') || + (c == ')') || (c == '"') || (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.')); +} + +bool is_valid_arm64(std::string str) { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); +} +} // namespace Detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::EncoderARM64::CheckLine(std::string line, std::string file) { + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || line.find('#') != std::string::npos || + LibCompiler::find_word(line, ";")) { + if (line.find('#') != std::string::npos) { + line.erase(line.find('#')); + } else if (line.find(';') != std::string::npos) { + line.erase(line.find(';')); + } else { + /// does the line contains valid input? + if (!Detail::algorithm::is_valid_arm64(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!Detail::algorithm::is_valid_arm64(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) { + if (line.find(',') + 1 == line.size()) { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } else { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto& ch : substr) { + if (ch != ' ' && ch != '\t') { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + + return err_str; +} + +bool LibCompiler::EncoderARM64::WriteNumber(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerARM64: found a base 16 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "AssemblerARM64: found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + kBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "AssemblerARM64: found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + kBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerARM64: found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::EncoderARM64::WriteLine(std::string line, std::string file) { + if (LibCompiler::find_word(line, "public_segment")) return false; + + if (!Detail::algorithm::is_valid_arm64(line)) return false; + + return true; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc b/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc new file mode 100644 index 0000000..86a70b9 --- /dev/null +++ b/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc @@ -0,0 +1,909 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file AssemblerPower.cxx +/// @author EL Mahrouss Amlal +/// @brief POWER Assembler. + +/// REMINDER: when dealing with an undefined symbol use (string +/// size):LinkerFindSymbol:(string) so that li will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_PPC__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +constexpr auto cPowerIPAlignment = 0x4U; + +static CharType kOutputArch = LibCompiler::kPefArchPowerPC; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector> kOriginLabel; + +static std::vector kBytes; + +static LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; +static const std::string kRelocSymbol = ":RuntimeSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string line); + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief POWER assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +LIBCOMPILER_MODULE(AssemblerMainPower64) { + ::signal(SIGSEGV, Detail::drvi_crash_handler); + + for (size_t i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "--ver") == 0 || strcmp(argv[i], "--v") == 0) { + kStdOut << "AssemblerPower: POWER64 Assembler Driver.\nAssemblerPower: " << kDistVersion + << "\nAssemblerPower: " + "Copyright (c) " + "Amlal El Mahrouss\n"; + return 0; + } else if (strcmp(argv[i], "--h") == 0) { + kStdOut << "AssemblerPower: POWER64 Assembler Driver.\nAssemblerPower: Copyright (c) 2024 " + "Amlal El Mahrouss\n"; + kStdOut << "--version,/v: print program version.\n"; + kStdOut << "--verbose: print verbose output.\n"; + kStdOut << "--binary: output as flat binary.\n"; + + return 0; + } else if (strcmp(argv[i], "--binary") == 0) { + kOutputAsBinary = true; + continue; + } else if (strcmp(argv[i], "--verbose") == 0) { + kVerbose = true; + continue; + } + + kStdOut << "AssemblerPower: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) { + kStdOut << "AssemblerPower: can't open: " << argv[i] << std::endl; + goto asm_fail_exit; + } + + std::string object_output(argv[i]); + + for (auto& ext : kAsmFileExts) { + if (object_output.find(ext) != std::string::npos) { + object_output.erase(object_output.find(ext), std::strlen(ext)); + } + } + + object_output += kOutputAsBinary ? kBinaryFileExt : kObjectFileExt; + + std::ifstream file_ptr(argv[i]); + std::ofstream file_ptr_out(object_output, std::ofstream::binary); + + if (file_ptr_out.bad()) { + if (kVerbose) { + kStdOut << "AssemblerPower: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::EncoderPowerPC asm64; + + while (std::getline(file_ptr, line)) { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { + Detail::print_error(ln, argv[i]); + continue; + } + + try { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } catch (const std::exception& e) { + if (kVerbose) { + std::string what = e.what(); + Detail::print_warning("exit because of: " + what, "LibCompiler"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) { + if (kVerbose) { + kStdOut << "AssemblerPower: Writing object file...\n"; + } + + // this is the final step, write everything to the file. + + auto pos = file_ptr_out.tellp(); + + hdr.fCount = kRecords.size() + kUndefinedSymbols.size(); + + file_ptr_out << hdr; + + if (kRecords.empty()) { + kStdErr << "AssemblerPower: At least one record is needed to write an object " + "file.\nAssemblerPower: Make one using `public_segment .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return 1; + } + + kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + std::size_t record_count = 0UL; + + for (auto& record_hdr : kRecords) { + record_hdr.fFlags |= LibCompiler::kKindRelocationAtRuntime; + record_hdr.fOffset = record_count; + ++record_count; + + file_ptr_out << record_hdr; + + if (kVerbose) kStdOut << "AssemblerPower: Wrote record " << record_hdr.fName << "...\n"; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto& sym : kUndefinedSymbols) { + LibCompiler::AERecordHeader undefined_sym{0}; + + if (kVerbose) kStdOut << "AssemblerPower: Wrote symbol " << sym << " to file...\n"; + + undefined_sym.fKind = kAENullType; + undefined_sym.fSize = sym.size(); + undefined_sym.fOffset = record_count; + + ++record_count; + + memset(undefined_sym.fPad, kAENullType, kAEPad); + memcpy(undefined_sym.fName, sym.c_str(), sym.size()); + + file_ptr_out << undefined_sym; + + ++kCounter; + } + + auto pos_end = file_ptr_out.tellp(); + + file_ptr_out.seekp(pos); + + hdr.fStartCode = pos_end; + hdr.fCodeSize = kBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } else { + if (kVerbose) { + kStdOut << "AssemblerPower: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto& byte : kBytes) { + file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); + } + + if (kVerbose) kStdOut << "AssemblerPower: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) kStdOut << "AssemblerPower: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) kStdOut << "AssemblerPower: Exit failed.\n"; + + return LIBCOMPILER_EXEC_ERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string line) { + // extern_segment is the opposite of public_segment, it signals to the li + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid extern_segment directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment") + 1); + + if (name.size() == 0) { + Detail::print_error("Invalid extern_segment", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char& j : name) { + if (j == ' ' || j == ',') j = '$'; + } + + result += name; + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by AssemblerPower to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (LibCompiler::find_word(line, "public_segment")) { + if (kOutputAsBinary) { + Detail::print_error("Invalid public_segment directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment")); + + std::string name_copy = name; + + for (char& j : name) { + if (j == ' ') j = '$'; + } + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = LibCompiler::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = LibCompiler::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Detail::algorithm { +// \brief authorize a brief set of characters. +static inline bool is_not_alnum_space(char c) { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || (c == ',') || (c == '(') || + (c == ')') || (c == '"') || (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.')); +} + +bool is_valid_power64(std::string str) { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); +} +} // namespace Detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::EncoderPowerPC::CheckLine(std::string line, std::string file) { + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || line.find('#') != std::string::npos || + LibCompiler::find_word(line, ";")) { + if (line.find('#') != std::string::npos) { + line.erase(line.find('#')); + } else if (line.find(';') != std::string::npos) { + line.erase(line.find(';')); + } else { + /// does the line contains valid input? + if (!Detail::algorithm::is_valid_power64(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!Detail::algorithm::is_valid_power64(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) { + if (line.find(',') + 1 == line.size()) { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } else { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto& ch : substr) { + if (ch != ' ' && ch != '\t') { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + + // these do take an argument. + std::vector operands_inst = {"stw", "li"}; + + // these don't. + std::vector filter_inst = {"blr", "bl", "sc"}; + + for (auto& opcode_risc : kOpcodesPowerPC) { + if (LibCompiler::find_word(line, opcode_risc.name)) { + for (auto& op : operands_inst) { + // if only the instruction was found. + if (line == op) { + err_str += "\nMalformed "; + err_str += op; + err_str += " instruction, here -> "; + err_str += line; + } + } + + // if it is like that -> addr1, 0x0 + if (auto it = std::find(filter_inst.begin(), filter_inst.end(), opcode_risc.name); + it == filter_inst.cend()) { + if (LibCompiler::find_word(line, opcode_risc.name)) { + if (!isspace(line[line.find(opcode_risc.name) + strlen(opcode_risc.name)])) { + err_str += "\nMissing space between "; + err_str += opcode_risc.name; + err_str += " and operands.\nhere -> "; + err_str += line; + } + } + } + + return err_str; + } + } + + err_str += "Unrecognized instruction: " + line; + + return err_str; +} + +bool LibCompiler::EncoderPowerPC::WriteNumber(const std::size_t& pos, std::string& jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerPower: found a base 16 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "AssemblerPower: found a base 2 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + kBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "AssemblerPower: found a base 8 number here: " << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) { + kBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + LibCompiler::NumberCast64 num(strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "AssemblerPower: found a base 10 number here: " << jump_label.substr(pos) << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::EncoderPowerPC::WriteLine(std::string line, std::string file) { + if (LibCompiler::find_word(line, "public_segment")) return false; + if (!Detail::algorithm::is_valid_power64(line)) return false; + + for (auto& opcode_risc : kOpcodesPowerPC) { + // strict check here + if (LibCompiler::find_word(line, opcode_risc.name)) { + std::string name(opcode_risc.name); + std::string jump_label, cpy_jump_label; + std::vector found_registers_index; + + // check funct7 type. + switch (opcode_risc.ops->type) { + default: { + NumberCast32 num(opcode_risc.opcode); + + for (auto ch : num.number) { + kBytes.emplace_back(ch); + } + break; + } + case BADDR: + case PCREL: { + auto num = GetNumber32(line, name); + + kBytes.emplace_back(num.number[0]); + kBytes.emplace_back(num.number[1]); + kBytes.emplace_back(num.number[2]); + kBytes.emplace_back(0x48); + + break; + } + /// General purpose, float, vector operations. Everything that involve + /// registers. + case G0REG: + case FREG: + case VREG: + case GREG: { + // \brief how many registers we found. + std::size_t found_some_count = 0UL; + std::size_t register_count = 0UL; + std::string opcodeName = opcode_risc.name; + std::size_t register_sum = 0; + + NumberCast64 num(opcode_risc.opcode); + + for (size_t line_index = 0UL; line_index < line.size(); line_index++) { + if (line[line_index] == kAsmRegisterPrefix[0] && isdigit(line[line_index + 1])) { + std::string register_syntax = kAsmRegisterPrefix; + register_syntax += line[line_index + 1]; + + if (isdigit(line[line_index + 2])) register_syntax += line[line_index + 2]; + + std::string reg_str; + reg_str += line[line_index + 1]; + + if (isdigit(line[line_index + 2])) reg_str += line[line_index + 2]; + + // it ranges from r0 to r19 + // something like r190 doesn't exist in the instruction set. + if (isdigit(line[line_index + 3]) && isdigit(line[line_index + 2])) { + reg_str += line[line_index + 3]; + Detail::print_error("invalid register index, r" + reg_str + + "\nnote: The POWER accepts registers from r0 to r32.", + file); + throw std::runtime_error("invalid_register_index"); + } + + // finally cast to a size_t + std::size_t reg_index = strtol(reg_str.c_str(), nullptr, 10); + + if (reg_index > kAsmRegisterLimit) { + Detail::print_error("invalid register index, r" + reg_str, file); + throw std::runtime_error("invalid_register_index"); + } + + if (opcodeName == "li") { + char numIndex = 0; + + for (size_t i = 0; i != reg_index; i++) { + numIndex += 0x20; + } + + auto num = GetNumber32(line, reg_str); + + kBytes.push_back(num.number[0]); + kBytes.push_back(num.number[1]); + kBytes.push_back(numIndex); + kBytes.push_back(0x38); + + // check if bigger than two. + for (size_t i = 2; i < 4; i++) { + if (num.number[i] > 0) { + Detail::print_warning("number overflow on li operation.", file); + break; + } + } + + break; + } + + if ((opcodeName[0] == 's' && opcodeName[1] == 't')) { + if (register_sum == 0) { + for (size_t indexReg = 0UL; indexReg < reg_index; ++indexReg) { + register_sum += 0x20; + } + } else { + register_sum += reg_index; + } + } + + if (opcodeName == "mr") { + switch (register_count) { + case 0: { + kBytes.push_back(0x78); + + char numIndex = 0x3; + + for (size_t i = 0; i != reg_index; i++) { + numIndex += 0x8; + } + + kBytes.push_back(numIndex); + + break; + } + case 1: { + char numIndex = 0x1; + + for (size_t i = 0; i != reg_index; i++) { + numIndex += 0x20; + } + + for (size_t i = 0; i != reg_index; i++) { + kBytes[kBytes.size() - 1] += 0x8; + } + + kBytes[kBytes.size() - 1] -= 0x8; + + kBytes.push_back(numIndex); + + if (reg_index >= 10 && reg_index < 20) + kBytes.push_back(0x7d); + else if (reg_index >= 20 && reg_index < 30) + kBytes.push_back(0x7e); + else if (reg_index >= 30) + kBytes.push_back(0x7f); + else + kBytes.push_back(0x7c); + + break; + } + default: + break; + } + + ++register_count; + ++found_some_count; + } + + if (opcodeName == "addi") { + if (found_some_count == 2 || found_some_count == 0) + kBytes.emplace_back(reg_index); + else if (found_some_count == 1) + kBytes.emplace_back(0x00); + + ++found_some_count; + + if (found_some_count > 3) { + Detail::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + } + + if (opcodeName.find("cmp") != std::string::npos) { + ++found_some_count; + + if (found_some_count > 3) { + Detail::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + } + + if (opcodeName.find("mf") != std::string::npos || + opcodeName.find("mt") != std::string::npos) { + char numIndex = 0; + + for (size_t i = 0; i != reg_index; i++) { + numIndex += 0x20; + } + + num.number[2] += numIndex; + + ++found_some_count; + + if (found_some_count > 1) { + Detail::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + + if (kVerbose) { + kStdOut << "AssemblerPower: Found register: " << register_syntax << "\n"; + kStdOut << "AssemblerPower: Amount of registers in instruction: " + << found_some_count << "\n"; + } + + if (reg_index >= 10 && reg_index < 20) + num.number[3] = 0x7d; + else if (reg_index >= 20 && reg_index < 30) + num.number[3] = 0x7e; + else if (reg_index >= 30) + num.number[3] = 0x7f; + else + num.number[3] = 0x7c; + + for (auto ch : num.number) { + kBytes.emplace_back(ch); + } + } + + found_registers_index.push_back(reg_index); + } + } + + if (opcodeName == "addi") { + kBytes.emplace_back(0x38); + } + + if (opcodeName.find("cmp") != std::string::npos) { + char rightReg = 0x0; + + for (size_t i = 0; i != found_registers_index[1]; i++) { + rightReg += 0x08; + } + + kBytes.emplace_back(0x00); + kBytes.emplace_back(rightReg); + kBytes.emplace_back(found_registers_index[0]); + kBytes.emplace_back(0x7c); + } + + if ((opcodeName[0] == 's' && opcodeName[1] == 't')) { + size_t offset = 0UL; + + if (line.find('+') != std::string::npos) { + auto number = GetNumber32(line.substr(line.find("+")), "+"); + offset = number.raw; + } + + kBytes.push_back(offset); + kBytes.push_back(0x00); + kBytes.push_back(register_sum); + + kBytes.emplace_back(0x90); + } + + if (opcodeName == "mr") { + if (register_count == 1) { + Detail::print_error("Too few registers. -> " + line, file); + throw std::runtime_error("too_few_registers"); + } + } + + // we're not in immediate addressing, reg to reg. + if (opcode_risc.ops->type != GREG) { + // remember! register to register! + if (found_some_count == 1) { + Detail::print_error( + "Unrecognized register found.\ntip: each AssemblerPower register " + "starts with 'r'.\nline: " + + line, + file); + + throw std::runtime_error("not_a_register"); + } + } + + if (found_some_count < 1 && name[0] != 'l' && name[0] != 's') { + Detail::print_error("invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } + + break; + } + } + + kOrigin += cPowerIPAlignment; + break; + } + } + + return true; +} + +// Last rev 13-1-24 -- cgit v1.2.3 From 46badbe70a36bb3cb5d86bd9f33aa5481c9709b9 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Wed, 28 May 2025 14:33:48 +0200 Subject: feat!: update the kernel codegen to output bit width and origin. refactor!: refactor codebase, breaking changes. Signed-off-by: Amlal El Mahrouss --- README.md | 28 +- dev/LibCompiler/BasicString.h | 2 +- dev/LibCompiler/CodeGen.h | 2 +- dev/LibCompiler/Defines.h | 9 + dev/LibCompiler/Util/AsmUtils.h | 93 +++ dev/LibCompiler/Util/CompilerUtils.h | 109 +++ dev/LibCompiler/Util/DylibHelpers.h | 2 +- dev/LibCompiler/Util/LCAsmUtils.h | 93 --- dev/LibCompiler/Util/LCClUtils.h | 110 --- .../deprecated/Frontend/CCompiler64x0.cc | 2 +- .../deprecated/Frontend/CCompilerARM64.cc | 2 +- .../deprecated/Frontend/CCompilerPower64.cc | 2 +- dev/LibCompiler/src/Backend/Assembler32x0.cc | 20 +- dev/LibCompiler/src/Backend/Assembler64x0.cc | 9 +- dev/LibCompiler/src/Backend/AssemblerAMD64.cc | 6 +- dev/LibCompiler/src/Backend/AssemblerARM64.cc | 6 +- dev/LibCompiler/src/Backend/AssemblerPowerPC.cc | 6 +- .../src/Frontend/CPlusPlusCompilerAMD64.cc | 887 -------------------- .../src/Frontend/CompilerCPlusPlusAMD64.cc | 890 +++++++++++++++++++++ dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc | 2 +- dev/LibDebugger/src/NeKernelContractCLI.cc | 14 +- meta/png/nekernel.png | Bin 0 -> 156843 bytes sample/.keep | 0 sample/asm/exit_nekernel_abi.asm | 10 + sample/asm/return_5_rax.asm | 4 + sample/cxx/example.cc | 16 + tests/example.cc | 16 - tests/exit_nekernel_abi.asm | 10 - tests/return_5_rax.asm | 4 - tools/cxxdrv.cc | 9 +- 30 files changed, 1194 insertions(+), 1169 deletions(-) create mode 100644 dev/LibCompiler/Util/AsmUtils.h create mode 100644 dev/LibCompiler/Util/CompilerUtils.h delete mode 100644 dev/LibCompiler/Util/LCAsmUtils.h delete mode 100644 dev/LibCompiler/Util/LCClUtils.h delete mode 100644 dev/LibCompiler/src/Frontend/CPlusPlusCompilerAMD64.cc create mode 100644 dev/LibCompiler/src/Frontend/CompilerCPlusPlusAMD64.cc create mode 100644 meta/png/nekernel.png create mode 100644 sample/.keep create mode 100644 sample/asm/exit_nekernel_abi.asm create mode 100644 sample/asm/return_5_rax.asm create mode 100644 sample/cxx/example.cc delete mode 100644 tests/example.cc delete mode 100644 tests/exit_nekernel_abi.asm delete mode 100644 tests/return_5_rax.asm (limited to 'dev/LibCompiler/src/Backend') diff --git a/README.md b/README.md index 97a5750..b76a345 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,21 @@ -# NeKernel C/C++ + + +
+ Logo +
+ +
![CI](https://github.com/amlel-el-mahrouss/cc/actions/workflows/c-cpp.yml/badge.svg) [![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue.svg)](LICENSE) -## Notice for Documentation: +## Requirements: + +- [CLang](https://clang.llvm.org/) +- [GIT](https://git-scm.com/) +- [BTB](https://github.com/nekernel-org/btb) + +## Notice for Doxygen: - Use the doxygen command to build documentation. @@ -11,16 +23,12 @@ - Always use `format.sh` before commiting and pushing your code! -## Requirements: +## Getting Started: -- [CLang](https://clang.llvm.org/) -- [GIT](https://git-scm.com/) -- [BTB](https://github.com/nekernel-org/btb) - -## Cloning: - -``` +```sh git clone git@github.com:nekernel-org/cc.git +cd cc +# Either build debugger or compiler using btb ``` ###### Copyright (C) 2024-2025 Amlal El Mahrouss, all rights reserved. diff --git a/dev/LibCompiler/BasicString.h b/dev/LibCompiler/BasicString.h index b69deae..53b21d3 100644 --- a/dev/LibCompiler/BasicString.h +++ b/dev/LibCompiler/BasicString.h @@ -71,7 +71,7 @@ class BasicString final { * @note These results shall call be delete[] after they're used. */ struct StringBuilder final { - static BasicString Construct(const CharType* data); + static BasicString Construct(const CharType* data); static const char* FromInt(const char* fmt, int n); static const char* FromBool(const char* fmt, bool n); static const char* Format(const char* fmt, const char* from); diff --git a/dev/LibCompiler/CodeGen.h b/dev/LibCompiler/CodeGen.h index 8edcff1..a189b93 100644 --- a/dev/LibCompiler/CodeGen.h +++ b/dev/LibCompiler/CodeGen.h @@ -6,9 +6,9 @@ #pragma once +#include #include #include -#include #define LC_ASSEMBLY_INTERFACE : public ::LibCompiler::AssemblyInterface #define LC_ENCODER : public ::LibCompiler::EncoderInterface diff --git a/dev/LibCompiler/Defines.h b/dev/LibCompiler/Defines.h index 1735606..24d9a96 100644 --- a/dev/LibCompiler/Defines.h +++ b/dev/LibCompiler/Defines.h @@ -138,6 +138,15 @@ inline bool to_str(CharType* str, Int32 limit, Int32 base) noexcept { return true; } +inline bool install_signal(Int32 signal, void (*handler)(int)) noexcept { + if (handler == nullptr) return false; + + if (::signal(signal, handler) == SIG_ERR) { + return false; + } + + return true; +} } // namespace LibCompiler #define PACKED __attribute__((packed)) diff --git a/dev/LibCompiler/Util/AsmUtils.h b/dev/LibCompiler/Util/AsmUtils.h new file mode 100644 index 0000000..f176eda --- /dev/null +++ b/dev/LibCompiler/Util/AsmUtils.h @@ -0,0 +1,93 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include +#include + +#include + +using namespace LibCompiler; + +/// @brief Get Number from lineBuffer. +/// @param lineBuffer the lineBuffer to fetch from. +/// @param numberKey where to seek that number. +/// @return +static NumberCast32 GetNumber32(std::string lineBuffer, std::string numberKey) { + auto pos = lineBuffer.find(numberKey) + numberKey.size(); + + while (lineBuffer[pos] == ' ') { + ++pos; + } + + switch (lineBuffer[pos + 1]) { + case 'x': { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 16); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 16)); + + if (kVerbose) { + kStdOut << "asm: found a base 16 number here: " << lineBuffer.substr(pos) << "\n"; + } + + return numOffset; + } + case 'b': { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 2); !res) { + if (errno != 0) { + Detail::print_error("invalid binary number:" + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "asm: found a base 2 number here:" << lineBuffer.substr(pos) << "\n"; + } + + return numOffset; + } + case 'o': { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 7); !res) { + if (errno != 0) { + Detail::print_error("invalid octal number: " + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "asm: found a base 8 number here:" << lineBuffer.substr(pos) << "\n"; + } + + return numOffset; + } + default: { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + Detail::print_error("invalid hex number: " + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 10)); + + if (kVerbose) { + kStdOut << "asm: found a base 10 number here:" << lineBuffer.substr(pos) << "\n"; + } + + return numOffset; + } + } +} diff --git a/dev/LibCompiler/Util/CompilerUtils.h b/dev/LibCompiler/Util/CompilerUtils.h new file mode 100644 index 0000000..19e247d --- /dev/null +++ b/dev/LibCompiler/Util/CompilerUtils.h @@ -0,0 +1,109 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include +#include +#include +#include +#include + +#define kZero64Section ".zero64" +#define kCode64Section ".code64" +#define kData64Section ".data64" + +#define kZero128Section ".zero128" +#define kCode128Section ".code128" +#define kData128Section ".data128" + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +#define kStdOut (std::cout << kRed << "drv: " << kWhite) +#define kStdErr (std::cout << kYellow << "drv: " << kWhite) + +inline static UInt32 kErrorLimit = 10; +inline static UInt32 kAcceptableErrors = 0; +inline static bool kVerbose = false; +inline static bool kOutputAsBinary = false; + +namespace Detail { +inline void print_error(std::string reason, std::string file) noexcept { + if (reason[0] == '\n') reason.erase(0, 1); + + kStdErr << reason << kBlank << std::endl; + + if (kAcceptableErrors > kErrorLimit) std::exit(LIBCOMPILER_EXEC_ERROR); + + ++kAcceptableErrors; +} + +inline void print_warning(std::string reason, std::string file) noexcept { + if (reason[0] == '\n') reason.erase(0, 1); + + kStdOut << kYellow << reason << kBlank << std::endl; +} + +/// @internal +/// @brief Handler for SIGSEGV signal. +inline void drvi_crash_handler(std::int32_t id) { + LibCompiler::STLString verbose_header = "LIBCOMPILER CRASH REPORT - "; + verbose_header += kDistVersion; + verbose_header += " - "; + verbose_header += LibCompiler::current_date(); + + for (auto& ch : verbose_header) { + std::cout << '='; + } + + std::cout << std::endl; + + std::cout << verbose_header << std::endl; + + for (auto& ch : verbose_header) { + std::cout << '='; + } + + std::cout << std::endl; + + kStdOut << "DATE: " << LibCompiler::current_date() << std::endl; + kStdOut << "VERSION: " << kDistVersion << std::endl; + kStdOut << "ERRNO: " << errno << std::endl; + kStdOut << "ERRNO(STRING): " << strerror(errno) << std::endl; + + switch (id) { + case SIGSEGV: { + kStdOut << "SIGNAL: Segmentation Fault." << kBlank << std::endl; + break; + } + case SIGABRT: { + kStdOut << "SIGNAL: Aborted." << kBlank << std::endl; + break; + } + } + + std::cout << kWhite; + + for (auto& ch : verbose_header) { + std::cout << '='; + } + + std::cout << std::endl; + + std::cout << verbose_header << std::endl; + + for (auto& ch : verbose_header) { + std::cout << '='; + } + + std::cout << std::endl; + + std::exit(LIBCOMPILER_EXEC_ERROR); +} +} // namespace Detail diff --git a/dev/LibCompiler/Util/DylibHelpers.h b/dev/LibCompiler/Util/DylibHelpers.h index 71ca230..e61cf9c 100644 --- a/dev/LibCompiler/Util/DylibHelpers.h +++ b/dev/LibCompiler/Util/DylibHelpers.h @@ -9,5 +9,5 @@ #include #include -typedef Int32(*LibCompilerEntrypoint)(Int32 argc, CharType const* argv[]); +typedef Int32 (*LibCompilerEntrypoint)(Int32 argc, CharType const* argv[]); typedef VoidPtr DylibHandle; diff --git a/dev/LibCompiler/Util/LCAsmUtils.h b/dev/LibCompiler/Util/LCAsmUtils.h deleted file mode 100644 index 7728444..0000000 --- a/dev/LibCompiler/Util/LCAsmUtils.h +++ /dev/null @@ -1,93 +0,0 @@ -/* ------------------------------------------- - - Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved - -------------------------------------------- */ - -#pragma once - -#include -#include - -#include - -using namespace LibCompiler; - -/// @brief Get Number from lineBuffer. -/// @param lineBuffer the lineBuffer to fetch from. -/// @param numberKey where to seek that number. -/// @return -static NumberCast32 GetNumber32(std::string lineBuffer, std::string numberKey) { - auto pos = lineBuffer.find(numberKey) + numberKey.size(); - - while (lineBuffer[pos] == ' ') { - ++pos; - } - - switch (lineBuffer[pos + 1]) { - case 'x': { - if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 16); !res) { - if (errno != 0) { - Detail::print_error("invalid hex number: " + lineBuffer, "LibCompiler"); - throw std::runtime_error("invalid_hex"); - } - } - - NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 16)); - - if (kVerbose) { - kStdOut << "asm: found a base 16 number here: " << lineBuffer.substr(pos) << "\n"; - } - - return numOffset; - } - case 'b': { - if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 2); !res) { - if (errno != 0) { - Detail::print_error("invalid binary number:" + lineBuffer, "LibCompiler"); - throw std::runtime_error("invalid_bin"); - } - } - - NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 2)); - - if (kVerbose) { - kStdOut << "asm: found a base 2 number here:" << lineBuffer.substr(pos) << "\n"; - } - - return numOffset; - } - case 'o': { - if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 7); !res) { - if (errno != 0) { - Detail::print_error("invalid octal number: " + lineBuffer, "LibCompiler"); - throw std::runtime_error("invalid_octal"); - } - } - - NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 7)); - - if (kVerbose) { - kStdOut << "asm: found a base 8 number here:" << lineBuffer.substr(pos) << "\n"; - } - - return numOffset; - } - default: { - if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 10); !res) { - if (errno != 0) { - Detail::print_error("invalid hex number: " + lineBuffer, "LibCompiler"); - throw std::runtime_error("invalid_hex"); - } - } - - NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 10)); - - if (kVerbose) { - kStdOut << "asm: found a base 10 number here:" << lineBuffer.substr(pos) << "\n"; - } - - return numOffset; - } - } -} diff --git a/dev/LibCompiler/Util/LCClUtils.h b/dev/LibCompiler/Util/LCClUtils.h deleted file mode 100644 index 94a113c..0000000 --- a/dev/LibCompiler/Util/LCClUtils.h +++ /dev/null @@ -1,110 +0,0 @@ -/* ------------------------------------------- - - Copyright (C) 2024-2025 Amlal EL Mahrouss, all rights reserved - -------------------------------------------- */ - -#pragma once - -#include -#include -#include -#include -#include - -#define kZero64Section ".zero64" -#define kCode64Section ".code64" -#define kData64Section ".data64" - -#define kZero128Section ".zero128" -#define kCode128Section ".code128" -#define kData128Section ".data128" - -#define kBlank "\e[0;30m" -#define kRed "\e[0;31m" -#define kWhite "\e[0;97m" -#define kYellow "\e[0;33m" - -#define kStdOut (std::cout << kRed << "drv: " << kWhite) -#define kStdErr (std::cout << kYellow << "drv: " << kWhite) - -inline static UInt32 kErrorLimit = 10; -inline static UInt32 kAcceptableErrors = 0; -inline static bool kVerbose = false; -inline static bool kOutputAsBinary = false; - -namespace Detail { -inline void print_error(std::string reason, std::string file) noexcept { - if (reason[0] == '\n') reason.erase(0, 1); - - kStdErr << reason << kBlank << std::endl; - - if (kAcceptableErrors > kErrorLimit) std::exit(LIBCOMPILER_EXEC_ERROR); - - ++kAcceptableErrors; -} - -inline void print_warning(std::string reason, std::string file) noexcept { - if (reason[0] == '\n') reason.erase(0, 1); - - kStdOut << kYellow << reason << kBlank << std::endl; -} - -/// @internal -/// @brief Handler for SIGSEGV signal. -inline void drvi_crash_handler(std::int32_t id) { - LibCompiler::STLString verbose_header = "LIBCOMPILER CRASH REPORT - "; - verbose_header += kDistVersion; - verbose_header += " - "; - verbose_header += LibCompiler::current_date(); - - for (auto& ch : verbose_header) { - std::cout << '='; - } - - std::cout << std::endl; - - std::cout << verbose_header << std::endl; - - for (auto& ch : verbose_header) { - std::cout << '='; - } - - std::cout << std::endl; - - kStdOut << "DATE: " << LibCompiler::current_date() << std::endl; - kStdOut << "VERSION: " << kDistVersion << std::endl; - kStdOut << "ERRNO: " << errno << std::endl; - kStdOut << "ERRNO(STRING): " << strerror(errno) << std::endl; - - - switch (id) { - case SIGSEGV: { - kStdOut << "SIGNAL: Segmentation Fault." << kBlank << std::endl; - break; - } - case SIGABRT: { - kStdOut << "SIGNAL: Aborted." << kBlank << std::endl; - break; - } - } - - std::cout << kWhite; - - for (auto& ch : verbose_header) { - std::cout << '='; - } - - std::cout << std::endl; - - std::cout << verbose_header << std::endl; - - for (auto& ch : verbose_header) { - std::cout << '='; - } - - std::cout << std::endl; - - std::exit(LIBCOMPILER_EXEC_ERROR); -} -} // namespace Detail diff --git a/dev/LibCompiler/deprecated/Frontend/CCompiler64x0.cc b/dev/LibCompiler/deprecated/Frontend/CCompiler64x0.cc index fc31ef9..7b908f7 100644 --- a/dev/LibCompiler/deprecated/Frontend/CCompiler64x0.cc +++ b/dev/LibCompiler/deprecated/Frontend/CCompiler64x0.cc @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/dev/LibCompiler/deprecated/Frontend/CCompilerARM64.cc b/dev/LibCompiler/deprecated/Frontend/CCompilerARM64.cc index 02e39f9..9e62747 100644 --- a/dev/LibCompiler/deprecated/Frontend/CCompilerARM64.cc +++ b/dev/LibCompiler/deprecated/Frontend/CCompilerARM64.cc @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/dev/LibCompiler/deprecated/Frontend/CCompilerPower64.cc b/dev/LibCompiler/deprecated/Frontend/CCompilerPower64.cc index 216891a..233ad98 100644 --- a/dev/LibCompiler/deprecated/Frontend/CCompilerPower64.cc +++ b/dev/LibCompiler/deprecated/Frontend/CCompilerPower64.cc @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/dev/LibCompiler/src/Backend/Assembler32x0.cc b/dev/LibCompiler/src/Backend/Assembler32x0.cc index 683b866..b7ff321 100644 --- a/dev/LibCompiler/src/Backend/Assembler32x0.cc +++ b/dev/LibCompiler/src/Backend/Assembler32x0.cc @@ -17,26 +17,15 @@ ///////////////////////////////////////////////////////////////////////////////////////// +#ifndef __ASM_NEED_32x0__ #define __ASM_NEED_32x0__ 1 +#endif #include #include #include #include - -///////////////////// - -// ANSI ESCAPE CODES - -///////////////////// - -#define kBlank "\e[0;30m" -#define kRed "\e[0;31m" -#define kWhite "\e[0;97m" -#define kYellow "\e[0;33m" - -#define kStdOut (std::cout << kWhite) -#define kStdErr (std::cout << kRed) +#include ///////////////////////////////////////////////////////////////////////////////////////// @@ -45,5 +34,6 @@ ///////////////////////////////////////////////////////////////////////////////////////// LIBCOMPILER_MODULE(NEAssemblerMain32000) { - return 0; + LibCompiler::install_signal(SIGSEGV, Detail::drvi_crash_handler); + return EXIT_SUCCESS; } diff --git a/dev/LibCompiler/src/Backend/Assembler64x0.cc b/dev/LibCompiler/src/Backend/Assembler64x0.cc index fb96708..765bea2 100644 --- a/dev/LibCompiler/src/Backend/Assembler64x0.cc +++ b/dev/LibCompiler/src/Backend/Assembler64x0.cc @@ -17,13 +17,15 @@ ///////////////////////////////////////////////////////////////////////////////////////// +#ifndef __ASM_NEED_64x0__ #define __ASM_NEED_64x0__ 1 +#endif #include #include #include #include -#include +#include #include #include #include @@ -38,8 +40,7 @@ static char kOutputArch = LibCompiler::kPefArch64000; -/// @note The 64x0 is VLSIW, so we need to jump to 4 bytes. -constexpr auto k64x0IPAlignment = 0x4U; +constexpr auto k64x0IPAlignment = 0x1U; static std::size_t kCounter = 1UL; @@ -67,7 +68,7 @@ static bool asm_read_attributes(std::string line); ///////////////////////////////////////////////////////////////////////////////////////// LIBCOMPILER_MODULE(AssemblerMain64x0) { - ::signal(SIGSEGV, Detail::drvi_crash_handler); + LibCompiler::install_signal(SIGSEGV, Detail::drvi_crash_handler); for (size_t i = 1; i < argc; ++i) { if (argv[i][0] == '-') { diff --git a/dev/LibCompiler/src/Backend/AssemblerAMD64.cc b/dev/LibCompiler/src/Backend/AssemblerAMD64.cc index e9f3ad5..6551743 100644 --- a/dev/LibCompiler/src/Backend/AssemblerAMD64.cc +++ b/dev/LibCompiler/src/Backend/AssemblerAMD64.cc @@ -21,7 +21,9 @@ ///////////////////////////////////////////////////////////////////////////////////////// +#ifndef __ASM_NEED_AMD64__ #define __ASM_NEED_AMD64__ 1 +#endif #define kAssemblerPragmaSymStr "#" #define kAssemblerPragmaSym '#' @@ -74,7 +76,7 @@ static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; // \brief forward decl. static bool asm_read_attributes(std::string line); -#include +#include ///////////////////////////////////////////////////////////////////////////////////////// @@ -85,7 +87,7 @@ static bool asm_read_attributes(std::string line); LIBCOMPILER_MODULE(AssemblerMainAMD64) { //////////////// CPU OPCODES BEGIN //////////////// - ::signal(SIGSEGV, Detail::drvi_crash_handler); + LibCompiler::install_signal(SIGSEGV, Detail::drvi_crash_handler); std::string opcodes_jump[kJumpLimit] = {"ja", "jae", "jb", "jbe", "jc", "je", "jg", "jge", "jl", "jle", "jna", "jnae", "jnb", "jnbe", "jnc", "jne", diff --git a/dev/LibCompiler/src/Backend/AssemblerARM64.cc b/dev/LibCompiler/src/Backend/AssemblerARM64.cc index 8fad6b5..142dcc7 100644 --- a/dev/LibCompiler/src/Backend/AssemblerARM64.cc +++ b/dev/LibCompiler/src/Backend/AssemblerARM64.cc @@ -15,14 +15,16 @@ ///////////////////////////////////////////////////////////////////////////////////////// +#ifndef __ASM_NEED_ARM64__ #define __ASM_NEED_ARM64__ 1 +#endif #include #include #include #include #include -#include +#include #include #include #include @@ -71,7 +73,7 @@ static bool asm_read_attributes(std::string line); ///////////////////////////////////////////////////////////////////////////////////////// LIBCOMPILER_MODULE(AssemblerMainARM64) { - ::signal(SIGSEGV, Detail::drvi_crash_handler); + LibCompiler::install_signal(SIGSEGV, Detail::drvi_crash_handler); for (size_t i = 1; i < argc; ++i) { if (argv[i][0] == '-') { diff --git a/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc b/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc index 86a70b9..3134748 100644 --- a/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc +++ b/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc @@ -15,14 +15,16 @@ ///////////////////////////////////////////////////////////////////////////////////////// +#ifndef __ASM_NEED_PPC__ #define __ASM_NEED_PPC__ 1 +#endif #include #include #include #include #include -#include +#include #include #include #include @@ -71,7 +73,7 @@ static bool asm_read_attributes(std::string line); ///////////////////////////////////////////////////////////////////////////////////////// LIBCOMPILER_MODULE(AssemblerMainPower64) { - ::signal(SIGSEGV, Detail::drvi_crash_handler); + LibCompiler::install_signal(SIGSEGV, Detail::drvi_crash_handler); for (size_t i = 1; i < argc; ++i) { if (argv[i][0] == '-') { diff --git a/dev/LibCompiler/src/Frontend/CPlusPlusCompilerAMD64.cc b/dev/LibCompiler/src/Frontend/CPlusPlusCompilerAMD64.cc deleted file mode 100644 index 217f86b..0000000 --- a/dev/LibCompiler/src/Frontend/CPlusPlusCompilerAMD64.cc +++ /dev/null @@ -1,887 +0,0 @@ -/* - * ======================================================== - * - * cxxdrv - * Copyright (C) 2024-2025 Amlal El Mahrouss, all rights reserved. - * - * ======================================================== - */ - -/// BUGS: 1 - -#include "LibCompiler/Defines.h" -#define kPrintF printf - -#define kExitOK (EXIT_SUCCESS) -#define kExitNO (EXIT_FAILURE) - -// extern_segment, @autodelete { ... }, fn foo() -> auto { ... } - -#include -#include -#include -#include - -/* NeKernel C++ Compiler Driver */ -/* This is part of the LibCompiler. */ -/* (c) Amlal El Mahrouss */ - -/// @author EL Mahrouss Amlal (amlal@nekernel.org) -/// @file CPlusPlusCompilerAMD64.cxx -/// @brief Optimized C++ Compiler Driver. -/// @todo Throw error for scoped inside scoped variables when they get referenced outside. -/// @todo Add class/struct/enum support. - -/////////////////////// - -// ANSI ESCAPE CODES // - -/////////////////////// - -#define kBlank "\e[0;30m" -#define kRed "\e[0;31m" -#define kWhite "\e[0;97m" - -///////////////////////////////////// - -// INTERNALS OF THE C++ COMPILER - -///////////////////////////////////// - -/// @internal -namespace Detail { -std::filesystem::path expand_home(const std::filesystem::path& p) { - if (!p.empty() && p.string()[0] == '~') { - const char* home = std::getenv("HOME"); // For Unix-like systems - - if (!home) { - home = std::getenv("USERPROFILE"); // For Windows - } - - if (home) { - return std::filesystem::path(home) / p.relative_path().string().substr(1); - } else { - throw std::runtime_error("Home directory not found in environment variables"); - } - } - return p; -} - -struct CompilerRegisterMap final { - LibCompiler::STLString fName; - LibCompiler::STLString fReg; -}; - -/// \brief Offset based struct/class -struct CompilerStructMap final { - LibCompiler::STLString fName; - LibCompiler::STLString fReg; - std::vector> fOffsets; -}; - -/// \brief Compiler state structure. -struct CompilerState final { - std::vector fStackMapVector; - std::vector fStructMapVector; - LibCompiler::STLString fLastFile; - LibCompiler::STLString fLastError; -}; -} // namespace Detail - -static Detail::CompilerState kState; - -static Int32 kOnClassScope = 0; - -///////////////////////////////////////////////////////////////////////////////////////// - -// Target architecture. -static Int32 kMachine = LibCompiler::AssemblyFactory::kArchAMD64; - -///////////////////////////////////////// - -// ARGUMENTS REGISTERS (R8, R15) - -///////////////////////////////////////// - -static std::vector kKeywords; - -///////////////////////////////////////// - -// COMPILER PARSING UTILITIES/STATES. - -///////////////////////////////////////// - -static LibCompiler::AssemblyFactory kFactory; -static Boolean kInStruct = false; -static Boolean kOnWhileLoop = false; -static Boolean kOnForLoop = false; -static Boolean kInBraces = false; -static size_t kBracesCount = 0UL; - -/* @brief C++ compiler backend for the NeKernel C++ driver */ -class CompilerFrontendCPlusPlus final LC_COMPILER_FRONTEND { - public: - explicit CompilerFrontendCPlusPlus() = default; - ~CompilerFrontendCPlusPlus() override = default; - - LIBCOMPILER_COPY_DEFAULT(CompilerFrontendCPlusPlus); - - LibCompiler::SyntaxLeafList::SyntaxLeaf Compile(const LibCompiler::STLString text, LibCompiler::STLString file) override; - - const char* Language() override; -}; - -/// @internal compiler variables - -static CompilerFrontendCPlusPlus* kCompilerFrontend = nullptr; - -static std::vector kRegisterMap; - -static std::vector kRegisterList = { - "rbx", "rsi", "r10", "r11", "r12", "r13", "r14", "r15", "xmm12", "xmm13", "xmm14", "xmm15", -}; - -/// @brief The PEF calling convention (caller must save rax, rbp) -/// @note callee must return via **rax**. -static std::vector kRegisterConventionCallList = { - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", -}; - -static std::size_t kFunctionEmbedLevel = 0UL; - -/// detail namespaces - -const char* CompilerFrontendCPlusPlus::Language() { - return "AMD64 C++"; -} - -static std::uintptr_t kOrigin = 0x1000000; -static std::vector> kOriginMap; - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @name Compile -/// @brief Generate assembly from a C++ source. - -///////////////////////////////////////////////////////////////////////////////////////// - -LibCompiler::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlus::Compile( - LibCompiler::STLString text, LibCompiler::STLString file) { - LibCompiler::SyntaxLeafList::SyntaxLeaf syntax_tree; - - if (text.length() < 1) return syntax_tree; - - std::size_t index = 0UL; - std::vector> keywords_list; - - for (auto& keyword : kKeywords) { - if (text.find(keyword.keyword_name) != std::string::npos) { - switch (keyword.keyword_kind) { - case LibCompiler::kKeywordKindCommentInline: { - break; - } - default: - break; - } - - std::size_t pos = text.find(keyword.keyword_name); - if (pos == std::string::npos) continue; - - // Safe guard: can't go before start of string - if (pos > 0 && text[pos - 1] == '+' && - keyword.keyword_kind == LibCompiler::kKeywordKindVariableAssign) - continue; - - if (pos > 0 && text[pos - 1] == '-' && - keyword.keyword_kind == LibCompiler::kKeywordKindVariableAssign) - continue; - - // Safe guard: don't go out of range - if ((pos + keyword.keyword_name.size()) < text.size() && - text[pos + keyword.keyword_name.size()] == '=' && - keyword.keyword_kind == LibCompiler::kKeywordKindVariableAssign) - continue; - - keywords_list.emplace_back(std::make_pair(keyword, index)); - ++index; - } - } - - for (auto& keyword : keywords_list) { - if (text.find(keyword.first.keyword_name) == LibCompiler::STLString::npos) continue; - - switch (keyword.first.keyword_kind) { - case LibCompiler::KeywordKind::kKeywordKindClass: { - ++kOnClassScope; - break; - } - case LibCompiler::KeywordKind::kKeywordKindIf: { - std::size_t keywordPos = text.find(keyword.first.keyword_name); - std::size_t openParen = text.find("(", keywordPos); - std::size_t closeParen = text.find(")", openParen); - - if (keywordPos == LibCompiler::STLString::npos || - openParen == LibCompiler::STLString::npos || - closeParen == LibCompiler::STLString::npos || closeParen <= openParen) { - Detail::print_error("Malformed if expression: " + text, file); - break; - } - - auto expr = text.substr(openParen + 1, closeParen - openParen - 1); - - if (expr.find(">=") != LibCompiler::STLString::npos) { - auto left = text.substr( - text.find(keyword.first.keyword_name) + keyword.first.keyword_name.size() + 2, - expr.find("<=") + strlen("<=")); - auto right = text.substr(expr.find(">=") + strlen(">="), text.find(")") - 1); - - size_t i = right.size() - 1; - - if (i < 1) break; - - try { - while (!std::isalnum(right[i])) { - right.erase(i, 1); - --i; - } - - right.erase(0, i); - } catch (...) { - right.erase(0, i); - } - - i = left.size() - 1; - try { - while (!std::isalnum(left[i])) { - left.erase(i, 1); - --i; - } - - left.erase(0, i); - } catch (...) { - left.erase(0, i); - } - - if (!isdigit(left[0]) || !isdigit(right[0])) { - auto indexRight = 0UL; - - auto& valueOfVar = !isdigit(left[0]) ? left : right; - - if (!valueOfVar.empty()) { - for (auto pairRight : kRegisterMap) { - ++indexRight; - - if (pairRight != valueOfVar) { - auto& valueOfVarOpposite = isdigit(left[0]) ? left : right; - - syntax_tree.fUserValue += - "mov " + kRegisterList[indexRight + 1] + ", " + valueOfVarOpposite + "\n"; - syntax_tree.fUserValue += "cmp " + kRegisterList[kRegisterMap.size() - 1] + "," + - kRegisterList[indexRight + 1] + "\n"; - - goto done_iterarting_on_if; - } - - auto& valueOfVarOpposite = isdigit(left[0]) ? left : right; - - syntax_tree.fUserValue += - "mov " + kRegisterList[indexRight + 1] + ", " + valueOfVarOpposite + "\n"; - syntax_tree.fUserValue += "cmp " + kRegisterList[kRegisterMap.size() - 1] + ", " + - kRegisterList[indexRight + 1] + "\n"; - - break; - } - } - } - - done_iterarting_on_if: - - LibCompiler::STLString fnName = text; - fnName.erase(fnName.find(keyword.first.keyword_name)); - - for (auto& ch : fnName) { - if (ch == ' ') ch = '_'; - } - - syntax_tree.fUserValue += - "jge __OFFSET_ON_TRUE_LC\nsegment .code64 __OFFSET_ON_TRUE_LC:\n"; - } - - break; - } - case LibCompiler::KeywordKind::kKeywordKindFunctionStart: { - for (auto& ch : text) { - if (isdigit(ch)) { - goto dont_accept; - } - } - - goto accept; - - dont_accept: - break; - - accept: - LibCompiler::STLString fnName = text; - size_t indexFnName = 0; - - // this one is for the type. - for (auto& ch : text) { - ++indexFnName; - - if (ch == '\t') break; - if (ch == ' ') break; - } - - fnName = text.substr(indexFnName); - - if (text.find("return ") != LibCompiler::STLString::npos) { - text.erase(0, text.find("return ")); - break; - } - - if (text.ends_with(";") && text.find("return") == LibCompiler::STLString::npos) - goto lc_write_assembly; - else if (text.size() <= indexFnName) - Detail::print_error("Invalid function name: " + fnName, file); - - indexFnName = 0; - - for (auto& ch : fnName) { - if (ch == ' ' || ch == '\t') { - if (fnName[indexFnName - 1] != ')') - Detail::print_error("Invalid function name: " + fnName, file); - } - - ++indexFnName; - } - - if (fnName.find("(") != LibCompiler::STLString::npos) { - fnName.erase(fnName.find("(")); - } - - syntax_tree.fUserValue = "public_segment .code64 __LIBCOMPILER_" + fnName + "\n"; - ++kFunctionEmbedLevel; - - kOriginMap.push_back({"__LIBCOMPILER_" + fnName, kOrigin}); - - break; - - lc_write_assembly: - auto it = - std::find_if(kOriginMap.begin(), kOriginMap.end(), - [&fnName](std::pair pair) -> bool { - return fnName == pair.first; - }); - - if (it != kOriginMap.end()) { - std::stringstream ss; - ss << std::hex << it->second; - - syntax_tree.fUserValue = "jmp " + ss.str() + "\n"; - kOrigin += 1UL; - } - } - case LibCompiler::KeywordKind::kKeywordKindFunctionEnd: { - if (kOnClassScope) --kOnClassScope; - - if (text.ends_with(";")) break; - - --kFunctionEmbedLevel; - - if (kRegisterMap.size() > kRegisterList.size()) { - --kFunctionEmbedLevel; - } - - if (kFunctionEmbedLevel < 1) kRegisterMap.clear(); - - break; - } - case LibCompiler::KeywordKind::kKeywordKindEndInstr: - case LibCompiler::KeywordKind::kKeywordKindVariableInc: - case LibCompiler::KeywordKind::kKeywordKindVariableDec: - case LibCompiler::KeywordKind::kKeywordKindVariableAssign: { - LibCompiler::STLString valueOfVar = ""; - - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { - valueOfVar = text.substr(text.find("+=") + 2); - } else if (keyword.first.keyword_kind == - LibCompiler::KeywordKind::kKeywordKindVariableDec) { - valueOfVar = text.substr(text.find("-=") + 2); - } else if (keyword.first.keyword_kind == - LibCompiler::KeywordKind::kKeywordKindVariableAssign) { - valueOfVar = text.substr(text.find("=") + 1); - } else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { - break; - } - - while (valueOfVar.find(";") != LibCompiler::STLString::npos && - keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindEndInstr) { - valueOfVar.erase(valueOfVar.find(";")); - } - - LibCompiler::STLString varName = text; - - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { - varName.erase(varName.find("+=")); - } else if (keyword.first.keyword_kind == - LibCompiler::KeywordKind::kKeywordKindVariableDec) { - varName.erase(varName.find("-=")); - } else if (keyword.first.keyword_kind == - LibCompiler::KeywordKind::kKeywordKindVariableAssign) { - varName.erase(varName.find("=")); - } else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { - varName.erase(varName.find(";")); - } - - static Boolean typeFound = false; - - for (auto& keyword : kKeywords) { - if (keyword.keyword_kind == LibCompiler::kKeywordKindType) { - if (text.find(keyword.keyword_name) != LibCompiler::STLString::npos) { - if (text[text.find(keyword.keyword_name)] == ' ') { - typeFound = false; - continue; - } - - typeFound = true; - } - } - } - - LibCompiler::STLString instr = "mov "; - - std::vector newVars; - - if (typeFound && - keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindVariableInc && - keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindVariableDec) { - if (kRegisterMap.size() > kRegisterList.size()) { - ++kFunctionEmbedLevel; - } - - while (varName.find(" ") != LibCompiler::STLString::npos) { - varName.erase(varName.find(" "), 1); - } - - while (varName.find("\t") != LibCompiler::STLString::npos) { - varName.erase(varName.find("\t"), 1); - } - - for (size_t i = 0; !isalnum(valueOfVar[i]); i++) { - if (i > valueOfVar.size()) break; - - valueOfVar.erase(i, 1); - } - - constexpr auto kTrueVal = "true"; - constexpr auto kFalseVal = "false"; - - if (valueOfVar == kTrueVal) { - valueOfVar = "1"; - } else if (valueOfVar == kFalseVal) { - valueOfVar = "0"; - } - - std::size_t indexRight = 0UL; - - for (auto pairRight : kRegisterMap) { - ++indexRight; - - if (pairRight != valueOfVar) { - if (valueOfVar[0] == '\"') { - syntax_tree.fUserValue = "segment .data64 __LIBCOMPILER_LOCAL_VAR_" + varName + - ": db " + valueOfVar + ", 0\n\n"; - syntax_tree.fUserValue += instr + kRegisterList[kRegisterMap.size() - 1] + ", " + - "__LIBCOMPILER_LOCAL_VAR_" + varName + "\n"; - kOrigin += 1UL; - } else { - syntax_tree.fUserValue = - instr + kRegisterList[kRegisterMap.size() - 1] + ", " + valueOfVar + "\n"; - kOrigin += 1UL; - } - - goto done; - } - } - - if (((int) indexRight - 1) < 0) { - if (valueOfVar[0] == '\"') { - syntax_tree.fUserValue = "segment .data64 __LIBCOMPILER_LOCAL_VAR_" + varName + - ": db " + valueOfVar + ", 0\n"; - syntax_tree.fUserValue += instr + kRegisterList[kRegisterMap.size()] + ", " + - "__LIBCOMPILER_LOCAL_VAR_" + varName + "\n"; - kOrigin += 1UL; - } else { - syntax_tree.fUserValue = - instr + kRegisterList[kRegisterMap.size()] + ", " + valueOfVar + "\n"; - kOrigin += 1UL; - } - - goto done; - } - - if (valueOfVar[0] != '\"' && valueOfVar[0] != '\'' && !isdigit(valueOfVar[0])) { - for (auto pair : kRegisterMap) { - if (pair == valueOfVar) goto done; - } - - Detail::print_error("Variable not declared: " + varName, file); - break; - } - - done: - for (auto& keyword : kKeywords) { - if (keyword.keyword_kind == LibCompiler::kKeywordKindType && - varName.find(keyword.keyword_name) != LibCompiler::STLString::npos) { - varName.erase(varName.find(keyword.keyword_name), keyword.keyword_name.size()); - break; - } - } - - newVars.push_back(varName); - - break; - } - - kRegisterMap.insert(kRegisterMap.end(), newVars.begin(), newVars.end()); - - if (keyword.second > 0 && - kKeywords[keyword.second - 1].keyword_kind == LibCompiler::kKeywordKindType || - kKeywords[keyword.second - 1].keyword_kind == LibCompiler::kKeywordKindTypePtr) { - syntax_tree.fUserValue = "\n"; - continue; - } - - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { - syntax_tree.fUserValue = "\n"; - continue; - } - - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { - instr = "add "; - } else if (keyword.first.keyword_kind == - LibCompiler::KeywordKind::kKeywordKindVariableDec) { - instr = "sub "; - } - - LibCompiler::STLString varErrCpy = varName; - - while (varName.find(" ") != LibCompiler::STLString::npos) { - varName.erase(varName.find(" "), 1); - } - - while (varName.find("\t") != LibCompiler::STLString::npos) { - varName.erase(varName.find("\t"), 1); - } - - std::size_t indxReg = 0UL; - - for (size_t i = 0; !isalnum(valueOfVar[i]); i++) { - if (i > valueOfVar.size()) break; - - valueOfVar.erase(i, 1); - } - - while (valueOfVar.find(" ") != LibCompiler::STLString::npos) { - valueOfVar.erase(valueOfVar.find(" "), 1); - } - - while (valueOfVar.find("\t") != LibCompiler::STLString::npos) { - valueOfVar.erase(valueOfVar.find("\t"), 1); - } - - constexpr auto kTrueVal = "true"; - constexpr auto kFalseVal = "false"; - - /// interpet boolean values, since we're on C++ - - if (valueOfVar == kTrueVal) { - valueOfVar = "1"; - } else if (valueOfVar == kFalseVal) { - valueOfVar = "0"; - } - - for (auto pair : kRegisterMap) { - ++indxReg; - - if (pair != varName) continue; - - std::size_t indexRight = 0ul; - - for (auto pairRight : kRegisterMap) { - ++indexRight; - - if (pairRight != varName) { - syntax_tree.fUserValue = - instr + kRegisterList[kRegisterMap.size()] + ", " + valueOfVar + "\n"; - kOrigin += 1UL; - continue; - } - - syntax_tree.fUserValue = - instr + kRegisterList[indexRight - 1] + ", " + valueOfVar + "\n"; - kOrigin += 1UL; - break; - } - - newVars.push_back(varName); - break; - } - - if (syntax_tree.fUserValue.empty()) { - Detail::print_error("Variable not declared: " + varName, file); - } - - kRegisterMap.insert(kRegisterMap.end(), newVars.begin(), newVars.end()); - - break; - } - case LibCompiler::KeywordKind::kKeywordKindReturn: { - try { - auto pos = text.find("return") + strlen("return") + 1; - LibCompiler::STLString subText = text.substr(pos); - subText = subText.erase(subText.find(";")); - size_t indxReg = 0UL; - - if (subText[0] != '\"' && subText[0] != '\'') { - if (!isdigit(subText[0])) { - for (auto pair : kRegisterMap) { - ++indxReg; - - if (pair != subText) continue; - - syntax_tree.fUserValue = "mov rax, " + kRegisterList[indxReg - 1] + "\nret\n"; - kOrigin += 1UL; - - break; - } - } else { - syntax_tree.fUserValue = "mov rax, " + subText + "\nret\n"; - kOrigin += 1UL; - - break; - } - } else { - syntax_tree.fUserValue = "__LIBCOMPILER_LOCAL_RETURN_STRING: db " + subText + - ", 0\nmov rcx, __LIBCOMPILER_LOCAL_RETURN_STRING\n"; - syntax_tree.fUserValue += "mov rax, rcx\nret\n"; - kOrigin += 1UL; - - break; - } - - if (syntax_tree.fUserValue.empty()) { - if (subText.find("(") != LibCompiler::STLString::npos) { - subText.erase(subText.find("(")); - - auto it = std::find_if( - kOriginMap.begin(), kOriginMap.end(), - [&subText](std::pair pair) -> bool { - return pair.first.find(subText) != LibCompiler::STLString::npos; - }); - - if (it == kOriginMap.end()) - Detail::print_error("Invalid return value: " + subText, file); - - std::stringstream ss; - ss << it->second; - - syntax_tree.fUserValue = "jmp " + ss.str() + "\nret\n"; - kOrigin += 1UL; - break; - } - } - - break; - } catch (...) { - syntax_tree.fUserValue = "ret\n"; - kOrigin += 1UL; - } - } - default: { - continue; - } - } - } - - return syntax_tree; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -/** - * @brief C++ assembler class. - */ - -///////////////////////////////////////////////////////////////////////////////////////// - -class AssemblyCPlusPlusInterface final LC_ASSEMBLY_INTERFACE { - public: - explicit AssemblyCPlusPlusInterface() = default; - ~AssemblyCPlusPlusInterface() override = default; - - LIBCOMPILER_COPY_DEFAULT(AssemblyCPlusPlusInterface); - - UInt32 Arch() noexcept override { return LibCompiler::AssemblyFactory::kArchAMD64; } - - Int32 CompileToFormat(LibCompiler::STLString src, Int32 arch) override { - if (kCompilerFrontend == nullptr) return kExitNO; - - LibCompiler::STLString dest = src; - dest += ".pp.masm"; - - std::ofstream out_fp(dest); - std::ifstream src_fp = std::ifstream(src + ".pp"); - - LibCompiler::STLString line_source; - - while (std::getline(src_fp, line_source)) { - out_fp << kCompilerFrontend->Compile(line_source, src).fUserValue; - } - - return kExitOK; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////////////////////////// - -#define kExtListCxx \ - { ".cpp", ".cxx", ".cc", ".c++", ".cp" } - -LIBCOMPILER_MODULE(CompilerCPlusPlusAMD64) { - Boolean skip = false; - - kKeywords.emplace_back("if", LibCompiler::kKeywordKindIf); - kKeywords.emplace_back("else", LibCompiler::kKeywordKindElse); - kKeywords.emplace_back("else if", LibCompiler::kKeywordKindElseIf); - - kKeywords.emplace_back("class", LibCompiler::kKeywordKindClass); - kKeywords.emplace_back("struct", LibCompiler::kKeywordKindClass); - kKeywords.emplace_back("namespace", LibCompiler::kKeywordKindNamespace); - kKeywords.emplace_back("typedef", LibCompiler::kKeywordKindTypedef); - kKeywords.emplace_back("using", LibCompiler::kKeywordKindTypedef); - kKeywords.emplace_back("{", LibCompiler::kKeywordKindBodyStart); - kKeywords.emplace_back("}", LibCompiler::kKeywordKindBodyEnd); - kKeywords.emplace_back("auto", LibCompiler::kKeywordKindVariable); - kKeywords.emplace_back("int", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("bool", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("unsigned", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("short", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("char", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("long", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("float", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("double", LibCompiler::kKeywordKindType); - kKeywords.emplace_back("void", LibCompiler::kKeywordKindType); - - kKeywords.emplace_back("auto*", LibCompiler::kKeywordKindVariablePtr); - kKeywords.emplace_back("int*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("bool*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("unsigned*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("short*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("char*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("long*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("float*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("double*", LibCompiler::kKeywordKindTypePtr); - kKeywords.emplace_back("void*", LibCompiler::kKeywordKindTypePtr); - - kKeywords.emplace_back("(", LibCompiler::kKeywordKindFunctionStart); - kKeywords.emplace_back(")", LibCompiler::kKeywordKindFunctionEnd); - kKeywords.emplace_back("=", LibCompiler::kKeywordKindVariableAssign); - kKeywords.emplace_back("+=", LibCompiler::kKeywordKindVariableInc); - kKeywords.emplace_back("-=", LibCompiler::kKeywordKindVariableDec); - kKeywords.emplace_back("const", LibCompiler::kKeywordKindConstant); - kKeywords.emplace_back("*", LibCompiler::kKeywordKindPtr); - kKeywords.emplace_back("->", LibCompiler::kKeywordKindPtrAccess); - kKeywords.emplace_back(".", LibCompiler::kKeywordKindAccess); - kKeywords.emplace_back(",", LibCompiler::kKeywordKindArgSeparator); - kKeywords.emplace_back(";", LibCompiler::kKeywordKindEndInstr); - kKeywords.emplace_back(":", LibCompiler::kKeywordKindSpecifier); - kKeywords.emplace_back("public:", LibCompiler::kKeywordKindSpecifier); - kKeywords.emplace_back("private:", LibCompiler::kKeywordKindSpecifier); - kKeywords.emplace_back("protected:", LibCompiler::kKeywordKindSpecifier); - kKeywords.emplace_back("final", LibCompiler::kKeywordKindSpecifier); - kKeywords.emplace_back("return", LibCompiler::kKeywordKindReturn); - kKeywords.emplace_back("/*", LibCompiler::kKeywordKindCommentMultiLineStart); - kKeywords.emplace_back("*/", LibCompiler::kKeywordKindCommentMultiLineEnd); - kKeywords.emplace_back("//", LibCompiler::kKeywordKindCommentInline); - kKeywords.emplace_back("==", LibCompiler::kKeywordKindEq); - kKeywords.emplace_back("!=", LibCompiler::kKeywordKindNotEq); - kKeywords.emplace_back(">=", LibCompiler::kKeywordKindGreaterEq); - kKeywords.emplace_back("<=", LibCompiler::kKeywordKindLessEq); - - kErrorLimit = 0; - - kCompilerFrontend = new CompilerFrontendCPlusPlus(); - kFactory.Mount(new AssemblyCPlusPlusInterface()); - - ::signal(SIGSEGV, Detail::drvi_crash_handler); - - for (auto index = 1UL; index < argc; ++index) { - if (!argv[index]) break; - - if (argv[index][0] == '-') { - if (skip) { - skip = false; - continue; - } - - if (strcmp(argv[index], "-cxx-verbose") == 0) { - kVerbose = true; - - continue; - } - - if (strcmp(argv[index], "-cxx-dialect") == 0) { - if (kCompilerFrontend) std::cout << kCompilerFrontend->Language() << "\n"; - - return LIBCOMPILER_SUCCESS; - } - - if (strcmp(argv[index], "-cxx-max-err") == 0) { - try { - kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); - } - // catch anything here - catch (...) { - kErrorLimit = 0; - } - - skip = true; - - continue; - } - - LibCompiler::STLString err = "Unknown option: "; - err += argv[index]; - - Detail::print_error(err, "cxxdrv"); - - continue; - } - - LibCompiler::STLString argv_i = argv[index]; - - std::vector exts = kExtListCxx; - - for (LibCompiler::STLString ext : exts) { - if (argv_i.ends_with(ext)) { - if (kFactory.Compile(argv_i, kMachine) != kExitOK) { - return LIBCOMPILER_INVALID_DATA; - } - - break; - } - } - } - - kFactory.Unmount(); - - return LIBCOMPILER_SUCCESS; -} - -// -// Last rev 23-5-25 -// diff --git a/dev/LibCompiler/src/Frontend/CompilerCPlusPlusAMD64.cc b/dev/LibCompiler/src/Frontend/CompilerCPlusPlusAMD64.cc new file mode 100644 index 0000000..df9035d --- /dev/null +++ b/dev/LibCompiler/src/Frontend/CompilerCPlusPlusAMD64.cc @@ -0,0 +1,890 @@ +/* + * ======================================================== + * + * cxxdrv + * Copyright (C) 2024-2025 Amlal El Mahrouss, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: 1 + +#define kPrintF printf + +#define kExitOK (EXIT_SUCCESS) +#define kExitNO (EXIT_FAILURE) + +// extern_segment, @autodelete { ... }, fn foo() -> auto { ... } + +#include +#include +#include +#include + +/* NeKernel C++ Compiler Driver */ +/* This is part of the LibCompiler. */ +/* (c) Amlal El Mahrouss */ + +/// @author EL Mahrouss Amlal (amlal@nekernel.org) +/// @file CPlusPlusCompilerAMD64.cxx +/// @brief Optimized C++ Compiler Driver. +/// @todo Throw error for scoped inside scoped variables when they get referenced outside. +/// @todo Add class/struct/enum support. + +/////////////////////// + +// ANSI ESCAPE CODES // + +/////////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +// INTERNALS OF THE C++ COMPILER + +///////////////////////////////////// + +/// @internal +namespace Detail { +std::filesystem::path expand_home(const std::filesystem::path& p) { + if (!p.empty() && p.string()[0] == '~') { + const char* home = std::getenv("HOME"); // For Unix-like systems + + if (!home) { + home = std::getenv("USERPROFILE"); // For Windows + } + + if (home) { + return std::filesystem::path(home) / p.relative_path().string().substr(1); + } else { + throw std::runtime_error("Home directory not found in environment variables"); + } + } + return p; +} + +struct CompilerRegisterMap final { + LibCompiler::STLString fName; + LibCompiler::STLString fReg; +}; + +/// \brief Offset based struct/class +struct CompilerStructMap final { + LibCompiler::STLString fName; + LibCompiler::STLString fReg; + std::vector> fOffsets; +}; + +/// \brief Compiler state structure. +struct CompilerState final { + std::vector fStackMapVector; + std::vector fStructMapVector; + LibCompiler::STLString fLastFile; + LibCompiler::STLString fLastError; +}; +} // namespace Detail + +static Detail::CompilerState kState; + +static Int32 kOnClassScope = 0; + +///////////////////////////////////////////////////////////////////////////////////////// + +// Target architecture. +static Int32 kMachine = LibCompiler::AssemblyFactory::kArchAMD64; + +///////////////////////////////////////// + +// ARGUMENTS REGISTERS (R8, R15) + +///////////////////////////////////////// + +static std::vector kKeywords; + +///////////////////////////////////////// + +// COMPILER PARSING UTILITIES/STATES. + +///////////////////////////////////////// + +static LibCompiler::AssemblyFactory kFactory; +static Boolean kInStruct = false; +static Boolean kOnWhileLoop = false; +static Boolean kOnForLoop = false; +static Boolean kInBraces = false; +static size_t kBracesCount = 0UL; + +/* @brief C++ compiler backend for the NeKernel C++ driver */ +class CompilerFrontendCPlusPlus final LC_COMPILER_FRONTEND { + public: + explicit CompilerFrontendCPlusPlus() = default; + ~CompilerFrontendCPlusPlus() override = default; + + LIBCOMPILER_COPY_DEFAULT(CompilerFrontendCPlusPlus); + + LibCompiler::SyntaxLeafList::SyntaxLeaf Compile(const LibCompiler::STLString text, + LibCompiler::STLString file) override; + + const char* Language() override; +}; + +/// @internal compiler variables + +static CompilerFrontendCPlusPlus* kCompilerFrontend = nullptr; + +static std::vector kRegisterMap; + +static std::vector kRegisterList = { + "rbx", "rsi", "r10", "r11", "r12", "r13", "r14", "r15", "xmm12", "xmm13", "xmm14", "xmm15", +}; + +/// @brief The PEF calling convention (caller must save rax, rbp) +/// @note callee must return via **rax**. +static std::vector kRegisterConventionCallList = { + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", +}; + +static std::size_t kFunctionEmbedLevel = 0UL; + +/// detail namespaces + +const char* CompilerFrontendCPlusPlus::Language() { + return "AMD64 C++"; +} + +static std::uintptr_t kOrigin = 0x1000000; +static std::vector> kOriginMap; + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @name Compile +/// @brief Generate assembly from a C++ source. + +///////////////////////////////////////////////////////////////////////////////////////// + +LibCompiler::SyntaxLeafList::SyntaxLeaf CompilerFrontendCPlusPlus::Compile( + LibCompiler::STLString text, LibCompiler::STLString file) { + LibCompiler::SyntaxLeafList::SyntaxLeaf syntax_tree; + + if (text.length() < 1) return syntax_tree; + + std::size_t index = 0UL; + std::vector> keywords_list; + + for (auto& keyword : kKeywords) { + if (text.find(keyword.keyword_name) != std::string::npos) { + switch (keyword.keyword_kind) { + case LibCompiler::kKeywordKindCommentInline: { + break; + } + default: + break; + } + + std::size_t pos = text.find(keyword.keyword_name); + if (pos == std::string::npos) continue; + + // Safe guard: can't go before start of string + if (pos > 0 && text[pos - 1] == '+' && + keyword.keyword_kind == LibCompiler::kKeywordKindVariableAssign) + continue; + + if (pos > 0 && text[pos - 1] == '-' && + keyword.keyword_kind == LibCompiler::kKeywordKindVariableAssign) + continue; + + // Safe guard: don't go out of range + if ((pos + keyword.keyword_name.size()) < text.size() && + text[pos + keyword.keyword_name.size()] == '=' && + keyword.keyword_kind == LibCompiler::kKeywordKindVariableAssign) + continue; + + keywords_list.emplace_back(std::make_pair(keyword, index)); + ++index; + } + } + + for (auto& keyword : keywords_list) { + if (text.find(keyword.first.keyword_name) == LibCompiler::STLString::npos) continue; + + switch (keyword.first.keyword_kind) { + case LibCompiler::KeywordKind::kKeywordKindClass: { + ++kOnClassScope; + break; + } + case LibCompiler::KeywordKind::kKeywordKindIf: { + std::size_t keywordPos = text.find(keyword.first.keyword_name); + std::size_t openParen = text.find("(", keywordPos); + std::size_t closeParen = text.find(")", openParen); + + if (keywordPos == LibCompiler::STLString::npos || + openParen == LibCompiler::STLString::npos || + closeParen == LibCompiler::STLString::npos || closeParen <= openParen) { + Detail::print_error("Malformed if expression: " + text, file); + break; + } + + auto expr = text.substr(openParen + 1, closeParen - openParen - 1); + + if (expr.find(">=") != LibCompiler::STLString::npos) { + auto left = text.substr( + text.find(keyword.first.keyword_name) + keyword.first.keyword_name.size() + 2, + expr.find("<=") + strlen("<=")); + auto right = text.substr(expr.find(">=") + strlen(">="), text.find(")") - 1); + + size_t i = right.size() - 1; + + if (i < 1) break; + + try { + while (!std::isalnum(right[i])) { + right.erase(i, 1); + --i; + } + + right.erase(0, i); + } catch (...) { + right.erase(0, i); + } + + i = left.size() - 1; + try { + while (!std::isalnum(left[i])) { + left.erase(i, 1); + --i; + } + + left.erase(0, i); + } catch (...) { + left.erase(0, i); + } + + if (!isdigit(left[0]) || !isdigit(right[0])) { + auto indexRight = 0UL; + + auto& valueOfVar = !isdigit(left[0]) ? left : right; + + if (!valueOfVar.empty()) { + for (auto pairRight : kRegisterMap) { + ++indexRight; + + if (pairRight != valueOfVar) { + auto& valueOfVarOpposite = isdigit(left[0]) ? left : right; + + syntax_tree.fUserValue += + "mov " + kRegisterList[indexRight + 1] + ", " + valueOfVarOpposite + "\n"; + syntax_tree.fUserValue += "cmp " + kRegisterList[kRegisterMap.size() - 1] + "," + + kRegisterList[indexRight + 1] + "\n"; + + goto done_iterarting_on_if; + } + + auto& valueOfVarOpposite = isdigit(left[0]) ? left : right; + + syntax_tree.fUserValue += + "mov " + kRegisterList[indexRight + 1] + ", " + valueOfVarOpposite + "\n"; + syntax_tree.fUserValue += "cmp " + kRegisterList[kRegisterMap.size() - 1] + ", " + + kRegisterList[indexRight + 1] + "\n"; + + break; + } + } + } + + done_iterarting_on_if: + + LibCompiler::STLString fnName = text; + fnName.erase(fnName.find(keyword.first.keyword_name)); + + for (auto& ch : fnName) { + if (ch == ' ') ch = '_'; + } + + syntax_tree.fUserValue += + "jge __OFFSET_ON_TRUE_LC\nsegment .code64 __OFFSET_ON_TRUE_LC:\n"; + } + + break; + } + case LibCompiler::KeywordKind::kKeywordKindFunctionStart: { + for (auto& ch : text) { + if (isdigit(ch)) { + goto dont_accept; + } + } + + goto accept; + + dont_accept: + break; + + accept: + LibCompiler::STLString fnName = text; + size_t indexFnName = 0; + + // this one is for the type. + for (auto& ch : text) { + ++indexFnName; + + if (ch == '\t') break; + if (ch == ' ') break; + } + + fnName = text.substr(indexFnName); + + if (text.find("return ") != LibCompiler::STLString::npos) { + text.erase(0, text.find("return ")); + break; + } + + if (text.ends_with(";") && text.find("return") == LibCompiler::STLString::npos) + goto lc_write_assembly; + else if (text.size() <= indexFnName) + Detail::print_error("Invalid function name: " + fnName, file); + + indexFnName = 0; + + for (auto& ch : fnName) { + if (ch == ' ' || ch == '\t') { + if (fnName[indexFnName - 1] != ')') + Detail::print_error("Invalid function name: " + fnName, file); + } + + ++indexFnName; + } + + if (fnName.find("(") != LibCompiler::STLString::npos) { + fnName.erase(fnName.find("(")); + } + + syntax_tree.fUserValue = "public_segment .code64 __LIBCOMPILER_" + fnName + "\n"; + ++kFunctionEmbedLevel; + + kOriginMap.push_back({"__LIBCOMPILER_" + fnName, kOrigin}); + + break; + + lc_write_assembly: + auto it = + std::find_if(kOriginMap.begin(), kOriginMap.end(), + [&fnName](std::pair pair) -> bool { + return fnName == pair.first; + }); + + if (it != kOriginMap.end()) { + std::stringstream ss; + ss << std::hex << it->second; + + syntax_tree.fUserValue = "jmp " + ss.str() + "\n"; + kOrigin += 1UL; + } + } + case LibCompiler::KeywordKind::kKeywordKindFunctionEnd: { + if (kOnClassScope) --kOnClassScope; + + if (text.ends_with(";")) break; + + --kFunctionEmbedLevel; + + if (kRegisterMap.size() > kRegisterList.size()) { + --kFunctionEmbedLevel; + } + + if (kFunctionEmbedLevel < 1) kRegisterMap.clear(); + + break; + } + case LibCompiler::KeywordKind::kKeywordKindEndInstr: + case LibCompiler::KeywordKind::kKeywordKindVariableInc: + case LibCompiler::KeywordKind::kKeywordKindVariableDec: + case LibCompiler::KeywordKind::kKeywordKindVariableAssign: { + LibCompiler::STLString valueOfVar = ""; + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { + valueOfVar = text.substr(text.find("+=") + 2); + } else if (keyword.first.keyword_kind == + LibCompiler::KeywordKind::kKeywordKindVariableDec) { + valueOfVar = text.substr(text.find("-=") + 2); + } else if (keyword.first.keyword_kind == + LibCompiler::KeywordKind::kKeywordKindVariableAssign) { + valueOfVar = text.substr(text.find("=") + 1); + } else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { + break; + } + + while (valueOfVar.find(";") != LibCompiler::STLString::npos && + keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindEndInstr) { + valueOfVar.erase(valueOfVar.find(";")); + } + + LibCompiler::STLString varName = text; + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { + varName.erase(varName.find("+=")); + } else if (keyword.first.keyword_kind == + LibCompiler::KeywordKind::kKeywordKindVariableDec) { + varName.erase(varName.find("-=")); + } else if (keyword.first.keyword_kind == + LibCompiler::KeywordKind::kKeywordKindVariableAssign) { + varName.erase(varName.find("=")); + } else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { + varName.erase(varName.find(";")); + } + + static Boolean typeFound = false; + + for (auto& keyword : kKeywords) { + if (keyword.keyword_kind == LibCompiler::kKeywordKindType) { + if (text.find(keyword.keyword_name) != LibCompiler::STLString::npos) { + if (text[text.find(keyword.keyword_name)] == ' ') { + typeFound = false; + continue; + } + + typeFound = true; + } + } + } + + LibCompiler::STLString instr = "mov "; + + std::vector newVars; + + if (typeFound && + keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindVariableInc && + keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindVariableDec) { + if (kRegisterMap.size() > kRegisterList.size()) { + ++kFunctionEmbedLevel; + } + + while (varName.find(" ") != LibCompiler::STLString::npos) { + varName.erase(varName.find(" "), 1); + } + + while (varName.find("\t") != LibCompiler::STLString::npos) { + varName.erase(varName.find("\t"), 1); + } + + for (size_t i = 0; !isalnum(valueOfVar[i]); i++) { + if (i > valueOfVar.size()) break; + + valueOfVar.erase(i, 1); + } + + constexpr auto kTrueVal = "true"; + constexpr auto kFalseVal = "false"; + + if (valueOfVar == kTrueVal) { + valueOfVar = "1"; + } else if (valueOfVar == kFalseVal) { + valueOfVar = "0"; + } + + std::size_t indexRight = 0UL; + + for (auto pairRight : kRegisterMap) { + ++indexRight; + + if (pairRight != valueOfVar) { + if (valueOfVar[0] == '\"') { + syntax_tree.fUserValue = "segment .data64 __LIBCOMPILER_LOCAL_VAR_" + varName + + ": db " + valueOfVar + ", 0\n\n"; + syntax_tree.fUserValue += instr + kRegisterList[kRegisterMap.size() - 1] + ", " + + "__LIBCOMPILER_LOCAL_VAR_" + varName + "\n"; + kOrigin += 1UL; + } else { + syntax_tree.fUserValue = + instr + kRegisterList[kRegisterMap.size() - 1] + ", " + valueOfVar + "\n"; + kOrigin += 1UL; + } + + goto done; + } + } + + if (((int) indexRight - 1) < 0) { + if (valueOfVar[0] == '\"') { + syntax_tree.fUserValue = "segment .data64 __LIBCOMPILER_LOCAL_VAR_" + varName + + ": db " + valueOfVar + ", 0\n"; + syntax_tree.fUserValue += instr + kRegisterList[kRegisterMap.size()] + ", " + + "__LIBCOMPILER_LOCAL_VAR_" + varName + "\n"; + kOrigin += 1UL; + } else { + syntax_tree.fUserValue = + instr + kRegisterList[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + kOrigin += 1UL; + } + + goto done; + } + + if (valueOfVar[0] != '\"' && valueOfVar[0] != '\'' && !isdigit(valueOfVar[0])) { + for (auto pair : kRegisterMap) { + if (pair == valueOfVar) goto done; + } + + Detail::print_error("Variable not declared: " + varName, file); + break; + } + + done: + for (auto& keyword : kKeywords) { + if (keyword.keyword_kind == LibCompiler::kKeywordKindType && + varName.find(keyword.keyword_name) != LibCompiler::STLString::npos) { + varName.erase(varName.find(keyword.keyword_name), keyword.keyword_name.size()); + break; + } + } + + newVars.push_back(varName); + + break; + } + + kRegisterMap.insert(kRegisterMap.end(), newVars.begin(), newVars.end()); + + if (keyword.second > 0 && + kKeywords[keyword.second - 1].keyword_kind == LibCompiler::kKeywordKindType || + kKeywords[keyword.second - 1].keyword_kind == LibCompiler::kKeywordKindTypePtr) { + syntax_tree.fUserValue = "\n"; + continue; + } + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { + syntax_tree.fUserValue = "\n"; + continue; + } + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { + instr = "add "; + } else if (keyword.first.keyword_kind == + LibCompiler::KeywordKind::kKeywordKindVariableDec) { + instr = "sub "; + } + + LibCompiler::STLString varErrCpy = varName; + + while (varName.find(" ") != LibCompiler::STLString::npos) { + varName.erase(varName.find(" "), 1); + } + + while (varName.find("\t") != LibCompiler::STLString::npos) { + varName.erase(varName.find("\t"), 1); + } + + std::size_t indxReg = 0UL; + + for (size_t i = 0; !isalnum(valueOfVar[i]); i++) { + if (i > valueOfVar.size()) break; + + valueOfVar.erase(i, 1); + } + + while (valueOfVar.find(" ") != LibCompiler::STLString::npos) { + valueOfVar.erase(valueOfVar.find(" "), 1); + } + + while (valueOfVar.find("\t") != LibCompiler::STLString::npos) { + valueOfVar.erase(valueOfVar.find("\t"), 1); + } + + constexpr auto kTrueVal = "true"; + constexpr auto kFalseVal = "false"; + + /// interpet boolean values, since we're on C++ + + if (valueOfVar == kTrueVal) { + valueOfVar = "1"; + } else if (valueOfVar == kFalseVal) { + valueOfVar = "0"; + } + + for (auto pair : kRegisterMap) { + ++indxReg; + + if (pair != varName) continue; + + std::size_t indexRight = 0ul; + + for (auto pairRight : kRegisterMap) { + ++indexRight; + + if (pairRight != varName) { + syntax_tree.fUserValue = + instr + kRegisterList[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + kOrigin += 1UL; + continue; + } + + syntax_tree.fUserValue = + instr + kRegisterList[indexRight - 1] + ", " + valueOfVar + "\n"; + kOrigin += 1UL; + break; + } + + newVars.push_back(varName); + break; + } + + if (syntax_tree.fUserValue.empty()) { + Detail::print_error("Variable not declared: " + varName, file); + } + + kRegisterMap.insert(kRegisterMap.end(), newVars.begin(), newVars.end()); + + break; + } + case LibCompiler::KeywordKind::kKeywordKindReturn: { + try { + auto pos = text.find("return") + strlen("return") + 1; + LibCompiler::STLString subText = text.substr(pos); + subText = subText.erase(subText.find(";")); + size_t indxReg = 0UL; + + if (subText[0] != '\"' && subText[0] != '\'') { + if (!isdigit(subText[0])) { + for (auto pair : kRegisterMap) { + ++indxReg; + + if (pair != subText) continue; + + syntax_tree.fUserValue = "mov rax, " + kRegisterList[indxReg - 1] + "\nret\n"; + kOrigin += 1UL; + + break; + } + } else { + syntax_tree.fUserValue = "mov rax, " + subText + "\nret\n"; + kOrigin += 1UL; + + break; + } + } else { + syntax_tree.fUserValue = "__LIBCOMPILER_LOCAL_RETURN_STRING: db " + subText + + ", 0\nmov rcx, __LIBCOMPILER_LOCAL_RETURN_STRING\n"; + syntax_tree.fUserValue += "mov rax, rcx\nret\n"; + kOrigin += 1UL; + + break; + } + + if (syntax_tree.fUserValue.empty()) { + if (subText.find("(") != LibCompiler::STLString::npos) { + subText.erase(subText.find("(")); + + auto it = std::find_if( + kOriginMap.begin(), kOriginMap.end(), + [&subText](std::pair pair) -> bool { + return pair.first.find(subText) != LibCompiler::STLString::npos; + }); + + if (it == kOriginMap.end()) + Detail::print_error("Invalid return value: " + subText, file); + + std::stringstream ss; + ss << it->second; + + syntax_tree.fUserValue = "jmp " + ss.str() + "\nret\n"; + kOrigin += 1UL; + break; + } + } + + break; + } catch (...) { + syntax_tree.fUserValue = "ret\n"; + kOrigin += 1UL; + } + } + default: { + continue; + } + } + } + + return syntax_tree; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/** + * @brief C++ assembler class. + */ + +///////////////////////////////////////////////////////////////////////////////////////// + +class AssemblyCPlusPlusInterface final LC_ASSEMBLY_INTERFACE { + public: + explicit AssemblyCPlusPlusInterface() = default; + ~AssemblyCPlusPlusInterface() override = default; + + LIBCOMPILER_COPY_DEFAULT(AssemblyCPlusPlusInterface); + + UInt32 Arch() noexcept override { return LibCompiler::AssemblyFactory::kArchAMD64; } + + Int32 CompileToFormat(LibCompiler::STLString src, Int32 arch) override { + if (kCompilerFrontend == nullptr) return kExitNO; + + LibCompiler::STLString dest = src; + dest += ".pp.masm"; + + std::ofstream out_fp(dest); + std::ifstream src_fp = std::ifstream(src + ".pp"); + + LibCompiler::STLString line_source; + + out_fp << "#bits 64\n"; + out_fp << "#org " << kOrigin << "\n\n"; + + while (std::getline(src_fp, line_source)) { + out_fp << kCompilerFrontend->Compile(line_source, src).fUserValue; + } + + return kExitOK; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExtListCxx \ + { ".cpp", ".cxx", ".cc", ".c++", ".cp" } + +LIBCOMPILER_MODULE(CompilerCPlusPlusAMD64) { + Boolean skip = false; + + kKeywords.emplace_back("if", LibCompiler::kKeywordKindIf); + kKeywords.emplace_back("else", LibCompiler::kKeywordKindElse); + kKeywords.emplace_back("else if", LibCompiler::kKeywordKindElseIf); + + kKeywords.emplace_back("class", LibCompiler::kKeywordKindClass); + kKeywords.emplace_back("struct", LibCompiler::kKeywordKindClass); + kKeywords.emplace_back("namespace", LibCompiler::kKeywordKindNamespace); + kKeywords.emplace_back("typedef", LibCompiler::kKeywordKindTypedef); + kKeywords.emplace_back("using", LibCompiler::kKeywordKindTypedef); + kKeywords.emplace_back("{", LibCompiler::kKeywordKindBodyStart); + kKeywords.emplace_back("}", LibCompiler::kKeywordKindBodyEnd); + kKeywords.emplace_back("auto", LibCompiler::kKeywordKindVariable); + kKeywords.emplace_back("int", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("bool", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("unsigned", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("short", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("char", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("long", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("float", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("double", LibCompiler::kKeywordKindType); + kKeywords.emplace_back("void", LibCompiler::kKeywordKindType); + + kKeywords.emplace_back("auto*", LibCompiler::kKeywordKindVariablePtr); + kKeywords.emplace_back("int*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("bool*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("unsigned*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("short*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("char*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("long*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("float*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("double*", LibCompiler::kKeywordKindTypePtr); + kKeywords.emplace_back("void*", LibCompiler::kKeywordKindTypePtr); + + kKeywords.emplace_back("(", LibCompiler::kKeywordKindFunctionStart); + kKeywords.emplace_back(")", LibCompiler::kKeywordKindFunctionEnd); + kKeywords.emplace_back("=", LibCompiler::kKeywordKindVariableAssign); + kKeywords.emplace_back("+=", LibCompiler::kKeywordKindVariableInc); + kKeywords.emplace_back("-=", LibCompiler::kKeywordKindVariableDec); + kKeywords.emplace_back("const", LibCompiler::kKeywordKindConstant); + kKeywords.emplace_back("*", LibCompiler::kKeywordKindPtr); + kKeywords.emplace_back("->", LibCompiler::kKeywordKindPtrAccess); + kKeywords.emplace_back(".", LibCompiler::kKeywordKindAccess); + kKeywords.emplace_back(",", LibCompiler::kKeywordKindArgSeparator); + kKeywords.emplace_back(";", LibCompiler::kKeywordKindEndInstr); + kKeywords.emplace_back(":", LibCompiler::kKeywordKindSpecifier); + kKeywords.emplace_back("public:", LibCompiler::kKeywordKindSpecifier); + kKeywords.emplace_back("private:", LibCompiler::kKeywordKindSpecifier); + kKeywords.emplace_back("protected:", LibCompiler::kKeywordKindSpecifier); + kKeywords.emplace_back("final", LibCompiler::kKeywordKindSpecifier); + kKeywords.emplace_back("return", LibCompiler::kKeywordKindReturn); + kKeywords.emplace_back("/*", LibCompiler::kKeywordKindCommentMultiLineStart); + kKeywords.emplace_back("*/", LibCompiler::kKeywordKindCommentMultiLineEnd); + kKeywords.emplace_back("//", LibCompiler::kKeywordKindCommentInline); + kKeywords.emplace_back("==", LibCompiler::kKeywordKindEq); + kKeywords.emplace_back("!=", LibCompiler::kKeywordKindNotEq); + kKeywords.emplace_back(">=", LibCompiler::kKeywordKindGreaterEq); + kKeywords.emplace_back("<=", LibCompiler::kKeywordKindLessEq); + + kErrorLimit = 0; + + kCompilerFrontend = new CompilerFrontendCPlusPlus(); + kFactory.Mount(new AssemblyCPlusPlusInterface()); + + LibCompiler::install_signal(SIGSEGV, Detail::drvi_crash_handler); + + for (auto index = 1UL; index < argc; ++index) { + if (!argv[index]) break; + + if (argv[index][0] == '-') { + if (skip) { + skip = false; + continue; + } + + if (strcmp(argv[index], "-cxx-verbose") == 0) { + kVerbose = true; + + continue; + } + + if (strcmp(argv[index], "-cxx-dialect") == 0) { + if (kCompilerFrontend) std::cout << kCompilerFrontend->Language() << "\n"; + + return LIBCOMPILER_SUCCESS; + } + + if (strcmp(argv[index], "-cxx-max-err") == 0) { + try { + kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); + } + // catch anything here + catch (...) { + kErrorLimit = 0; + } + + skip = true; + + continue; + } + + LibCompiler::STLString err = "Unknown option: "; + err += argv[index]; + + Detail::print_error(err, "cxxdrv"); + + continue; + } + + LibCompiler::STLString argv_i = argv[index]; + + std::vector exts = kExtListCxx; + + for (LibCompiler::STLString ext : exts) { + if (argv_i.ends_with(ext)) { + if (kFactory.Compile(argv_i, kMachine) != kExitOK) { + return LIBCOMPILER_INVALID_DATA; + } + + break; + } + } + } + + kFactory.Unmount(); + + return LIBCOMPILER_SUCCESS; +} + +// +// Last rev 23-5-25 +// diff --git a/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc b/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc index edfd47a..820b06d 100644 --- a/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc +++ b/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc @@ -35,7 +35,7 @@ #include //! LibCompiler utils. -#include +#include //! I/O stream from std c++ #include diff --git a/dev/LibDebugger/src/NeKernelContractCLI.cc b/dev/LibDebugger/src/NeKernelContractCLI.cc index dddefda..0107b7c 100644 --- a/dev/LibDebugger/src/NeKernelContractCLI.cc +++ b/dev/LibDebugger/src/NeKernelContractCLI.cc @@ -23,12 +23,20 @@ LIBCOMPILER_MODULE(DebuggerNeKernel) { pfd::notify("Debugger Event", "Kernel Debugger\n(C) 2025 Amlal El Mahrouss, all rights reserved."); - if (argc >= 3 && std::string(argv[1]) == "-p" && argv[2] != nullptr) { + if (argc >= 3 && std::string(argv[1]) == "-k" && argv[2] != nullptr) { kPath = argv[2]; - kStdOut << "[+] Kernel image set to: " << kPath << "\n"; + kStdOut << "[+] Kernel (ne_kernel) set to: " << kPath << "\n"; + + kKernelDebugger.Attach(kPath, nullptr, kPID); + kKernelDebugger.Breakpoint("$HANDOVER_START"); + + return EXIT_SUCCESS; } - return EXIT_SUCCESS; + kStdOut << "Usage: " << argv[0] << " -k \n"; + kStdOut << "Example: " << argv[0] << " -k /path/to/ne_kernel\n"; + + return EXIT_FAILURE; } #endif // LD_NEKERNEL_DEBUGGER \ No newline at end of file diff --git a/meta/png/nekernel.png b/meta/png/nekernel.png new file mode 100644 index 0000000..565a1db Binary files /dev/null and b/meta/png/nekernel.png differ diff --git a/sample/.keep b/sample/.keep new file mode 100644 index 0000000..e69de29 diff --git a/sample/asm/exit_nekernel_abi.asm b/sample/asm/exit_nekernel_abi.asm new file mode 100644 index 0000000..a97252f --- /dev/null +++ b/sample/asm/exit_nekernel_abi.asm @@ -0,0 +1,10 @@ +#bits 64 +#org 0x40000000 + +public_segment .code64 __ImageStart + xor rax, rax + mov rcx, 1 ;; syscall id + mov rdx, 0 ;; arg1 + syscall ;; exit + mov rax, rbx + ret \ No newline at end of file diff --git a/sample/asm/return_5_rax.asm b/sample/asm/return_5_rax.asm new file mode 100644 index 0000000..82c0320 --- /dev/null +++ b/sample/asm/return_5_rax.asm @@ -0,0 +1,4 @@ +public_segment .code64 __ImageStart + ;; rax is the return value register. + mov rax, 5 + ret \ No newline at end of file diff --git a/sample/cxx/example.cc b/sample/cxx/example.cc new file mode 100644 index 0000000..9ee2a30 --- /dev/null +++ b/sample/cxx/example.cc @@ -0,0 +1,16 @@ +#define main __ImageStart +#warning test macro warning #1 + +int bar() { + int yyy = 100; + return yyy; +} + +int foo() { + int arg1 = 0; + return bar(); +} + +int main() { + return foo(); +} diff --git a/tests/example.cc b/tests/example.cc deleted file mode 100644 index 9ee2a30..0000000 --- a/tests/example.cc +++ /dev/null @@ -1,16 +0,0 @@ -#define main __ImageStart -#warning test macro warning #1 - -int bar() { - int yyy = 100; - return yyy; -} - -int foo() { - int arg1 = 0; - return bar(); -} - -int main() { - return foo(); -} diff --git a/tests/exit_nekernel_abi.asm b/tests/exit_nekernel_abi.asm deleted file mode 100644 index a97252f..0000000 --- a/tests/exit_nekernel_abi.asm +++ /dev/null @@ -1,10 +0,0 @@ -#bits 64 -#org 0x40000000 - -public_segment .code64 __ImageStart - xor rax, rax - mov rcx, 1 ;; syscall id - mov rdx, 0 ;; arg1 - syscall ;; exit - mov rax, rbx - ret \ No newline at end of file diff --git a/tests/return_5_rax.asm b/tests/return_5_rax.asm deleted file mode 100644 index 82c0320..0000000 --- a/tests/return_5_rax.asm +++ /dev/null @@ -1,4 +0,0 @@ -public_segment .code64 __ImageStart - ;; rax is the return value register. - mov rax, 5 - ret \ No newline at end of file diff --git a/tools/cxxdrv.cc b/tools/cxxdrv.cc index a6f12ee..bc790ff 100644 --- a/tools/cxxdrv.cc +++ b/tools/cxxdrv.cc @@ -9,9 +9,9 @@ #include #include -#include -#include +#include #include +#include static auto kPath = "/usr/local/lib/libCompiler.dylib"; @@ -21,11 +21,12 @@ Int32 main(Int32 argc, CharType const* argv[]) { if (!handler) { kStdOut; std::printf("error: Could not load dylib in %s: %s\n", kPath, dlerror()); - + return EXIT_FAILURE; } - LibCompilerEntrypoint entrypoint_cxx = (LibCompilerEntrypoint)dlsym(handler, "CompilerCPlusPlusAMD64"); + LibCompilerEntrypoint entrypoint_cxx = + (LibCompilerEntrypoint) dlsym(handler, "CompilerCPlusPlusAMD64"); if (!entrypoint_cxx) { kStdOut; -- cgit v1.2.3 From d37f1a7381825d414e4b71c487eea509325f24c3 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Wed, 28 May 2025 18:12:17 +0200 Subject: refactor: Refactor toolchain source code. Signed-off-by: Amlal El Mahrouss --- dev/LibCompiler/AE.h | 14 +++++++------- dev/LibCompiler/BasicString.h | 16 ++++++++-------- dev/LibCompiler/CodeGen.h | 8 ++++---- dev/LibCompiler/Defines.h | 4 ++-- dev/LibCompiler/PEF.h | 4 ++-- dev/LibCompiler/Util/DylibHelpers.h | 2 +- dev/LibCompiler/src/Backend/AssemblerARM64.cc | 2 +- dev/LibCompiler/src/Backend/AssemblerPowerPC.cc | 2 +- dev/LibCompiler/src/BasicString.cc | 14 +++++++------- dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc | 6 +++--- tools/cxxdrv.cc | 2 +- tools/dbg.cc | 4 ++-- tools/kdbg.cc | 4 ++-- 13 files changed, 41 insertions(+), 41 deletions(-) (limited to 'dev/LibCompiler/src/Backend') diff --git a/dev/LibCompiler/AE.h b/dev/LibCompiler/AE.h index fcc37a5..8c05c3a 100644 --- a/dev/LibCompiler/AE.h +++ b/dev/LibCompiler/AE.h @@ -28,14 +28,14 @@ namespace LibCompiler { // One thing to keep in mind. // This object format, is reloctable. typedef struct AEHeader final { - CharType fMagic[kAEMagLen]; - CharType fArch; - CharType fSubArch; + Char fMagic[kAEMagLen]; + Char fArch; + Char fSubArch; SizeType fCount; - CharType fSize; + Char fSize; SizeType fStartCode; SizeType fCodeSize; - CharType fPad[kAEPad]; + Char fPad[kAEPad]; } PACKED AEHeader, *AEHeaderPtr; // @brief Advanced Executable Record. @@ -43,12 +43,12 @@ typedef struct AEHeader final { // fKind must be filled with PEF fields. typedef struct AERecordHeader final { - CharType fName[kAESymbolLen]; + Char fName[kAESymbolLen]; SizeType fKind; SizeType fSize; SizeType fFlags; UIntPtr fOffset; - CharType fPad[kAEPad]; + Char fPad[kAEPad]; } PACKED AERecordHeader, *AERecordHeaderPtr; enum { diff --git a/dev/LibCompiler/BasicString.h b/dev/LibCompiler/BasicString.h index 53b21d3..5af9da9 100644 --- a/dev/LibCompiler/BasicString.h +++ b/dev/LibCompiler/BasicString.h @@ -26,7 +26,7 @@ class BasicString final { explicit BasicString() = delete; explicit BasicString(SizeType Sz) noexcept : m_Sz(Sz) { - m_Data = new CharType[Sz]; + m_Data = new Char[Sz]; assert(m_Data); } @@ -41,17 +41,17 @@ class BasicString final { LIBCOMPILER_COPY_DEFAULT(BasicString); - CharType* Data(); - const CharType* CData() const; + Char* Data(); + const Char* CData() const; SizeType Length() const; - bool operator==(const CharType* rhs) const; - bool operator!=(const CharType* rhs) const; + bool operator==(const Char* rhs) const; + bool operator!=(const Char* rhs) const; bool operator==(const BasicString& rhs) const; bool operator!=(const BasicString& rhs) const; - BasicString& operator+=(const CharType* rhs); + BasicString& operator+=(const Char* rhs); BasicString& operator+=(const BasicString& rhs); operator bool() { return m_Data && m_Data[0] != 0; } @@ -59,7 +59,7 @@ class BasicString final { bool operator!() { return !m_Data || m_Data[0] == 0; } private: - CharType* m_Data{nullptr}; + Char* m_Data{nullptr}; SizeType m_Sz{0}; SizeType m_Cur{0}; @@ -71,7 +71,7 @@ class BasicString final { * @note These results shall call be delete[] after they're used. */ struct StringBuilder final { - static BasicString Construct(const CharType* data); + static BasicString Construct(const Char* data); static const char* FromInt(const char* fmt, int n); static const char* FromBool(const char* fmt, bool n); static const char* Format(const char* fmt, const char* from); diff --git a/dev/LibCompiler/CodeGen.h b/dev/LibCompiler/CodeGen.h index a189b93..b932cdf 100644 --- a/dev/LibCompiler/CodeGen.h +++ b/dev/LibCompiler/CodeGen.h @@ -74,7 +74,7 @@ union NumberCast64 final { ~NumberCast64() { raw = 0; } - CharType number[8]; + Char number[8]; UInt64 raw; }; @@ -84,7 +84,7 @@ union NumberCast32 final { ~NumberCast32() { raw = 0; } - CharType number[4]; + Char number[4]; UInt32 raw; }; @@ -94,7 +94,7 @@ union NumberCast16 final { ~NumberCast16() { raw = 0; } - CharType number[2]; + Char number[2]; UInt16 raw; }; @@ -104,7 +104,7 @@ union NumberCast8 final { ~NumberCast8() { raw = 0; } - CharType number; + Char number; UInt8 raw; }; diff --git a/dev/LibCompiler/Defines.h b/dev/LibCompiler/Defines.h index 24d9a96..1e1d4d9 100644 --- a/dev/LibCompiler/Defines.h +++ b/dev/LibCompiler/Defines.h @@ -48,7 +48,7 @@ #define Int8 int8_t #define UInt8 uint8_t -#define CharType char +#define Char char #define Boolean bool #include @@ -118,7 +118,7 @@ inline STLString current_date() noexcept { return fmt; } -inline bool to_str(CharType* str, Int32 limit, Int32 base) noexcept { +inline bool to_str(Char* str, Int32 limit, Int32 base) noexcept { if (limit == 0) return false; Int32 copy_limit = limit; diff --git a/dev/LibCompiler/PEF.h b/dev/LibCompiler/PEF.h index f2662c8..3f5a2b1 100644 --- a/dev/LibCompiler/PEF.h +++ b/dev/LibCompiler/PEF.h @@ -71,7 +71,7 @@ enum { /* PEF container */ typedef struct PEFContainer final { - CharType Magic[kPefMagicLen]; + Char Magic[kPefMagicLen]; UInt32 Linker; /* Linker used to link executable */ UInt32 Version; UInt32 Kind; @@ -90,7 +90,7 @@ typedef struct PEFContainer final { /* PEF executable section and commands. */ typedef struct PEFCommandHeader final { - CharType Name[kPefNameLen]; /* container name */ + Char Name[kPefNameLen]; /* container name */ UInt32 Cpu; /* container cpu */ UInt32 SubCpu; /* container sub-cpu */ UInt32 Flags; /* container flags */ diff --git a/dev/LibCompiler/Util/DylibHelpers.h b/dev/LibCompiler/Util/DylibHelpers.h index e61cf9c..be8119f 100644 --- a/dev/LibCompiler/Util/DylibHelpers.h +++ b/dev/LibCompiler/Util/DylibHelpers.h @@ -9,5 +9,5 @@ #include #include -typedef Int32 (*LibCompilerEntrypoint)(Int32 argc, CharType const* argv[]); +typedef Int32 (*LibCompilerEntrypoint)(Int32 argc, Char const* argv[]); typedef VoidPtr DylibHandle; diff --git a/dev/LibCompiler/src/Backend/AssemblerARM64.cc b/dev/LibCompiler/src/Backend/AssemblerARM64.cc index 142dcc7..a1cc6dc 100644 --- a/dev/LibCompiler/src/Backend/AssemblerARM64.cc +++ b/dev/LibCompiler/src/Backend/AssemblerARM64.cc @@ -45,7 +45,7 @@ constexpr auto cPowerIPAlignment = 0x1U; -static CharType kOutputArch = LibCompiler::kPefArchARM64; +static Char kOutputArch = LibCompiler::kPefArchARM64; static std::size_t kCounter = 1UL; diff --git a/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc b/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc index 3134748..b979f64 100644 --- a/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc +++ b/dev/LibCompiler/src/Backend/AssemblerPowerPC.cc @@ -45,7 +45,7 @@ constexpr auto cPowerIPAlignment = 0x4U; -static CharType kOutputArch = LibCompiler::kPefArchPowerPC; +static Char kOutputArch = LibCompiler::kPefArchPowerPC; static std::size_t kCounter = 1UL; diff --git a/dev/LibCompiler/src/BasicString.cc b/dev/LibCompiler/src/BasicString.cc index 78bfcaa..41989fe 100644 --- a/dev/LibCompiler/src/BasicString.cc +++ b/dev/LibCompiler/src/BasicString.cc @@ -21,11 +21,11 @@ #include namespace LibCompiler { -CharType* BasicString::Data() { +Char* BasicString::Data() { return m_Data; } -const CharType* BasicString::CData() const { +const Char* BasicString::CData() const { return m_Data; } @@ -43,7 +43,7 @@ bool BasicString::operator==(const BasicString& rhs) const { return true; } -bool BasicString::operator==(const CharType* rhs) const { +bool BasicString::operator==(const Char* rhs) const { if (string_length(rhs) != Length()) return false; for (SizeType index = 0; index < string_length(rhs); ++index) { @@ -63,7 +63,7 @@ bool BasicString::operator!=(const BasicString& rhs) const { return true; } -bool BasicString::operator!=(const CharType* rhs) const { +bool BasicString::operator!=(const Char* rhs) const { if (string_length(rhs) != Length()) return false; for (SizeType index = 0; index < string_length(rhs); ++index) { @@ -73,7 +73,7 @@ bool BasicString::operator!=(const CharType* rhs) const { return true; } -BasicString StringBuilder::Construct(const CharType* data) { +BasicString StringBuilder::Construct(const Char* data) { if (!data || *data == 0) return BasicString(0); BasicString view(strlen(data)); @@ -92,7 +92,7 @@ const char* StringBuilder::FromInt(const char* fmt, int i) { memset(ret, 0, ret_len); - CharType result[sizeof(int64_t)]; + Char result[sizeof(int64_t)]; if (!to_str(result, sizeof(int64_t), i)) { delete[] ret; @@ -183,7 +183,7 @@ const char* StringBuilder::Format(const char* fmt, const char* fmtRight) { return ret; } -BasicString& BasicString::operator+=(const CharType* rhs) { +BasicString& BasicString::operator+=(const Char* rhs) { if (strlen(rhs) > this->m_Sz) { throw std::runtime_error("out_of_bounds: BasicString"); } diff --git a/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc b/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc index 820b06d..b58c786 100644 --- a/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc +++ b/dev/LibCompiler/src/Linkers/DynamicLinkerPEF.cc @@ -68,7 +68,7 @@ namespace Detail { struct DynamicLinkerBlob final { - std::vector mBlob{}; // PEF code/bss/data blob. + std::vector mBlob{}; // PEF code/bss/data blob. UIntPtr mOffset{0UL}; // the offset of the PEF container header... }; } // namespace Detail @@ -89,8 +89,8 @@ static Bool kStartFound = false; static Bool kDuplicateSymbols = false; /* ld64 is to be found, mld is to be found at runtime. */ -static const CharType* kLdDefineSymbol = ":UndefinedSymbol:"; -static const CharType* kLdDynamicSym = ":RuntimeSymbol:"; +static const Char* kLdDefineSymbol = ":UndefinedSymbol:"; +static const Char* kLdDynamicSym = ":RuntimeSymbol:"; /* object code and list. */ static std::vector kObjectList; diff --git a/tools/cxxdrv.cc b/tools/cxxdrv.cc index bc790ff..99696aa 100644 --- a/tools/cxxdrv.cc +++ b/tools/cxxdrv.cc @@ -15,7 +15,7 @@ static auto kPath = "/usr/local/lib/libCompiler.dylib"; -Int32 main(Int32 argc, CharType const* argv[]) { +Int32 main(Int32 argc, Char const* argv[]) { DylibHandle handler = dlopen(kPath, RTLD_LAZY | RTLD_GLOBAL); if (!handler) { diff --git a/tools/dbg.cc b/tools/dbg.cc index 1d89c21..35b0702 100644 --- a/tools/dbg.cc +++ b/tools/dbg.cc @@ -9,10 +9,10 @@ /// @file dbg.cxx /// @brief NE debugger. -LC_IMPORT_C int DebuggerMachPOSIX(int argc, char const* argv[]); +LC_IMPORT_C Int32 DebuggerMachPOSIX(Int32 argc, Char const* argv[]); /// @brief Debugger entrypoint. /// @return Status code of debugger. -int main(int argc, char const* argv[]) { +Int32 main(Int32 argc, Char const* argv[]) { return DebuggerMachPOSIX(argc, argv); } diff --git a/tools/kdbg.cc b/tools/kdbg.cc index 08a0465..7616abe 100644 --- a/tools/kdbg.cc +++ b/tools/kdbg.cc @@ -9,10 +9,10 @@ /// @file kdbg.cxx /// @brief NeKernel debugger. -LC_IMPORT_C int DebuggerNeKernel(int argc, char const* argv[]); +LC_IMPORT_C Int32 DebuggerNeKernel(Int32 argc, Char const* argv[]); /// @brief Debugger entrypoint. /// @return Status code of debugger. -int main(int argc, char const* argv[]) { +Int32 main(Int32 argc, Char const* argv[]) { return DebuggerNeKernel(argc, argv); } -- cgit v1.2.3