Skip to content

Commit f960090

Browse files
committed
Lots of reorganization and commenting
1 parent 143844f commit f960090

File tree

1 file changed

+43
-43
lines changed

1 file changed

+43
-43
lines changed

build/xtask/src/dist.rs

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,8 @@ fn task_can_overflow(
10901090
let data = std::fs::read(f).context("could not open ELF file")?;
10911091
let elf = goblin::elf::Elf::parse(&data)?;
10921092

1093+
// Read the .stack_sizes section, which is an array of
1094+
// `(address: u32, stack size: unsigned leb128)` tuples
10931095
let sizes = crate::elf::get_section_by_name(&elf, ".stack_sizes")
10941096
.context("could not get .stack_sizes")?;
10951097
let mut sizes = &data[sizes.sh_offset as usize..][..sizes.sh_size as usize];
@@ -1102,18 +1104,9 @@ fn task_can_overflow(
11021104
addr_to_frame_size.insert(addr, size);
11031105
}
11041106

1105-
let text = crate::elf::get_section_by_name(&elf, ".text")
1106-
.context("could not get .text")?;
1107-
1108-
// Pack everything into a single data structure
1109-
#[derive(Debug)]
1110-
struct FunctionData {
1111-
name: String,
1112-
short_name: String,
1113-
frame_size: Option<u64>,
1114-
calls: BTreeSet<u32>,
1115-
}
1116-
1107+
// There are `$t` and `$d` symbols which indicate the beginning of text
1108+
// versus data in the `.text` region. We collect them into a `BTreeMap`
1109+
// here so that we can avoid trying to decode inline data words.
11171110
let mut text_regions = BTreeMap::new();
11181111
for sym in elf.syms.iter() {
11191112
if sym.st_name == 0
@@ -1123,11 +1116,7 @@ fn task_can_overflow(
11231116
continue;
11241117
}
11251118

1126-
// Clear the lowest bit, which indicates that the function contains
1127-
// thumb instructions (always true for our systems!)
1128-
let val = sym.st_value;
1129-
let addr = val as u32;
1130-
1119+
let addr = sym.st_value as u32;
11311120
let is_text = match elf.strtab.get_at(sym.st_name) {
11321121
Some("$t") => true,
11331122
Some("$d") => false,
@@ -1138,6 +1127,22 @@ fn task_can_overflow(
11381127
};
11391128
text_regions.insert(addr, is_text);
11401129
}
1130+
let is_code = |addr| {
1131+
let mut iter = text_regions.range(..=addr);
1132+
*iter.next_back().unwrap().1
1133+
};
1134+
1135+
// We'll be packing everything into this data structure
1136+
#[derive(Debug)]
1137+
struct FunctionData {
1138+
name: String,
1139+
short_name: String,
1140+
frame_size: Option<u64>,
1141+
calls: BTreeSet<u32>,
1142+
}
1143+
1144+
let text = crate::elf::get_section_by_name(&elf, ".text")
1145+
.context("could not get .text")?;
11411146

11421147
use capstone::{
11431148
arch::{arm, ArchOperand, BuildsCapstone, BuildsCapstoneExtraMode},
@@ -1151,17 +1156,16 @@ fn task_can_overflow(
11511156
.build()
11521157
.map_err(|e| anyhow!("failed to initialize disassembler: {e:?}"))?;
11531158

1159+
// Disassemble each function, building a map of its call sites
11541160
let mut fns = BTreeMap::new();
11551161
for sym in elf.syms.iter() {
1162+
// We only care about named function symbols here
11561163
if sym.st_name == 0 || !sym.is_function() || sym.st_size == 0 {
11571164
continue;
11581165
}
11591166

1160-
let name = match elf.strtab.get_at(sym.st_name) {
1161-
Some(n) => n,
1162-
None => {
1163-
bail!("bad symbol in {task_name}: {}", sym.st_name);
1164-
}
1167+
let Some(name) = elf.strtab.get_at(sym.st_name) else {
1168+
bail!("bad symbol in {task_name}: {}", sym.st_name);
11651169
};
11661170

11671171
// Clear the lowest bit, which indicates that the function contains
@@ -1178,21 +1182,13 @@ fn task_can_overflow(
11781182
let mut chunk = None;
11791183
for (i, b) in text.iter().enumerate() {
11801184
let addr = base_addr + i as u32;
1181-
let mut iter = text_regions.range(..=addr);
1182-
// Check if this is a code or data byte
1183-
if *iter.next_back().unwrap().1 {
1184-
if chunk.is_none() {
1185-
chunk = Some((addr, vec![]));
1186-
}
1187-
chunk.as_mut().unwrap().1.push(*b);
1188-
} else if let Some(c) = chunk.take() {
1189-
chunks.push(c);
1185+
if is_code(addr) {
1186+
chunk.get_or_insert((addr, vec![])).1.push(*b);
1187+
} else {
1188+
chunks.extend(chunk.take());
11901189
}
11911190
}
1192-
// Process data in the trailing chunk
1193-
if let Some(c) = chunk {
1194-
chunks.push(c);
1195-
}
1191+
chunks.extend(chunk); // don't forget the trailing chunk!
11961192

11971193
let mut calls = BTreeSet::new();
11981194
for (addr, chunk) in chunks {
@@ -1254,12 +1250,6 @@ fn task_can_overflow(
12541250
);
12551251
}
12561252

1257-
let start_addr = fns
1258-
.iter()
1259-
.find(|(_addr, v)| v.name.as_str() == "_start")
1260-
.map(|(addr, _v)| *addr)
1261-
.ok_or_else(|| anyhow!("could not find _start"))?;
1262-
12631253
fn recurse(
12641254
call_stack: &mut Vec<u32>,
12651255
recurse_depth: usize,
@@ -1293,7 +1283,8 @@ fn task_can_overflow(
12931283
}
12941284
for j in &f.calls {
12951285
if call_stack.contains(j) {
1296-
// Skip recurse calls, because we can't resolve them
1286+
// Skip recursive / mutually recursive calls, because we can't
1287+
// reason about them.
12971288
continue;
12981289
} else {
12991290
call_stack.push(*j);
@@ -1309,11 +1300,20 @@ fn task_can_overflow(
13091300
}
13101301
}
13111302
}
1312-
let mut deepest = None;
1303+
1304+
// Find stack sizes by traversing the graph
13131305
if verbose {
13141306
println!("finding stack sizes for {task_name}");
13151307
}
1308+
let start_addr = fns
1309+
.iter()
1310+
.find(|(_addr, v)| v.name.as_str() == "_start")
1311+
.map(|(addr, _v)| *addr)
1312+
.ok_or_else(|| anyhow!("could not find _start"))?;
1313+
let mut deepest = None;
13161314
recurse(&mut vec![start_addr], 0, 0, &fns, &mut deepest, verbose);
1315+
1316+
// Check against our configured task stack size
13171317
let Some((max_depth, max_stack)) = deepest else {
13181318
unreachable!("must have at least one call stack");
13191319
};

0 commit comments

Comments
 (0)