From c3b10ee1e28737375d65c3811f390d77a84fc165 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Sun, 5 Jan 2025 11:10:54 +0100 Subject: WIP: ARM64 support is coming soon. WIP: Prototyping ELF linker for ZkaOS. Signed-off-by: Amlal El Mahrouss --- dev/LibCompiler/src/AssemblerARM64.cc | 0 dev/LibCompiler/src/CCompiler64x0.cc | 2 +- dev/LibCompiler/src/CCompilerARM64.cc | 1596 +++++++++++++++++++++++++ dev/LibCompiler/src/CCompilerPower64.cc | 2 +- dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc | 178 +-- dev/LibCompiler/src/CPlusPlusLinkerELF.cc | 92 ++ dev/LibCompiler/src/CPlusPlusLinkerPEF.cc | 774 ++++++++++++ dev/LibCompiler/src/Detail/ClUtils.h | 4 + dev/LibCompiler/src/DynamicLinker64PEF.cc | 774 ------------ 9 files changed, 2557 insertions(+), 865 deletions(-) create mode 100644 dev/LibCompiler/src/AssemblerARM64.cc create mode 100644 dev/LibCompiler/src/CCompilerARM64.cc create mode 100644 dev/LibCompiler/src/CPlusPlusLinkerELF.cc create mode 100644 dev/LibCompiler/src/CPlusPlusLinkerPEF.cc delete mode 100644 dev/LibCompiler/src/DynamicLinker64PEF.cc (limited to 'dev/LibCompiler/src') diff --git a/dev/LibCompiler/src/AssemblerARM64.cc b/dev/LibCompiler/src/AssemblerARM64.cc new file mode 100644 index 0000000..e69de29 diff --git a/dev/LibCompiler/src/CCompiler64x0.cc b/dev/LibCompiler/src/CCompiler64x0.cc index 657c2f9..8888a72 100644 --- a/dev/LibCompiler/src/CCompiler64x0.cc +++ b/dev/LibCompiler/src/CCompiler64x0.cc @@ -1493,7 +1493,7 @@ static void cc_print_help() #define kExt ".c" -TOOLCHAINKIT_MODULE(NewOSCompilerCLang64x0) +TOOLCHAINKIT_MODULE(ZkaOSCompilerCLang64x0) { kCompilerTypes.push_back({.fName = "void", .fValue = "void"}); kCompilerTypes.push_back({.fName = "char", .fValue = "byte"}); diff --git a/dev/LibCompiler/src/CCompilerARM64.cc b/dev/LibCompiler/src/CCompilerARM64.cc new file mode 100644 index 0000000..d985b2e --- /dev/null +++ b/dev/LibCompiler/src/CCompilerARM64.cc @@ -0,0 +1,1596 @@ +/* + * ======================================================== + * + * cc + * Copyright (C) 2024 Theater Quality Corp, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: 0 +/// TODO: none + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* C driver */ +/* This is part of the LibCompiler. */ +/* (c) Theater Quality Corp. */ + +/// @author EL Mahrouss Amlal (amlel) +/// @file ARM64-cc.cxx +/// @brief ARM64 C Compiler. + +/// TODO: support structures, else if, else, . and -> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kExitOK (0) + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +// INTERNAL STUFF OF THE C COMPILER + +///////////////////////////////////// + +namespace Details +{ + // \brief Register map structure, used to keep track of each variable's registers. + struct CompilerRegisterMap final + { + std::string fName; + std::string fReg; + }; + + // \brief Map for C structs + // \author amlel + struct CompilerStructMap final + { + // 'my_foo' + std::string fName; + + // if instance: stores a valid register. + std::string fReg; + + // offset count + std::size_t fOffsetsCnt; + + // offset array. + std::vector> fOffsets; + }; + + struct CompilerState final + { + std::vector fSyntaxTreeList; + std::vector kStackFrame; + std::vector kStructMap; + LibCompiler::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace Details + +static Details::CompilerState kState; +static SizeType kErrorLimit = 100; +static std::string kIfFunction = ""; +static Int32 kAcceptableErrors = 0; + +namespace Details +{ + /// @brief prints an error into stdout. + /// @param reason the reason of the error. + /// @param file where does it originate from? + void print_error(std::string reason, std::string file) noexcept; + + struct CompilerType final + { + std::string fName; + std::string fValue; + }; +} // namespace Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// Target architecture. +static int kMachine = 0; + +///////////////////////////////////////// + +// REGISTERS ACCORDING TO USED ASSEMBLER + +///////////////////////////////////////// + +static size_t kRegisterCnt = kAsmRegisterLimit; +static size_t kStartUsable = 8; +static size_t kUsableLimit = 15; +static size_t kRegisterCounter = kStartUsable; +static std::string kRegisterPrefix = kAsmRegisterPrefix; + +///////////////////////////////////////// + +// COMPILER PARSING UTILITIES/STATES. + +///////////////////////////////////////// + +static std::vector kFileList; +static LibCompiler::AssemblyFactory kFactory; +static bool kInStruct = false; +static bool kOnWhileLoop = false; +static bool kOnForLoop = false; +static bool kInBraces = false; +static bool kIfFound = false; +static size_t kBracesCount = 0UL; + +/* @brief C compiler backend for C */ +class CompilerFrontendARM64 final : public LibCompiler::ICompilerFrontend +{ +public: + explicit CompilerFrontendARM64() = default; + ~CompilerFrontendARM64() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(CompilerFrontendARM64); + + std::string Check(const char* text, const char* file); + bool Compile(const std::string text, const std::string file) override; + + const char* Language() override + { + return "64k C"; + } +}; + +static CompilerFrontendARM64* kCompilerFrontend = nullptr; +static std::vector kCompilerVariables; +static std::vector kCompilerFunctions; +static std::vector kCompilerTypes; + +namespace Details +{ + union number_cast final { + public: + number_cast(UInt64 _Raw) + : _Raw(_Raw) + { + } + + public: + char _Num[8]; + UInt64 _Raw; + }; + + union double_cast final { + public: + double_cast(float _Raw) + : _Raw(_Raw) + { + } + + public: + char _Sign; + char _Lh[8]; + char _Rh[23]; + + float _Raw; + }; +} // namespace Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name Compile +// @brief Generate MASM from a C assignement. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerFrontendARM64::Compile(const std::string text, const std::string file) +{ + std::string textBuffer = text; + + bool typeFound = false; + bool fnFound = false; + + // setup generator. + std::random_device rd; + + auto seed_data = std::array{}; + std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + // start parsing + for (size_t text_index = 0; text_index < textBuffer.size(); ++text_index) + { + auto syntaxLeaf = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid out = gen(); + + Details::number_cast time_off = (UInt64)out.as_bytes().data(); + + if (!typeFound) + { + auto substr = textBuffer.substr(text_index); + std::string match_type; + + for (size_t y = 0; y < substr.size(); ++y) + { + if (substr[y] == ' ') + { + while (match_type.find(' ') != std::string::npos) + { + match_type.erase(match_type.find(' ')); + } + + for (auto& clType : kCompilerTypes) + { + if (clType.fName == match_type) + { + match_type.clear(); + + std::string buf; + + buf += clType.fValue; + buf += ' '; + + if (substr.find('=') != std::string::npos) + { + break; + } + + if (textBuffer.find('(') != std::string::npos) + { + syntaxLeaf.fUserValue = buf; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + typeFound = true; + break; + } + } + + break; + } + + match_type += substr[y]; + } + } + + if (textBuffer[text_index] == '{') + { + if (kInStruct) + { + continue; + } + + kInBraces = true; + ++kBracesCount; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + // return keyword handler + if (textBuffer[text_index] == 'r') + { + std::string return_keyword; + return_keyword += "return"; + + std::size_t index = 0UL; + + std::string value; + + for (size_t return_index = text_index; return_index < textBuffer.size(); + ++return_index) + { + if (textBuffer[return_index] != return_keyword[index]) + { + for (size_t value_index = return_index; + value_index < textBuffer.size(); ++value_index) + { + if (textBuffer[value_index] == ';') + break; + + value += textBuffer[value_index]; + } + + break; + } + + ++index; + } + + if (index == return_keyword.size()) + { + if (!value.empty()) + { + if (value.find('(') != std::string::npos) + { + value.erase(value.find('(')); + } + + if (!isdigit(value[value.find('(') + 2])) + { + std::string tmp = value; + bool reg_to_reg = false; + + value.clear(); + + value += " extern_segment"; + value += tmp; + } + + syntaxLeaf.fUserValue = "\tldw r19, "; + + // make it pretty. + if (value.find('\t') != std::string::npos) + value.erase(value.find('\t'), 1); + + syntaxLeaf.fUserValue += value + "\n"; + } + + syntaxLeaf.fUserValue += "\tjlr"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + break; + } + } + + if (textBuffer[text_index] == 'i' && textBuffer[text_index + 1] == 'f') + { + auto expr = textBuffer.substr(text_index + 2); + textBuffer.erase(text_index, 2); + + if (expr.find("{") != std::string::npos) + { + expr.erase(expr.find("{")); + } + + if (expr.find("(") != std::string::npos) + expr.erase(expr.find("(")); + + if (expr.find(")") != std::string::npos) + expr.erase(expr.find(")")); + + kIfFunction = "__TOOLCHAINKIT_IF_PROC_"; + kIfFunction += std::to_string(time_off._Raw); + + syntaxLeaf.fUserValue = "\tlda r12, extern_segment "; + syntaxLeaf.fUserValue += + kIfFunction + + "\n\t#r12 = Code to jump on, r11 right cond, r10 left cond.\n\tbeq " + "r10, r11, r12\ndword public_segment .code64 " + + kIfFunction + "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + kIfFound = true; + } + + // Parse expressions and instructions here. + // what does this mean? + // we encounter an assignment, or we reached the end of an expression. + if (textBuffer[text_index] == '=' || textBuffer[text_index] == ';') + { + if (fnFound) + continue; + if (kIfFound) + continue; + + if (textBuffer[text_index] == ';' && kInStruct) + continue; + + if (textBuffer.find("typedef ") != std::string::npos) + continue; + + if (textBuffer[text_index] == '=' && kInStruct) + { + Details::print_error("assignement of value in struct " + textBuffer, + file); + continue; + } + + if (textBuffer[text_index] == ';' && kInStruct) + { + bool space_found_ = false; + std::string sym; + + for (auto& ch : textBuffer) + { + if (ch == ' ') + { + space_found_ = true; + } + + if (ch == ';') + break; + + if (space_found_) + sym.push_back(ch); + } + + kState.kStructMap[kState.kStructMap.size() - 1].fOffsets.push_back( + std::make_pair( + kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4, + sym)); + + kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt = + kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4; + + continue; + } + + if (textBuffer[text_index] == '=' && kInStruct) + { + continue; + } + + if (textBuffer[text_index + 1] == '=' || + textBuffer[text_index - 1] == '!' || + textBuffer[text_index - 1] == '<' || + textBuffer[text_index - 1] == '>') + { + continue; + } + + std::string substr; + + if (textBuffer.find('=') != std::string::npos && kInBraces && !kIfFound) + { + if (textBuffer.find("*") != std::string::npos) + { + if (textBuffer.find("=") > textBuffer.find("*")) + substr += "\tlda "; + else + substr += "\tldw "; + } + else + { + substr += "\tldw "; + } + } + else if (textBuffer.find('=') != std::string::npos && !kInBraces) + { + substr += "stw public_segment .data64 "; + } + + int first_encountered = 0; + + std::string str_name; + + for (size_t text_index_2 = 0; text_index_2 < textBuffer.size(); + ++text_index_2) + { + if (textBuffer[text_index_2] == '\"') + { + ++text_index_2; + + // want to add this, so that the parser recognizes that this is a + // string. + substr += '"'; + + for (; text_index_2 < textBuffer.size(); ++text_index_2) + { + if (textBuffer[text_index_2] == '\"') + break; + + substr += textBuffer[text_index_2]; + } + } + + if (textBuffer[text_index_2] == '{' || textBuffer[text_index_2] == '}') + continue; + + if (textBuffer[text_index_2] == ';') + { + break; + } + + if (textBuffer[text_index_2] == ' ' || + textBuffer[text_index_2] == '\t') + { + if (first_encountered != 2) + { + if (textBuffer[text_index] != '=' && + substr.find("public_segment .data64") == std::string::npos && + !kInStruct) + substr += "public_segment .data64 "; + } + + ++first_encountered; + + continue; + } + + if (textBuffer[text_index_2] == '=') + { + if (!kInBraces) + { + substr.replace(substr.find("public_segment .data64"), + strlen("public_segment .data64"), "public_segment .zero64 "); + } + + substr += ","; + continue; + } + + substr += textBuffer[text_index_2]; + } + + for (auto& clType : kCompilerTypes) + { + if (substr.find(clType.fName) != std::string::npos) + { + if (substr.find(clType.fName) > substr.find('"')) + continue; + + substr.erase(substr.find(clType.fName), clType.fName.size()); + } + else if (substr.find(clType.fValue) != std::string::npos) + { + if (substr.find(clType.fValue) > substr.find('"')) + continue; + + if (clType.fName == "const") + continue; + + substr.erase(substr.find(clType.fValue), clType.fValue.size()); + } + } + + if (substr.find("extern") != std::string::npos) + { + substr.replace(substr.find("extern"), strlen("extern"), "extern_segment "); + + if (substr.find("public_segment .data64") != std::string::npos) + substr.erase(substr.find("public_segment .data64"), strlen("public_segment .data64")); + } + + auto var_to_find = + std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), + [&](Details::CompilerType type) { + return type.fName.find(substr) != std::string::npos; + }); + + if (kRegisterCounter == 5 || kRegisterCounter == 6) + ++kRegisterCounter; + + std::string reg = kAsmRegisterPrefix; + reg += std::to_string(kRegisterCounter); + + if (var_to_find == kCompilerVariables.cend()) + { + ++kRegisterCounter; + + kState.kStackFrame.push_back({.fName = substr, .fReg = reg}); + kCompilerVariables.push_back({.fName = substr}); + } + + syntaxLeaf.fUserValue += substr; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + if (textBuffer[text_index] == '=') + break; + } + + // function handler. + + if (textBuffer[text_index] == '(' && !fnFound && !kIfFound) + { + std::string substr; + std::string args_buffer; + std::string args; + + bool type_crossed = false; + + for (size_t idx = textBuffer.find('(') + 1; idx < textBuffer.size(); + ++idx) + { + if (textBuffer[idx] == ',') + continue; + + if (textBuffer[idx] == ' ') + continue; + + if (textBuffer[idx] == ')') + break; + } + + for (char substr_first_index : textBuffer) + { + if (substr_first_index != ',') + args_buffer += substr_first_index; + else + args_buffer += '$'; + + if (substr_first_index == ';') + { + args_buffer = args_buffer.erase(0, args_buffer.find('(')); + args_buffer = args_buffer.erase(args_buffer.find(';'), 1); + args_buffer = args_buffer.erase(args_buffer.find(')'), 1); + args_buffer = args_buffer.erase(args_buffer.find('('), 1); + + if (!args_buffer.empty()) + args += "\tldw r6, "; + + std::string register_type; + std::size_t index = 7UL; + + while (args_buffer.find("$") != std::string::npos) + { + register_type = kRegisterPrefix; + register_type += std::to_string(index); + + ++index; + + args_buffer.replace(args_buffer.find('$'), 1, + "\n\tldw " + register_type + ","); + } + + args += args_buffer; + args += "\n\tlda r19, "; + } + } + + for (char _text_i : textBuffer) + { + if (_text_i == '\t' || _text_i == ' ') + { + if (!type_crossed) + { + substr.clear(); + type_crossed = true; + } + + continue; + } + + if (_text_i == '(') + break; + + substr += _text_i; + } + + if (kInBraces) + { + syntaxLeaf.fUserValue = args; + syntaxLeaf.fUserValue += substr; + syntaxLeaf.fUserValue += "\n\tjrl\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + fnFound = true; + } + else + { + syntaxLeaf.fUserValue.clear(); + + syntaxLeaf.fUserValue += "public_segment .code64 "; + + syntaxLeaf.fUserValue += substr; + syntaxLeaf.fUserValue += "\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + fnFound = true; + } + + kCompilerFunctions.push_back(textBuffer); + } + + if (textBuffer[text_index] == '-' && textBuffer[text_index + 1] == '-') + { + textBuffer = textBuffer.replace(textBuffer.find("--"), strlen("--"), ""); + + for (int _text_i = 0; _text_i < textBuffer.size(); ++_text_i) + { + if (textBuffer[_text_i] == '\t' || textBuffer[_text_i] == ' ') + textBuffer.erase(_text_i, 1); + } + + syntaxLeaf.fUserValue += "sub "; + syntaxLeaf.fUserValue += textBuffer; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + break; + } + + if (textBuffer[text_index] == '}') + { + kRegisterCounter = kStartUsable; + + --kBracesCount; + + if (kBracesCount < 1) + { + kInBraces = false; + kBracesCount = 0; + } + + if (kIfFound) + kIfFound = false; + + if (kInStruct) + kInStruct = false; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + syntaxLeaf.fUserValue.clear(); + } + + auto syntaxLeaf = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + syntaxLeaf.fUserValue = "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + return true; +} + +static bool kShouldHaveBraces = false; +static std::string kFnName; + +std::string CompilerFrontendARM64::Check(const char* text, const char* file) +{ + std::string err_str; + std::string ln = text; + + if (ln.empty()) + { + return err_str; + } + + bool non_ascii_found = false; + + for (int i = 0; i < ln.size(); ++i) + { + if (isalnum(ln[i])) + { + non_ascii_found = true; + break; + } + } + + if (kShouldHaveBraces && ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + } + + if (!non_ascii_found) + return err_str; + + size_t string_index = 1UL; + + if (ln.find('\'') != std::string::npos) + { + string_index = ln.find('\'') + 1; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == '\'') + { + if (ln[string_index + 1] != ';') + { + ln.erase(string_index, 1); + } + + return err_str; + } + } + } + else if (ln.find('"') != std::string::npos) + { + string_index = ln.find('"') + 1; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == '"') + { + if (ln[string_index + 1] != ';') + { + ln.erase(string_index, 1); + } + else + { + break; + } + } + } + } + else if (ln.find('"') == std::string::npos && + ln.find('\'') == std::string::npos) + { + std::vector forbidden_words; + + forbidden_words.push_back("\\"); + forbidden_words.push_back("?"); + forbidden_words.push_back("@"); + forbidden_words.push_back("~"); + forbidden_words.push_back("::"); + forbidden_words.push_back("--*"); + forbidden_words.push_back("*/"); + + // add them to avoid stupid mistakes. + forbidden_words.push_back("namespace"); + forbidden_words.push_back("class"); + forbidden_words.push_back("extern \"C\""); + + for (auto& forbidden : forbidden_words) + { + if (ln.find(forbidden) != std::string::npos) + { + err_str += "\nForbidden character detected: "; + err_str += forbidden; + + return err_str; + } + } + } + + struct CompilerVariableRange final + { + std::string fBegin; + std::string fEnd; + }; + + const std::vector variables_list = { + {.fBegin = "static ", .fEnd = "="}, + {.fBegin = "=", .fEnd = ";"}, + {.fBegin = "if(", .fEnd = "="}, + {.fBegin = "if (", .fEnd = "="}, + {.fBegin = "if(", .fEnd = "<"}, + {.fBegin = "if (", .fEnd = "<"}, + {.fBegin = "if(", .fEnd = ">"}, + {.fBegin = "if (", .fEnd = ">"}, + {.fBegin = "if(", .fEnd = ")"}, + {.fBegin = "if (", .fEnd = ")"}, + + {.fBegin = "else(", .fEnd = "="}, + {.fBegin = "else (", .fEnd = "="}, + {.fBegin = "else(", .fEnd = "<"}, + {.fBegin = "else (", .fEnd = "<"}, + {.fBegin = "else(", .fEnd = ">"}, + {.fBegin = "else (", .fEnd = ">"}, + {.fBegin = "else(", .fEnd = ")"}, + {.fBegin = "else (", .fEnd = ")"}, + }; + + for (auto& variable : variables_list) + { + if (ln.find(variable.fBegin) != std::string::npos) + { + string_index = ln.find(variable.fBegin) + variable.fBegin.size(); + + while (ln[string_index] == ' ') + ++string_index; + + std::string keyword; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == variable.fEnd[0]) + { + std::string varname = ""; + + for (size_t index_keyword = ln.find(' '); + ln[index_keyword] != variable.fBegin[0]; ++index_keyword) + { + if (ln[index_keyword] == ' ') + { + continue; + } + + if (isdigit(ln[index_keyword])) + { + goto cc_next_loop; + } + + varname += ln[index_keyword]; + } + + if (varname.find(' ') != std::string::npos) + { + varname.erase(0, varname.find(' ')); + + if (variable.fBegin == "extern") + { + varname.erase(0, varname.find(' ')); + } + } + + if (kRegisterCounter == 5 || kRegisterCounter == 6) + ++kRegisterCounter; + + std::string reg = kAsmRegisterPrefix; + reg += std::to_string(kRegisterCounter); + + kCompilerVariables.push_back({.fValue = varname}); + goto cc_check_done; + } + + keyword.push_back(ln[string_index]); + } + + goto cc_next_loop; + + cc_check_done: + + // skip digit value. + if (isdigit(keyword[0]) || keyword[0] == '"') + { + goto cc_next_loop; + } + + while (keyword.find(' ') != std::string::npos) + keyword.erase(keyword.find(' '), 1); + + for (auto& var : kCompilerVariables) + { + if (var.fValue.find(keyword) != std::string::npos) + { + err_str.clear(); + goto cc_next; + } + } + + for (auto& fn : kCompilerFunctions) + { + if (fn.find(keyword[0]) != std::string::npos) + { + auto where_begin = fn.find(keyword[0]); + auto keyword_begin = 0UL; + auto failed = false; + + for (; where_begin < keyword.size(); ++where_begin) + { + if (fn[where_begin] == '(' && keyword[keyword_begin] == '(') + break; + + if (fn[where_begin] != keyword[keyword_begin]) + { + failed = true; + break; + } + + ++keyword_begin; + } + + if (!failed) + { + err_str.clear(); + goto cc_next; + } + else + { + continue; + } + } + } + + cc_error_value: + if (keyword.find("->") != std::string::npos) + return err_str; + + if (keyword.find(".") != std::string::npos) + return err_str; + + if (isalnum(keyword[0])) + err_str += "\nUndefined value: " + keyword; + + return err_str; + } + + cc_next_loop: + continue; + } + +cc_next: + + // extern does not declare anything, it extern_segments a variable. + // so that's why it's not declare upper. + if (LibCompiler::find_word(ln, "extern")) + { + auto substr = ln.substr(ln.find("extern") + strlen("extern")); + kCompilerVariables.push_back({.fValue = substr}); + } + + if (kShouldHaveBraces && ln.find('{') == std::string::npos) + { + err_str += "Missing '{' for function "; + err_str += kFnName; + err_str += "\n"; + + kShouldHaveBraces = false; + kFnName.clear(); + } + else if (kShouldHaveBraces && ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + kFnName.clear(); + } + + bool type_not_found = true; + + if (ln.find('\'') != std::string::npos) + { + ln.replace(ln.find('\''), 3, "0"); + } + + auto first = ln.find('"'); + if (first != std::string::npos) + { + auto second = 0UL; + bool found_second_quote = false; + + for (size_t i = first + 1; i < ln.size(); ++i) + { + if (ln[i] == '\"') + { + found_second_quote = true; + second = i; + + break; + } + } + + if (!found_second_quote) + { + err_str += "Missing terminating \"."; + err_str += " here -> " + ln.substr(ln.find('"'), second); + } + } + + if (ln.find(')') != std::string::npos && ln.find(';') == std::string::npos) + { + if (ln.find('{') == std::string::npos) + { + kFnName = ln; + kShouldHaveBraces = true; + + goto skip_braces_check; + } + else if (ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + } + } + +skip_braces_check: + + for (auto& key : kCompilerTypes) + { + if (LibCompiler::find_word(ln, key.fName)) + { + if (isdigit(ln[ln.find(key.fName) + key.fName.size() + 1])) + { + err_str += "\nNumber cannot be set for "; + err_str += key.fName; + err_str += "'s name. here -> "; + err_str += ln; + } + + if (ln.find(key.fName) == 0 || ln[ln.find(key.fName) - 1] == ' ' || + ln[ln.find(key.fName) - 1] == '\t') + { + type_not_found = false; + + if (ln[ln.find(key.fName) + key.fName.size()] != ' ') + { + type_not_found = true; + + if (ln[ln.find(key.fName) + key.fName.size()] == '\t') + type_not_found = false; + + goto next; + } + else if (ln[ln.find(key.fName) + key.fName.size()] != '\t') + { + type_not_found = true; + + if (ln[ln.find(key.fName) + key.fName.size()] == ' ') + type_not_found = false; + } + } + + next: + + if (ln.find(';') == std::string::npos) + { + if (ln.find('(') != std::string::npos) + { + if (ln.find('=') == std::string::npos) + continue; + } + + err_str += "\nMissing ';', here -> "; + err_str += ln; + } + else + { + continue; + } + + if (ln.find('=') != std::string::npos) + { + if (ln.find('(') != std::string::npos) + { + if (ln.find(')') == std::string::npos) + { + err_str += "\nMissing ')', after '(' here -> "; + err_str += ln.substr(ln.find('(')); + } + } + } + } + } + + if (kInBraces && ln.find("struct") != std::string::npos && + ln.find("union") != std::string::npos && + ln.find("enum") != std::string::npos && + ln.find('=') != std::string::npos) + { + if (ln.find(';') == std::string::npos) + { + err_str += "\nMissing ';' after struct/union/enum declaration, here -> "; + err_str += ln; + } + } + + if (ln.find(';') != std::string::npos && + ln.find("for") == std::string::npos) + { + if (ln.find(';') + 1 != ln.size()) + { + for (int i = 0; i < ln.substr(ln.find(';') + 1).size(); ++i) + { + if ((ln.substr(ln.find(';') + 1)[i] != ' ') || + (ln.substr(ln.find(';') + 1)[i] != '\t')) + { + if (auto err = this->Check(ln.substr(ln.find(';') + 1).c_str(), file); + !err.empty()) + { + err_str += "\nUnexpected text after ';' -> "; + err_str += ln.substr(ln.find(';')); + err_str += err; + } + } + } + } + } + + if (ln.find('(') != std::string::npos) + { + if (ln.find(';') == std::string::npos && !LibCompiler::find_word(ln, "|") && + !LibCompiler::find_word(ln, "||") && !LibCompiler::find_word(ln, "&") && + !LibCompiler::find_word(ln, "&&") && !LibCompiler::find_word(ln, "~")) + { + bool found_func = false; + size_t i = ln.find('('); + std::vector opens; + std::vector closes; + + for (; i < ln.size(); ++i) + { + if (ln[i] == ')') + { + closes.push_back(1); + } + + if (ln[i] == '(') + { + opens.push_back(1); + } + } + + if (closes.size() != opens.size()) + err_str += "Unterminated (), here -> " + ln; + + bool space_found = false; + + for (int i = 0; i < ln.size(); ++i) + { + if (ln[i] == ')' && !space_found) + { + space_found = true; + continue; + } + + if (space_found) + { + if (ln[i] == ' ' && isalnum(ln[i + 1])) + { + err_str += "\nBad function format here -> "; + err_str += ln; + } + } + } + } + + if (ln.find('(') < 1) + { + err_str += "\nMissing identifier before '(' here -> "; + err_str += ln; + } + else + { + if (type_not_found && ln.find(';') == std::string::npos && + ln.find("if") == std::string::npos && + ln.find("|") == std::string::npos && + ln.find("&") == std::string::npos && + ln.find("(") == std::string::npos && + ln.find(")") == std::string::npos) + { + err_str += "\n Missing ';' or type, here -> "; + err_str += ln; + } + } + + if (ln.find(')') == std::string::npos) + { + err_str += "\nMissing ')', after '(' here -> "; + err_str += ln.substr(ln.find('(')); + } + } + else + { + if (ln.find("for") != std::string::npos || + ln.find("while") != std::string::npos) + { + err_str += "\nMissing '(', after \"for\", here -> "; + err_str += ln; + } + } + + if (ln.find('}') != std::string::npos && !kInBraces) + { + if (!kInStruct && ln.find(';') == std::string::npos) + { + err_str += "\nMismatched '}', here -> "; + err_str += ln; + } + } + + if (!ln.empty()) + { + if (ln.find(';') == std::string::npos && + ln.find('{') == std::string::npos && + ln.find('}') == std::string::npos && + ln.find(')') == std::string::npos && + ln.find('(') == std::string::npos && + ln.find(',') == std::string::npos) + { + if (ln.size() <= 2) + return err_str; + + err_str += "\nMissing ';', here -> "; + err_str += ln; + } + } + + return err_str; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/** + * @brief C To Assembly mount-point. + */ + +///////////////////////////////////////////////////////////////////////////////////////// + +class AssemblyCCInterface final ASSEMBLY_INTERFACE +{ +public: + explicit AssemblyCCInterface() = default; + ~AssemblyCCInterface() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(AssemblyCCInterface); + + [[maybe_unused]] static Int32 Arch() noexcept + { + return LibCompiler::AssemblyFactory::kArchARM64; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyCCInterface::Arch()) + return 1; + + if (kCompilerFrontend == nullptr) + return 1; + + /* @brief copy contents wihtout extension */ + std::string src_file = src.data(); + std::ifstream src_fp = std::ifstream(src_file, std::ios::in); + std::string dest; + + for (auto& ch : src_file) + { + if (ch == '.') + { + break; + } + + dest += ch; + } + + /* According to PEF ABI. */ + std::vector exts = kAsmFileExts; + dest += exts[4]; + + kState.fOutputAssembly = std::make_unique(dest); + + auto fmt = LibCompiler::current_date(); + + (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; + (*kState.fOutputAssembly) + << "# Language: ARM64 Assembly (Generated from ANSI C)\n"; + (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; + + LibCompiler::SyntaxLeafList syntax; + + kState.fSyntaxTreeList.push_back(syntax); + kState.fSyntaxTree = + &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; + + std::string line_src; + + while (std::getline(src_fp, line_src)) + { + if (auto err = kCompilerFrontend->Check(line_src.c_str(), src.data()); + err.empty()) + { + kCompilerFrontend->Compile(line_src, src.data()); + } + else + { + Details::print_error(err, src.data()); + } + } + + if (kAcceptableErrors > 0) + return 1; + + std::vector keywords = {"ldw", "stw", "lda", "sta", + "add", "sub", "mv"}; + + /// + /// Replace, optimize, fix assembly output. + /// + + for (auto& leaf : kState.fSyntaxTree->fLeafList) + { + std::vector access_keywords = {"->", "."}; + + for (auto& access_ident : access_keywords) + { + if (LibCompiler::find_word(leaf.fUserValue, access_ident)) + { + for (auto& struc : kState.kStructMap) + { + /// TODO: + } + } + } + + for (auto& keyword : keywords) + { + if (LibCompiler::find_word(leaf.fUserValue, keyword)) + { + std::size_t cnt = 0UL; + + for (auto& reg : kState.kStackFrame) + { + std::string needle; + + for (size_t i = 0; i < reg.fName.size(); i++) + { + if (reg.fName[i] == ' ') + { + ++i; + + for (; i < reg.fName.size(); i++) + { + if (reg.fName[i] == ',') + { + break; + } + + if (reg.fName[i] == ' ') + continue; + + needle += reg.fName[i]; + } + + break; + } + } + + if (LibCompiler::find_word(leaf.fUserValue, needle)) + { + if (leaf.fUserValue.find("extern_segment " + needle) != + std::string::npos) + { + std::string range = "extern_segment " + needle; + leaf.fUserValue.replace( + leaf.fUserValue.find("extern_segment " + needle), range.size(), + needle); + } + + if (leaf.fUserValue.find("ldw r6") != std::string::npos) + { + std::string::difference_type countComma = std::count( + leaf.fUserValue.begin(), leaf.fUserValue.end(), ','); + + if (countComma == 1) + { + leaf.fUserValue.replace(leaf.fUserValue.find("ldw"), + strlen("ldw"), "mv"); + } + } + + leaf.fUserValue.replace(leaf.fUserValue.find(needle), + needle.size(), reg.fReg); + + ++cnt; + } + } + + if (cnt > 1 && keyword != "mv" && keyword != "add" && + keyword != "sub") + { + leaf.fUserValue.replace(leaf.fUserValue.find(keyword), + keyword.size(), "mv"); + } + } + } + } + + for (auto& leaf : kState.fSyntaxTree->fLeafList) + { + (*kState.fOutputAssembly) << leaf.fUserValue; + } + + kState.fSyntaxTree = nullptr; + + kState.fOutputAssembly->flush(); + kState.fOutputAssembly.reset(); + + return kExitOK; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include + +#define kPrintF printf +#define kSplashCxx() \ + kPrintF(kWhite "ZKA C Driver, %s, (c) Theater Quality Corp.\n", kDistVersion) + +static void cc_print_help() +{ + kSplashCxx(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kCExtension ".c" + +TOOLCHAINKIT_MODULE(ZkaOSCompilerCLangARM64) +{ + kCompilerTypes.push_back({.fName = "void", .fValue = "void"}); + kCompilerTypes.push_back({.fName = "char", .fValue = "byte"}); + kCompilerTypes.push_back({.fName = "short", .fValue = "hword"}); + kCompilerTypes.push_back({.fName = "int", .fValue = "dword"}); + kCompilerTypes.push_back({.fName = "long", .fValue = "qword"}); + kCompilerTypes.push_back({.fName = "*", .fValue = "offset"}); + + bool skip = false; + + kFactory.Mount(new AssemblyCCInterface()); + kMachine = LibCompiler::AssemblyFactory::kArchARM64; + kCompilerFrontend = new CompilerFrontendARM64(); + + for (auto index = 1UL; index < argc; ++index) + { + if (skip) + { + skip = false; + continue; + } + + if (argv[index][0] == '-') + { + if (strcmp(argv[index], "--v") == 0 || + strcmp(argv[index], "--version") == 0) + { + kSplashCxx(); + return kExitOK; + } + + if (strcmp(argv[index], "--verbose") == 0) + { + kState.fVerbose = true; + + continue; + } + + if (strcmp(argv[index], "--h") == 0 || strcmp(argv[index], "--help") == 0) + { + cc_print_help(); + + return kExitOK; + } + + if (strcmp(argv[index], "--dialect") == 0) + { + if (kCompilerFrontend) + std::cout << kCompilerFrontend->Language() << "\n"; + + return kExitOK; + } + + if (strcmp(argv[index], "--fmax-exceptions") == 0) + { + try + { + kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); + } + // catch anything here + catch (...) + { + kErrorLimit = 0; + } + + skip = true; + + continue; + } + + std::string err = "Unknown command: "; + err += argv[index]; + + Details::print_error(err, "cc"); + + continue; + } + + kFileList.emplace_back(argv[index]); + + std::string srcFile = argv[index]; + + if (strstr(argv[index], kCExtension) == nullptr) + { + if (kState.fVerbose) + { + Details::print_error(srcFile + " is not a valid C source.\n", "cc"); + } + + return 1; + } + + if (kFactory.Compile(srcFile, kMachine) != kExitOK) + return 1; + } + + return kExitOK; +} + +// Last rev 8-1-24 diff --git a/dev/LibCompiler/src/CCompilerPower64.cc b/dev/LibCompiler/src/CCompilerPower64.cc index dca96b3..328c429 100644 --- a/dev/LibCompiler/src/CCompilerPower64.cc +++ b/dev/LibCompiler/src/CCompilerPower64.cc @@ -1513,7 +1513,7 @@ static void cc_print_help() #define kExt ".c" -TOOLCHAINKIT_MODULE(NewOSCompilerCLangPowerPC) +TOOLCHAINKIT_MODULE(ZkaOSCompilerCLangPowerPC) { kCompilerTypes.push_back({.fName = "void", .fValue = "void"}); kCompilerTypes.push_back({.fName = "char", .fValue = "byte"}); diff --git a/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc b/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc index 4994644..ba805bd 100644 --- a/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc +++ b/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc @@ -237,15 +237,15 @@ bool CompilerFrontendCPlusPlus::Compile(const std::string text, { switch (keyword.keyword_kind) { - case LibCompiler::eKeywordKindCommentMultiLineStart: { + case LibCompiler::kKeywordKindCommentMultiLineStart: { commentBlock = true; return true; } - case LibCompiler::eKeywordKindCommentMultiLineEnd: { + case LibCompiler::kKeywordKindCommentMultiLineEnd: { commentBlock = false; break; } - case LibCompiler::eKeywordKindCommentInline: { + case LibCompiler::kKeywordKindCommentInline: { break; } default: @@ -253,15 +253,15 @@ bool CompilerFrontendCPlusPlus::Compile(const std::string text, } if (text[text.find(keyword.keyword_name) - 1] == '+' && - keyword.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + keyword.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableAssign) continue; if (text[text.find(keyword.keyword_name) - 1] == '-' && - keyword.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + keyword.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableAssign) continue; if (text[text.find(keyword.keyword_name) + 1] == '=' && - keyword.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + keyword.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableAssign) continue; keywords_list.emplace_back(std::make_pair(keyword, index)); @@ -289,7 +289,7 @@ bool CompilerFrontendCPlusPlus::Compile(const std::string text, switch (keyword.first.keyword_kind) { - case LibCompiler::KeywordKind::eKeywordKindIf: { + case LibCompiler::KeywordKind::kKeywordKindIf: { auto expr = text.substr(text.find(keyword.first.keyword_name) + keyword.first.keyword_name.size() + 1, text.find(")") - 1); if (expr.find(">=") != std::string::npos) @@ -377,7 +377,7 @@ bool CompilerFrontendCPlusPlus::Compile(const std::string text, break; } - case LibCompiler::KeywordKind::eKeywordKindFunctionStart: { + case LibCompiler::KeywordKind::kKeywordKindFunctionStart: { for (auto& ch : text) { if (isdigit(ch)) @@ -439,7 +439,7 @@ bool CompilerFrontendCPlusPlus::Compile(const std::string text, tk_write_assembly: syntax_tree.fUserValue = "jmp __TOOLCHAINKIT_" + fnName + "\n"; } - case LibCompiler::KeywordKind::eKeywordKindFunctionEnd: { + case LibCompiler::KeywordKind::kKeywordKindFunctionEnd: { if (text.ends_with(";")) break; @@ -454,50 +454,50 @@ tk_write_assembly: kRegisterMap.clear(); break; } - case LibCompiler::KeywordKind::eKeywordKindEndInstr: - case LibCompiler::KeywordKind::eKeywordKindVariableInc: - case LibCompiler::KeywordKind::eKeywordKindVariableDec: - case LibCompiler::KeywordKind::eKeywordKindVariableAssign: { + case LibCompiler::KeywordKind::kKeywordKindEndInstr: + case LibCompiler::KeywordKind::kKeywordKindVariableInc: + case LibCompiler::KeywordKind::kKeywordKindVariableDec: + case LibCompiler::KeywordKind::kKeywordKindVariableAssign: { std::string valueOfVar = ""; - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableInc) + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { valueOfVar = text.substr(text.find("+=") + 2); } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableDec) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableDec) { valueOfVar = text.substr(text.find("-=") + 2); } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableAssign) { valueOfVar = text.substr(text.find("=") + 1); } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindEndInstr) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { break; } while (valueOfVar.find(";") != std::string::npos && - keyword.first.keyword_kind != LibCompiler::KeywordKind::eKeywordKindEndInstr) + keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindEndInstr) { valueOfVar.erase(valueOfVar.find(";")); } std::string varName = text; - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableInc) + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { varName.erase(varName.find("+=")); } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableDec) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableDec) { varName.erase(varName.find("-=")); } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableAssign) { varName.erase(varName.find("=")); } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindEndInstr) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { varName.erase(varName.find(";")); } @@ -506,7 +506,7 @@ tk_write_assembly: for (auto& keyword : kKeywords) { - if (keyword.keyword_kind == LibCompiler::eKeywordKindType) + if (keyword.keyword_kind == LibCompiler::kKeywordKindType) { if (text.find(keyword.keyword_name) != std::string::npos) { @@ -523,8 +523,8 @@ tk_write_assembly: std::string instr = "mov "; - if (typeFound && keyword.first.keyword_kind != LibCompiler::KeywordKind::eKeywordKindVariableInc && - keyword.first.keyword_kind != LibCompiler::KeywordKind::eKeywordKindVariableDec) + if (typeFound && keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindVariableInc && + keyword.first.keyword_kind != LibCompiler::KeywordKind::kKeywordKindVariableDec) { if (kRegisterMap.size() > kRegisterList.size()) { @@ -617,7 +617,7 @@ tk_write_assembly: done: for (auto& keyword : kKeywords) { - if (keyword.keyword_kind == LibCompiler::eKeywordKindType && + if (keyword.keyword_kind == LibCompiler::kKeywordKindType && varName.find(keyword.keyword_name) != std::string::npos) { varName.erase(varName.find(keyword.keyword_name), keyword.keyword_name.size()); @@ -630,24 +630,24 @@ tk_write_assembly: break; } - if (kKeywords[keyword.second - 1].keyword_kind == LibCompiler::eKeywordKindType || - kKeywords[keyword.second - 1].keyword_kind == LibCompiler::eKeywordKindTypePtr) + if (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::eKeywordKindEndInstr) + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindEndInstr) { syntax_tree.fUserValue = "\n"; continue; } - if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableInc) + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableInc) { instr = "add "; } - else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableDec) + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::kKeywordKindVariableDec) { instr = "sub "; } @@ -731,7 +731,7 @@ tk_write_assembly: break; } - case LibCompiler::KeywordKind::eKeywordKindReturn: { + case LibCompiler::KeywordKind::kKeywordKindReturn: { try { auto pos = text.find("return") + strlen("return") + 1; @@ -928,63 +928,63 @@ TOOLCHAINKIT_MODULE(CompilerCPlusPlusX8664) { bool skip = false; - kKeywords.push_back({.keyword_name = "if", .keyword_kind = LibCompiler::eKeywordKindIf}); - kKeywords.push_back({.keyword_name = "else", .keyword_kind = LibCompiler::eKeywordKindElse}); - kKeywords.push_back({.keyword_name = "else if", .keyword_kind = LibCompiler::eKeywordKindElseIf}); - - kKeywords.push_back({.keyword_name = "class", .keyword_kind = LibCompiler::eKeywordKindClass}); - kKeywords.push_back({.keyword_name = "struct", .keyword_kind = LibCompiler::eKeywordKindClass}); - kKeywords.push_back({.keyword_name = "namespace", .keyword_kind = LibCompiler::eKeywordKindNamespace}); - kKeywords.push_back({.keyword_name = "typedef", .keyword_kind = LibCompiler::eKeywordKindTypedef}); - kKeywords.push_back({.keyword_name = "using", .keyword_kind = LibCompiler::eKeywordKindTypedef}); - kKeywords.push_back({.keyword_name = "{", .keyword_kind = LibCompiler::eKeywordKindBodyStart}); - kKeywords.push_back({.keyword_name = "}", .keyword_kind = LibCompiler::eKeywordKindBodyEnd}); - kKeywords.push_back({.keyword_name = "auto", .keyword_kind = LibCompiler::eKeywordKindVariable}); - kKeywords.push_back({.keyword_name = "int", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "bool", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "unsigned", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "short", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "char", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "long", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "float", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "double", .keyword_kind = LibCompiler::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "void", .keyword_kind = LibCompiler::eKeywordKindType}); - - kKeywords.push_back({.keyword_name = "auto*", .keyword_kind = LibCompiler::eKeywordKindVariablePtr}); - kKeywords.push_back({.keyword_name = "int*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "bool*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "unsigned*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "short*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "char*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "long*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "float*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "double*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "void*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); - - kKeywords.push_back({.keyword_name = "(", .keyword_kind = LibCompiler::eKeywordKindFunctionStart}); - kKeywords.push_back({.keyword_name = ")", .keyword_kind = LibCompiler::eKeywordKindFunctionEnd}); - kKeywords.push_back({.keyword_name = "=", .keyword_kind = LibCompiler::eKeywordKindVariableAssign}); - kKeywords.push_back({.keyword_name = "+=", .keyword_kind = LibCompiler::eKeywordKindVariableInc}); - kKeywords.push_back({.keyword_name = "-=", .keyword_kind = LibCompiler::eKeywordKindVariableDec}); - kKeywords.push_back({.keyword_name = "const", .keyword_kind = LibCompiler::eKeywordKindConstant}); - kKeywords.push_back({.keyword_name = "*", .keyword_kind = LibCompiler::eKeywordKindPtr}); - kKeywords.push_back({.keyword_name = "->", .keyword_kind = LibCompiler::eKeywordKindPtrAccess}); - kKeywords.push_back({.keyword_name = ".", .keyword_kind = LibCompiler::eKeywordKindAccess}); - kKeywords.push_back({.keyword_name = ",", .keyword_kind = LibCompiler::eKeywordKindArgSeparator}); - kKeywords.push_back({.keyword_name = ";", .keyword_kind = LibCompiler::eKeywordKindEndInstr}); - kKeywords.push_back({.keyword_name = ":", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "public:", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "private:", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "protected:", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "final", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "return", .keyword_kind = LibCompiler::eKeywordKindReturn}); - kKeywords.push_back({.keyword_name = "--*", .keyword_kind = LibCompiler::eKeywordKindCommentMultiLineStart}); - kKeywords.push_back({.keyword_name = "*/", .keyword_kind = LibCompiler::eKeywordKindCommentMultiLineStart}); - kKeywords.push_back({.keyword_name = "--/", .keyword_kind = LibCompiler::eKeywordKindCommentInline}); - kKeywords.push_back({.keyword_name = "==", .keyword_kind = LibCompiler::eKeywordKindEq}); - kKeywords.push_back({.keyword_name = "!=", .keyword_kind = LibCompiler::eKeywordKindNotEq}); - kKeywords.push_back({.keyword_name = ">=", .keyword_kind = LibCompiler::eKeywordKindGreaterEq}); - kKeywords.push_back({.keyword_name = "<=", .keyword_kind = LibCompiler::eKeywordKindLessEq}); + kKeywords.push_back({.keyword_name = "if", .keyword_kind = LibCompiler::kKeywordKindIf}); + kKeywords.push_back({.keyword_name = "else", .keyword_kind = LibCompiler::kKeywordKindElse}); + kKeywords.push_back({.keyword_name = "else if", .keyword_kind = LibCompiler::kKeywordKindElseIf}); + + kKeywords.push_back({.keyword_name = "class", .keyword_kind = LibCompiler::kKeywordKindClass}); + kKeywords.push_back({.keyword_name = "struct", .keyword_kind = LibCompiler::kKeywordKindClass}); + kKeywords.push_back({.keyword_name = "namespace", .keyword_kind = LibCompiler::kKeywordKindNamespace}); + kKeywords.push_back({.keyword_name = "typedef", .keyword_kind = LibCompiler::kKeywordKindTypedef}); + kKeywords.push_back({.keyword_name = "using", .keyword_kind = LibCompiler::kKeywordKindTypedef}); + kKeywords.push_back({.keyword_name = "{", .keyword_kind = LibCompiler::kKeywordKindBodyStart}); + kKeywords.push_back({.keyword_name = "}", .keyword_kind = LibCompiler::kKeywordKindBodyEnd}); + kKeywords.push_back({.keyword_name = "auto", .keyword_kind = LibCompiler::kKeywordKindVariable}); + kKeywords.push_back({.keyword_name = "int", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "bool", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "unsigned", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "short", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "char", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "long", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "float", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "double", .keyword_kind = LibCompiler::kKeywordKindType}); + kKeywords.push_back({.keyword_name = "void", .keyword_kind = LibCompiler::kKeywordKindType}); + + kKeywords.push_back({.keyword_name = "auto*", .keyword_kind = LibCompiler::kKeywordKindVariablePtr}); + kKeywords.push_back({.keyword_name = "int*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "bool*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "unsigned*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "short*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "char*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "long*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "float*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "double*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "void*", .keyword_kind = LibCompiler::kKeywordKindTypePtr}); + + kKeywords.push_back({.keyword_name = "(", .keyword_kind = LibCompiler::kKeywordKindFunctionStart}); + kKeywords.push_back({.keyword_name = ")", .keyword_kind = LibCompiler::kKeywordKindFunctionEnd}); + kKeywords.push_back({.keyword_name = "=", .keyword_kind = LibCompiler::kKeywordKindVariableAssign}); + kKeywords.push_back({.keyword_name = "+=", .keyword_kind = LibCompiler::kKeywordKindVariableInc}); + kKeywords.push_back({.keyword_name = "-=", .keyword_kind = LibCompiler::kKeywordKindVariableDec}); + kKeywords.push_back({.keyword_name = "const", .keyword_kind = LibCompiler::kKeywordKindConstant}); + kKeywords.push_back({.keyword_name = "*", .keyword_kind = LibCompiler::kKeywordKindPtr}); + kKeywords.push_back({.keyword_name = "->", .keyword_kind = LibCompiler::kKeywordKindPtrAccess}); + kKeywords.push_back({.keyword_name = ".", .keyword_kind = LibCompiler::kKeywordKindAccess}); + kKeywords.push_back({.keyword_name = ",", .keyword_kind = LibCompiler::kKeywordKindArgSeparator}); + kKeywords.push_back({.keyword_name = ";", .keyword_kind = LibCompiler::kKeywordKindEndInstr}); + kKeywords.push_back({.keyword_name = ":", .keyword_kind = LibCompiler::kKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "public:", .keyword_kind = LibCompiler::kKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "private:", .keyword_kind = LibCompiler::kKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "protected:", .keyword_kind = LibCompiler::kKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "final", .keyword_kind = LibCompiler::kKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "return", .keyword_kind = LibCompiler::kKeywordKindReturn}); + kKeywords.push_back({.keyword_name = "--*", .keyword_kind = LibCompiler::kKeywordKindCommentMultiLineStart}); + kKeywords.push_back({.keyword_name = "*/", .keyword_kind = LibCompiler::kKeywordKindCommentMultiLineStart}); + kKeywords.push_back({.keyword_name = "--/", .keyword_kind = LibCompiler::kKeywordKindCommentInline}); + kKeywords.push_back({.keyword_name = "==", .keyword_kind = LibCompiler::kKeywordKindEq}); + kKeywords.push_back({.keyword_name = "!=", .keyword_kind = LibCompiler::kKeywordKindNotEq}); + kKeywords.push_back({.keyword_name = ">=", .keyword_kind = LibCompiler::kKeywordKindGreaterEq}); + kKeywords.push_back({.keyword_name = "<=", .keyword_kind = LibCompiler::kKeywordKindLessEq}); kFactory.Mount(new AssemblyCPlusPlusInterface()); kCompilerFrontend = new CompilerFrontendCPlusPlus(); diff --git a/dev/LibCompiler/src/CPlusPlusLinkerELF.cc b/dev/LibCompiler/src/CPlusPlusLinkerELF.cc new file mode 100644 index 0000000..5b85afc --- /dev/null +++ b/dev/LibCompiler/src/CPlusPlusLinkerELF.cc @@ -0,0 +1,92 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Corp, all rights reserved + + @file DynamicLinker64PEF.cc + @brief: C++ 64-Bit PEF Linker. + +------------------------------------------- */ + +/// @author EL Mahrouss Amlal (amlel) +/// @brief TQ 64-bit PEF Linker. +/// Last Rev: Sat Feb 24 CET 2024 +/// @note Do not look up for anything with .code64/.data64/.zero64! +/// It will be loaded when the program loader will start the image. + +//! Toolchain Kit. +#include + +#include + +//! Assembler Kit +#include + +//! Preferred Executable Format +#include +#include + +//! Release macros. +#include + +//! Advanced Executable Object Format. +#include +#include + +#define kLinkerVersionStr "TQ 64-Bit Linker (ELF) %s, (c) Theater Quality Corp. 2024, all rights reserved.\n" + +#define MemoryCopy(DST, SRC, SZ) memcpy(DST, SRC, SZ) +#define StringCompare(DST, SRC) strcmp(DST, SRC) + +#define kPefNoCpu 0U +#define kPefNoSubCpu 0U + +#define kWhite "\e[0;97m" + +#define kStdOut (std::cout << kWhite << "ld64: ") + +#define kLinkerDefaultOrigin kPefBaseOrigin +#define kLinkerId (0x5046FF) +#define kLinkerAbiContainer "Container:ABI:" + +/// @brief PEF stack size symbol. +#define kLinkerStackSizeSymbol "SizeOfReserveStack" + +namespace Details +{ +struct DynamicLinkerBlob final +{ + std::vector fPefBlob; // PEF code/bss/data blob. + std::uintptr_t fAEOffset; // the offset of the PEF container header.. +}; +} + +enum +{ + kABITypeStart = 0x1010, /* Invalid ABI start of ABI list. */ + kABITypeZKA = 0x5046, /* PF (ZKA PEF ABI) */ + kABITypeInvalid = 0xFFFF, +}; + +static Bool kFatBinaryEnable = false; +static Bool kStartFound = false; +static Bool kDuplicateSymbols = false; +static Bool kVerbose = false; + +/* object code and list. */ +static std::vector kObjectList; +static std::vector kObjectBytes; + +static uintptr_t kMIBCount = 8; +static uintptr_t kByteCount = 1024; + +#define kPrintF printf +#define kLinkerSplash() kPrintF(kWhite kLinkerVersionStr, kDistVersion) + +/// @brief ZKA 64-bit Linker. +/// @note This linker is made for XCOFF executable, thus ZKA based OSes. +TOOLCHAINKIT_MODULE(DynamicLinker64XCOFF) +{ + return EXIT_SUCCESS; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/CPlusPlusLinkerPEF.cc b/dev/LibCompiler/src/CPlusPlusLinkerPEF.cc new file mode 100644 index 0000000..2e5e40b --- /dev/null +++ b/dev/LibCompiler/src/CPlusPlusLinkerPEF.cc @@ -0,0 +1,774 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Corp, all rights reserved + + @file DynamicLinker64PEF.cc + @brief: C++ 64-Bit PEF Linker. + +------------------------------------------- */ + +/// @author EL Mahrouss Amlal (amlel) +/// @brief TQ 64-bit PEF Linker. +/// Last Rev: Sat Feb 24 CET 2024 +/// @note Do not look up for anything with .code64/.data64/.zero64! +/// It will be loaded when the program loader will start the image. + +//! Toolchain Kit. +#include + +#include + +//! Assembler Kit +#include + +//! Preferred Executable Format +#include +#include + +//! Release macros. +#include + +//! Advanced Executable Object Format. +#include +#include + +#define kLinkerVersionStr "TQ 64-Bit Linker (Preferred Executable) %s, (c) Theater Quality Corp. 2024, all rights reserved.\n" + +#define MemoryCopy(DST, SRC, SZ) memcpy(DST, SRC, SZ) +#define StringCompare(DST, SRC) strcmp(DST, SRC) + +#define kPefNoCpu 0U +#define kPefNoSubCpu 0U + +#define kWhite "\e[0;97m" + +#define kStdOut (std::cout << kWhite << "ld64: ") + +#define kLinkerDefaultOrigin kPefBaseOrigin +#define kLinkerId (0x5046FF) +#define kLinkerAbiContainer "Container:ABI:" + +/// @brief PEF stack size symbol. +#define kLinkerStackSizeSymbol "SizeOfReserveStack" + +namespace Details +{ +struct DynamicLinkerBlob final +{ + std::vector fPefBlob; // PEF code/bss/data blob. + std::uintptr_t fAEOffset; // the offset of the PEF container header.. +}; +} + +enum +{ + kABITypeStart = 0x1010, /* Invalid ABI start of ABI list. */ + kABITypeZKA = 0x5046, /* PF (ZKA PEF ABI) */ + kABITypeInvalid = 0xFFFF, +}; + +static LibCompiler::String kOutput = ""; +static Int32 kAbi = kABITypeZKA; +static Int32 kSubArch = kPefNoSubCpu; +static Int32 kArch = LibCompiler::kPefArchInvalid; +static Bool kFatBinaryEnable = false; +static Bool kStartFound = false; +static Bool kDuplicateSymbols = false; +static Bool kVerbose = false; + +/* ld64 is to be found, mld is to be found at runtime. */ +static const char* kLdDefineSymbol = ":UndefinedSymbol:"; +static const char* kLdDynamicSym = ":RuntimeSymbol:"; + +/* object code and list. */ +static std::vector kObjectList; +static std::vector kObjectBytes; + +static uintptr_t kMIBCount = 8; +static uintptr_t kByteCount = 1024; + +#define kPrintF printf +#define kLinkerSplash() kPrintF(kWhite kLinkerVersionStr, kDistVersion) + +/// @brief ZKA 64-bit Linker. +/// @note This linker is made for PEF executable, thus ZKA based OSes. +TOOLCHAINKIT_MODULE(DynamicLinker64PEF) +{ + bool is_executable = true; + + /** + * @brief parse flags and trigger options. + */ + for (size_t linker_arg = 1; linker_arg < argc; ++linker_arg) + { + if (StringCompare(argv[linker_arg], "--ld64:help") == 0) + { + kLinkerSplash(); + + kStdOut << "--ld64:ver: Show linker version.\n"; + kStdOut << "--ld64:?: Show linker help.\n"; + kStdOut << "--ld64:verbose: Enable linker trace.\n"; + kStdOut << "--ld64:dylib: Output as a Dylib PEF.\n"; + kStdOut << "--ld64:fat: Output as a FAT PEF.\n"; + kStdOut << "--ld64:32k: Output as a 32x0 PEF.\n"; + kStdOut << "--ld64:64k: Output as a 64x0 PEF.\n"; + kStdOut << "--ld64:amd64: Output as a AMD64 PEF.\n"; + kStdOut << "--ld64:rv64: Output as a RISC-V PEF.\n"; + kStdOut << "--ld64:power64: Output as a POWER PEF.\n"; + kStdOut << "--ld64:arm64: Output as a ARM64 PEF.\n"; + kStdOut << "--ld64:output: Select the output file name.\n"; + + return EXIT_SUCCESS; + } + else if (StringCompare(argv[linker_arg], "--ld64:version") == 0) + { + kLinkerSplash(); + return EXIT_SUCCESS; + } + else if (StringCompare(argv[linker_arg], "--ld64:fat-binary") == 0) + { + kFatBinaryEnable = true; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:64k") == 0) + { + kArch = LibCompiler::kPefArch64000; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:amd64") == 0) + { + kArch = LibCompiler::kPefArchAMD64; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:32k") == 0) + { + kArch = LibCompiler::kPefArch32000; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:power64") == 0) + { + kArch = LibCompiler::kPefArchPowerPC; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:riscv64") == 0) + { + kArch = LibCompiler::kPefArchRISCV; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:arm64") == 0) + { + kArch = LibCompiler::kPefArchARM64; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:verbose") == 0) + { + kVerbose = true; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:dylib") == 0) + { + if (kOutput.empty()) + { + continue; + } + + if (kOutput.find(kPefExt) != LibCompiler::String::npos) + kOutput.erase(kOutput.find(kPefExt), strlen(kPefExt)); + + kOutput += kPefDylibExt; + + is_executable = false; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:output") == 0) + { + kOutput = argv[linker_arg + 1]; + ++linker_arg; + + continue; + } + else + { + if (argv[linker_arg][0] == '-') + { + kStdOut << "unknown flag: " << argv[linker_arg] << "\n"; + return EXIT_FAILURE; + } + + kObjectList.emplace_back(argv[linker_arg]); + + continue; + } + } + + if (kOutput.empty()) + { + kStdOut << "no output filename set." << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + + // sanity check. + if (kObjectList.empty()) + { + kStdOut << "no input files." << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + else + { + namespace fs = std::filesystem; + + // check for existing files, if they don't throw an error. + for (auto& obj : kObjectList) + { + if (!fs::exists(obj)) + { + // if filesystem doesn't find file + // -> throw error. + kStdOut << "no such file: " << obj << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + } + } + + // PEF expects a valid target architecture when outputing a binary. + if (kArch == 0) + { + kStdOut << "no target architecture set, can't continue." << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + + LibCompiler::PEFContainer pef_container{}; + + int32_t archs = kArch; + + pef_container.Count = 0UL; + pef_container.Kind = is_executable ? LibCompiler::kPefKindExec : LibCompiler::kPefKindDylib; + pef_container.SubCpu = kSubArch; + pef_container.Linker = kLinkerId; // Theater Quality Corp. Linker + pef_container.Abi = kAbi; // Multi-Processor UX ABI + pef_container.Magic[0] = kPefMagic[kFatBinaryEnable ? 2 : 0]; + pef_container.Magic[1] = kPefMagic[1]; + pef_container.Magic[2] = kPefMagic[kFatBinaryEnable ? 0 : 2]; + pef_container.Magic[3] = kPefMagic[3]; + pef_container.Version = kPefVersion; + + // specify the start address, can be 0x10000 + pef_container.Start = kLinkerDefaultOrigin; + pef_container.HdrSz = sizeof(LibCompiler::PEFContainer); + + std::ofstream output_fc(kOutput, std::ofstream::binary); + + if (output_fc.bad()) + { + if (kVerbose) + { + kStdOut << "error: " << strerror(errno) << "\n"; + } + + return TOOLCHAINKIT_FILE_NOT_FOUND; + } + + //! Read AE to convert as PEF. + + std::vector command_headers; + LibCompiler::Utils::AEReadableProtocol reader_protocol{}; + + for (const auto& objectFile : kObjectList) + { + if (!std::filesystem::exists(objectFile)) + continue; + + LibCompiler::AEHeader hdr{}; + + reader_protocol.FP = std::ifstream(objectFile, std::ifstream::binary); + reader_protocol.FP >> hdr; + + auto ae_header = hdr; + + if (ae_header.fMagic[0] == kAEMag0 && ae_header.fMagic[1] == kAEMag1 && + ae_header.fSize == sizeof(LibCompiler::AEHeader)) + { + if (ae_header.fArch != kArch) + { + if (kVerbose) + kStdOut << "info: is this a FAT binary? : "; + + if (!kFatBinaryEnable) + { + if (kVerbose) + kStdOut << "No.\n"; + + kStdOut << "error: object " << objectFile + << " is a different kind of architecture and output isn't " + "treated as a FAT binary." + << std::endl; + + return TOOLCHAINKIT_FAT_ERROR; + } + else + { + if (kVerbose) + { + kStdOut << "Architecture matches what we expect.\n"; + } + } + } + + // append arch type to archs varaible. + archs |= ae_header.fArch; + std::size_t cnt = ae_header.fCount; + + if (kVerbose) + kStdOut << "object header found, record count: " << cnt << "\n"; + + pef_container.Count = cnt; + + char_type* raw_ae_records = + new char_type[cnt * sizeof(LibCompiler::AERecordHeader)]; + + memset(raw_ae_records, 0, cnt * sizeof(LibCompiler::AERecordHeader)); + + auto* ae_records = reader_protocol.Read(raw_ae_records, cnt); + + for (size_t ae_record_index = 0; ae_record_index < cnt; + ++ae_record_index) + { + LibCompiler::PEFCommandHeader command_header{0}; + std::size_t offset_of_obj = ae_records[ae_record_index].fOffset; + + MemoryCopy(command_header.Name, ae_records[ae_record_index].fName, + kPefNameLen); + + LibCompiler::String cmd_hdr_name(command_header.Name); + + // check this header if it's any valid. + if (cmd_hdr_name.find(kPefCode64) == + LibCompiler::String::npos && + cmd_hdr_name.find(kPefData64) == + LibCompiler::String::npos && + cmd_hdr_name.find(kPefZero64) == + LibCompiler::String::npos) + { + if (cmd_hdr_name.find(kPefStart) == + LibCompiler::String::npos && + *command_header.Name == 0) + { + if (cmd_hdr_name.find(kLdDefineSymbol) != + LibCompiler::String::npos) + { + goto ld_mark_header; + } + else + { + continue; + } + } + } + + if (cmd_hdr_name.find(kPefStart) != + LibCompiler::String::npos && + cmd_hdr_name.find(kPefCode64) != + LibCompiler::String::npos) + { + kStartFound = true; + } + + ld_mark_header: + command_header.Offset = offset_of_obj; + command_header.Kind = ae_records[ae_record_index].fKind; + command_header.Size = ae_records[ae_record_index].fSize; + command_header.Cpu = ae_header.fArch; + command_header.SubCpu = ae_header.fSubArch; + + if (kVerbose) + { + kStdOut << "Record: " + << ae_records[ae_record_index].fName << " is marked.\n"; + + kStdOut << "Record offset: " << command_header.Offset << "\n"; + } + + command_headers.emplace_back(command_header); + } + + delete[] raw_ae_records; + + std::vector bytes; + bytes.resize(ae_header.fCodeSize); + + // TODO: Port this to NeFS. + + reader_protocol.FP.seekg(std::streamsize(ae_header.fStartCode)); + reader_protocol.FP.read(bytes.data(), std::streamsize(ae_header.fCodeSize)); + + for (auto& byte : bytes) + { + kObjectBytes.push_back({ .fPefBlob = bytes, .fAEOffset = ae_header.fStartCode }); + } + + reader_protocol.FP.close(); + + continue; + } + + kStdOut << "Not an object container: " << objectFile << std::endl; + // don't continue, it is a fatal error. + return TOOLCHAINKIT_EXEC_ERROR; + } + + pef_container.Cpu = archs; + + output_fc << pef_container; + + if (kVerbose) + { + kStdOut << "Wrote container header.\n"; + } + + output_fc.seekp(std::streamsize(pef_container.HdrSz)); + + std::vector not_found; + std::vector symbols; + + // step 2: check for errors (multiple symbols, undefined ones) + + for (auto& command_hdr : command_headers) + { + // check if this symbol needs to be resolved. + if (LibCompiler::String(command_hdr.Name).find(kLdDefineSymbol) != + LibCompiler::String::npos && + LibCompiler::String(command_hdr.Name).find(kLdDynamicSym) == LibCompiler::String::npos) + { + if (kVerbose) + kStdOut << "Found undefined symbol: " << command_hdr.Name << "\n"; + + if (auto it = std::find(not_found.begin(), not_found.end(), + LibCompiler::String(command_hdr.Name)); + it == not_found.end()) + { + not_found.emplace_back(command_hdr.Name); + } + } + + symbols.emplace_back(command_hdr.Name); + } + + // Now try to solve these symbols. + + for (size_t not_found_idx = 0; not_found_idx < command_headers.size(); + ++not_found_idx) + { + if (const auto it = std::find(not_found.begin(), not_found.end(), + LibCompiler::String(command_headers[not_found_idx].Name)); + it != not_found.end()) + { + LibCompiler::String symbol_imp = *it; + + if (symbol_imp.find(kLdDefineSymbol) == LibCompiler::String::npos) + continue; + + // erase the lookup prefix. + symbol_imp.erase( + 0, symbol_imp.find(kLdDefineSymbol) + strlen(kLdDefineSymbol)); + + // demangle everything. + while (symbol_imp.find('$') != LibCompiler::String::npos) + symbol_imp.erase(symbol_imp.find('$'), 1); + + // the reason we do is because, this may not match the symbol, and we need + // to look for other matching symbols. + for (auto& command_hdr : command_headers) + { + if (LibCompiler::String(command_hdr.Name).find(symbol_imp) != + LibCompiler::String::npos && + LibCompiler::String(command_hdr.Name).find(kLdDefineSymbol) == + LibCompiler::String::npos) + { + LibCompiler::String undefined_symbol = command_hdr.Name; + auto result_of_sym = + undefined_symbol.substr(undefined_symbol.find(symbol_imp)); + + for (int i = 0; result_of_sym[i] != 0; ++i) + { + if (result_of_sym[i] != symbol_imp[i]) + goto ld_continue_search; + } + + not_found.erase(it); + + if (kVerbose) + kStdOut << "found symbol: " << command_hdr.Name << "\n"; + + break; + } + } + + ld_continue_search: + continue; + } + } + + // step 3: check for errors (recheck if we have those symbols.) + + if (!kStartFound && is_executable) + { + if (kVerbose) + kStdOut + << "undefined entrypoint: " << kPefStart << ", you may have forget to ld64 " + "against your compiler's runtime library.\n"; + + kStdOut << "undefined entrypoint " << kPefStart + << " for executable: " << kOutput << "\n"; + } + + // step 4: write all PEF commands. + + LibCompiler::PEFCommandHeader date_cmd_hdr{}; + + time_t timestamp = time(nullptr); + + LibCompiler::String timeStampStr = "Container:BuildEpoch:"; + timeStampStr += std::to_string(timestamp); + + strncpy(date_cmd_hdr.Name, timeStampStr.c_str(), timeStampStr.size()); + + date_cmd_hdr.Flags = 0; + date_cmd_hdr.Kind = LibCompiler::kPefZero; + date_cmd_hdr.Offset = output_fc.tellp(); + date_cmd_hdr.Size = timeStampStr.size(); + + command_headers.push_back(date_cmd_hdr); + + LibCompiler::PEFCommandHeader abi_cmd_hdr{}; + + LibCompiler::String abi = kLinkerAbiContainer; + + switch (kArch) + { + case LibCompiler::kPefArchAMD64: { + abi += "MSFT"; + break; + } + case LibCompiler::kPefArchPowerPC: { + abi += "SYSV"; + break; + } + case LibCompiler::kPefArch32000: + case LibCompiler::kPefArch64000: { + abi += " ZWS"; + break; + } + default: { + abi += " IDK"; + break; + } + } + + MemoryCopy(abi_cmd_hdr.Name, abi.c_str(), abi.size()); + + abi_cmd_hdr.Size = abi.size(); + abi_cmd_hdr.Offset = output_fc.tellp(); + abi_cmd_hdr.Flags = 0; + abi_cmd_hdr.Kind = LibCompiler::kPefLinkerID; + + command_headers.push_back(abi_cmd_hdr); + + LibCompiler::PEFCommandHeader stack_cmd_hdr{0}; + + stack_cmd_hdr.Cpu = kArch; + stack_cmd_hdr.Flags = 0; + stack_cmd_hdr.Size = sizeof(uintptr_t); + stack_cmd_hdr.Offset = 0; + + MemoryCopy(stack_cmd_hdr.Name, kLinkerStackSizeSymbol, strlen(kLinkerStackSizeSymbol)); + + command_headers.push_back(stack_cmd_hdr); + + LibCompiler::PEFCommandHeader uuid_cmd_hdr{}; + + std::random_device rd; + + auto seedData = std::array{}; + std::generate(std::begin(seedData), std::end(seedData), std::ref(rd)); + std::seed_seq seq(std::begin(seedData), std::end(seedData)); + std::mt19937 generator(seq); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid id = gen(); + auto uuidStr = uuids::to_string(id); + + MemoryCopy(uuid_cmd_hdr.Name, "Container:GUID:4:", strlen("Container:GUID:4:")); + MemoryCopy(uuid_cmd_hdr.Name + strlen("Container:GUID:4:"), uuidStr.c_str(), + uuidStr.size()); + + uuid_cmd_hdr.Size = strlen(uuid_cmd_hdr.Name); + uuid_cmd_hdr.Offset = output_fc.tellp(); + uuid_cmd_hdr.Flags = LibCompiler::kPefLinkerID; + uuid_cmd_hdr.Kind = LibCompiler::kPefZero; + + command_headers.push_back(uuid_cmd_hdr); + + // prepare a symbol vector. + std::vector undef_symbols; + std::vector dupl_symbols; + std::vector resolve_symbols; + + constexpr Int32 cPaddingOffset = 16; + + size_t previous_offset = (command_headers.size() * sizeof(LibCompiler::PEFCommandHeader)) + cPaddingOffset; + + // Finally write down the command headers. + // And check for any duplications + for (size_t commandHeaderIndex = 0UL; + commandHeaderIndex < command_headers.size(); ++commandHeaderIndex) + { + if (LibCompiler::String(command_headers[commandHeaderIndex].Name) + .find(kLdDefineSymbol) != LibCompiler::String::npos && + LibCompiler::String(command_headers[commandHeaderIndex].Name) + .find(kLdDynamicSym) == LibCompiler::String::npos) + { + // ignore :UndefinedSymbol: headers, they do not contain code. + continue; + } + + LibCompiler::String symbol_name = command_headers[commandHeaderIndex].Name; + + if (!symbol_name.empty()) + { + undef_symbols.emplace_back(symbol_name); + } + + command_headers[commandHeaderIndex].Offset += previous_offset; + previous_offset += command_headers[commandHeaderIndex].Size; + + LibCompiler::String name = command_headers[commandHeaderIndex].Name; + + /// so this is valid when we get to the entrypoint. + /// it is always a code64 container. And should equal to kPefStart as well. + /// this chunk of code updates the pef_container.Start with the updated offset. + if (name.find(kPefStart) != LibCompiler::String::npos && + name.find(kPefCode64) != LibCompiler::String::npos) + { + pef_container.Start = command_headers[commandHeaderIndex].Offset; + auto tellCurPos = output_fc.tellp(); + + output_fc.seekp(0); + output_fc << pef_container; + + output_fc.seekp(tellCurPos); + } + + if (kVerbose) + { + kStdOut << "Command header name: " << name << "\n"; + kStdOut << "Real address of command header content: " << command_headers[commandHeaderIndex].Offset << "\n"; + } + + output_fc << command_headers[commandHeaderIndex]; + + for (size_t sub_command_header_index = 0UL; + sub_command_header_index < command_headers.size(); + ++sub_command_header_index) + { + if (sub_command_header_index == commandHeaderIndex) + continue; + + if (LibCompiler::String(command_headers[sub_command_header_index].Name) + .find(kLdDefineSymbol) != LibCompiler::String::npos && + LibCompiler::String(command_headers[sub_command_header_index].Name) + .find(kLdDynamicSym) == LibCompiler::String::npos) + { + if (kVerbose) + { + kStdOut << "ignore :UndefinedSymbol: command header...\n"; + } + + // ignore :UndefinedSymbol: headers, they do not contain code. + continue; + } + + auto& command_hdr = command_headers[sub_command_header_index]; + + if (command_hdr.Name == + LibCompiler::String(command_headers[commandHeaderIndex].Name)) + { + if (std::find(dupl_symbols.cbegin(), dupl_symbols.cend(), + command_hdr.Name) == dupl_symbols.cend()) + { + dupl_symbols.emplace_back(command_hdr.Name); + } + + if (kVerbose) + kStdOut << "found duplicate symbol: " << command_hdr.Name + << "\n"; + + kDuplicateSymbols = true; + } + } + } + + if (!dupl_symbols.empty()) + { + for (auto& symbol : dupl_symbols) + { + kStdOut << "Multiple symbols of " << symbol << ".\n"; + } + + return TOOLCHAINKIT_EXEC_ERROR; + } + + // step 2.5: write program bytes. + + for (auto& struct_of_blob : kObjectBytes) + { + output_fc.write(struct_of_blob.fPefBlob.data(), struct_of_blob.fPefBlob.size()); + } + + if (kVerbose) + kStdOut << "wrote contents of: " << kOutput << "\n"; + + // step 3: check if we have those symbols + + std::vector unreferenced_symbols; + + for (auto& command_hdr : command_headers) + { + if (auto it = std::find(not_found.begin(), not_found.end(), + LibCompiler::String(command_hdr.Name)); + it != not_found.end()) + { + unreferenced_symbols.emplace_back(command_hdr.Name); + } + } + + if (!unreferenced_symbols.empty()) + { + for (auto& unreferenced_symbol : unreferenced_symbols) + { + kStdOut << "undefined symbol " << unreferenced_symbol << "\n"; + } + } + + if (!kStartFound || kDuplicateSymbols && std::filesystem::exists(kOutput) || + !unreferenced_symbols.empty()) + { + if (kVerbose) + kStdOut << "file: " << kOutput + << ", is corrupt, removing file...\n"; + + return TOOLCHAINKIT_EXEC_ERROR; + } + + return EXIT_SUCCESS; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/Detail/ClUtils.h b/dev/LibCompiler/src/Detail/ClUtils.h index 5d3ff79..393a3cc 100644 --- a/dev/LibCompiler/src/Detail/ClUtils.h +++ b/dev/LibCompiler/src/Detail/ClUtils.h @@ -12,3 +12,7 @@ #define kZero64Section ".zero64" #define kCode64Section ".code64" #define kData64Section ".data64" + +#define kZero128Section ".zero128" +#define kCode128Section ".code128" +#define kData128Section ".data128" diff --git a/dev/LibCompiler/src/DynamicLinker64PEF.cc b/dev/LibCompiler/src/DynamicLinker64PEF.cc deleted file mode 100644 index aaab9b6..0000000 --- a/dev/LibCompiler/src/DynamicLinker64PEF.cc +++ /dev/null @@ -1,774 +0,0 @@ -/* ------------------------------------------- - - Copyright (C) 2024 Theater Quality Corp, all rights reserved - - @file DynamicLinker64PEF.cc - @brief: C++ 64-Bit PEF Linker. - -------------------------------------------- */ - -/// @author EL Mahrouss Amlal (amlel) -/// @brief TQ 64-bit PEF Linker. -/// Last Rev: Sat Feb 24 CET 2024 -/// @note Do not look up for anything with .code64/.data64/.zero64! -/// It will be loaded when the program loader will start the image. - -//! Toolchain Kit. -#include - -#include - -//! Assembler Kit -#include - -//! Preferred Executable Format -#include -#include - -//! Release macros. -#include - -//! Advanced Executable Object Format. -#include -#include - -#define kLinkerVersionStr "TQ 64-Bit Linker %s, (c) Theater Quality Corp. 2024, all rights reserved.\n" - -#define MemoryCopy(DST, SRC, SZ) memcpy(DST, SRC, SZ) -#define StringCompare(DST, SRC) strcmp(DST, SRC) - -#define kPefNoCpu 0U -#define kPefNoSubCpu 0U - -#define kWhite "\e[0;97m" - -#define kStdOut (std::cout << kWhite << "ld64: ") - -#define kLinkerDefaultOrigin kPefBaseOrigin -#define kLinkerId (0x5046FF) -#define kLinkerAbiContainer "Container:ABI:" - -/// @brief PEF stack size symbol. -#define kLinkerStackSizeSymbol "SizeOfReserveStack" - -namespace Details -{ -struct DynamicLinkerBlob final -{ - std::vector fPefBlob; // PEF code/bss/data blob. - std::uintptr_t fAEOffset; // the offset of the PEF container header.. -}; -} - -enum -{ - kABITypeStart = 0x1010, /* Invalid ABI start of ABI list. */ - kABITypeZKA = 0x5046, /* PF (ZKA PEF ABI) */ - kABITypeInvalid = 0xFFFF, -}; - -static LibCompiler::String kOutput = ""; -static Int32 kAbi = kABITypeZKA; -static Int32 kSubArch = kPefNoSubCpu; -static Int32 kArch = LibCompiler::kPefArchInvalid; -static Bool kFatBinaryEnable = false; -static Bool kStartFound = false; -static Bool kDuplicateSymbols = false; -static Bool kVerbose = false; - -/* ld64 is to be found, mld is to be found at runtime. */ -static const char* kLdDefineSymbol = ":UndefinedSymbol:"; -static const char* kLdDynamicSym = ":RuntimeSymbol:"; - -/* object code and list. */ -static std::vector kObjectList; -static std::vector kObjectBytes; - -static uintptr_t kMIBCount = 8; -static uintptr_t kByteCount = 1024; - -#define kPrintF printf -#define kLinkerSplash() kPrintF(kWhite kLinkerVersionStr, kDistVersion) - -/// @brief ZKA 64-bit Linker. -/// @note This linker is made for PEF executable, thus ZKA based OSes. -TOOLCHAINKIT_MODULE(DynamicLinker64PEF) -{ - bool is_executable = true; - - /** - * @brief parse flags and trigger options. - */ - for (size_t linker_arg = 1; linker_arg < argc; ++linker_arg) - { - if (StringCompare(argv[linker_arg], "--ld64:help") == 0) - { - kLinkerSplash(); - - kStdOut << "--ld64:ver: Show linker version.\n"; - kStdOut << "--ld64:?: Show linker help.\n"; - kStdOut << "--ld64:verbose: Enable linker trace.\n"; - kStdOut << "--ld64:dylib: Output as a Dylib PEF.\n"; - kStdOut << "--ld64:fat: Output as a FAT PEF.\n"; - kStdOut << "--ld64:32k: Output as a 32x0 PEF.\n"; - kStdOut << "--ld64:64k: Output as a 64x0 PEF.\n"; - kStdOut << "--ld64:amd64: Output as a AMD64 PEF.\n"; - kStdOut << "--ld64:rv64: Output as a RISC-V PEF.\n"; - kStdOut << "--ld64:power64: Output as a POWER PEF.\n"; - kStdOut << "--ld64:arm64: Output as a ARM64 PEF.\n"; - kStdOut << "--ld64:output: Select the output file name.\n"; - - return EXIT_SUCCESS; - } - else if (StringCompare(argv[linker_arg], "--ld64:version") == 0) - { - kLinkerSplash(); - return EXIT_SUCCESS; - } - else if (StringCompare(argv[linker_arg], "--ld64:fat-binary") == 0) - { - kFatBinaryEnable = true; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:64k") == 0) - { - kArch = LibCompiler::kPefArch64000; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:amd64") == 0) - { - kArch = LibCompiler::kPefArchAMD64; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:32k") == 0) - { - kArch = LibCompiler::kPefArch32000; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:power64") == 0) - { - kArch = LibCompiler::kPefArchPowerPC; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:riscv64") == 0) - { - kArch = LibCompiler::kPefArchRISCV; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:arm64") == 0) - { - kArch = LibCompiler::kPefArchARM64; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:verbose") == 0) - { - kVerbose = true; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:dylib") == 0) - { - if (kOutput.empty()) - { - continue; - } - - if (kOutput.find(kPefExt) != LibCompiler::String::npos) - kOutput.erase(kOutput.find(kPefExt), strlen(kPefExt)); - - kOutput += kPefDylibExt; - - is_executable = false; - - continue; - } - else if (StringCompare(argv[linker_arg], "--ld64:output") == 0) - { - kOutput = argv[linker_arg + 1]; - ++linker_arg; - - continue; - } - else - { - if (argv[linker_arg][0] == '-') - { - kStdOut << "unknown flag: " << argv[linker_arg] << "\n"; - return EXIT_FAILURE; - } - - kObjectList.emplace_back(argv[linker_arg]); - - continue; - } - } - - if (kOutput.empty()) - { - kStdOut << "no output filename set." << std::endl; - return TOOLCHAINKIT_EXEC_ERROR; - } - - // sanity check. - if (kObjectList.empty()) - { - kStdOut << "no input files." << std::endl; - return TOOLCHAINKIT_EXEC_ERROR; - } - else - { - namespace fs = std::filesystem; - - // check for existing files, if they don't throw an error. - for (auto& obj : kObjectList) - { - if (!fs::exists(obj)) - { - // if filesystem doesn't find file - // -> throw error. - kStdOut << "no such file: " << obj << std::endl; - return TOOLCHAINKIT_EXEC_ERROR; - } - } - } - - // PEF expects a valid target architecture when outputing a binary. - if (kArch == 0) - { - kStdOut << "no target architecture set, can't continue." << std::endl; - return TOOLCHAINKIT_EXEC_ERROR; - } - - LibCompiler::PEFContainer pef_container{}; - - int32_t archs = kArch; - - pef_container.Count = 0UL; - pef_container.Kind = is_executable ? LibCompiler::kPefKindExec : LibCompiler::kPefKindDylib; - pef_container.SubCpu = kSubArch; - pef_container.Linker = kLinkerId; // Theater Quality Corp. Linker - pef_container.Abi = kAbi; // Multi-Processor UX ABI - pef_container.Magic[0] = kPefMagic[kFatBinaryEnable ? 2 : 0]; - pef_container.Magic[1] = kPefMagic[1]; - pef_container.Magic[2] = kPefMagic[kFatBinaryEnable ? 0 : 2]; - pef_container.Magic[3] = kPefMagic[3]; - pef_container.Version = kPefVersion; - - // specify the start address, can be 0x10000 - pef_container.Start = kLinkerDefaultOrigin; - pef_container.HdrSz = sizeof(LibCompiler::PEFContainer); - - std::ofstream output_fc(kOutput, std::ofstream::binary); - - if (output_fc.bad()) - { - if (kVerbose) - { - kStdOut << "error: " << strerror(errno) << "\n"; - } - - return TOOLCHAINKIT_FILE_NOT_FOUND; - } - - //! Read AE to convert as PEF. - - std::vector command_headers; - LibCompiler::Utils::AEReadableProtocol reader_protocol{}; - - for (const auto& objectFile : kObjectList) - { - if (!std::filesystem::exists(objectFile)) - continue; - - LibCompiler::AEHeader hdr{}; - - reader_protocol.FP = std::ifstream(objectFile, std::ifstream::binary); - reader_protocol.FP >> hdr; - - auto ae_header = hdr; - - if (ae_header.fMagic[0] == kAEMag0 && ae_header.fMagic[1] == kAEMag1 && - ae_header.fSize == sizeof(LibCompiler::AEHeader)) - { - if (ae_header.fArch != kArch) - { - if (kVerbose) - kStdOut << "info: is this a FAT binary? : "; - - if (!kFatBinaryEnable) - { - if (kVerbose) - kStdOut << "No.\n"; - - kStdOut << "error: object " << objectFile - << " is a different kind of architecture and output isn't " - "treated as a FAT binary." - << std::endl; - - return TOOLCHAINKIT_FAT_ERROR; - } - else - { - if (kVerbose) - { - kStdOut << "Architecture matches what we expect.\n"; - } - } - } - - // append arch type to archs varaible. - archs |= ae_header.fArch; - std::size_t cnt = ae_header.fCount; - - if (kVerbose) - kStdOut << "object header found, record count: " << cnt << "\n"; - - pef_container.Count = cnt; - - char_type* raw_ae_records = - new char_type[cnt * sizeof(LibCompiler::AERecordHeader)]; - - memset(raw_ae_records, 0, cnt * sizeof(LibCompiler::AERecordHeader)); - - auto* ae_records = reader_protocol.Read(raw_ae_records, cnt); - - for (size_t ae_record_index = 0; ae_record_index < cnt; - ++ae_record_index) - { - LibCompiler::PEFCommandHeader command_header{0}; - std::size_t offset_of_obj = ae_records[ae_record_index].fOffset; - - MemoryCopy(command_header.Name, ae_records[ae_record_index].fName, - kPefNameLen); - - LibCompiler::String cmd_hdr_name(command_header.Name); - - // check this header if it's any valid. - if (cmd_hdr_name.find(kPefCode64) == - LibCompiler::String::npos && - cmd_hdr_name.find(kPefData64) == - LibCompiler::String::npos && - cmd_hdr_name.find(kPefZero64) == - LibCompiler::String::npos) - { - if (cmd_hdr_name.find(kPefStart) == - LibCompiler::String::npos && - *command_header.Name == 0) - { - if (cmd_hdr_name.find(kLdDefineSymbol) != - LibCompiler::String::npos) - { - goto ld_mark_header; - } - else - { - continue; - } - } - } - - if (cmd_hdr_name.find(kPefStart) != - LibCompiler::String::npos && - cmd_hdr_name.find(kPefCode64) != - LibCompiler::String::npos) - { - kStartFound = true; - } - - ld_mark_header: - command_header.Offset = offset_of_obj; - command_header.Kind = ae_records[ae_record_index].fKind; - command_header.Size = ae_records[ae_record_index].fSize; - command_header.Cpu = ae_header.fArch; - command_header.SubCpu = ae_header.fSubArch; - - if (kVerbose) - { - kStdOut << "Record: " - << ae_records[ae_record_index].fName << " is marked.\n"; - - kStdOut << "Record offset: " << command_header.Offset << "\n"; - } - - command_headers.emplace_back(command_header); - } - - delete[] raw_ae_records; - - std::vector bytes; - bytes.resize(ae_header.fCodeSize); - - // TODO: Port this to NeFS. - - reader_protocol.FP.seekg(std::streamsize(ae_header.fStartCode)); - reader_protocol.FP.read(bytes.data(), std::streamsize(ae_header.fCodeSize)); - - for (auto& byte : bytes) - { - kObjectBytes.push_back({ .fPefBlob = bytes, .fAEOffset = ae_header.fStartCode }); - } - - reader_protocol.FP.close(); - - continue; - } - - kStdOut << "Not an object container: " << objectFile << std::endl; - // don't continue, it is a fatal error. - return TOOLCHAINKIT_EXEC_ERROR; - } - - pef_container.Cpu = archs; - - output_fc << pef_container; - - if (kVerbose) - { - kStdOut << "Wrote container header.\n"; - } - - output_fc.seekp(std::streamsize(pef_container.HdrSz)); - - std::vector not_found; - std::vector symbols; - - // step 2: check for errors (multiple symbols, undefined ones) - - for (auto& command_hdr : command_headers) - { - // check if this symbol needs to be resolved. - if (LibCompiler::String(command_hdr.Name).find(kLdDefineSymbol) != - LibCompiler::String::npos && - LibCompiler::String(command_hdr.Name).find(kLdDynamicSym) == LibCompiler::String::npos) - { - if (kVerbose) - kStdOut << "Found undefined symbol: " << command_hdr.Name << "\n"; - - if (auto it = std::find(not_found.begin(), not_found.end(), - LibCompiler::String(command_hdr.Name)); - it == not_found.end()) - { - not_found.emplace_back(command_hdr.Name); - } - } - - symbols.emplace_back(command_hdr.Name); - } - - // Now try to solve these symbols. - - for (size_t not_found_idx = 0; not_found_idx < command_headers.size(); - ++not_found_idx) - { - if (const auto it = std::find(not_found.begin(), not_found.end(), - LibCompiler::String(command_headers[not_found_idx].Name)); - it != not_found.end()) - { - LibCompiler::String symbol_imp = *it; - - if (symbol_imp.find(kLdDefineSymbol) == LibCompiler::String::npos) - continue; - - // erase the lookup prefix. - symbol_imp.erase( - 0, symbol_imp.find(kLdDefineSymbol) + strlen(kLdDefineSymbol)); - - // demangle everything. - while (symbol_imp.find('$') != LibCompiler::String::npos) - symbol_imp.erase(symbol_imp.find('$'), 1); - - // the reason we do is because, this may not match the symbol, and we need - // to look for other matching symbols. - for (auto& command_hdr : command_headers) - { - if (LibCompiler::String(command_hdr.Name).find(symbol_imp) != - LibCompiler::String::npos && - LibCompiler::String(command_hdr.Name).find(kLdDefineSymbol) == - LibCompiler::String::npos) - { - LibCompiler::String undefined_symbol = command_hdr.Name; - auto result_of_sym = - undefined_symbol.substr(undefined_symbol.find(symbol_imp)); - - for (int i = 0; result_of_sym[i] != 0; ++i) - { - if (result_of_sym[i] != symbol_imp[i]) - goto ld_continue_search; - } - - not_found.erase(it); - - if (kVerbose) - kStdOut << "found symbol: " << command_hdr.Name << "\n"; - - break; - } - } - - ld_continue_search: - continue; - } - } - - // step 3: check for errors (recheck if we have those symbols.) - - if (!kStartFound && is_executable) - { - if (kVerbose) - kStdOut - << "undefined entrypoint: " << kPefStart << ", you may have forget to ld64 " - "against your compiler's runtime library.\n"; - - kStdOut << "undefined entrypoint " << kPefStart - << " for executable: " << kOutput << "\n"; - } - - // step 4: write all PEF commands. - - LibCompiler::PEFCommandHeader date_cmd_hdr{}; - - time_t timestamp = time(nullptr); - - LibCompiler::String timeStampStr = "Container:BuildEpoch:"; - timeStampStr += std::to_string(timestamp); - - strncpy(date_cmd_hdr.Name, timeStampStr.c_str(), timeStampStr.size()); - - date_cmd_hdr.Flags = 0; - date_cmd_hdr.Kind = LibCompiler::kPefZero; - date_cmd_hdr.Offset = output_fc.tellp(); - date_cmd_hdr.Size = timeStampStr.size(); - - command_headers.push_back(date_cmd_hdr); - - LibCompiler::PEFCommandHeader abi_cmd_hdr{}; - - LibCompiler::String abi = kLinkerAbiContainer; - - switch (kArch) - { - case LibCompiler::kPefArchAMD64: { - abi += "MSFT"; - break; - } - case LibCompiler::kPefArchPowerPC: { - abi += "SYSV"; - break; - } - case LibCompiler::kPefArch32000: - case LibCompiler::kPefArch64000: { - abi += " ZWS"; - break; - } - default: { - abi += " IDK"; - break; - } - } - - MemoryCopy(abi_cmd_hdr.Name, abi.c_str(), abi.size()); - - abi_cmd_hdr.Size = abi.size(); - abi_cmd_hdr.Offset = output_fc.tellp(); - abi_cmd_hdr.Flags = 0; - abi_cmd_hdr.Kind = LibCompiler::kPefLinkerID; - - command_headers.push_back(abi_cmd_hdr); - - LibCompiler::PEFCommandHeader stack_cmd_hdr{0}; - - stack_cmd_hdr.Cpu = kArch; - stack_cmd_hdr.Flags = 0; - stack_cmd_hdr.Size = sizeof(uintptr_t); - stack_cmd_hdr.Offset = 0; - - MemoryCopy(stack_cmd_hdr.Name, kLinkerStackSizeSymbol, strlen(kLinkerStackSizeSymbol)); - - command_headers.push_back(stack_cmd_hdr); - - LibCompiler::PEFCommandHeader uuid_cmd_hdr{}; - - std::random_device rd; - - auto seedData = std::array{}; - std::generate(std::begin(seedData), std::end(seedData), std::ref(rd)); - std::seed_seq seq(std::begin(seedData), std::end(seedData)); - std::mt19937 generator(seq); - - auto gen = uuids::uuid_random_generator{generator}; - uuids::uuid id = gen(); - auto uuidStr = uuids::to_string(id); - - MemoryCopy(uuid_cmd_hdr.Name, "Container:GUID:4:", strlen("Container:GUID:4:")); - MemoryCopy(uuid_cmd_hdr.Name + strlen("Container:GUID:4:"), uuidStr.c_str(), - uuidStr.size()); - - uuid_cmd_hdr.Size = strlen(uuid_cmd_hdr.Name); - uuid_cmd_hdr.Offset = output_fc.tellp(); - uuid_cmd_hdr.Flags = LibCompiler::kPefLinkerID; - uuid_cmd_hdr.Kind = LibCompiler::kPefZero; - - command_headers.push_back(uuid_cmd_hdr); - - // prepare a symbol vector. - std::vector undef_symbols; - std::vector dupl_symbols; - std::vector resolve_symbols; - - constexpr Int32 cPaddingOffset = 16; - - size_t previous_offset = (command_headers.size() * sizeof(LibCompiler::PEFCommandHeader)) + cPaddingOffset; - - // Finally write down the command headers. - // And check for any duplications - for (size_t commandHeaderIndex = 0UL; - commandHeaderIndex < command_headers.size(); ++commandHeaderIndex) - { - if (LibCompiler::String(command_headers[commandHeaderIndex].Name) - .find(kLdDefineSymbol) != LibCompiler::String::npos && - LibCompiler::String(command_headers[commandHeaderIndex].Name) - .find(kLdDynamicSym) == LibCompiler::String::npos) - { - // ignore :UndefinedSymbol: headers, they do not contain code. - continue; - } - - LibCompiler::String symbol_name = command_headers[commandHeaderIndex].Name; - - if (!symbol_name.empty()) - { - undef_symbols.emplace_back(symbol_name); - } - - command_headers[commandHeaderIndex].Offset += previous_offset; - previous_offset += command_headers[commandHeaderIndex].Size; - - LibCompiler::String name = command_headers[commandHeaderIndex].Name; - - /// so this is valid when we get to the entrypoint. - /// it is always a code64 container. And should equal to kPefStart as well. - /// this chunk of code updates the pef_container.Start with the updated offset. - if (name.find(kPefStart) != LibCompiler::String::npos && - name.find(kPefCode64) != LibCompiler::String::npos) - { - pef_container.Start = command_headers[commandHeaderIndex].Offset; - auto tellCurPos = output_fc.tellp(); - - output_fc.seekp(0); - output_fc << pef_container; - - output_fc.seekp(tellCurPos); - } - - if (kVerbose) - { - kStdOut << "Command header name: " << name << "\n"; - kStdOut << "Real address of command header content: " << command_headers[commandHeaderIndex].Offset << "\n"; - } - - output_fc << command_headers[commandHeaderIndex]; - - for (size_t sub_command_header_index = 0UL; - sub_command_header_index < command_headers.size(); - ++sub_command_header_index) - { - if (sub_command_header_index == commandHeaderIndex) - continue; - - if (LibCompiler::String(command_headers[sub_command_header_index].Name) - .find(kLdDefineSymbol) != LibCompiler::String::npos && - LibCompiler::String(command_headers[sub_command_header_index].Name) - .find(kLdDynamicSym) == LibCompiler::String::npos) - { - if (kVerbose) - { - kStdOut << "ignore :UndefinedSymbol: command header...\n"; - } - - // ignore :UndefinedSymbol: headers, they do not contain code. - continue; - } - - auto& command_hdr = command_headers[sub_command_header_index]; - - if (command_hdr.Name == - LibCompiler::String(command_headers[commandHeaderIndex].Name)) - { - if (std::find(dupl_symbols.cbegin(), dupl_symbols.cend(), - command_hdr.Name) == dupl_symbols.cend()) - { - dupl_symbols.emplace_back(command_hdr.Name); - } - - if (kVerbose) - kStdOut << "found duplicate symbol: " << command_hdr.Name - << "\n"; - - kDuplicateSymbols = true; - } - } - } - - if (!dupl_symbols.empty()) - { - for (auto& symbol : dupl_symbols) - { - kStdOut << "Multiple symbols of " << symbol << ".\n"; - } - - return TOOLCHAINKIT_EXEC_ERROR; - } - - // step 2.5: write program bytes. - - for (auto& struct_of_blob : kObjectBytes) - { - output_fc.write(struct_of_blob.fPefBlob.data(), struct_of_blob.fPefBlob.size()); - } - - if (kVerbose) - kStdOut << "wrote contents of: " << kOutput << "\n"; - - // step 3: check if we have those symbols - - std::vector unreferenced_symbols; - - for (auto& command_hdr : command_headers) - { - if (auto it = std::find(not_found.begin(), not_found.end(), - LibCompiler::String(command_hdr.Name)); - it != not_found.end()) - { - unreferenced_symbols.emplace_back(command_hdr.Name); - } - } - - if (!unreferenced_symbols.empty()) - { - for (auto& unreferenced_symbol : unreferenced_symbols) - { - kStdOut << "undefined symbol " << unreferenced_symbol << "\n"; - } - } - - if (!kStartFound || kDuplicateSymbols && std::filesystem::exists(kOutput) || - !unreferenced_symbols.empty()) - { - if (kVerbose) - kStdOut << "file: " << kOutput - << ", is corrupt, removing file...\n"; - - return TOOLCHAINKIT_EXEC_ERROR; - } - - return EXIT_SUCCESS; -} - -// Last rev 13-1-24 -- cgit v1.2.3