Skip to content

Commit cee7a98

Browse files
committed
added error handling with thiserror crate
1 parent 8e6b4fc commit cee7a98

File tree

8 files changed

+105
-82
lines changed

8 files changed

+105
-82
lines changed

Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ edition = "2021"
66
[dependencies]
77
clap = { version = "4.5.4", features = ["derive"] }
88
byteorder = "1.5.0"
9-
prettytable-rs = "0.10.0"
9+
prettytable-rs = "0.10.0"
10+
thiserror = "1.0.61"

src/error.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// error.rs
2+
use thiserror::Error;
3+
use std::io;
4+
5+
#[derive(Error, Debug)]
6+
pub enum AppError {
7+
#[error("IO Error")]
8+
Io(#[from] io::Error),
9+
#[error("Other Error: {0}")]
10+
Other(String),
11+
}

src/header.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::io;
21
use std::io::Read;
32

43
use byteorder::ReadBytesExt;
4+
use crate::error::AppError;
55

66
#[derive(Debug)]
77
pub enum MachHeader {
@@ -45,7 +45,7 @@ pub struct MachHeader32 {
4545
}
4646

4747
impl MachHeader32 {
48-
pub fn from_file<R: Read, E: byteorder::ByteOrder>(file: &mut R, magic: u32) -> io::Result<MachHeader> {
48+
pub fn from_file<R: Read, E: byteorder::ByteOrder>(file: &mut R, magic: u32) -> Result<MachHeader, AppError> {
4949
let header = MachHeader32 {
5050
magic,
5151
cputype: file.read_i32::<E>()?,
@@ -73,7 +73,7 @@ pub struct MachHeader64 {
7373
}
7474

7575
impl MachHeader64 {
76-
pub fn from_file<R: Read, E: byteorder::ByteOrder>(file: &mut R, magic: u32) -> io::Result<MachHeader> {
76+
pub fn from_file<R: Read, E: byteorder::ByteOrder>(file: &mut R, magic: u32) -> Result<MachHeader, AppError> {
7777
let header = MachHeader64 {
7878
magic,
7979
cputype: file.read_i32::<E>()?,

src/load_commands.rs

Lines changed: 48 additions & 48 deletions
Large diffs are not rendered by default.

src/mach_o.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
use crate::header::MachHeader;
22
use crate::load_commands::{LcStr, LoadCommand, Section};
33

4-
// TODO: make it more generic. we shoudl make a pair of (Vec<LoadCommand>, Vec<extra_memory>).
5-
// TODO: we encapsulte each extra_memory with its type by making and extrra_memory an enum (what to do with it?) and then we iterate and resolve! :D
6-
7-
// 1. create an enum for extra_memory and add all the possible options. extra_memory subtype should be a vector of u8.
8-
// 2. make every load command that has extra memory after it read the chunk to a new extra_memory and append it to Vec<extra_memory>
9-
// 3. after reading the load commands we immideatly start going over the chunks and parse them according to each type :D
10-
114
pub struct MachO {
125
pub header: MachHeader,
136
pub load_commands: (Vec<LoadCommand>, Vec<Vec<Section>>, Vec<LcStr>),

src/main.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::fs::File;
44
use std::path::PathBuf;
55

66
use clap::Parser;
7+
use crate::error::AppError;
8+
use crate::mach_o::MachO;
79

810
mod constants;
911
mod header;
@@ -12,6 +14,7 @@ mod mach_o;
1214
mod memory_utils;
1315
mod parser;
1416
mod printer;
17+
mod error;
1518

1619
/// A command-line tool written in Rust to view and explore mach-o files.
1720
#[derive(Parser)]
@@ -32,13 +35,19 @@ struct Args {
3235
load_commands: bool,
3336
}
3437

35-
// TODO: createt a new mod called "error.rs" which main will send errors to in order to be handled
36-
fn main() {
38+
fn main() -> Result<(), AppError> {
3739
let args = Args::parse();
38-
let mach_o = match File::open(&args.file.as_path()) {
39-
Ok(mut file) => parser::parse(&mut file).unwrap(),
40-
Err(e) => panic!("Error: Could not open input file for reading! {}", e),
41-
};
40+
41+
if let Err(e) = run(&args) {
42+
eprintln!("Error: {}", e);
43+
std::process::exit(1);
44+
}
45+
46+
Ok(())
47+
}
48+
49+
fn run(args: &Args) -> Result<(), AppError> {
50+
let mach_o = parse_from_file(&args.file)?;
4251

4352
if args.interactive {
4453
println!("Not yet implemented!");
@@ -52,4 +61,11 @@ fn main() {
5261
printer::print_load_commands(&mach_o.load_commands);
5362
}
5463

64+
Ok(())
65+
}
66+
67+
fn parse_from_file(path: &PathBuf) -> Result<MachO, AppError> {
68+
let mut file = File::open(path)?;
69+
let mach_o = parser::parse(&mut file)?;
70+
Ok(mach_o)
5571
}

src/parser.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ use std::io::{Read, Seek};
44
use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
55

66
use crate::constants::*;
7+
use crate::error::AppError;
78
use crate::header::*;
89
use crate::load_commands::*;
910
use crate::mach_o::MachO;
1011
use crate::memory_utils::{advance_to_next_load_command, get_file_offset};
1112

12-
pub fn parse<R: Read + Seek>(file: &mut R) -> io::Result<MachO> {
13+
pub fn parse<R: Read + Seek>(file: &mut R) -> Result<MachO, AppError> {
1314
let magic = file.read_u32::<BigEndian>()?;
1415
check_magic_number(magic)?;
1516

@@ -33,22 +34,22 @@ pub fn parse<R: Read + Seek>(file: &mut R) -> io::Result<MachO> {
3334
})
3435
}
3536

36-
fn check_magic_number(magic: u32) -> io::Result<()> {
37+
fn check_magic_number(magic: u32) -> Result<(), AppError> {
3738
match magic {
3839
MH_MAGIC | MH_MAGIC_64 | MH_CIGAM | MH_CIGAM_64 => Ok(()),
39-
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid Mach-O magic number"))
40+
_ => Err(AppError::from(io::Error::new(io::ErrorKind::InvalidData, "Invalid Mach-O magic number")))
4041
}
4142
}
4243

43-
fn parse_header<R: Read + Seek, E: ByteOrder>(file: &mut R, magic: u32) -> io::Result<MachHeader> {
44+
fn parse_header<R: Read + Seek, E: ByteOrder>(file: &mut R, magic: u32) -> Result<MachHeader, AppError> {
4445
match magic {
4546
MH_MAGIC | MH_CIGAM => MachHeader32::from_file::<R, E>(file, magic),
4647
MH_MAGIC_64 | MH_CIGAM_64 => MachHeader64::from_file::<R, E>(file, magic),
4748
_ => unreachable!(),
4849
}
4950
}
5051

51-
fn parse_load_commands<R: Read + Seek, E: ByteOrder>(file: &mut R, header: &MachHeader) -> io::Result<(Vec<LoadCommand>, Vec<Vec<Section>>, Vec<LcStr>)> {
52+
fn parse_load_commands<R: Read + Seek, E: ByteOrder>(file: &mut R, header: &MachHeader) -> Result<(Vec<LoadCommand>, Vec<Vec<Section>>, Vec<LcStr>), AppError> {
5253
let mut load_commands: Vec<LoadCommand> = Vec::new();
5354
let mut sections: Vec<Vec<Section>> = Vec::new();
5455
let mut load_commands_strings: Vec<LcStr> = Vec::new();
@@ -70,9 +71,8 @@ fn parse_load_commands<R: Read + Seek, E: ByteOrder>(file: &mut R, header: &Mach
7071
Ok((load_commands, sections, load_commands_strings))
7172
}
7273

73-
fn parse_command<R: Read, E: ByteOrder>(file: &mut R, load_command_prefix: &LoadCommandPrefix) -> io::Result<LoadCommand> {
74+
fn parse_command<R: Read, E: ByteOrder>(file: &mut R, load_command_prefix: &LoadCommandPrefix) ->Result<LoadCommand, AppError> {
7475
match load_command_prefix.cmd {
75-
LC_SEGMENT => SegmentCommand32::from_file::<R, E>(file, load_command_prefix),
7676
LC_SYMTAB => SymtabCommand::from_file::<R, E>(file, load_command_prefix),
7777
LC_SYMSEG => SymsegCommand::from_file::<R, E>(file, load_command_prefix),
7878
LC_THREAD | LC_UNIXTHREAD => ThreadCommand::from_file::<E>(load_command_prefix),
@@ -88,6 +88,7 @@ fn parse_command<R: Read, E: ByteOrder>(file: &mut R, load_command_prefix: &Load
8888
LC_SUB_LIBRARY => SubLibraryCommand::from_file::<R, E>(file, load_command_prefix),
8989
LC_TWOLEVEL_HINTS => TwoLevelHintsCommand::from_file::<R, E>(file, load_command_prefix),
9090
LC_PREBIND_CKSUM => PrebindCksumCommand::from_file::<R, E>(file, load_command_prefix),
91+
LC_SEGMENT => SegmentCommand32::from_file::<R, E>(file, load_command_prefix),
9192
LC_SEGMENT_64 => SegmentCommand64::from_file::<R, E>(file, load_command_prefix),
9293
LC_ROUTINES_64 => RoutinesCommand64::from_file::<R, E>(file, load_command_prefix),
9394
LC_UUID => UuidCommand::from_file::<R, E>(file, load_command_prefix),
@@ -102,23 +103,23 @@ fn parse_command<R: Read, E: ByteOrder>(file: &mut R, load_command_prefix: &Load
102103
LC_LINKER_OPTION => LinkerOptionCommand::from_file::<R, E>(file, load_command_prefix),
103104
LC_NOTE => NoteCommand::from_file::<R, E>(file, load_command_prefix),
104105
LC_BUILD_VERSION => BuildVersionCommand::from_file::<R, E>(file, load_command_prefix),
105-
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "unknown load command type!"))
106+
_ => Err(AppError::from(io::Error::new(io::ErrorKind::InvalidData, "unknown load command type!")))
106107
}
107108
}
108109

109-
fn parse_sections_for_segment<R: Read + Seek, E: ByteOrder>(file: &mut R, load_command: &LoadCommand) -> io::Result<Vec<Section>> {
110+
fn parse_sections_for_segment<R: Read + Seek, E: ByteOrder>(file: &mut R, load_command: &LoadCommand) -> Result<Vec<Section>, AppError> {
110111
let mut load_command_sections = Vec::new();
111112
match load_command {
112113
LoadCommand::SegmentCommand(command) => {
113114
match command {
114115
SegmentCommand::SEG32(command) => {
115-
for i in 0..command.nsects {
116+
for _ in 0..command.nsects {
116117
let section = Section32::from_file::<R, E>(file)?;
117118
load_command_sections.push(section);
118119
}
119120
}
120121
SegmentCommand::SEG64(command) => {
121-
for i in 0..command.nsects {
122+
for _ in 0..command.nsects {
122123
let section = Section64::from_file::<R, E>(file)?;
123124
load_command_sections.push(section);
124125
}
@@ -130,7 +131,7 @@ fn parse_sections_for_segment<R: Read + Seek, E: ByteOrder>(file: &mut R, load_c
130131
Ok(load_command_sections)
131132
}
132133

133-
fn parse_load_command_string<R: Read + Seek, E: ByteOrder>(file: &mut R, load_command: &LoadCommand, lc_offset: u64, cmdsize: u32) -> io::Result<LcStr> {
134+
fn parse_load_command_string<R: Read + Seek, E: ByteOrder>(file: &mut R, load_command: &LoadCommand, lc_offset: u64, cmdsize: u32) -> Result<LcStr, AppError> {
134135
let mut load_command_string = Vec::new();
135136
match load_command {
136137
LoadCommand::DylibCommand(_) |
@@ -153,6 +154,6 @@ fn parse_load_command_string<R: Read + Seek, E: ByteOrder>(file: &mut R, load_co
153154
Ok(load_command_string)
154155
}
155156

156-
fn get_load_command_remaining_size(lc_offset: u64, lc_size: u64, file_offset: u64) -> io::Result<u64> {
157+
fn get_load_command_remaining_size(lc_offset: u64, lc_size: u64, file_offset: u64) -> Result<u64, AppError> {
157158
Ok((lc_offset + lc_size) - file_offset)
158159
}

0 commit comments

Comments
 (0)