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

Feature - Assembler directives #569

Merged
merged 1 commit into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 20 additions & 2 deletions src/asm_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ pub enum Operand {
pub enum Statement {
/// Parsed label (name).
Label { name: String },
/// Parsed directive (name, operands).
Directive {
name: String,
operands: Vec<Operand>,
},
/// Parsed instruction (name, operands).
Instruction {
name: String,
Expand Down Expand Up @@ -105,6 +110,14 @@ parser! {
}
}

parser! {
fn directive[I]()(I) -> Statement where [I: Stream<Item=char>] {
let operands = sep_by(operand(), char(',').skip(skip_many(char(' '))));
(char('.').with(many1(alpha_num())).skip(skip_many(char(' '))), operands)
.map(|t| Statement::Directive { name: t.0, operands: t.1 })
}
}

parser! {
fn instruction[I]()(I) -> Statement where [I: Stream<Item=char>] {
let operands = sep_by(operand(), char(',').skip(skip_many(char(' '))));
Expand Down Expand Up @@ -150,7 +163,12 @@ fn format_parse_error(parse_error: &Errors<char, &str, SourcePosition>) -> Strin
/// The instructions are not validated and may have invalid names and operand types.
pub fn parse(input: &str) -> Result<Vec<Statement>, String> {
match spaces()
.with(many(attempt(label()).or(instruction()).skip(spaces())))
.with(many(
attempt(label())
.or(directive())
.or(instruction())
.skip(spaces()),
))
.skip(eof())
.easy_parse(State::with_positioner(input, SourcePosition::default()))
{
Expand Down Expand Up @@ -648,7 +666,7 @@ exit
assert_eq!(
parse("exit\n^"),
Err(
"Parse error at line 2 column 1: unexpected '^', expected letter or digit, expected \'_\', expected whitespaces, expected end of input".to_string()
"Parse error at line 2 column 1: unexpected '^', expected letter or digit, expected '_', expected '.', expected whitespaces, expected end of input".to_string()
)
);
}
Expand Down
221 changes: 128 additions & 93 deletions src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,16 @@ pub fn assemble<C: ContextObject>(
}
labels.insert(name.as_str(), insn_ptr);
}
Statement::Directive { name, operands } =>
{
#[allow(clippy::single_match)]
match (name.as_str(), operands.as_slice()) {
("fill", [Integer(repeat), Integer(_value)]) => {
insn_ptr += *repeat as usize;
}
_ => {}
}
}
Statement::Instruction { name, .. } => {
insn_ptr += if name == "lddw" { 2 } else { 1 };
}
Expand All @@ -335,106 +345,131 @@ pub fn assemble<C: ContextObject>(
insn_ptr = 0;
let mut instructions: Vec<Insn> = Vec::new();
for statement in statements.iter() {
if let Statement::Instruction { name, operands } = statement {
let name = name.as_str();
match instruction_map.get(name) {
Some(&(inst_type, opc)) => {
let mut insn = match (inst_type, operands.as_slice()) {
(AluBinary, [Register(dst), Register(src)]) => {
insn(opc | ebpf::BPF_X, *dst, *src, 0, 0)
}
(AluBinary, [Register(dst), Integer(imm)]) => {
insn(opc | ebpf::BPF_K, *dst, 0, 0, *imm)
}
(AluUnary, [Register(dst)]) => insn(opc, *dst, 0, 0, 0),
(LoadAbs, [Integer(imm)]) => insn(opc, 0, 0, 0, *imm),
(LoadInd, [Register(src), Integer(imm)]) => insn(opc, 0, *src, 0, *imm),
(LoadReg, [Register(dst), Memory(src, off)])
| (StoreReg, [Memory(dst, off), Register(src)]) => {
insn(opc, *dst, *src, *off, 0)
}
(StoreImm, [Memory(dst, off), Integer(imm)]) => {
insn(opc, *dst, 0, *off, *imm)
}
(NoOperand, []) => insn(opc, 0, 0, 0, 0),
(JumpUnconditional, [Integer(off)]) => insn(opc, 0, 0, *off, 0),
(JumpConditional, [Register(dst), Register(src), Integer(off)]) => {
insn(opc | ebpf::BPF_X, *dst, *src, *off, 0)
}
(JumpConditional, [Register(dst), Integer(imm), Integer(off)]) => {
insn(opc | ebpf::BPF_K, *dst, 0, *off, *imm)
}
(JumpUnconditional, [Label(label)]) => {
insn(opc, 0, 0, resolve_label(insn_ptr, &labels, label)?, 0)
}
(CallImm, [Integer(imm)]) => {
let target_pc = *imm + insn_ptr as i64 + 1;
let label = format!("function_{}", target_pc as usize);
function_registry
.register_function(
target_pc as u32,
label.as_bytes(),
target_pc as usize,
)
.map_err(|_| format!("Label hash collision {name}"))?;
insn(opc, 0, 1, 0, target_pc)
}
(CallReg, [Register(dst)]) => {
if sbpf_version.callx_uses_src_reg() {
insn(opc, 0, *dst, 0, 0)
} else {
insn(opc, 0, 0, 0, *dst)
}
}
(JumpConditional, [Register(dst), Register(src), Label(label)]) => insn(
opc | ebpf::BPF_X,
*dst,
*src,
resolve_label(insn_ptr, &labels, label)?,
0,
),
(JumpConditional, [Register(dst), Integer(imm), Label(label)]) => insn(
opc | ebpf::BPF_K,
*dst,
0,
resolve_label(insn_ptr, &labels, label)?,
*imm,
),
(Syscall, [Label(label)]) => insn(
opc,
0,
0,
0,
ebpf::hash_symbol_name(label.as_bytes()) as i32 as i64,
),
(CallImm, [Label(label)]) => {
let label: &str = label;
let target_pc = *labels
.get(label)
.ok_or_else(|| format!("Label not found {label}"))?;
insn(opc, 0, 1, 0, target_pc as i64)
}
(Endian(size), [Register(dst)]) => insn(opc, *dst, 0, 0, size),
(LoadDwImm, [Register(dst), Integer(imm)]) => {
insn(opc, *dst, 0, 0, (*imm << 32) >> 32)
}
_ => Err(format!("Unexpected operands: {operands:?}")),
}?;
insn.ptr = insn_ptr;
instructions.push(insn);
insn_ptr += 1;
if let LoadDwImm = inst_type {
if let Integer(imm) = operands[1] {
match statement {
Statement::Label { .. } => {}
Statement::Directive { name, operands } =>
{
#[allow(clippy::single_match)]
match (name.as_str(), operands.as_slice()) {
("fill", [Integer(repeat), Integer(value)]) => {
for _ in 0..*repeat {
instructions.push(Insn {
ptr: insn_ptr,
imm: imm >> 32,
..Insn::default()
opc: *value as u8,
dst: (*value >> 8) as u8 & 0xF,
src: (*value >> 12) as u8 & 0xF,
off: (*value >> 16) as u16 as i16,
imm: (*value >> 32) as u32 as i64,
});
insn_ptr += 1;
}
}
_ => return Err(format!("Invalid directive {name:?}")),
}
}
Statement::Instruction { name, operands } => {
let name = name.as_str();
match instruction_map.get(name) {
Some(&(inst_type, opc)) => {
let mut insn = match (inst_type, operands.as_slice()) {
(AluBinary, [Register(dst), Register(src)]) => {
insn(opc | ebpf::BPF_X, *dst, *src, 0, 0)
}
(AluBinary, [Register(dst), Integer(imm)]) => {
insn(opc | ebpf::BPF_K, *dst, 0, 0, *imm)
}
(AluUnary, [Register(dst)]) => insn(opc, *dst, 0, 0, 0),
(LoadAbs, [Integer(imm)]) => insn(opc, 0, 0, 0, *imm),
(LoadInd, [Register(src), Integer(imm)]) => insn(opc, 0, *src, 0, *imm),
(LoadReg, [Register(dst), Memory(src, off)])
| (StoreReg, [Memory(dst, off), Register(src)]) => {
insn(opc, *dst, *src, *off, 0)
}
(StoreImm, [Memory(dst, off), Integer(imm)]) => {
insn(opc, *dst, 0, *off, *imm)
}
(NoOperand, []) => insn(opc, 0, 0, 0, 0),
(JumpUnconditional, [Integer(off)]) => insn(opc, 0, 0, *off, 0),
(JumpConditional, [Register(dst), Register(src), Integer(off)]) => {
insn(opc | ebpf::BPF_X, *dst, *src, *off, 0)
}
(JumpConditional, [Register(dst), Integer(imm), Integer(off)]) => {
insn(opc | ebpf::BPF_K, *dst, 0, *off, *imm)
}
(JumpUnconditional, [Label(label)]) => {
insn(opc, 0, 0, resolve_label(insn_ptr, &labels, label)?, 0)
}
(CallImm, [Integer(imm)]) => {
let target_pc = *imm + insn_ptr as i64 + 1;
let label = format!("function_{}", target_pc as usize);
function_registry
.register_function(
target_pc as u32,
label.as_bytes(),
target_pc as usize,
)
.map_err(|_| format!("Label hash collision {name}"))?;
insn(opc, 0, 1, 0, target_pc)
}
(CallReg, [Register(dst)]) => {
if sbpf_version.callx_uses_src_reg() {
insn(opc, 0, *dst, 0, 0)
} else {
insn(opc, 0, 0, 0, *dst)
}
}
(JumpConditional, [Register(dst), Register(src), Label(label)]) => {
insn(
opc | ebpf::BPF_X,
*dst,
*src,
resolve_label(insn_ptr, &labels, label)?,
0,
)
}
(JumpConditional, [Register(dst), Integer(imm), Label(label)]) => insn(
opc | ebpf::BPF_K,
*dst,
0,
resolve_label(insn_ptr, &labels, label)?,
*imm,
),
(Syscall, [Label(label)]) => insn(
opc,
0,
0,
0,
ebpf::hash_symbol_name(label.as_bytes()) as i32 as i64,
),
(CallImm, [Label(label)]) => {
let label: &str = label;
let target_pc = *labels
.get(label)
.ok_or_else(|| format!("Label not found {label}"))?;
insn(opc, 0, 1, 0, target_pc as i64)
}
(Endian(size), [Register(dst)]) => insn(opc, *dst, 0, 0, size),
(LoadDwImm, [Register(dst), Integer(imm)]) => {
insn(opc, *dst, 0, 0, (*imm << 32) >> 32)
}
_ => Err(format!("Unexpected operands: {operands:?}")),
}?;
insn.ptr = insn_ptr;
instructions.push(insn);
insn_ptr += 1;
if let LoadDwImm = inst_type {
if let Integer(imm) = operands[1] {
instructions.push(Insn {
ptr: insn_ptr,
imm: imm >> 32,
..Insn::default()
});
insn_ptr += 1;
}
}
}
None => return Err(format!("Invalid instruction {name:?}")),
}
None => return Err(format!("Invalid instruction {name:?}")),
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions tests/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ fn test_empty() {
assert_eq!(asm(""), Ok(vec![]));
}

#[test]
fn test_fill() {
assert_eq!(
asm(".fill 2, 0x210F"),
Ok(vec![
insn(0, ebpf::ADD64_REG, 1, 2, 0, 0),
insn(1, ebpf::ADD64_REG, 1, 2, 0, 0)
])
);
}

// Example for InstructionType::NoOperand.
#[test]
fn test_exit() {
Expand Down