From e0b1faf993bab97288540769f6f5f7955b3e0996 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 3 Sep 2025 21:31:31 +0200 Subject: Add a check for variable names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This check ensures that asan-friendly names actually match their expected scope. Signed-off-by: Miquel Sabaté Solà --- lib/xixanta/src/assembler.rs | 67 +++++++++++++++++++++++++++++++++++++++ scripts/test-e2e.sh | 5 +++ tests/expected/variable_names.txt | 4 +++ tests/variable_names.s | 25 +++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 tests/expected/variable_names.txt create mode 100644 tests/variable_names.s diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs index 09f0c9d..01a1982 100644 --- a/lib/xixanta/src/assembler.rs +++ b/lib/xixanta/src/assembler.rs @@ -498,6 +498,13 @@ impl<'a> Assembler<'a> { match self.evaluate_node(node.left.as_ref().unwrap()) { Ok(value) => { + // Check that the given name makes sense if the + // address sanitizer is enable and the assigned + // value is known. + if self.asan_enabled && value.resolved { + self.asan_check_variable_name(node, &value); + } + if let Err(err) = self.context.set_variable( &node.value, &Object { @@ -2626,6 +2633,66 @@ impl<'a> Assembler<'a> { } } + // Check that the variable name follows the coding convention as expected + // from the address sanitizer. + fn asan_check_variable_name(&mut self, node: &PNode, bundle: &Bundle) { + // If this is not an asan-friendly name, skip it, as it might be a + // constant or something else that might be catched later. + if !is_asan_friendly_name(&node.value.value) { + return; + } + + // Let's create this object now to makes things easier for possible + // error messages. + let actual_name = node.value.value.split("::").last().unwrap_or(""); + + if actual_name.starts_with("zp_") { + if bundle.size > 1 { + let value = bundle.value() as usize; + let range = MemoryRange { + name: node.value.value.to_owned(), + range: (value..(value + self.asan_next_reserve as usize)), + }; + self.warnings.push(Error { + line: node.value.line, + message: format!("you are assigning the zero-page variable {range} to something outside of zero page"), + source: self.source_for(node), + global: false, + }); + } + } else if bundle.size != 2 { + let value = bundle.value() as usize; + let range = MemoryRange { + name: node.value.value.to_owned(), + range: (value..(value + self.asan_next_reserve as usize)), + }; + self.warnings.push(Error { + line: node.value.line, + message: format!( + "you are assigning the variable {range} to something in zero page" + ), + source: self.source_for(node), + global: false, + }); + } else if actual_name.starts_with("wr_") { + let value = bundle.value() as usize; + let range = MemoryRange { + name: node.value.value.to_owned(), + range: (value..(value + self.asan_next_reserve as usize)), + }; + if !(0x6000..0x8000).contains(&value) { + self.warnings.push(Error { + line: node.value.line, + message: format!( + "you are assigning the variable {range} to something outside of Working RAM" + ), + source: self.source_for(node), + global: false, + }); + } + } + } + fn to_relative_address(&self, node: &PNode, bundle: &mut Bundle) -> Result<(), Error> { if !bundle.resolved { return Ok(()); diff --git a/scripts/test-e2e.sh b/scripts/test-e2e.sh index ab7312b..1c94cfd 100755 --- a/scripts/test-e2e.sh +++ b/scripts/test-e2e.sh @@ -61,6 +61,11 @@ echo "test: custom => bare_accesses.nes" diff tests/out/bare_accesses.txt tests/expected/bare_accesses.txt exit_code=$((exit_code + $?)) +echo "test: custom => variable_names.nes" +./target/debug/nasm -c empty --asan tests/variable_names.s 2>tests/out/variable_names.txt +diff tests/out/variable_names.txt tests/expected/variable_names.txt +exit_code=$((exit_code + $?)) + ## # code.nes diff --git a/tests/expected/variable_names.txt b/tests/expected/variable_names.txt new file mode 100644 index 0000000..416ca69 --- /dev/null +++ b/tests/expected/variable_names.txt @@ -0,0 +1,4 @@ +warning: you are assigning the zero-page variable 'zp_bad' ($0300) to something outside of zero page (variable_names.s: line 11) +warning: you are assigning the variable 'm_bad' ($02) to something in zero page (variable_names.s: line 13) +warning: you are assigning the variable 'wr_bad' ($01) to something in zero page (variable_names.s: line 14) +warning: you are assigning the variable 'wr_out' ($0400) to something outside of Working RAM (variable_names.s: line 16) diff --git a/tests/variable_names.s b/tests/variable_names.s new file mode 100644 index 0000000..0859473 --- /dev/null +++ b/tests/variable_names.s @@ -0,0 +1,25 @@ +.segment "HEADER" + .byte 'N', 'E', 'S', $1A + .byte $02 + .byte $01 + .byte $12 ; let's fake working RAM + .byte $00 + +.segment "CODE" + +zp_good = $00 +zp_bad = $0300 +m_good = $0200 +m_bad = $02 +wr_bad = $01 +wr_good = $6001 +wr_out = $0400 + +;; Just so we don't get "unused" warnings +lda zp_bad +lda zp_good +lda m_bad +lda m_good +lda wr_bad +lda wr_good +lda wr_out -- cgit v1.2.3