pub struct StackSlot { /* private fields */ }Expand description
One slot in a function’s stack frame, its frame-pointer-relative offset and byte size, plus a
kind that is either a real variable (with name/type) or a reserved slot.
Implementations§
Source§impl StackSlot
impl StackSlot
Sourcepub const fn offset(&self) -> i64
pub const fn offset(&self) -> i64
The frame-pointer-relative offset IDA displays: negative below the frame pointer (locals), positive above it (the return address, then stack arguments).
Examples found in repository?
examples/types.rs (line 146)
130fn print_frame(frame: &StackFrame, ea: Address) {
131 println!(
132 "\n== stack frame: {ea:#x} ({} bytes, {} slots) ==",
133 frame.size(),
134 frame.len()
135 );
136 for v in frame.slots() {
137 let ty = v
138 .ty()
139 .map_or_else(|| "-".to_owned(), |id| one_line(frame.types(), id));
140 let label = match v.kind() {
141 StackSlotKind::Variable { name, .. } if !name.is_empty() => name.clone(),
142 StackSlotKind::Variable { .. } => "<unnamed>".to_owned(),
143 StackSlotKind::ReturnAddress => "<return address>".to_owned(),
144 StackSlotKind::SavedRegisters => "<saved registers>".to_owned(),
145 };
146 println!(" {:>8} {label}", soff(v.offset()));
147 if ty != "-" {
148 println!(" {ty}");
149 }
150 }
151}
152
153fn main() -> Result<(), Box<dyn std::error::Error>> {
154 let mut argv = std::env::args().skip(1);
155 let db = argv.next().expect("usage: types <db.i64> [TypeName]");
156 let arg_type = argv.next();
157
158 Ida::run(move |ida| -> Result<(), Error> {
159 ida.call(move |idb| -> Result<(), Error> {
160 idb.open(&db).call()?;
161
162 // Prototypes are sparse in a stripped release binary, so scan every function, not a
163 // prefix, so the reported ratio is honest and the sample isn't just entry-point stubs.
164 let mut total = 0usize;
165 let mut typed = 0usize;
166 let mut shown: Vec<(Address, String, Type)> = Vec::new();
167 let mut names: Vec<String> = Vec::new();
168 let mut best_frame: Option<(Address, StackFrame)> = None;
169 let mut best_vars = 0usize;
170 let mut frames_tried = 0usize;
171
172 for f in idb.functions() {
173 total += 1;
174
175 if let Some(image) = f.prototype_type()? {
176 typed += 1;
177 for (_, t) in image.types().iter() {
178 if let Some(n) = referenced_name(&t.shape)
179 && names.len() < MAX_NAMES
180 && !names.iter().any(|x| x == n)
181 {
182 names.push(n.to_owned());
183 }
184 }
185 if shown.len() < SHOW_PROTOS {
186 shown.push((f.address(), f.name().as_str().to_owned(), image));
187 }
188 }
189
190 if frames_tried < FRAME_BUDGET
191 && let Some(frame) = idb.frame(f.address())?
192 {
193 frames_tried += 1;
194 // A local lives within its frame; an offset in the millions is IDA's own
195 // misanalysis of a garbage function. Skip such frames so we showcase a real one.
196 let locals = frame.slots().iter().filter(|v| !v.is_special());
197 let sane = locals.clone().all(|v| v.offset().unsigned_abs() < 0x1_0000);
198 let n = locals.count();
199 if sane && (best_frame.is_none() || n > best_vars) {
200 best_vars = n;
201 best_frame = Some((f.address(), frame));
202 }
203 }
204 }
205
206 println!("== function prototypes ==");
207 println!("{typed} of {total} functions carry a stored prototype.\n");
208 for (ea, sym, image) in &shown {
209 println!(" {ea:#x} {}", one_line(image.types(), image.root()));
210 let short: String = sym.chars().take(64).collect();
211 if !short.is_empty() {
212 println!(" {short}");
213 }
214 }
215 if let Some((ea, _, image)) = shown.iter().max_by_key(|(_, _, im)| param_count(im))
216 && let TypeShape::Function {
217 ret,
218 params,
219 varargs,
220 } = image.shape()
221 && !params.is_empty()
222 {
223 println!("\n every parameter is a resolved TypeId -- {ea:#x}:");
224 println!(" ret {}", one_line(image.types(), *ret));
225 for (i, p) in params.iter().enumerate() {
226 println!(" arg{i} {}", one_line(image.types(), *p));
227 }
228 if *varargs {
229 println!(" ...");
230 }
231 }
232
233 // The named-type pass: resolve every name the prototypes reference and classify what
234 // the database actually holds, a full body to expand, or just a forward declaration.
235 println!("\n== referenced named types ==");
236 if let Some(name) = &arg_type {
237 match idb.type_named(name) {
238 Ok(image) => print_layout(&image, name),
239 Err(e) => println!(" type_named({name:?}): {e}"),
240 }
241 } else {
242 let mut bodies: Vec<(String, Type)> = Vec::new();
243 let mut forward: Vec<String> = Vec::new();
244 let mut not_local = 0usize;
245 for name in &names {
246 match idb.type_named(name) {
247 Ok(image) if image.members().is_some_and(|m| !m.is_empty()) => {
248 bodies.push((name.clone(), image));
249 }
250 Ok(_) => forward.push(name.clone()),
251 Err(Error::TypeNotFound { .. }) => not_local += 1,
252 Err(e) => println!(" type_named({name:?}): {e}"),
253 }
254 }
255 println!(
256 "{} referenced: {} with a full body, {} forward-declared, {} not a local type.",
257 names.len(),
258 bodies.len(),
259 forward.len(),
260 not_local
261 );
262 if let Some((name, image)) = bodies
263 .iter()
264 .max_by_key(|(_, im)| im.members().map_or(0, <[_]>::len))
265 {
266 print_layout(image, name);
267 } else {
268 for n in forward.iter().take(6) {
269 println!(" forward-decl: {n}");
270 }
271 }
272 }
273
274 match &best_frame {
275 Some((ea, frame)) => print_frame(frame, *ea),
276 None => println!("\n(no function has a stack frame)"),
277 }
278
279 idb.close(false);
280 println!("\nTYPES OK");
281 Ok(())
282 })?
283 })??;
284
285 Ok(())
286}Sourcepub const fn kind(&self) -> &StackSlotKind
pub const fn kind(&self) -> &StackSlotKind
What this slot is: a real variable (with name/type) or a reserved slot.
Examples found in repository?
examples/types.rs (line 140)
130fn print_frame(frame: &StackFrame, ea: Address) {
131 println!(
132 "\n== stack frame: {ea:#x} ({} bytes, {} slots) ==",
133 frame.size(),
134 frame.len()
135 );
136 for v in frame.slots() {
137 let ty = v
138 .ty()
139 .map_or_else(|| "-".to_owned(), |id| one_line(frame.types(), id));
140 let label = match v.kind() {
141 StackSlotKind::Variable { name, .. } if !name.is_empty() => name.clone(),
142 StackSlotKind::Variable { .. } => "<unnamed>".to_owned(),
143 StackSlotKind::ReturnAddress => "<return address>".to_owned(),
144 StackSlotKind::SavedRegisters => "<saved registers>".to_owned(),
145 };
146 println!(" {:>8} {label}", soff(v.offset()));
147 if ty != "-" {
148 println!(" {ty}");
149 }
150 }
151}Sourcepub fn name(&self) -> Option<&str>
pub fn name(&self) -> Option<&str>
The variable’s name, or None for a reserved slot.
Shortcut into kind.
Sourcepub fn ty(&self) -> Option<TypeId>
pub fn ty(&self) -> Option<TypeId>
The variable’s structured type handle, or None for a reserved slot or an untyped stack
slot.
Resolve it against the owning StackFrame with StackFrame::type_of. Shortcut into
kind.
Examples found in repository?
examples/types.rs (line 138)
130fn print_frame(frame: &StackFrame, ea: Address) {
131 println!(
132 "\n== stack frame: {ea:#x} ({} bytes, {} slots) ==",
133 frame.size(),
134 frame.len()
135 );
136 for v in frame.slots() {
137 let ty = v
138 .ty()
139 .map_or_else(|| "-".to_owned(), |id| one_line(frame.types(), id));
140 let label = match v.kind() {
141 StackSlotKind::Variable { name, .. } if !name.is_empty() => name.clone(),
142 StackSlotKind::Variable { .. } => "<unnamed>".to_owned(),
143 StackSlotKind::ReturnAddress => "<return address>".to_owned(),
144 StackSlotKind::SavedRegisters => "<saved registers>".to_owned(),
145 };
146 println!(" {:>8} {label}", soff(v.offset()));
147 if ty != "-" {
148 println!(" {ty}");
149 }
150 }
151}Sourcepub const fn is_special(&self) -> bool
pub const fn is_special(&self) -> bool
Whether this is one of IDA’s reserved slots (return address or saved registers) rather than a real variable.
Examples found in repository?
examples/types.rs (line 196)
153fn main() -> Result<(), Box<dyn std::error::Error>> {
154 let mut argv = std::env::args().skip(1);
155 let db = argv.next().expect("usage: types <db.i64> [TypeName]");
156 let arg_type = argv.next();
157
158 Ida::run(move |ida| -> Result<(), Error> {
159 ida.call(move |idb| -> Result<(), Error> {
160 idb.open(&db).call()?;
161
162 // Prototypes are sparse in a stripped release binary, so scan every function, not a
163 // prefix, so the reported ratio is honest and the sample isn't just entry-point stubs.
164 let mut total = 0usize;
165 let mut typed = 0usize;
166 let mut shown: Vec<(Address, String, Type)> = Vec::new();
167 let mut names: Vec<String> = Vec::new();
168 let mut best_frame: Option<(Address, StackFrame)> = None;
169 let mut best_vars = 0usize;
170 let mut frames_tried = 0usize;
171
172 for f in idb.functions() {
173 total += 1;
174
175 if let Some(image) = f.prototype_type()? {
176 typed += 1;
177 for (_, t) in image.types().iter() {
178 if let Some(n) = referenced_name(&t.shape)
179 && names.len() < MAX_NAMES
180 && !names.iter().any(|x| x == n)
181 {
182 names.push(n.to_owned());
183 }
184 }
185 if shown.len() < SHOW_PROTOS {
186 shown.push((f.address(), f.name().as_str().to_owned(), image));
187 }
188 }
189
190 if frames_tried < FRAME_BUDGET
191 && let Some(frame) = idb.frame(f.address())?
192 {
193 frames_tried += 1;
194 // A local lives within its frame; an offset in the millions is IDA's own
195 // misanalysis of a garbage function. Skip such frames so we showcase a real one.
196 let locals = frame.slots().iter().filter(|v| !v.is_special());
197 let sane = locals.clone().all(|v| v.offset().unsigned_abs() < 0x1_0000);
198 let n = locals.count();
199 if sane && (best_frame.is_none() || n > best_vars) {
200 best_vars = n;
201 best_frame = Some((f.address(), frame));
202 }
203 }
204 }
205
206 println!("== function prototypes ==");
207 println!("{typed} of {total} functions carry a stored prototype.\n");
208 for (ea, sym, image) in &shown {
209 println!(" {ea:#x} {}", one_line(image.types(), image.root()));
210 let short: String = sym.chars().take(64).collect();
211 if !short.is_empty() {
212 println!(" {short}");
213 }
214 }
215 if let Some((ea, _, image)) = shown.iter().max_by_key(|(_, _, im)| param_count(im))
216 && let TypeShape::Function {
217 ret,
218 params,
219 varargs,
220 } = image.shape()
221 && !params.is_empty()
222 {
223 println!("\n every parameter is a resolved TypeId -- {ea:#x}:");
224 println!(" ret {}", one_line(image.types(), *ret));
225 for (i, p) in params.iter().enumerate() {
226 println!(" arg{i} {}", one_line(image.types(), *p));
227 }
228 if *varargs {
229 println!(" ...");
230 }
231 }
232
233 // The named-type pass: resolve every name the prototypes reference and classify what
234 // the database actually holds, a full body to expand, or just a forward declaration.
235 println!("\n== referenced named types ==");
236 if let Some(name) = &arg_type {
237 match idb.type_named(name) {
238 Ok(image) => print_layout(&image, name),
239 Err(e) => println!(" type_named({name:?}): {e}"),
240 }
241 } else {
242 let mut bodies: Vec<(String, Type)> = Vec::new();
243 let mut forward: Vec<String> = Vec::new();
244 let mut not_local = 0usize;
245 for name in &names {
246 match idb.type_named(name) {
247 Ok(image) if image.members().is_some_and(|m| !m.is_empty()) => {
248 bodies.push((name.clone(), image));
249 }
250 Ok(_) => forward.push(name.clone()),
251 Err(Error::TypeNotFound { .. }) => not_local += 1,
252 Err(e) => println!(" type_named({name:?}): {e}"),
253 }
254 }
255 println!(
256 "{} referenced: {} with a full body, {} forward-declared, {} not a local type.",
257 names.len(),
258 bodies.len(),
259 forward.len(),
260 not_local
261 );
262 if let Some((name, image)) = bodies
263 .iter()
264 .max_by_key(|(_, im)| im.members().map_or(0, <[_]>::len))
265 {
266 print_layout(image, name);
267 } else {
268 for n in forward.iter().take(6) {
269 println!(" forward-decl: {n}");
270 }
271 }
272 }
273
274 match &best_frame {
275 Some((ea, frame)) => print_frame(frame, *ea),
276 None => println!("\n(no function has a stack frame)"),
277 }
278
279 idb.close(false);
280 println!("\nTYPES OK");
281 Ok(())
282 })?
283 })??;
284
285 Ok(())
286}Trait Implementations§
Source§impl<'de> Deserialize<'de> for StackSlot
impl<'de> Deserialize<'de> for StackSlot
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
impl Eq for StackSlot
impl StructuralPartialEq for StackSlot
Auto Trait Implementations§
impl Freeze for StackSlot
impl RefUnwindSafe for StackSlot
impl Send for StackSlot
impl Sync for StackSlot
impl Unpin for StackSlot
impl UnsafeUnpin for StackSlot
impl UnwindSafe for StackSlot
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more