Initial Commit

This commit is contained in:
Charlotte Delenk 2021-08-01 19:25:08 +01:00
parent a05c5b18b8
commit d765c01039
18 changed files with 1001 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
/target
/bin
/build.ninja
/build
.ninja_log

281
Cargo.lock generated Normal file
View file

@ -0,0 +1,281 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bstr"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "donut-decomp"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"csv",
"goblin",
]
[[package]]
name = "goblin"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b1800b95efee8ad4ef04517d4d69f8e209e763b1668f1179aeeedd0e454da55"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "scroll"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde"
version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

13
Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "donut-decomp"
version = "0.1.0"
edition = "2018"
default-run = "buildgen"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.42"
goblin = "0.4.2"
clap = "2.33.3"
csv = "1.1.6"

15
README.md Normal file
View file

@ -0,0 +1,15 @@
# Kirby's 20th Anniversary Special Edition Decompilation Project
## Dependencies
- Rust
- Ninja
- On Linux: Wine with 32 bit support
- On Macos: Docker
- llvm (any recent version)
- donut.dol from the NTSC version (sha256 92a9f3662d85e440f2c0d6ea0d30ead1e7bf0eb51be279320b5e9aac6e5216e5), placed in bin/donut/donut.dol
- donut.MAP from the NTSC version (sha256 d26fab6e6a58d3dd5abc0c2ba1b745c1095d4b2887b44b3ea7128258a0ec8666), placed in bin/donut/donut.MAP
- The following files from "CW for Wii v1.7": license.dat, mwasmeppc.exe, mwcceppc.exe, mwldeppc.exe, all placed in bin/mwcc
- The following file from the misc directory: lmgr8c.dll
## Building
You can build by running `cargo run` in the root directory.

13
data/objects.txt Normal file
View file

@ -0,0 +1,13 @@
./build/obj/init_80004000_80006728.o
./build/obj/extab_80006740_800068cc.o
./build/obj/extabindex_800068e0_800069e4.o
./build/obj/text_80006a00_80406244.o
./build/obj/ctors_80406260_80406530.o
./build/obj/dtors_80406540_8040654c.o
./build/obj/rodata_80406560_80421030.o
./build/obj/data_80421040_804966fc.o
./build/obj/bss_80496700_8055640c.o
./build/obj/sdata_80556420_8055c6d0.o
./build/obj/sbss_8055c6e0_8055df74.o
./build/obj/sdata2_8055df80_805643b0.o
./build/obj/sbss2_805643c0_805643fc.o

1
data/ranges.csv Normal file
View file

@ -0,0 +1 @@
StartAddress,EndAddress,Section,File,function
1 StartAddress EndAddress Section File function

2
data/symbols.csv Normal file
View file

@ -0,0 +1,2 @@
Address,SymbolName
80006310,__start
1 Address SymbolName
2 80006310 __start

BIN
misc/lmgr8c.dll Executable file

Binary file not shown.

7
misc/lmgr8c.dll.c Normal file
View file

@ -0,0 +1,7 @@
// Implmeentation of lmgr8c which hopefully is enough to run mw(asm|cc|ld)eppc
__declspec(dllexport) void __cdecl lp_checkin(void) {}
__declspec(dllexport) int __cdecl lp_checkout(void) { return 0; }
__declspec(dllexport) const char *__cdecl lp_errstring(void) {
return "no error";
}

5
misc/lmgr8c.dll.def Normal file
View file

@ -0,0 +1,5 @@
LIBRARY lmgr8c
EXPORTS
lp_checkin @ 189
lp_checkout @ 190
lp_errstring @ 191

12
rules.ninja Normal file
View file

@ -0,0 +1,12 @@
rule extract-object
command = cargo run --quiet --release --bin extract-dol -- $in $out $start $end
rule directory
command = mkdir $out
rule genlcf
command = cargo run --quiet --release --bin genlcf -- $out
rule ccld
command = ./run-exe.sh bin/mwcc/mwldeppc.exe $in -o $out -lcf $lcf -fp hard -linkmode moreram -map $map

14
run-exe.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/sh
set -e
#if [[ $(uname -m) != 'x86_64' ]]; then
# echo "Can currently only run on x86_64"
# exit 1
#fi
#if [[ $OSTYPE == 'darwin' ]]; then
# docker run -it --rm --volume $PWD/build/wine:/home/wineuser/.wine --volume $PWD:$PWD -w $PWD scottyhardy/docker-wine wine "$@"
#else
wine "$@"
#fi

72
src/bin/buildgen.rs Normal file
View file

@ -0,0 +1,72 @@
use std::{
fs::File,
io::{BufReader, BufWriter, Write},
num::ParseIntError,
process::Command,
};
use anyhow::Result;
use donut_decomp::{dol::Dol, range::Ranges};
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
u32::from_str_radix(s.trim_start_matches("0x"), 16)
}
fn main() -> Result<()> {
let mut ninja = donut_decomp::ninja::NinjaFile::new("build.ninja")?;
let mut ranges = Ranges::new();
let dolfile = Dol::new(BufReader::new(File::open("bin/donut/donut.dol")?))?;
ranges
.add_range(0x80004000, 0x80006728)
.add_range(0x80006740, 0x800068cc)
.add_range(0x800068e0, 0x800069e4)
.add_range(0x80006a00, 0x80406244)
.add_range(0x80406260, 0x80406530)
.add_range(0x80406540, 0x8040654c)
.add_range(0x80406560, 0x80421030)
.add_range(0x80421040, 0x804966fc)
.add_range(0x80496700, 0x8055640c)
.add_range(0x80556420, 0x8055c6d0)
.add_range(0x8055c6e0, 0x8055df74)
.add_range(0x8055df80, 0x805643b0)
.add_range(0x805643c0, 0x805643fc);
let mut rdr = csv::Reader::from_reader(BufReader::new(File::open("data/ranges.csv")?));
for result in rdr.records() {
let result = result?;
ranges.remove_range(
parse_hex(result.get(0).unwrap())?,
parse_hex(result.get(1).unwrap())?,
);
}
let mut objs = vec![];
for (start, end) in ranges.into_iter() {
let objfile = format!(
"build/obj/{}_{:08x}_{:08x}.o",
dolfile.get_section_name(start),
start,
end
);
ninja.emit_extract("bin/donut/donut.dol", &objfile, start, end)?;
objs.push(objfile);
}
ninja.emit_genlcf("build/donut.lcf")?;
ninja.emit_ccld("build/donut.elf", "build/donut.lcf", &objs)?;
let mut obj_list = BufWriter::new(File::create("data/objects.txt")?);
for obj in objs.iter() {
obj_list.write_all(b"./")?;
obj_list.write_all(obj.as_bytes())?;
obj_list.write_all(b"\n")?;
}
Command::new("ninja")
.arg("build/donut.elf")
.spawn()?
.wait()?;
Ok(())
}

50
src/bin/extract-dol.rs Normal file
View file

@ -0,0 +1,50 @@
use std::{fs::File, io::BufReader, num::ParseIntError};
use anyhow::Result;
use clap::{App, Arg};
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
u32::from_str_radix(s.trim_start_matches("0x"), 16)
}
fn main() -> Result<()> {
let matches = App::new("DOL file extractor")
.version("0.0")
.author("Charlotte D. <darkkirb@darkkirb.de>")
.about("Extracts part of a DOL file section")
.arg(
Arg::with_name("INPUT")
.help("Input DOL file")
.required(true)
.index(1),
)
.arg(
Arg::with_name("OUTPUT")
.help("Output ELF file")
.required(true)
.index(2),
)
.arg(
Arg::with_name("START_ADDRESS")
.help("Start Address")
.required(true)
.index(3),
)
.arg(
Arg::with_name("END_ADDRESS")
.help("End Address")
.required(true)
.index(4),
)
.get_matches();
let input = matches.value_of("INPUT").unwrap();
let output = matches.value_of("OUTPUT").unwrap();
let start_address = parse_hex(matches.value_of("START_ADDRESS").unwrap())?;
let end_address = parse_hex(matches.value_of("END_ADDRESS").unwrap())?;
donut_decomp::dol::Dol::new(BufReader::new(File::open(input)?))?.extract_from_to(
start_address,
end_address,
output,
)?;
Ok(())
}

90
src/bin/genlcf.rs Normal file
View file

@ -0,0 +1,90 @@
use std::{
fs::File,
io::{BufRead, BufReader, BufWriter, Write},
num::ParseIntError,
};
use anyhow::Result;
use clap::{App, Arg};
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
u32::from_str_radix(s.trim_start_matches("0x"), 16)
}
fn main() -> Result<()> {
let matches = App::new("LCF generator")
.version("0.0")
.author("Charlotte D. <darkkirb@darkkirb.de>")
.about("Generates an LCF file")
.arg(
Arg::with_name("OUTPUT")
.help("Output LCF file")
.required(true)
.index(1),
)
.get_matches();
let output = matches.value_of("OUTPUT").unwrap();
let mut out = BufWriter::new(File::create(output)?);
out.write_all(
r#"ENTRY(__start)
MEMORY {
text : origin = 0x80004000
// Dummy address
binary_blobs : origin = 0xA0000000
}
SECTIONS {
GROUP:{
.init ALIGN(0x20):{}
extab_ ALIGN(0x20):{}
extabindex_ ALIGN(0x20):{}
.text ALIGN(0x20):{}
.ctors ALIGN(0x20):{}
.dtors ALIGN(0x20):{}
.rodata ALIGN(0x20):{}
.data ALIGN(0x20):{}
.bss ALIGN(0x80):{}
.sdata ALIGN(0x20):{}
.sbss ALIGN(0x20):{}
.sdata2 ALIGN(0x20):{}
.sbss2 ALIGN(0x20):{}
.stack ALIGN(0x100):{}
} > text
GROUP:{
binary_blobs ALIGN(0x20):{}
} > binary_blobs
_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;
_stack_end = _f_sbss2 + SIZEOF(.sbss2);
_db_stack_addr = (_stack_addr + 0x2000);
_db_stack_end = _stack_addr;
__ArenaLo = (_db_stack_addr + 0x1f) & ~0x1f;
__ArenaHi = 0x81700000;
"#
.as_bytes(),
)?;
let mut rdr = csv::Reader::from_reader(BufReader::new(File::open("data/symbols.csv")?));
for result in rdr.records() {
let result = result?;
out.write_all(
format!(
" {} = 0x{:08x};\n",
result.get(1).unwrap(),
parse_hex(result.get(0).unwrap())?
)
.as_bytes(),
)?;
}
out.write_all(b"}\n")?;
let reader = BufReader::new(File::open("data/objects.txt")?);
out.write_all(b"FORCEFILES { \n")?;
for line in reader.lines() {
let line = line?;
out.write_all(format!("\"{}\"\n", line).as_bytes())?;
}
out.write_all(b"}\n")?;
Ok(())
}

222
src/dol/mod.rs Normal file
View file

@ -0,0 +1,222 @@
//! DOL utilities
use std::{
fs::{self, File},
io::{Read, Seek, SeekFrom, Write},
process::Command,
};
use anyhow::Result;
pub const SECTION_NAMES: &[&str] = &[
".init",
".text",
".text2",
".text3",
".text4",
".text5",
".text6",
"extab",
"extabindex",
".ctors",
".dtors",
".rodata",
".data",
".sdata",
".sdata2",
".data8",
".data9",
".data10",
".bss",
".sbss",
".sbss2",
];
pub const SECTION_BITS: &[&str] = &[
"alloc,readonly,load,code",
"alloc,readonly,load,code",
"alloc,readonly,load,code",
"alloc,readonly,load,code",
"alloc,readonly,load,code",
"alloc,readonly,load,code",
"alloc,readonly,load,code",
"alloc,readonly,load",
"alloc,readonly,load",
"alloc,readonly,load",
"alloc,readonly,load",
"alloc,readonly,load",
"alloc,load",
"alloc,load",
"alloc,load",
"alloc,load",
"alloc,load",
"alloc,load",
"alloc",
"alloc",
"alloc",
];
pub const SECTION_FILE_NAMES: &[&str] = &[
"init",
"text",
"text2",
"text3",
"text4",
"text5",
"text6",
"extab",
"extabindex",
"ctors",
"dtors",
"rodata",
"data",
"sdata",
"sdata2",
"data8",
"data9",
"data10",
"bss",
"sbss",
"sbss2",
];
#[derive(Debug, Clone)]
pub struct Dol<R: Read + Seek + ?Sized> {
file_offsets: [u32; 18],
loading_addresses: [u32; 18],
section_sizes: [u32; 18],
bss_address: u32,
bss_size: u32,
entry_point: u32,
reader: R,
}
impl<R: Read + Seek> Dol<R> {
pub fn new(mut reader: R) -> Result<Self> {
let mut file_offsets: [u32; 18] = [0; 18];
let mut loading_addresses: [u32; 18] = [0; 18];
let mut section_sizes: [u32; 18] = [0; 18];
for file_offset in file_offsets.iter_mut() {
let mut u32_buf = [0u8; 4];
reader.read_exact(&mut u32_buf)?;
*file_offset = u32::from_be_bytes(u32_buf);
}
for loading_address in loading_addresses.iter_mut() {
let mut u32_buf = [0u8; 4];
reader.read_exact(&mut u32_buf)?;
*loading_address = u32::from_be_bytes(u32_buf);
}
for section_size in section_sizes.iter_mut() {
let mut u32_buf = [0u8; 4];
reader.read_exact(&mut u32_buf)?;
*section_size = u32::from_be_bytes(u32_buf);
}
let mut u32_buf = [0u8; 4];
reader.read_exact(&mut u32_buf)?;
let bss_address = u32::from_be_bytes(u32_buf);
reader.read_exact(&mut u32_buf)?;
let bss_size = u32::from_be_bytes(u32_buf);
reader.read_exact(&mut u32_buf)?;
let entry_point = u32::from_be_bytes(u32_buf);
Ok(Self {
file_offsets,
loading_addresses,
section_sizes,
bss_address,
bss_size,
entry_point,
reader,
})
}
fn is_in_bss(&self, address: u32) -> Option<usize> {
if address >= self.loading_addresses[13] + self.section_sizes[13]
&& address < self.loading_addresses[14]
{
return Some(19); // SBSS
}
if address >= self.loading_addresses[14] + self.section_sizes[14] {
return Some(20); // SBSS2
}
if address >= self.bss_address && address < self.bss_address + self.bss_size {
return Some(18);
}
None
}
pub fn read_va_block(&mut self, address: u32, block: &mut [u8]) -> Result<usize> {
for i in 0..18 {
if address < self.loading_addresses[i]
|| address >= self.loading_addresses[i] + self.section_sizes[i]
{
continue;
}
self.reader.seek(SeekFrom::Start(
(self.file_offsets[i] + (address - self.loading_addresses[i])) as u64,
))?;
self.reader.read_exact(block)?;
return Ok(i);
}
if let Some(v) = self.is_in_bss(address) {
for c in block.iter_mut() {
*c = 0;
}
return Ok(v);
}
anyhow::bail!("Unknown virtual address!");
}
pub fn extract_range(&mut self, address: u32, size: u32, outname: &str) -> Result<()> {
let mut data = vec![0u8; size as usize];
let binfile_name = format!("{}.bin", outname);
let section = self.read_va_block(address, &mut data)?;
{
let mut binfile = File::create(&binfile_name)?;
binfile.write_all(&data)?;
}
Command::new("objcopy")
.arg("-I")
.arg("binary")
.arg("-O")
.arg("elf32-big")
.arg(&binfile_name)
.arg(outname)
.arg("--rename-section")
.arg(format!(
".data={},{}",
SECTION_NAMES[section], SECTION_BITS[section]
))
.arg("--change-section-address")
.arg(format!(".data={}", address))
.arg("--strip-all")
.spawn()?
.wait()?;
fs::remove_file(&binfile_name)?;
Command::new("objcopy")
.arg("--format")
.arg("elf32-big")
.arg("--add-symbol")
.arg(format!(
"_LOC_{:08x}={}:0x{:08x},global",
address, SECTION_NAMES[section], address
))
.arg(outname)
.spawn()?
.wait()?;
Ok(())
}
pub fn extract_from_to(&mut self, start: u32, end: u32, outname: &str) -> Result<()> {
self.extract_range(start, end - start, outname)
}
pub fn get_section_name(&self, addr: u32) -> &str {
for i in 0..18 {
if addr >= self.loading_addresses[i]
&& addr < self.loading_addresses[i] + self.section_sizes[i]
{
return SECTION_FILE_NAMES[i];
}
}
if let Some(section) = self.is_in_bss(addr) {
return SECTION_FILE_NAMES[section];
}
unreachable!();
}
}

130
src/ninja/mod.rs Normal file
View file

@ -0,0 +1,130 @@
//! Ninja file generator
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use anyhow::Result;
#[derive(Debug)]
pub struct NinjaFile {
emitted_targets: HashSet<PathBuf>,
writer: BufWriter<File>,
}
impl NinjaFile {
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
let mut this = Self {
emitted_targets: HashSet::new(),
writer: BufWriter::new(File::create(path)?),
};
this.writer.write_all(b"include rules.ninja\n")?;
Ok(this)
}
pub fn emit_mkdir(&mut self, dir: impl AsRef<Path>) -> Result<()> {
let dir = dir.as_ref();
if self.emitted_targets.contains(dir) {
return Ok(());
}
self.emitted_targets.insert(dir.to_owned());
match dir.parent() {
Some(parent) if parent.to_string_lossy() != "" => {
self.writer.write_all(
format!(
"build {}: directory || {}\n",
dir.display(),
parent.display()
)
.as_bytes(),
)?;
self.emit_mkdir(parent)?;
}
_ => {
self.writer
.write_all(format!("build {}: directory\n", dir.display()).as_bytes())?;
}
}
Ok(())
}
pub fn emit_extract(
&mut self,
dolfile: impl AsRef<Path>,
objfile: impl AsRef<Path>,
start_addr: u32,
end_addr: u32,
) -> Result<()> {
let dolfile = dolfile.as_ref();
let objfile = objfile.as_ref();
if self.emitted_targets.contains(objfile) {
return Ok(());
}
self.emitted_targets.insert(objfile.to_owned());
let parent = objfile.parent().unwrap();
self.writer.write_all(
format!(
r#"build {}: extract-object {} || {}
start = {:08x}
end = {:08x}
"#,
objfile.display(),
dolfile.display(),
parent.display(),
start_addr,
end_addr
)
.as_bytes(),
)?;
self.emit_mkdir(parent)?;
Ok(())
}
pub fn emit_genlcf(&mut self, outfile: impl AsRef<Path>) -> Result<()> {
let outfile = outfile.as_ref();
if self.emitted_targets.contains(outfile) {
return Ok(());
}
self.emitted_targets.insert(outfile.to_owned());
let parent = outfile.parent().unwrap();
self.writer.write_all(
format!(
"build {}: genlcf data/symbols.csv || {}\n",
outfile.display(),
parent.display()
)
.as_bytes(),
)?;
self.emit_mkdir(parent)?;
Ok(())
}
pub fn emit_ccld(
&mut self,
outfile: impl AsRef<Path>,
lcffile: impl AsRef<Path>,
objects: &[impl AsRef<Path>],
) -> Result<()> {
let outfile = outfile.as_ref();
let lcffile = lcffile.as_ref();
if self.emitted_targets.contains(outfile) {
return Ok(());
}
self.emitted_targets.insert(outfile.to_owned());
let parent = outfile.parent().unwrap();
self.writer
.write_all(format!("build {}: ccld ", outfile.display()).as_bytes())?;
for object in objects.iter() {
self.writer
.write_all(format!("{} ", object.as_ref().display()).as_bytes())?;
}
self.writer.write_all(
format!(
"| {} data/objects.txt || {}\n lcf={}\n map=build/donut.map\n",
lcffile.display(),
parent.display(),
lcffile.display()
)
.as_bytes(),
)?;
self.emit_mkdir(parent)?;
Ok(())
}
}

69
src/range.rs Normal file
View file

@ -0,0 +1,69 @@
use std::collections::BTreeMap;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ranges {
range_list: BTreeMap<u32, u32>,
}
impl Ranges {
pub fn new() -> Self {
Self {
range_list: BTreeMap::new(),
}
}
pub fn add_range(&mut self, start: u32, end: u32) -> &mut Self {
self.range_list.insert(start, end);
self.merge_ranges();
self
}
pub fn remove_range(&mut self, start: u32, end: u32) -> &mut Self {
let mut range_start = 0;
let mut range_end = 0;
// Find the range it is part of
for (curr_start, curr_end) in self.range_list.iter() {
if *curr_start > start || *curr_end < end {
continue;
}
range_start = *curr_start;
range_end = *curr_end;
}
self.split_range(range_start, start);
self.split_range(start, end);
self.split_range(end, range_end);
self.range_list.remove(&start);
self.merge_ranges();
self
}
fn merge_ranges(&mut self) {
let mut last_start = 0;
let mut last_end = 0;
let mut block_gc_list = vec![];
let mut block_updates = BTreeMap::new();
for (curr_start, curr_end) in self.range_list.iter() {
if *curr_start == *curr_end {
block_gc_list.push(*curr_start);
} else if *curr_start <= last_end {
block_gc_list.push(*curr_start);
last_end = *curr_end;
block_updates.insert(last_start, last_end);
} else {
last_start = *curr_start;
last_end = *curr_end;
}
}
for (block_start, block_end) in block_updates.into_iter() {
self.range_list.insert(block_start, block_end);
}
for block_start in block_gc_list.into_iter() {
self.range_list.remove(&block_start);
}
}
fn split_range(&mut self, start: u32, new_end: u32) {
let end_addr = self.range_list[&start];
self.range_list.insert(start, new_end);
self.range_list.insert(new_end, end_addr);
}
pub fn into_iter(self) -> impl Iterator<Item = (u32, u32)> {
self.range_list.into_iter()
}
}