aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xixanta/src')
-rw-r--r--lib/xixanta/src/assembler.rs81
-rw-r--r--lib/xixanta/src/node.rs2
-rw-r--r--lib/xixanta/src/parser.rs130
3 files changed, 189 insertions, 24 deletions
diff --git a/lib/xixanta/src/assembler.rs b/lib/xixanta/src/assembler.rs
index 98ae19d..26d12b2 100644
--- a/lib/xixanta/src/assembler.rs
+++ b/lib/xixanta/src/assembler.rs
@@ -184,6 +184,10 @@ struct Assembler<'a> {
// is a tuple where the first member is the String identifier of the
// variable/address/label/etc. that is being stored in the second member.
objects_visited: Vec<(String, Object)>,
+
+ // List of mapping/segment indeces which the programmer asked to be
+ // considered safe.
+ safe_segments: Vec<(usize, usize)>,
}
/// The result to be given at the end of `assembler::assemble` and
@@ -453,6 +457,7 @@ impl<'a> Assembler<'a> {
macro_context: vec![],
pending_defines: vec![],
objects_visited: vec![],
+ safe_segments: vec![],
}
}
@@ -950,6 +955,14 @@ impl<'a> Assembler<'a> {
next_safe = true;
self.asan_next_safe = true;
}
+ NodeType::Comment(CommentType::AsanFixedSegments(segments)) => {
+ for name in segments {
+ let (midx, sidx) = self.get_mapping_segment_by_name(node, name)?;
+ if !self.is_in_safe_segment(midx, sidx) {
+ self.safe_segments.push((midx, sidx));
+ }
+ }
+ }
NodeType::Instruction => {
self.literal_mode = None;
match self.evaluate_node(node) {
@@ -1107,7 +1120,7 @@ impl<'a> Assembler<'a> {
bundle.address = current.bundles[pn.bundle_index].address;
// The first check deals on whether there is any reference
- // that crosses the mapping boundary. There are two
+ // that crosses the mapping boundary. There are some
// exceptions to this check:
//
// 1. We are currently on the Vector segment, which
@@ -1115,6 +1128,9 @@ impl<'a> Assembler<'a> {
// that is to be expected and safe.
// 2. If the bundle was marked as "safe" by the programmer
// via the asan:safe/check:safe comment.
+ // 3. The object belongs to a segment that the programmer
+ // explicitely marked as safe via the
+ // asan:fixed-segments/check:fixed-segments comment.
//
// Otherwise iterate over the objects that were visited over
// the previous evaluate_node() call, and check for
@@ -1125,7 +1141,9 @@ impl<'a> Assembler<'a> {
&& !matches!(self.mappings[pn.mapping].section_type, SectionType::Vector)
{
for (name, object) in &self.objects_visited {
- if pn.mapping != object.mapping {
+ if pn.mapping != object.mapping
+ && !self.is_in_safe_segment(object.mapping, object.segment)
+ {
let reference =
&self.mappings[object.mapping].segments[object.segment].name;
@@ -2925,6 +2943,39 @@ impl<'a> Assembler<'a> {
Ok(())
}
+ // Returns true if the given mapping/segment pair were marked by the
+ // programmer to be safe.
+ fn is_in_safe_segment(&self, mapping: usize, segment: usize) -> bool {
+ self.safe_segments
+ .iter()
+ .any(|s| s.0 == mapping && s.1 == segment)
+ }
+
+ // Return the mapping and segment indeces which identify the given segment
+ // identified by 'name'. If it cannot be found, then it returns an error by
+ // pinning the location of the given 'node'.
+ fn get_mapping_segment_by_name(
+ &self,
+ node: &'a PNode,
+ name: &str,
+ ) -> Result<(usize, usize), Error> {
+ for (mapping_idx, mapping) in self.mappings.iter().enumerate() {
+ for (segment_idx, segment) in mapping.segments.iter().enumerate() {
+ if segment.name == name {
+ return Ok((mapping_idx, segment_idx));
+ }
+ }
+ }
+
+ Err(Error {
+ line: node.value.line,
+ message: format!("unknown segment '{name}'"),
+ source: self.source_for(node),
+ expanded_from: self.macro_context.clone(),
+ global: false,
+ })
+ }
+
// Change the current segment to the one referenced in `node`.
fn switch_to_segment(&mut self, node: &'a PNode) -> Result<(), Error> {
let name = self.fetch_quoted_first_argument(node)?;
@@ -2941,28 +2992,10 @@ impl<'a> Assembler<'a> {
});
}
- // Find the segment being referenced and update the
- // `self.current_segment` accordingly.
- let mut found = false;
- for (mapping_idx, mapping) in self.mappings.iter().enumerate() {
- for (segment_idx, segment) in mapping.segments.iter().enumerate() {
- if segment.name == name {
- self.current_mapping = mapping_idx;
- self.current_segment = segment_idx;
- found = true;
- break;
- }
- }
- }
- if !found {
- return Err(Error {
- line: node.value.line,
- message: format!("unknown segment '{name}'"),
- source: self.source_for(node),
- expanded_from: self.macro_context.clone(),
- global: false,
- });
- }
+ let (midx, sidx) = self.get_mapping_segment_by_name(node, name)?;
+ self.current_mapping = midx;
+ self.current_segment = sidx;
+
Ok(())
}
diff --git a/lib/xixanta/src/node.rs b/lib/xixanta/src/node.rs
index 4f25158..649f333 100644
--- a/lib/xixanta/src/node.rs
+++ b/lib/xixanta/src/node.rs
@@ -276,6 +276,7 @@ pub enum CommentType {
AsanStack(Range<usize>),
AsanIgnore,
AsanSafe,
+ AsanFixedSegments(Vec<String>),
}
/// The PNode type.
@@ -380,6 +381,7 @@ impl fmt::Display for NodeType {
CommentType::AsanStack(_) => write!(f, ";; asan:stack"),
CommentType::AsanIgnore => write!(f, ";; asan:ignore"),
CommentType::AsanSafe => write!(f, ";; asan:safe"),
+ CommentType::AsanFixedSegments(_) => write!(f, ";; asan:fixed-segments"),
},
}
}
diff --git a/lib/xixanta/src/parser.rs b/lib/xixanta/src/parser.rs
index 03039f1..abcf1e8 100644
--- a/lib/xixanta/src/parser.rs
+++ b/lib/xixanta/src/parser.rs
@@ -264,6 +264,77 @@ impl Parser {
source: self.current_source,
});
}
+ "check:fixed-segments" | "asan:fixed-segments" => {
+ let mut args = vec![];
+ let mut eol = false;
+ let end = offset;
+
+ while !eol {
+ // We will assume that this is the last argument unless the
+ // last loop sees a comma.
+ eol = true;
+
+ // Skip initial whitespaces.
+ for c in line.get(offset..).unwrap_or("").chars() {
+ if !c.is_whitespace() {
+ break;
+ }
+ offset += 1;
+ }
+
+ // Fetch the argument and push it unless it's empty.
+ let mut arg = String::from("");
+ for c in line.get(offset..).unwrap_or("").chars() {
+ if c.is_whitespace() {
+ offset += 1;
+ break;
+ } else if c == ',' {
+ break;
+ }
+ offset += 1;
+
+ arg.push(c);
+ }
+ if !arg.is_empty() {
+ args.push(arg);
+ }
+
+ // Skip whitespaces until a comma if available.
+ for c in line.get(offset..).unwrap_or("").chars() {
+ offset += 1;
+
+ if c == ',' {
+ eol = false;
+ break;
+ }
+ }
+ }
+
+ if args.is_empty() {
+ return Err(Error {
+ line: self.line,
+ global: false,
+ source: self.sources[self.current_source].clone(),
+ message: "'asan:fixed-segments' expects at least one argument".to_string(),
+ expanded_from: vec![],
+ }
+ .into());
+ }
+
+ self.nodes.last_mut().unwrap().push(PNode {
+ node_type: NodeType::Comment(CommentType::AsanFixedSegments(args)),
+ value: PString {
+ value: cmd,
+ line: self.line,
+ start,
+ end,
+ },
+ left: None,
+ right: None,
+ args: None,
+ source: self.current_source,
+ });
+ }
&_ => {}
}
@@ -3843,4 +3914,63 @@ VAR3 = $200 ;; asan:reserve $100
);
assert_eq!(errors.get(4).unwrap().message, "bad 'asan:stack' range");
}
+
+ #[test]
+ fn parse_fixed_segments() {
+ let code = r#";; asan:fixed-segments CODE
+;; asan:fixed-segments CODE , FIXED
+;; asan:fixed-segments TAIL, FIXED,
+"#;
+ let mut parser = Parser::default();
+ assert!(
+ parser
+ .parse(code.as_bytes(), &SourceInfo::default())
+ .is_ok()
+ );
+
+ let nodes = parser.nodes();
+ assert_eq!(nodes.len(), 3);
+
+ assert_node(
+ nodes.first().unwrap(),
+ NodeType::Comment(CommentType::AsanFixedSegments(vec!["CODE".to_string()])),
+ code,
+ "asan:fixed-segments",
+ );
+ assert_node(
+ nodes.get(1).unwrap(),
+ NodeType::Comment(CommentType::AsanFixedSegments(vec![
+ "CODE".to_string(),
+ "FIXED".to_string(),
+ ])),
+ code,
+ "asan:fixed-segments",
+ );
+ assert_node(
+ nodes.get(2).unwrap(),
+ NodeType::Comment(CommentType::AsanFixedSegments(vec![
+ "TAIL".to_string(),
+ "FIXED".to_string(),
+ ])),
+ code,
+ "asan:fixed-segments",
+ );
+ }
+
+ #[test]
+ fn parse_bad_asan_fixed_segments() {
+ let code = ";; asan:fixed-segments";
+
+ let mut parser = Parser::default();
+ let res = parser.parse(code.as_bytes(), &SourceInfo::default());
+
+ assert!(res.is_err());
+ let errors = res.unwrap_err();
+ assert_eq!(errors.len(), 1);
+
+ assert_eq!(
+ errors.first().unwrap().message,
+ "'asan:fixed-segments' expects at least one argument"
+ );
+ }
}