Read from DOL

This commit is contained in:
Richard Patel 2021-08-22 04:09:51 +02:00
parent 7b63bfa533
commit 8c5ddf4057
5 changed files with 110 additions and 74 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ env/
lib/ lib/
*.bin *.bin
*.png *.png
*.dol
*.rel

1
Cargo.lock generated
View File

@ -321,6 +321,7 @@ name = "ppc750cl-flow-graph"
version = "0.1.1" version = "0.1.1"
dependencies = [ dependencies = [
"clap", "clap",
"dol",
"itertools", "itertools",
"parse_int", "parse_int",
"petgraph", "petgraph",

View File

@ -20,10 +20,10 @@ pub enum Error {
IOError(std::io::Error), IOError(std::io::Error),
#[error("No sections in DOL")] #[error("No sections in DOL")]
NoSections, NoSections,
#[error("Overlapping sections")] #[error("Overlapping sections: {0:8>X} {1:8>X}")]
OverlappingSections, OverlappingSections(u32, u32),
#[error("Section sizes overflow")] #[error("Section sizes too large")]
SizeOverflow, SectionsTooLarge,
} }
impl From<bincode::Error> for Error { impl From<bincode::Error> for Error {
@ -48,7 +48,8 @@ impl Dol {
R: Read + Seek, R: Read + Seek,
{ {
// Read header. // Read header.
let header = DolHeader::read_from(&mut r)?; let header_data = DolHeaderData::read_from(&mut r)?;
let header: DolHeader = (&header_data).into();
Dol::read_with_header(r, header) Dol::read_with_header(r, header)
} }
@ -57,68 +58,45 @@ impl Dol {
where where
R: Read + Seek, R: Read + Seek,
{ {
let dol_start = r.stream_position()? - DolHeader::SERIALIZED_SIZE; let dol_start = r.stream_position()? - DolHeaderData::SERIALIZED_SIZE;
// Re-arrange section specifiers into a more workable format. if header.sections.is_empty() {
// Also remove the text vs data distinction because it's irrelevant.
struct DolSection {
offset: u32,
target: u32,
size: u32,
}
let mut dol_sections =
Vec::with_capacity(DolHeader::TEXT_SECTION_COUNT + DolHeader::DATA_SECTION_COUNT);
for i in 0..DolHeader::TEXT_SECTION_COUNT {
if header.text_sizes[i] > 0 {
dol_sections.push(DolSection {
offset: header.text_offsets[i],
target: header.text_targets[i],
size: header.text_sizes[i],
});
}
}
for i in 0..DolHeader::DATA_SECTION_COUNT {
if header.data_sizes[i] > 0 {
dol_sections.push(DolSection {
offset: header.data_offsets[i],
target: header.data_targets[i],
size: header.data_sizes[i],
});
}
}
if dol_sections.is_empty() {
return Err(Error::NoSections); return Err(Error::NoSections);
} }
// Sort sections by target address to prepare them for mapping. /*
dol_sections.sort_by_key(|s| s.target);
// Verify that sections are not overlapping. // Verify that sections are not overlapping.
let mut end_target_addr = 0u32; let mut end_target_addr = 0u32;
for section in &dol_sections { for section in &header.sections {
if section.target < end_target_addr { if section.target < end_target_addr {
return Err(Error::OverlappingSections); return Err(Error::OverlappingSections(end_target_addr, section.target));
} }
end_target_addr = section.target + section.size end_target_addr = section.target + section.size
} }
*/
// Remember the memory offset of the first section. // Remember the memory offset of the first section.
// Then shift all sections to the beginning of memory. // Then shift all sections to the beginning of memory.
let memory_offset = dol_sections[0].target; let memory_offset = header.sections[0].target;
for mut section in &mut dol_sections {
section.target -= memory_offset;
}
// Get the size of all sections combined. // Get the size of all sections combined.
let mut total_size = 0usize; let mut total_size = 0usize;
for section in &dol_sections { for section in &header.sections {
if let Some(sum) = total_size.checked_add(section.size as usize) { if let Some(sum) = total_size.checked_add(section.size as usize) {
total_size = sum; total_size = sum;
} else { } else {
return Err(Error::SizeOverflow); return Err(Error::SectionsTooLarge);
} }
} }
// Cannot be larger than 24 MiB.
if total_size > 0x180_0000 {
return Err(Error::SectionsTooLarge);
}
// Create memory. // Create memory.
let mut memory = vec![0u8; total_size]; let mut memory = vec![0u8; total_size];
// Read sections into memory. // Read sections into memory.
for section in &dol_sections { for section in &header.sections {
if section.kind == DolSectionType::Bss {
continue;
}
r.seek(SeekFrom::Start(dol_start + section.offset as u64))?; r.seek(SeekFrom::Start(dol_start + section.offset as u64))?;
let mem_start = section.target as usize; let mem_start = (section.target - memory_offset) as usize;
let mem_end = mem_start + section.size as usize; let mem_end = mem_start + section.size as usize;
r.read_exact(&mut memory[mem_start..mem_end])?; r.read_exact(&mut memory[mem_start..mem_end])?;
} }
@ -128,38 +106,84 @@ impl Dol {
memory_offset, memory_offset,
}) })
} }
/// Reads bytes into a destination buffer given a virtual address.
pub fn virtual_read(&self, dest: &mut [u8], virtual_addr: u32) {
let offset = (virtual_addr - self.memory_offset) as usize;
// TODO Gracefully handle errors.
dest.copy_from_slice(&self.memory[offset..offset + dest.len()])
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum DolSectionType {
Data,
Bss,
}
#[derive()]
pub struct DolSection {
pub kind: DolSectionType,
pub index: usize,
pub offset: u32,
pub target: u32,
pub size: u32,
}
pub struct DolHeader {
pub sections: Vec<DolSection>,
}
impl From<&DolHeaderData> for DolHeader {
fn from(header: &DolHeaderData) -> Self {
let mut sections = Vec::with_capacity(DolHeaderData::SECTION_COUNT);
for i in 0..DolHeaderData::SECTION_COUNT {
if header.section_sizes[i] > 0 {
sections.push(DolSection {
kind: DolSectionType::Data,
index: i,
offset: header.section_offsets[i],
target: header.section_targets[i],
size: header.section_sizes[i],
});
}
}
if header.bss_target > 0 {
sections.push(DolSection {
kind: DolSectionType::Bss,
index: 0,
offset: 0,
target: header.bss_target,
size: header.bss_size,
})
}
// Sort sections by target address to prepare them for mapping.
sections.sort_by_key(|s| s.target);
Self { sections }
}
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct DolHeader { pub struct DolHeaderData {
text_offsets: [u32; Self::TEXT_SECTION_COUNT], pub section_offsets: [u32; Self::SECTION_COUNT],
data_offsets: [u32; Self::DATA_SECTION_COUNT], pub section_targets: [u32; Self::SECTION_COUNT],
text_targets: [u32; Self::TEXT_SECTION_COUNT], pub section_sizes: [u32; Self::SECTION_COUNT],
data_targets: [u32; Self::DATA_SECTION_COUNT], pub bss_target: u32,
text_sizes: [u32; Self::TEXT_SECTION_COUNT], pub bss_size: u32,
data_sizes: [u32; Self::DATA_SECTION_COUNT], pub entry_point: u32,
bss_target: u32, pub padding: [u8; 0x1c],
bss_size: u32,
entry_point: u32,
} }
impl DolHeader { impl DolHeaderData {
const TEXT_SECTION_COUNT: usize = 7; const SECTION_COUNT: usize = 18;
const DATA_SECTION_COUNT: usize = 11;
const SERIALIZED_SIZE: u64 = 0x100; const SERIALIZED_SIZE: u64 = 0x100;
fn bincode_options() -> impl bincode::Options { /// Reads the DOL header from a `Reader`.
pub fn read_from<R: Read + Seek>(mut r: R) -> bincode::Result<Self> {
bincode::DefaultOptions::new() bincode::DefaultOptions::new()
.with_big_endian() .with_big_endian()
.allow_trailing_bytes() .allow_trailing_bytes()
} .with_fixint_encoding()
.deserialize_from(&mut r)
/// Reads the DOL header from a `Reader`.
pub fn read_from<R: Read>(mut r: R) -> bincode::Result<Self> {
// Deserialize DOL header.
let this: Self = Self::bincode_options().deserialize_from(&mut r)?;
// Read padding.
let _: [u8; 0x1c] = Self::bincode_options().deserialize_from(&mut r)?;
Ok(this)
} }
} }

View File

@ -9,6 +9,7 @@ repository = "https://github.com/terorie/ppc750cl"
[dependencies] [dependencies]
clap = "2.33" clap = "2.33"
dol = { version = "0.1.0", path = "../dol" }
itertools = "0.10" itertools = "0.10"
parse_int = "0.5" parse_int = "0.5"
petgraph = "0.6" petgraph = "0.6"

View File

@ -8,24 +8,32 @@ pub mod slices;
use crate::flow::FlowGraph; use crate::flow::FlowGraph;
use crate::slices::BasicSlices; use crate::slices::BasicSlices;
use dol::Dol;
fn main() { fn main() {
let matches = clap_app!(myapp => let matches = clap_app!(myapp =>
(version: "1.0") (version: "1.0")
(about: "Control flow graph analysis for PowerPC 750CL") (about: "Control flow graph analysis for PowerPC 750CL")
(@arg ADDR: --addr +required +takes_value "Address") (@arg START: --start +required +takes_value "Start address")
(@arg STOP: --stop +required +takes_value "Stop address")
(@arg INPUT: +required "Binary input file") (@arg INPUT: +required "Binary input file")
) )
.get_matches(); .get_matches();
let addr = matches.value_of("ADDR").unwrap(); let start_addr = matches.value_of("START").unwrap();
let addr: u32 = ::parse_int::parse(addr).expect("Invalid address flag"); let start_addr: u32 = ::parse_int::parse(start_addr).expect("Invalid address flag");
let stop_addr = matches.value_of("STOP").unwrap();
let stop_addr: u32 = ::parse_int::parse(stop_addr).expect("Invalid address flag");
let file_path = matches.value_of("INPUT").unwrap(); let file_path = matches.value_of("INPUT").unwrap();
let bytes = std::fs::read(file_path).expect("Failed to read file"); let dol_file = std::fs::File::open(file_path).expect("Failed to read file");
let dol = Dol::read_from(&dol_file).expect("Invalid DOL file");
drop(dol_file);
let mut bytes = vec![0u8; (stop_addr - start_addr) as usize];
dol.virtual_read(&mut bytes, start_addr);
// Create control flow graph. // Create control flow graph.
let ins_list: Vec<Ins> = disasm_iter(&bytes, addr).collect(); let ins_list: Vec<Ins> = disasm_iter(&bytes, start_addr).collect();
let basic_slices = BasicSlices::from_code(&ins_list); let basic_slices = BasicSlices::from_code(&ins_list);
let graph = FlowGraph::from_basic_slices(&basic_slices, &ins_list); let graph = FlowGraph::from_basic_slices(&basic_slices, &ins_list);