| // Copyright 2025 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <fstream> |
| #include <iostream> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/flags/flag.h" |
| #include "absl/flags/parse.h" |
| #include "absl/log/check.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "elfio/elf_types.hpp" |
| #include "elfio/elfio.hpp" |
| #include "elfio/elfio_dump.hpp" |
| #include "mpact/sim/generic/type_helpers.h" |
| #include "mpact/sim/util/asm/opcode_assembler_interface.h" |
| #include "mpact/sim/util/asm/resolver_interface.h" |
| #include "mpact/sim/util/asm/simple_assembler.h" |
| #include "riscv/riscv64g_bin_encoder_interface.h" |
| #include "riscv/riscv64g_encoder.h" |
| #include "util/regexp/re2/re2.h" |
| |
| using ::mpact::sim::riscv::isa64::RiscV64GBinEncoderInterface; |
| using ::mpact::sim::riscv::isa64::Riscv64gSlotMatcher; |
| using ::mpact::sim::util::assembler::SimpleAssembler; |
| |
| // This file implements the main function for the generated assembler for |
| // RiscV64G. |
| |
| namespace { |
| |
| using ::mpact::sim::generic::operator*; // NOLINT(misc-unused-using-decls) |
| using ::mpact::sim::util::assembler::OpcodeAssemblerInterface; |
| using ::mpact::sim::util::assembler::RelocationInfo; |
| using ::mpact::sim::util::assembler::ResolverInterface; |
| using AddSymbolCallback = |
| ::mpact::sim::util::assembler::OpcodeAssemblerInterface::AddSymbolCallback; |
| |
| // This class implements the byte oriented OpcodeAssemblerInterface, converting |
| // from the bit interface provided by the SlotMatcher interface. Since there is |
| // only one slot in the RiscV64G ISA, only one slot matcher is needed. |
| class RiscV64GAssembler : public OpcodeAssemblerInterface { |
| public: |
| RiscV64GAssembler(Riscv64gSlotMatcher* matcher) |
| : label_re_("^(\\S+)\\s*:"), matcher_(matcher) {}; |
| ~RiscV64GAssembler() override = default; |
| absl::StatusOr<size_t> Encode( |
| uint64_t address, absl::string_view text, |
| AddSymbolCallback add_symbol_callback, ResolverInterface* resolver, |
| std::vector<uint8_t>& bytes, |
| std::vector<RelocationInfo>& relocations) override { |
| // First check to see if there is a label, if so, add it to the symbol table |
| // with the current address. |
| std::string label; |
| if (RE2::Consume(&text, label_re_, &label)) { |
| auto status = |
| add_symbol_callback(label, address, 0, ELFIO::STT_NOTYPE, 0, 0); |
| if (!status.ok()) return status; |
| } |
| auto res = matcher_->Encode(address, text, 0, resolver, relocations); |
| if (!res.status().ok()) return res.status(); |
| auto [value, size] = res.value(); |
| union { |
| uint64_t i; |
| uint8_t b[sizeof(uint64_t)]; |
| } u; |
| u.i = value; |
| for (int i = 0; i < size / 8; ++i) { |
| bytes.push_back(u.b[i]); |
| } |
| return bytes.size(); |
| } |
| |
| private: |
| RE2 label_re_; |
| Riscv64gSlotMatcher* matcher_; |
| }; |
| |
| } // namespace |
| |
| // This flag dumps the info for the generated ELF file. |
| ABSL_FLAG(bool, dump_elf, false, "Dump the ELF file"); |
| // Produce a relocatable file as opposed to an executable. |
| ABSL_FLAG(bool, c, false, "Produce a relocatable file"); |
| // Specify the output file name. |
| ABSL_FLAG(std::optional<std::string>, o, std::nullopt, "Output file name"); |
| |
| // Supported RiscV ELF flags (TSO memory ordering and compact encodings). |
| enum class RiscVElfFlags { |
| kNone = 0, |
| kRiscvTso = 0x0001, |
| kRiscvRvc = 0x0010, |
| }; |
| |
| int main(int argc, char* argv[]) { |
| auto arg_vec = absl::ParseCommandLine(argc, argv); |
| |
| if (arg_vec.size() > 2) { |
| std::cout << "Too many arguments\n"; |
| return 1; |
| } |
| |
| std::istream* is; |
| |
| if (arg_vec.size() == 1) { |
| is = &std::cin; |
| } else { |
| is = new std::fstream(arg_vec[1], std::fstream::in); |
| } |
| |
| RiscV64GBinEncoderInterface bin_encoder_interface; |
| Riscv64gSlotMatcher matcher(&bin_encoder_interface); |
| RiscV64GAssembler riscv_64g_assembler(&matcher); |
| CHECK_OK(matcher.Initialize()); |
| // Instantiate the assembler. |
| SimpleAssembler assembler("#", ELFIO::ELFCLASS64, &riscv_64g_assembler); |
| // Set up the abi and the machine type. |
| assembler.writer().set_os_abi(ELFIO::ELFOSABI_LINUX); |
| assembler.writer().set_machine(ELFIO::EM_RISCV); |
| // Set the appropriate ELF header flags. |
| assembler.writer().set_flags(*RiscVElfFlags::kRiscvTso | |
| *RiscVElfFlags::kRiscvRvc); |
| // Perform the first pass of parsing the assembly code. |
| auto status = assembler.Parse(*is); |
| if (!status.ok()) { |
| std::cout << "Failed to parse assembly: " << status.message() << "\n"; |
| return 1; |
| } |
| // Perform the second pass of parsing the assembly code. This pass will either |
| // generate an executable or a relocatable file. |
| std::string output_file_name; |
| if (absl::GetFlag(FLAGS_c)) { |
| status = assembler.CreateRelocatable(); |
| if (arg_vec.size() == 1) { |
| output_file_name = "stdin.o"; |
| } else { |
| std::string input_file_name = arg_vec[1]; |
| auto dot_pos = input_file_name.find_last_of('.'); |
| if (dot_pos == std::string::npos) { |
| output_file_name = absl::StrCat(input_file_name, ".o"); |
| } else { |
| output_file_name = |
| absl::StrCat(input_file_name.substr(0, dot_pos), ".o"); |
| } |
| } |
| } else { |
| status = assembler.CreateExecutable(0x1000, "main"); |
| output_file_name = "a.out"; |
| } |
| if (!status.ok()) { |
| std::cout << "Assembly failure: " << status.message() << "\n"; |
| return 1; |
| } |
| // Write out the output file. |
| if (absl::GetFlag(FLAGS_o).has_value()) { |
| output_file_name = absl::GetFlag(FLAGS_o).value(); |
| } |
| std::ofstream output_file(output_file_name); |
| if (!output_file.is_open()) { |
| std::cout << "Failed to open output file: " << output_file_name << "\n"; |
| return 1; |
| } |
| status = assembler.Write(output_file); |
| if (!status.ok()) { |
| std::cout << "Failed to write output file: " << status.message() << "\n"; |
| return 1; |
| } |
| output_file.close(); |
| if (is != &std::cin) delete is; |
| |
| // Dump the ELF info if requested. |
| if (absl::GetFlag(FLAGS_dump_elf)) { |
| ELFIO::elfio reader; |
| if (!reader.load(output_file_name)) { |
| std::cout << "Failed to load output file: " << output_file_name << "\n"; |
| return 1; |
| } |
| |
| ELFIO::dump::header(std::cout, reader); |
| ELFIO::dump::section_headers(std::cout, reader); |
| ELFIO::dump::segment_headers(std::cout, reader); |
| ELFIO::dump::symbol_tables(std::cout, reader); |
| ELFIO::dump::notes(std::cout, reader); |
| ELFIO::dump::modinfo(std::cout, reader); |
| ELFIO::dump::dynamic_tags(std::cout, reader); |
| ELFIO::dump::section_datas(std::cout, reader); |
| ELFIO::dump::segment_datas(std::cout, reader); |
| } |
| return 0; |
| } |