1pub mod common;
2mod default;
3mod directive;
4mod llvm;
5
6use {
7 crate::{
8 SbpfArch,
9 ast::AST,
10 astnode::{ASTNode, Label},
11 dynsym::{DynamicSymbolMap, RelDynMap},
12 errors::CompileError,
13 section::{CodeSection, DataSection, DebugSection},
14 },
15 directive::{process_directive_statement, process_rodata_directive},
16 pest::{
17 Parser,
18 error::{ErrorVariant, InputLocation},
19 iterators::Pair,
20 },
21 pest_derive::Parser,
22 sbpf_common::{inst_param::Number, instruction::Instruction},
23 std::collections::HashMap,
24};
25
26#[derive(Parser)]
27#[grammar = "sbpf.pest"]
28pub struct SbpfParser;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub(crate) enum Section {
33 Text,
34 Rodata,
35}
36
37pub(crate) struct ParseContext<'a> {
39 pub ast: &'a mut AST,
40 pub const_map: &'a mut HashMap<String, Number>,
41 pub label_spans: &'a mut HashMap<String, std::ops::Range<usize>>,
42 pub label_offset_map: &'a mut HashMap<String, (Number, Section)>,
43 pub errors: Vec<CompileError>,
44 pub rodata_phase: bool,
45 pub text_offset: u64,
46 pub rodata_offset: u64,
47 pub missing_text_directive: bool,
48 pub pending_rodata_label: Option<(String, std::ops::Range<usize>)>,
50}
51
52const BPF_X: u8 = 0x08;
54
55#[derive(Debug, Clone)]
57pub enum Token {
58 Directive(String, std::ops::Range<usize>),
59 Identifier(String, std::ops::Range<usize>),
60 ImmediateValue(Number, std::ops::Range<usize>),
61 StringLiteral(String, std::ops::Range<usize>),
62 VectorLiteral(Vec<Number>, std::ops::Range<usize>),
63}
64
65pub struct ParseResult {
66 pub code_section: CodeSection,
68
69 pub data_section: DataSection,
70
71 pub dynamic_symbols: DynamicSymbolMap,
72
73 pub relocation_data: RelDynMap,
74
75 pub prog_is_static: bool,
78
79 pub arch: SbpfArch,
80
81 pub debug_sections: Vec<DebugSection>,
83}
84
85pub fn parse(source: &str, arch: SbpfArch) -> Result<ParseResult, Vec<CompileError>> {
86 let pairs = SbpfParser::parse(Rule::program, source).map_err(|e| {
87 let span = match e.location {
90 InputLocation::Pos(pos) => pos..pos + 1,
91 InputLocation::Span((start, end)) => start..end,
92 };
93
94 let message = match &e.variant {
97 ErrorVariant::ParsingError {
98 positives,
99 negatives,
100 } => {
101 let pos: Vec<String> = positives.iter().filter_map(rule_display_name).collect();
102 let neg: Vec<String> = negatives.iter().filter_map(rule_display_name).collect();
103 let mut parts = Vec::new();
104 if !pos.is_empty() {
105 parts.push(format!("expected {}", pos.join(", ")));
106 }
107 if !neg.is_empty() {
108 parts.push(format!("unexpected {}", neg.join(", ")));
109 }
110 if parts.is_empty() {
111 "Parse error".to_string()
112 } else {
113 parts.join("; ")
114 }
115 }
116 ErrorVariant::CustomError { message } => message.clone(),
117 };
118
119 vec![CompileError::ParseError {
120 error: message,
121 span,
122 custom_label: None,
123 }]
124 })?;
125
126 let mut ast = AST::new();
127 let mut const_map = HashMap::<String, Number>::new();
128 let mut label_spans = HashMap::<String, std::ops::Range<usize>>::new();
129
130 let pairs_clone = pairs.clone();
132 let mut label_offset_map = collect_label_offsets(pairs_clone);
133
134 let (text_offset, rodata_offset, errors) = {
136 let mut ctx = ParseContext {
137 ast: &mut ast,
138 const_map: &mut const_map,
139 label_spans: &mut label_spans,
140 label_offset_map: &mut label_offset_map,
141 errors: Vec::new(),
142 rodata_phase: false,
143 text_offset: 0,
144 rodata_offset: 0,
145 missing_text_directive: false,
146 pending_rodata_label: None,
147 };
148
149 for pair in pairs {
150 match pair.as_rule() {
151 Rule::program_default | Rule::program_llvm => {
152 for statement in pair.into_inner() {
153 if statement.as_rule() == Rule::EOI {
154 continue;
155 }
156 process_statement(statement, &mut ctx);
157 }
158 }
159 _ => {}
160 }
161 }
162
163 (ctx.text_offset, ctx.rodata_offset, ctx.errors)
164 };
165
166 if !errors.is_empty() {
167 return Err(errors);
168 }
169
170 ast.set_text_size(text_offset);
171 ast.set_rodata_size(rodata_offset);
172
173 ast.build_program(arch)
174}
175
176fn collect_label_offsets(
180 pairs: pest::iterators::Pairs<Rule>,
181) -> HashMap<String, (Number, Section)> {
182 let mut map = HashMap::new();
183 let mut rodata_phase = false;
184 let mut text_offset: u64 = 0;
185 let mut rodata_offset: u64 = 0;
186
187 for pair in pairs {
188 match pair.as_rule() {
189 Rule::program_default | Rule::program_llvm => {
190 for statement in pair.into_inner() {
191 if statement.as_rule() == Rule::EOI {
192 continue;
193 }
194 scan_statement_for_labels(
195 statement,
196 &mut map,
197 &mut rodata_phase,
198 &mut text_offset,
199 &mut rodata_offset,
200 );
201 }
202 }
203 _ => {}
204 }
205 }
206 map
207}
208
209fn scan_statement_for_labels(
211 pair: Pair<Rule>,
212 map: &mut HashMap<String, (Number, Section)>,
213 rodata_phase: &mut bool,
214 text_offset: &mut u64,
215 rodata_offset: &mut u64,
216) {
217 for inner in pair.into_inner() {
218 match inner.as_rule() {
219 Rule::label_default | Rule::label_llvm => {
220 scan_label(inner, map, rodata_phase, text_offset, rodata_offset);
221 }
222 Rule::directive => {
223 for dir_inner in inner.into_inner() {
225 let dir_inner_clone = dir_inner.clone();
226 for dir_item in dir_inner.into_inner() {
227 if dir_item.as_rule() == Rule::directive_section {
228 let section_name = dir_item.as_str().trim_start_matches('.');
229 match section_name {
230 "text" => *rodata_phase = false,
231 "rodata" => *rodata_phase = true,
232 _ => {}
233 }
234 } else if *rodata_phase {
235 match dir_item.as_rule() {
237 Rule::directive_ascii
238 | Rule::directive_byte
239 | Rule::directive_short
240 | Rule::directive_word
241 | Rule::directive_int
242 | Rule::directive_long
243 | Rule::directive_quad => {
244 *rodata_offset += rodata_directive_size(&dir_inner_clone);
245 }
246 _ => {}
247 }
248 }
249 }
250 }
251 }
252 Rule::instr_default | Rule::instr_llvm if !*rodata_phase => {
253 let size = instr_size(&inner);
254 *text_offset += size;
255 }
256 _ => {}
257 }
258 }
259}
260
261fn scan_label(
264 pair: Pair<Rule>,
265 map: &mut HashMap<String, (Number, Section)>,
266 rodata_phase: &mut bool,
267 text_offset: &mut u64,
268 rodata_offset: &mut u64,
269) {
270 let mut label_name = None;
271
272 for item in pair.into_inner() {
273 match item.as_rule() {
274 Rule::identifier | Rule::numeric_label => {
275 label_name = Some(item.as_str().to_string());
276 }
277 Rule::directive_inner => {
278 if *rodata_phase {
280 if let Some(ref name) = label_name {
281 map.insert(
282 name.clone(),
283 (Number::Int(*rodata_offset as i64), Section::Rodata),
284 );
285 }
286 let size = rodata_directive_size(&item);
287 *rodata_offset += size;
288 }
289 return;
290 }
291 Rule::instr_default | Rule::instr_llvm => {
292 if !*rodata_phase {
293 if let Some(ref name) = label_name {
294 map.insert(
295 name.clone(),
296 (Number::Int(*text_offset as i64), Section::Text),
297 );
298 }
299 let size = instr_size(&item);
300 *text_offset += size;
301 }
302 return;
303 }
304 _ => {}
305 }
306 }
307
308 if let Some(name) = label_name {
310 if *rodata_phase {
311 map.insert(name, (Number::Int(*rodata_offset as i64), Section::Rodata));
312 } else {
313 map.insert(name, (Number::Int(*text_offset as i64), Section::Text));
314 }
315 }
316}
317
318fn instr_size(pair: &Pair<Rule>) -> u64 {
320 for inner in pair.clone().into_inner() {
321 match inner.as_rule() {
322 Rule::instr_lddw | Rule::instr_llvm_lddw => return 16,
323 _ => {}
324 }
325 }
326 8
327}
328
329fn rodata_directive_size(pair: &Pair<Rule>) -> u64 {
331 for inner in pair.clone().into_inner() {
332 match inner.as_rule() {
333 Rule::directive_ascii => {
334 for ascii_inner in inner.into_inner() {
335 if ascii_inner.as_rule() == Rule::string_literal {
336 for content in ascii_inner.into_inner() {
337 if content.as_rule() == Rule::string_content {
338 return content.as_str().len() as u64;
339 }
340 }
341 }
342 }
343 }
344 Rule::directive_byte => {
345 return inner
346 .into_inner()
347 .filter(|p| p.as_rule() == Rule::number)
348 .count() as u64;
349 }
350 Rule::directive_short | Rule::directive_word => {
351 return inner
352 .into_inner()
353 .filter(|p| p.as_rule() == Rule::number)
354 .count() as u64
355 * 2;
356 }
357 Rule::directive_int | Rule::directive_long => {
358 return inner
359 .into_inner()
360 .filter(|p| p.as_rule() == Rule::number)
361 .count() as u64
362 * 4;
363 }
364 Rule::directive_quad => {
365 return inner
366 .into_inner()
367 .filter(|p| p.as_rule() == Rule::number)
368 .count() as u64
369 * 8;
370 }
371 _ => {}
372 }
373 }
374 0
375}
376
377fn rule_display_name(rule: &Rule) -> Option<String> {
379 let name = match rule {
380 Rule::program_default | Rule::program_llvm => return None,
382 Rule::statement_default | Rule::statement_llvm => "statement",
383 Rule::label_default | Rule::label_llvm => "label",
384
385 Rule::directive | Rule::directive_inner => "directive",
387 Rule::directive_globl => ".globl",
388 Rule::directive_extern => ".extern",
389 Rule::directive_equ => ".equ",
390 Rule::directive_section => "section (.text, .rodata)",
391 Rule::directive_ascii => ".ascii",
392 Rule::directive_byte => ".byte",
393 Rule::directive_short => ".short",
394 Rule::directive_word => ".word",
395 Rule::directive_int => ".int",
396 Rule::directive_long => ".long",
397 Rule::directive_quad => ".quad",
398
399 Rule::instr_default | Rule::instr_llvm => "instruction",
401 Rule::instr_lddw | Rule::instr_llvm_lddw => "lddw",
402 Rule::instr_call => "call",
403 Rule::instr_callx => "callx",
404 Rule::instr_exit => "exit",
405
406 Rule::register => "register",
408 Rule::operand => "operand",
409 Rule::number => "number",
410 Rule::symbol => "symbol",
411 Rule::identifier => "identifier",
412 Rule::expression => "expression",
413 Rule::string_literal => "string literal",
414
415 Rule::memory_ref | Rule::llvm_memory_ref => "memory reference",
417 Rule::jump_target => "jump target",
418
419 Rule::EOI => "end of input",
421 _ => return Some(format!("{:?}", rule)),
422 };
423 Some(name.to_string())
424}
425
426fn process_statement(pair: Pair<Rule>, ctx: &mut ParseContext) {
427 for inner in pair.into_inner() {
428 match inner.as_rule() {
429 Rule::label_default | Rule::label_llvm => {
430 process_label(inner, ctx);
431 }
432 Rule::directive => {
433 process_directive_statement(inner, ctx);
434 }
435 Rule::instr_default | Rule::instr_llvm => {
436 let span = inner.as_span();
437 let span_range = span.start()..span.end();
438 let is_llvm = inner.as_rule() == Rule::instr_llvm;
439
440 match process_instruction(inner, ctx.const_map, ctx.label_offset_map, is_llvm) {
441 Ok(instruction) => {
442 if !ctx.rodata_phase {
443 let size = instruction.get_size();
444 ctx.ast.nodes.push(ASTNode::Instruction {
445 instruction,
446 offset: ctx.text_offset,
447 });
448 ctx.text_offset += size;
449 }
450 }
451 Err(e) => ctx.errors.push(e),
452 }
453
454 if ctx.rodata_phase && !ctx.missing_text_directive {
455 ctx.missing_text_directive = true;
456 ctx.errors.push(CompileError::MissingTextDirective {
457 span: span_range,
458 custom_label: None,
459 });
460 }
461 }
462 _ => {}
463 }
464 }
465}
466
467fn process_label(pair: Pair<Rule>, ctx: &mut ParseContext) {
468 let is_llvm = pair.as_rule() == Rule::label_llvm;
469 let mut label_opt = None;
470 let mut directive_opt = None;
471 let mut instruction_opt = None;
472
473 for item in pair.into_inner() {
474 match item.as_rule() {
475 Rule::identifier | Rule::numeric_label => match extract_label_from_pair(item) {
476 Ok(label) => label_opt = Some(label),
477 Err(e) => ctx.errors.push(e),
478 },
479 Rule::directive_inner => {
480 directive_opt = Some(item);
481 }
482 Rule::instr_default | Rule::instr_llvm => {
483 instruction_opt = Some(item);
484 }
485 _ => {}
486 }
487 }
488
489 if let Some((label_name, label_span)) = label_opt {
490 if let Some(original_span) = ctx.label_spans.get(&label_name) {
492 ctx.errors.push(CompileError::DuplicateLabel {
493 label: label_name,
494 span: label_span,
495 original_span: original_span.clone(),
496 custom_label: Some("Label already defined".to_string()),
497 });
498 return;
499 }
500 ctx.label_spans
501 .insert(label_name.clone(), label_span.clone());
502
503 if ctx.rodata_phase {
504 ctx.label_offset_map.insert(
506 label_name.clone(),
507 (Number::Int(ctx.rodata_offset as i64), Section::Rodata),
508 );
509
510 if let Some(dir_pair) = directive_opt {
512 match process_rodata_directive(label_name.clone(), label_span.clone(), dir_pair) {
513 Ok(rodata) => {
514 let size = rodata.get_size();
515 ctx.ast.rodata_nodes.push(ASTNode::ROData {
516 rodata,
517 offset: ctx.rodata_offset,
518 });
519 ctx.rodata_offset += size;
520 }
521 Err(e) => ctx.errors.push(e),
522 }
523 } else if let Some(inst_pair) = instruction_opt {
524 if let Err(e) =
525 process_instruction(inst_pair, ctx.const_map, ctx.label_offset_map, is_llvm)
526 {
527 ctx.errors.push(e);
528 }
529 if !ctx.missing_text_directive {
530 ctx.missing_text_directive = true;
531 ctx.errors.push(CompileError::MissingTextDirective {
532 span: label_span,
533 custom_label: None,
534 });
535 }
536 } else {
537 ctx.pending_rodata_label = Some((label_name, label_span));
540 }
541 } else {
542 ctx.label_offset_map.insert(
544 label_name.clone(),
545 (Number::Int(ctx.text_offset as i64), Section::Text),
546 );
547
548 ctx.ast.nodes.push(ASTNode::Label {
549 label: Label {
550 name: label_name,
551 span: label_span,
552 },
553 offset: ctx.text_offset,
554 });
555
556 if let Some(inst_pair) = instruction_opt {
557 match process_instruction(inst_pair, ctx.const_map, ctx.label_offset_map, is_llvm) {
558 Ok(instruction) => {
559 let size = instruction.get_size();
560 ctx.ast.nodes.push(ASTNode::Instruction {
561 instruction,
562 offset: ctx.text_offset,
563 });
564 ctx.text_offset += size;
565 }
566 Err(e) => ctx.errors.push(e),
567 }
568 }
569 }
570 }
571}
572
573fn process_instruction(
574 pair: Pair<Rule>,
575 const_map: &HashMap<String, Number>,
576 label_offset_map: &HashMap<String, (Number, Section)>,
577 is_llvm: bool,
578) -> Result<Instruction, CompileError> {
579 if is_llvm {
580 llvm::process_instruction(pair, const_map, label_offset_map)
581 } else {
582 default::process_instruction(pair, const_map, label_offset_map)
583 }
584}
585
586fn extract_label_from_pair(
587 pair: Pair<Rule>,
588) -> Result<(String, std::ops::Range<usize>), CompileError> {
589 let span = pair.as_span();
590 Ok((pair.as_str().to_string(), span.start()..span.end()))
591}