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