diff --git a/src/asm_parser.rs b/src/asm_parser.rs index b006b8329..391d4f668 100644 --- a/src/asm_parser.rs +++ b/src/asm_parser.rs @@ -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, + }, /// Parsed instruction (name, operands). Instruction { name: String, @@ -105,6 +110,14 @@ parser! { } } +parser! { + fn directive[I]()(I) -> Statement where [I: Stream] { + 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] { let operands = sep_by(operand(), char(',').skip(skip_many(char(' ')))); @@ -150,7 +163,12 @@ fn format_parse_error(parse_error: &Errors) -> Strin /// The instructions are not validated and may have invalid names and operand types. pub fn parse(input: &str) -> Result, 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())) { @@ -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() ) ); } diff --git a/src/assembler.rs b/src/assembler.rs index ff124b700..01b802a07 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -327,6 +327,12 @@ pub fn assemble( } labels.insert(name.as_str(), insn_ptr); } + Statement::Directive { name, operands } => 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 }; } @@ -335,106 +341,127 @@ pub fn assemble( insn_ptr = 0; let mut instructions: Vec = 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, + match statement { + Statement::Label { .. } => {} + Statement::Directive { name, operands } => match (name.as_str(), operands.as_slice()) { + ("fill", [Integer(repeat), Integer(value)]) => { + for _ in 0..*repeat { + instructions.push(Insn { + ptr: insn_ptr, + 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, ) - .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; + (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:?}")), } } } diff --git a/tests/assembler.rs b/tests/assembler.rs index 86f929219..4020d6cde 100644 --- a/tests/assembler.rs +++ b/tests/assembler.rs @@ -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() {