diff options
Diffstat (limited to 'lib/xixanta/src/assembler.rs')
| -rw-r--r-- | lib/xixanta/src/assembler.rs | 86 |
1 files changed, 81 insertions, 5 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 81ac9ee..959840d 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -187,6 +187,18 @@ struct Assembler<'a> { // List of mapping/segment indeces which the programmer asked to be // considered safe. safe_segments: Vec<(usize, usize)>, + + // Name of the last proc that was encountered while traversing the parsed + // nodes. + last_proc: String, + + // Target address that was last saved in push_bundle(). + last_address: Option<usize>, + + // Map that identifies for each given string identifying a piece of code + // (e.g. a proc's name), the address which can be used as the 'end' for a + // Range object. + address_ends: HashMap<String, usize>, } /// The result to be given at the end of `assembler::assemble` and @@ -232,6 +244,14 @@ pub struct AssemblerResult { /// The resaulting memory analysis from the address sanitizer. Ignore this /// if you did not assemble the source with the address sanitizer enabled. pub memory: MemoryResult, + + /// The addresses that have been evaluated. Note that some addresses will be + /// proper ranges (i.e. proc's), while others will only be a value (i.e. + /// labels). This is done in purpose as there's no reliable way to guess + /// whether a label was actually a subroutine or not. Hence, we don't even + /// try and encourage programmers to use .proc's when necessary (see + /// style.nes). + pub addresses: Vec<Range>, } /// Read the contents from the `reader` as a source file and produce a list of @@ -264,6 +284,7 @@ pub fn assemble( mappings: vec![], accessing_working_ram: false, memory: MemoryResult::default(), + addresses: vec![], }; } }; @@ -291,6 +312,7 @@ pub fn assemble_with_mapping( let mut asm = Assembler::new(mapping); let mut memory = MemoryResult::default(); + let mut addresses = vec![]; asm.asan_enabled = asan; asm.allow_unused = allow_unused; @@ -305,6 +327,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }; } @@ -322,6 +345,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }; } } @@ -336,6 +360,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }; } @@ -372,6 +397,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }; } @@ -385,12 +411,13 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }; } // Before filling in the holes with fill(), let's check what we have // processed so far. - if let Err(errors) = asm.check(&mut memory) { + if let Err(errors) = asm.check(&mut memory, &mut addresses) { return AssemblerResult { bundles: vec![], errors, @@ -398,6 +425,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }; } @@ -416,6 +444,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }, Err(errors) => AssemblerResult { bundles: vec![], @@ -424,6 +453,7 @@ pub fn assemble_with_mapping( mappings: asm.mappings, accessing_working_ram: asm.accessing_working_ram, memory, + addresses, }, } } @@ -457,6 +487,9 @@ impl<'a> Assembler<'a> { pending_defines: vec![], objects_visited: vec![], safe_segments: vec![], + last_proc: String::new(), + last_address: None, + address_ends: HashMap::new(), } } @@ -915,6 +948,8 @@ impl<'a> Assembler<'a> { // then open up its inner context. NodeType::Control(ControlType::StartProc) => { let proc_name = &node.left.as_ref().unwrap(); + self.last_proc = proc_name.value.value.clone(); + if let Err(e) = self.apply_segment_offset_to_label(proc_name, ObjectType::Proc) { errors.push(e); @@ -985,6 +1020,23 @@ impl<'a> Assembler<'a> { ControlType::StartRepeat => { self.evaluate_repeat_statement(node)?; } + ControlType::EndProc => { + // Save the last visited proc as the index for the + // 'address_ends' map. Note that we need to have it + // here because after the previous + // evaluate_control_statement() call the context + // will be the right one. + + let context_name = self.context.name(); + let full_name = if context_name == GLOBAL_CONTEXT { + self.last_proc.clone() + } else { + format!("{context_name}::{}", self.last_proc) + }; + if let Some(last_address) = self.last_address { + self.address_ends.insert(full_name, last_address); + } + } ControlType::StartScope // If this is the start of a .scope statement, then go // inside of its body too if it exists (note that its @@ -1275,8 +1327,12 @@ impl<'a> Assembler<'a> { } // Perform different checks on the bundles we have produced so far, and - // update the 'memory' if possible (i.e. ASAN is enabled). - fn check(&mut self, memory: &mut MemoryResult) -> Result<(), Vec<Error>> { + // update the 'memory' and the 'addresses' if possible. + fn check( + &mut self, + memory: &mut MemoryResult, + addresses: &mut Vec<Range>, + ) -> Result<(), Vec<Error>> { let mut errors = vec![]; for (context_name, map) in self.context.map.iter() { @@ -1370,8 +1426,21 @@ impl<'a> Assembler<'a> { // addresses. match bundle.object_type { ObjectType::Proc | ObjectType::Address => { - // TODO - println!("{full_name}"); + // Using unwrap() is safe as it only returns an error on + // badly formatted addresses. This will not happen at + // this point. + let resolved = self.context.resolve_label(&self.mappings, &bundle).unwrap(); + + let val = resolved.bundle.value() as usize; + let end = match self.address_ends.get(&full_name) { + Some(v) => v, + None => &val, + }; + let range = Range { + range: (val..*end), + name: full_name, + }; + addresses.push(range); } ObjectType::Value // We have a value at hand. If the address sanitizer is @@ -1696,6 +1765,13 @@ impl<'a> Assembler<'a> { current.offset += bundle.size as usize; current.segments[self.current_segment].offset += bundle.size as usize; + // Save this bundle as the last address being used. Note that the + // address being pointed at is _after_ consuming the bundle (i.e. by + // adding 'bundle.size'). This is because in the end this 'last_address' + // is going to be used by the Range struct as its 'end' member. Since + // this is going to be inclusive, we need to point at the "next" bundle. + self.last_address = Some(bundle.address + bundle.size as usize); + // Mark this bundle as reference-safe, so the checker afterwards doesn't // complain on different checks. bundle.safe = self.asan_next_safe; |
