blob: b3b65bafe3bb3196f55465cc9fd169c3126c03c7 [file]
// 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;
}