Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Translating RISC-V programs directly from ELF files. #1453

Merged
merged 108 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
bc8e5c1
Attempt to make vec_median a binary.
lvella May 22, 2024
2544ec3
Missing moved file.
lvella May 22, 2024
e750285
works
leonardoalt May 22, 2024
6050592
Starting implementation of instructions lifting.
lvella May 22, 2024
424115f
Refactoring instruction processor to be more generic.
lvella May 29, 2024
d362de7
Implementing more general jalr.
lvella May 29, 2024
6261fa8
Merge branch 'jalr-instruction' into HEAD
lvella May 29, 2024
2a0920a
Lifting sets of instructions into high level (pseudo)instructions.
lvella May 29, 2024
6879d74
Instruction lifting possibly done.
lvella May 30, 2024
5866a4b
Loading data and lifting references to text labels.
lvella May 30, 2024
69130e1
Relocation apparently working.
lvella May 31, 2024
d87c3dc
Merge remote-tracking branch 'origin/main' into elf-support
lvella May 31, 2024
c3c8842
Merge branch 'main' into elf-support
lvella Jun 3, 2024
d92987e
Refactoring powdr asm generator to make it source agnostic.
lvella Jun 4, 2024
3c71255
Make Label generic.
lvella Jun 5, 2024
bc53657
Setting the stack pointer.
lvella Jun 5, 2024
708dfee
Using a non-conflicting start function name.
lvella Jun 5, 2024
27872cd
Merge branch 'main' into split-code-gen
lvella Jun 6, 2024
856a128
Moving global declarations to asm_translate.
lvella Jun 6, 2024
adfc41d
Doing RISC-V initializations on runtime lib, with linker support.
lvella Jun 6, 2024
d04c23f
Modify RISC-V compilation to build binaries instead of libraries.
lvella Jun 4, 2024
7ce3446
Correctly finding all the assembly files.
lvella Jun 7, 2024
890f529
Linker settings for the ELF file to be usable.
lvella Jun 7, 2024
ce334a1
Relocation table test.
lvella Jun 7, 2024
42af5f7
Removing unrelated test.
lvella Jun 7, 2024
9f509a8
Fixing lint.
lvella Jun 7, 2024
bad34de
Updating comments.
lvella Jun 7, 2024
5c703da
Bumping version because the runtime changes are incompatible.
lvella Jun 7, 2024
531e8ee
Fixing lint.
lvella Jun 7, 2024
d138007
Addressing reviews.
lvella Jun 10, 2024
7280a0c
Merge remote-tracking branch 'origin/main' into split-code-gen
lvella Jun 10, 2024
f4309de
Merge branch 'split-code-gen' into rv-init-in-runtime
lvella Jun 10, 2024
d18ae3f
Merge branch 'rv-init-in-runtime' into build-to-executable
lvella Jun 10, 2024
5ab5959
Building rust into executable instead of library.
lvella Jun 10, 2024
18989e4
Fixing test and improving comments.
lvella Jun 10, 2024
e002fcf
Merge commit '7280a0c7701581620d39ca' into elf-support
lvella Jun 10, 2024
dbfadca
Merge commit 'f4309defa9aa017c1f90b5b' into elf-support
lvella Jun 10, 2024
6b516ed
Merge branch 'build-to-executable' into elf-support
lvella Jun 10, 2024
8c5a23b
Reverting useless change.
lvella Jun 10, 2024
71c5626
Fixing comments.
lvella Jun 11, 2024
bfad2ae
Addressing review.
lvella Jun 11, 2024
69ee478
Implementing args interface.
lvella Jun 11, 2024
66060e5
Complete and compiling, but not tested.
lvella Jun 11, 2024
9d54a16
Some fixes.
lvella Jun 11, 2024
d3f3375
Fixing regression.
lvella Jun 11, 2024
6126723
Running from end-to-end, but the result is wrong.
lvella Jun 12, 2024
d54b0e4
Loading labels from text, but relocation is broken.
lvella Jun 12, 2024
29e4744
Merge remote-tracking branch 'origin/main' into rv-init-in-runtime
lvella Jun 13, 2024
e423ddb
Merge branch 'rv-init-in-runtime' into build-to-executable
lvella Jun 13, 2024
75f1740
Fix comment.
lvella Jun 13, 2024
47fecb6
Comment and error message.
lvella Jun 13, 2024
2f78019
Apparently working on function pointer example.
lvella Jun 13, 2024
c6d171d
Best so far.
lvella Jun 13, 2024
e248978
Fixing LUI.
lvella Jun 13, 2024
691413f
Commenting back unsupported instructions section.
lvella Jun 13, 2024
9960886
vec-median working
lvella Jun 13, 2024
32e8425
Implementing static relocation, too
lvella Jun 14, 2024
8b2f7f7
Small fixes and reorg.
lvella Jun 14, 2024
0576d72
Using ELF pipeline for rust tests.
lvella Jun 14, 2024
26323ef
Deduplicating symbols.
lvella Jun 14, 2024
df9c45f
Found flaw in logic lifting LUI + ADDI.
lvella Jun 14, 2024
cbf840e
Doing two passes to decide wether to merge two instructions or not.
lvella Jun 14, 2024
1f567b6
Comments improvement.
lvella Jun 14, 2024
bccfd8a
Merge branch 'main' into build-to-executable
lvella Jun 14, 2024
144bd80
Merge branch 'build-to-executable' into elf-support
lvella Jun 14, 2024
5940340
Small changes.
lvella Jun 14, 2024
04a379f
Humoring lint.
lvella Jun 14, 2024
31f365c
More clippy.
lvella Jun 14, 2024
18cac28
Update riscv/src/lib.rs
lvella Jun 17, 2024
2b67195
Merge remote-tracking branch 'origin/main' into elf-support
lvella Jul 2, 2024
2d41b75
Trying to run tests on both paths.
lvella Jul 2, 2024
a2aa483
Failing if result is unexpected.
lvella Jul 2, 2024
70b62ad
Also running assembly tests from elf.
lvella Jul 2, 2024
861f66f
Fixing running assembly tests via ELF.
lvella Jul 2, 2024
55ab22e
Merge branch 'elf-support' of github.com:powdr-labs/powdr into elf-su…
lvella Jul 2, 2024
a5f01f6
Merge remote-tracking branch 'origin/main' into elf-support
lvella Jul 2, 2024
856ac0d
Fixing argument order of amoadd.w.
lvella Jul 2, 2024
ce6b66a
Fixing assembly compilation of tests.
lvella Jul 2, 2024
d81c9c6
Using GNU Assembler with LLVM Linker.
lvella Jul 3, 2024
357b77d
Supporting auipc followed by load or store.
lvella Jul 3, 2024
bcaeaab
Detecting assembler.
lvella Jul 3, 2024
b56814d
Fixing lrsc test.
lvella Jul 3, 2024
291ebf3
Fixed dynamic relocation test.
lvella Jul 3, 2024
87f7467
I tried as much as I could not to fork raki.
lvella Jul 3, 2024
95534ba
Fixing dynamic relocation test.
lvella Jul 3, 2024
640e996
Fixing dynamic relocation test even more.
lvella Jul 3, 2024
e40b8b1
Removing debug leftover.
lvella Jul 3, 2024
205a9cb
Adding partial rvc.S test.
lvella Jul 3, 2024
1c706e9
Added command line switch to use the ELF path.
lvella Jul 3, 2024
0d36343
Install the linker and assembler in the CI VM.
lvella Jul 3, 2024
1638f1a
Documenting TwoOrOneMapper.
lvella Jul 3, 2024
41d83cb
Documented a few functions.
lvella Jul 4, 2024
490ca7b
Small improvements.
lvella Jul 4, 2024
7691764
Better wording.
lvella Jul 4, 2024
d20a613
Update riscv/src/elf.rs
lvella Jul 5, 2024
ce69fea
Grammar fix.
lvella Jul 5, 2024
7c134f7
Update riscv/src/elf.rs
lvella Jul 5, 2024
750d569
Addressing lots of reviews.
lvella Jul 5, 2024
6be0212
Merge branch 'elf-support' of github.com:powdr-labs/powdr into elf-su…
lvella Jul 5, 2024
0c46213
Update riscv/src/elf.rs
lvella Jul 8, 2024
799aa23
More review addressing.
lvella Jul 8, 2024
c2743ed
Merge branch 'elf-support' of github.com:powdr-labs/powdr into elf-su…
lvella Jul 8, 2024
d753b58
Translating from assembly by default.
lvella Jul 8, 2024
d36cf75
Using `is_executable`.
lvella Jul 8, 2024
f2b8c34
Placing private below pub.
lvella Jul 8, 2024
cc0b5ed
Reverting itertools to 0.12.
lvella Jul 8, 2024
e3cce8f
Merge branch 'main' into elf-support
lvella Jul 8, 2024
42cf0cc
Update riscv/tests/instruction_tests/README.md
lvella Jul 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions cli-rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use powdr_pipeline::Pipeline;
use powdr_riscv_executor::ProfilerOptions;

use std::ffi::OsStr;
use std::io;
use std::{borrow::Cow, io::Write, path::Path};
use std::{fs, io};
use strum::{Display, EnumString, EnumVariantNames};

#[derive(Clone, EnumString, EnumVariantNames, Display)]
Expand Down Expand Up @@ -63,9 +63,9 @@ enum Commands {
coprocessors: Option<String>,

/// Convert from the executable ELF file instead of the assembly.
#[arg(short = 'e', long)]
#[arg(short, long)]
#[arg(default_value_t = false)]
via_elf: bool,
elf: bool,

/// Run a long execution in chunks (Experimental and not sound!)
#[arg(short, long)]
Expand Down Expand Up @@ -224,14 +224,14 @@ fn run_command(command: Commands) {
field,
output_directory,
coprocessors,
via_elf,
elf,
continuations,
} => {
call_with_field!(compile_rust::<field>(
&file,
Path::new(&output_directory),
coprocessors,
via_elf,
elf,
continuations
))
}
Expand Down Expand Up @@ -359,9 +359,14 @@ fn compile_riscv_asm<F: FieldElement>(
None => powdr_riscv::Runtime::base(),
};

powdr_riscv::compile_riscv_asm::<F>(
powdr_riscv::compile_riscv_asm_bundle::<F>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is elf still the default compilation path here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no ELF path in this function, this only from assembly.

original_file_name,
file_names,
file_names
.map(|name| {
let contents = fs::read_to_string(&name).unwrap();
(name, contents)
})
.collect(),
output_dir,
true,
&runtime,
Expand Down
2 changes: 2 additions & 0 deletions riscv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ lalrpop-util = { version = "^0.19", features = ["lexer"] }
log = "0.4.17"
mktemp = "0.5.0"
num-traits = "0.2.15"
# Use the patched version of raki until the fix is merged.
# Fixes the name of "mulhsu" instruction.
raki = { git = "https://github.com/powdr-labs/raki.git", branch = "patch-1" }
pacheco marked this conversation as resolved.
Show resolved Hide resolved
leonardoalt marked this conversation as resolved.
Show resolved Hide resolved
serde_json = "1.0"
# This is only here to work around https://github.com/lalrpop/lalrpop/issues/750
Expand Down
5 changes: 2 additions & 3 deletions riscv/src/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,8 @@ impl InstructionArgs for &[Argument] {

fn rr(&self) -> Result<(Register, Register), &'static str> {
match self {
[Argument::Register(r1), Argument::Register(r2) | Argument::RegOffset(None, r2)] => {
Ok((*r1, *r2))
}
[Argument::Register(r1), Argument::Register(r2)
| Argument::RegOffset(None | Some(Expression::Number(0)), r2)] => Ok((*r1, *r2)),
_ => Err("Expected: register, register"),
}
}
Expand Down
83 changes: 36 additions & 47 deletions riscv/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use goblin::{
elf::sym::STT_OBJECT,
elf::{
header::{EI_CLASS, EI_DATA, ELFCLASS32, ELFDATA2LSB, EM_RISCV, ET_DYN},
program_header,
program_header::PT_LOAD,
reloc::{R_RISCV_32, R_RISCV_HI20, R_RISCV_RELATIVE},
sym::STT_FUNC,
Elf,
Elf, ProgramHeader,
},
};
use itertools::{Either, Itertools};
Expand All @@ -30,16 +30,8 @@ use crate::{
Runtime,
};

struct ElfProgram {
symbol_table: SymbolTable,
data_map: BTreeMap<u32, Data>,
text_labels: BTreeSet<u32>,
instructions: Vec<HighLevelInsn>,
entry_point: u32,
}

/// Generates a Powdr Assembly program from a RISC-V 32 executable ELF file.
pub fn elf_translate<F: FieldElement>(
pub fn translate<F: FieldElement>(
file_name: &Path,
runtime: &Runtime,
with_bootloader: bool,
Expand All @@ -48,6 +40,14 @@ pub fn elf_translate<F: FieldElement>(
code_gen::translate_program::<F>(elf_program, runtime, with_bootloader)
}

struct ElfProgram {
symbol_table: SymbolTable,
data_map: BTreeMap<u32, Data>,
text_labels: BTreeSet<u32>,
instructions: Vec<HighLevelInsn>,
entry_point: u32,
}

fn load_elf(file_name: &Path) -> ElfProgram {
log::info!("Loading ELF file: {}", file_name.display());
let file_buffer = fs::read(file_name).unwrap();
Expand Down Expand Up @@ -85,7 +85,7 @@ fn load_elf(file_name: &Path) -> ElfProgram {
let address_map = AddressMap(
elf.program_headers
.iter()
.filter(|p| p.p_type == program_header::PT_LOAD)
.filter(|p| p.p_type == PT_LOAD)
.map(|p| (p.p_vaddr as u32, p))
.collect(),
);
Expand All @@ -108,8 +108,7 @@ fn load_elf(file_name: &Path) -> ElfProgram {
for (&addr, &p) in address_map.0.iter() {
let section_data = &file_buffer[p.p_offset as usize..(p.p_offset + p.p_filesz) as usize];

// Test if executable
if p.p_flags & 1 == 1 {
if p.is_executable() {
search_text_addrs(
addr,
section_data,
Expand Down Expand Up @@ -137,7 +136,7 @@ fn load_elf(file_name: &Path) -> ElfProgram {

// Load all the text sections.
let mut lifted_text_sections = Vec::new();
for (&addr, &p) in address_map.0.iter().filter(|(_, p)| p.p_flags & 1 == 1) {
for (&addr, &p) in address_map.0.iter().filter(|(_, p)| p.is_executable()) {
let section_data = &file_buffer[p.p_offset as usize..(p.p_offset + p.p_filesz) as usize];
let insns = lift_instructions(
addr,
Expand Down Expand Up @@ -241,9 +240,10 @@ struct SymbolTable(HashMap<u32, String>);

impl SymbolTable {
fn new(elf: &Elf) -> SymbolTable {
// TODO: read the symbols from the debug information to be more comprehensive.
let mut deduplicator = HashMap::new();
for sym in elf.syms.iter() {
// We only care about global symbols that have string names, and are
// either functions or variables.
if sym.st_name == 0 || (sym.st_type() != STT_OBJECT && sym.st_type() != STT_FUNC) {
leonardoalt marked this conversation as resolved.
Show resolved Hide resolved
continue;
}
Expand All @@ -270,11 +270,6 @@ impl SymbolTable {
.map(|name| Cow::Borrowed(name.as_str()))
.unwrap_or_else(|| Cow::Owned(format!("L{addr:08x}")))
}

/// Get the symbol or a default label formed from the address value.
fn get_as_string(&self, addr: u32) -> String {
self.get(addr).into_owned()
}
}

impl RiscVProgram for ElfProgram {
Expand All @@ -287,7 +282,7 @@ impl RiscVProgram for ElfProgram {
self.data_map.iter().map(|(addr, data)| {
let value = match data {
Data::TextLabel(label) => {
SingleDataValue::LabelReference(self.symbol_table.get_as_string(*label))
SingleDataValue::LabelReference(self.symbol_table.get(*label).into())
}
Data::Value(value) => SingleDataValue::Value(*value),
};
Expand Down Expand Up @@ -348,7 +343,7 @@ impl<'a> InstructionArgs for WrappedArgs<'a> {
rd: None,
rs1: None,
rs2: None,
} => Ok(self.symbol_table.get_as_string(*addr)),
} => Ok(self.symbol_table.get(*addr).into()),
_ => Err(format!("Expected: label, got {:?}", self.args)),
}
}
Expand Down Expand Up @@ -447,7 +442,7 @@ impl<'a> InstructionArgs for WrappedArgs<'a> {
} => Ok((
Register::new(*rs1 as u8),
Register::new(*rs2 as u8),
self.symbol_table.get_as_string(*addr),
self.symbol_table.get(*addr).into(),
)),
_ => Err(format!("Expected: rs1, rs2, label, got {:?}", self.args)),
}
Expand All @@ -462,7 +457,7 @@ impl<'a> InstructionArgs for WrappedArgs<'a> {
rs2: None,
} => Ok((
Register::new(*rs1 as u8),
self.symbol_table.get_as_string(*addr),
self.symbol_table.get(*addr).into(),
)),
HighLevelArgs {
imm: HighLevelImmediate::CodeLabel(addr),
Expand All @@ -471,7 +466,7 @@ impl<'a> InstructionArgs for WrappedArgs<'a> {
rs2: None,
} => Ok((
Register::new(*rd as u8),
self.symbol_table.get_as_string(*addr),
self.symbol_table.get(*addr).into(),
)),
_ => Err(format!("Expected: {{rs1|rd}}, label, got {:?}", self.args)),
}
Expand Down Expand Up @@ -522,26 +517,20 @@ impl<'a> InstructionArgs for WrappedArgs<'a> {
/// Indexes the program sections by their virtual address.
///
/// Allows for querying if an address is in a data or text section.
struct AddressMap<'a>(BTreeMap<u32, &'a program_header::ProgramHeader>);
struct AddressMap<'a>(BTreeMap<u32, &'a ProgramHeader>);

impl AddressMap<'_> {
fn is_in_data_section(&self, addr: u32) -> bool {
if let Some(section) = self.get_section_of_addr(addr) {
section.p_flags & 1 != 1
} else {
false
}
self.get_section_of_addr(addr)
.map_or(false, |section| !section.is_executable())
}

fn is_in_text_section(&self, addr: u32) -> bool {
if let Some(section) = self.get_section_of_addr(addr) {
section.p_flags & 1 == 1
} else {
false
}
self.get_section_of_addr(addr)
.map_or(false, ProgramHeader::is_executable)
}

fn get_section_of_addr(&self, addr: u32) -> Option<&program_header::ProgramHeader> {
fn get_section_of_addr(&self, addr: u32) -> Option<&ProgramHeader> {
// Get the latest section that starts before the address.
let section = self
.0
Expand Down Expand Up @@ -710,8 +699,8 @@ impl TwoOrOneMapper<MaybeInstruction, HighLevelInsn> for InstructionLifter<'_> {
) if rd_lui == rs1_addi => {
// Sometimes, in non-PIE code, this pair of instructions is used
// to load an address into a register. We must check if this is
// the case, and if the address points to the text section, so
// we must load it from a label.
// the case, and if the address points to a text section, we
// must load it from a label.
let is_address = self.rellocs_set.contains(&original_address);
let (op, args) =
self.composed_immediate(*hi, *lo, *rd_lui, *rd_addi, insn2_addr, is_address)?;
Expand All @@ -723,7 +712,7 @@ impl TwoOrOneMapper<MaybeInstruction, HighLevelInsn> for InstructionLifter<'_> {
}
}
(
// All other double instructions we can lift starts with auipc.
// All other double instructions we can lift start with auipc.
Ins {
opc: Op::AUIPC,
rd: Some(rd_auipc),
Expand Down Expand Up @@ -865,7 +854,7 @@ impl TwoOrOneMapper<MaybeInstruction, HighLevelInsn> for InstructionLifter<'_> {
};

let mut imm = match insn.opc {
// All jump instructions that have the immediate as an address
// All jump instructions that have an address as immediate
Op::JAL | Op::BEQ | Op::BNE | Op::BLT | Op::BGE | Op::BLTU | Op::BGEU => {
let addr = (insn.imm.unwrap() + original_address as i32) as u32;
if let ReadOrWrite::Write(refs) = &mut self.referenced_text_addrs {
Expand Down Expand Up @@ -909,9 +898,9 @@ impl TwoOrOneMapper<MaybeInstruction, HighLevelInsn> for InstructionLifter<'_> {
},
};

// For some reason, atomic instructions comes with the immediate set to
// zero instead of None (maybe to mimic assembly syntax? Who knows). Fix
// this.
// For some reason, atomic instructions come with the immediate set to
// zero instead of None (maybe to mimic assembly syntax? Who knows). We
// must fix this:
if let Extensions::A = insn.extension {
assert!(matches!(imm, HighLevelImmediate::Value(0)));
imm = HighLevelImmediate::None;
Expand Down Expand Up @@ -1010,7 +999,7 @@ impl Iterator for RiscVInstructionIterator<'_> {
.decode(Isa::Rv32)
.unwrap_or_else(|_| {
// TODO: maybe instead of failing we should just emit `unimp`.
// This way we would support the garbage default GNU binutils
// This way we would support the default GNU binutils
// linker script, that places the ELF header in the text
// section.
panic!(
Expand Down Expand Up @@ -1042,7 +1031,7 @@ impl Iterator for RiscVInstructionIterator<'_> {
// Otherwise, there is something more fishy going on, and we
// panic.

// TODO: we should just emit `unimp` for every unknown.
// TODO: maybe we should just emit `unimp` for every unknown.
assert_eq!(
bin_instruction, 0,
"Failed to decode 16-bit instruction at {:08x}",
Expand Down
Loading
Loading