diff --git a/Cargo.lock b/Cargo.lock index 5864e02..4005b18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" dependencies = [ "backtrace", ] @@ -80,7 +80,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", ] [[package]] @@ -187,6 +187,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -320,6 +330,7 @@ dependencies = [ "multimap", "nintendo-lz", "num_enum", + "objdiff-core", "object", "once_cell", "owo-colors", @@ -397,14 +408,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -469,6 +480,20 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", + "serde", +] + [[package]] name = "hashbrown" version = "0.14.2" @@ -507,9 +532,9 @@ checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "967d6dd42f16dbf0eb8040cb9e477933562684d3918f7d253f2ff9087fb3e7a3" dependencies = [ "equivalent", "hashbrown", @@ -555,9 +580,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" @@ -588,9 +613,9 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" -version = "0.9.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -644,9 +669,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -669,14 +694,37 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", +] + +[[package]] +name = "objdiff-core" +version = "1.0.0" +source = "git+https://github.com/encounter/objdiff?rev=5b9ac93c084bd0a9ae710e8c8195c4b0db939b8a#5b9ac93c084bd0a9ae710e8c8195c4b0db939b8a" +dependencies = [ + "anyhow", + "byteorder", + "cwdemangle", + "filetime", + "flagset", + "globset", + "log", + "memmap2", + "num-traits", + "object", + "ppc750cl", + "semver", + "serde", + "serde_json", + "serde_yaml", + "similar", ] [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "crc32fast", "hashbrown", @@ -775,9 +823,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -796,9 +844,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -825,9 +873,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] @@ -934,31 +982,37 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" -version = "1.0.192" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -973,14 +1027,14 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", ] [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" dependencies = [ "indexmap", "itoa", @@ -1009,6 +1063,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "similar" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" + [[package]] name = "smallvec" version = "1.11.2" @@ -1054,9 +1114,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -1108,7 +1168,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", ] [[package]] @@ -1157,7 +1217,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", ] [[package]] @@ -1312,7 +1372,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -1330,6 +1399,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -1342,6 +1426,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -1354,6 +1444,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -1366,6 +1462,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -1378,6 +1480,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -1390,6 +1498,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -1402,6 +1516,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -1414,6 +1534,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + [[package]] name = "winnow" version = "0.5.19" @@ -1446,5 +1572,5 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.52", ] diff --git a/Cargo.toml b/Cargo.toml index 9415f61..c0705e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ memmap2 = "0.9.0" multimap = "0.9.1" nintendo-lz = "0.1.3" num_enum = "0.7.1" +objdiff-core = { git = "https://github.com/encounter/objdiff", rev = "5b9ac93c084bd0a9ae710e8c8195c4b0db939b8a", features = ["ppc"] } object = { version = "0.32.1", features = ["read_core", "std", "elf", "write_std"], default-features = false } once_cell = "1.18.0" owo-colors = { version = "3.5.0", features = ["supports-colors"] } diff --git a/src/cmd/dol.rs b/src/cmd/dol.rs index 7abed32..6ca651e 100644 --- a/src/cmd/dol.rs +++ b/src/cmd/dol.rs @@ -836,7 +836,8 @@ fn split_write_obj( } debug!("Splitting {} objects", module.obj.link_order.len()); - let split_objs = split_obj(&module.obj)?; + let module_name = module.config.name().to_string(); + let split_objs = split_obj(&module.obj, Some(module_name.as_str()))?; debug!("Writing object files"); DirBuilder::new() @@ -855,7 +856,7 @@ fn split_write_obj( module.obj.symbols.by_name("_prolog")?.map(|(_, s)| s.name.clone()) }; let mut out_config = OutputModule { - name: module.config.name().to_string(), + name: module_name, module_id, ldscript: out_dir.join("ldscript.lcf"), units: Vec::with_capacity(split_objs.len()), diff --git a/src/cmd/elf.rs b/src/cmd/elf.rs index 0a677a5..4d8699b 100644 --- a/src/cmd/elf.rs +++ b/src/cmd/elf.rs @@ -8,6 +8,7 @@ use std::{ use anyhow::{anyhow, bail, ensure, Context, Result}; use argp::FromArgs; +use objdiff_core::obj::split_meta::{SplitMeta, SPLITMETA_SECTION}; use object::{ elf, write::{Mangling, SectionId, SymbolId}, @@ -148,7 +149,7 @@ fn disasm(args: DisasmArgs) -> Result<()> { match obj.kind { ObjKind::Executable => { log::info!("Splitting {} objects", obj.link_order.len()); - let split_objs = split_obj(&obj)?; + let split_objs = split_obj(&obj, None)?; let asm_dir = args.out.join("asm"); let include_dir = args.out.join("include"); @@ -183,7 +184,7 @@ fn split(args: SplitArgs) -> Result<()> { let mut file_map = HashMap::>::new(); - let split_objs = split_obj(&obj)?; + let split_objs = split_obj(&obj, None)?; for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { let out_obj = write_elf(split_obj, false)?; match file_map.entry(unit.name.clone()) { @@ -596,5 +597,27 @@ fn info(args: InfoArgs) -> Result<()> { } } + if let Some(split_meta_section) = in_file.section_by_name(SPLITMETA_SECTION) { + let data = split_meta_section.uncompressed_data()?; + if !data.is_empty() { + let meta = + SplitMeta::from_reader(&mut data.as_ref(), in_file.endianness(), in_file.is_64()) + .context("While reading .splitmeta section")?; + println!("\nSplit metadata (.splitmeta):"); + if let Some(generator) = &meta.generator { + println!("\tGenerator: {}", generator); + } + if let Some(virtual_addresses) = &meta.virtual_addresses { + println!("\tVirtual addresses:"); + println!("\t{: >10} | {: <10}", "Addr", "Symbol"); + for (symbol, addr) in in_file.symbols().zip(virtual_addresses) { + if symbol.is_definition() { + println!("\t{: >10} | {: <10}", format!("{:#X}", addr), symbol.name()?); + } + } + } + } + } + Ok(()) } diff --git a/src/cmd/rel.rs b/src/cmd/rel.rs index 8d8f374..85c87e7 100644 --- a/src/cmd/rel.rs +++ b/src/cmd/rel.rs @@ -485,7 +485,7 @@ fn merge(args: MergeArgs) -> Result<()> { align: mod_section.align, elf_index: mod_section.elf_index, relocations: Default::default(), - original_address: mod_section.original_address, + virtual_address: mod_section.virtual_address, file_offset: mod_section.file_offset, section_known: mod_section.section_known, splits: mod_section.splits.clone(), diff --git a/src/main.rs b/src/main.rs index a1aa8d8..afee2ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,7 +100,7 @@ enum SubCommand { // Duplicated from supports-color so we can check early. fn env_no_color() -> bool { match env::var("NO_COLOR").as_deref() { - Ok("0") | Err(_) => false, + Ok("") | Ok("0") | Err(_) => false, Ok(_) => true, } } diff --git a/src/obj/mod.rs b/src/obj/mod.rs index fb3750d..8f8feff 100644 --- a/src/obj/mod.rs +++ b/src/obj/mod.rs @@ -10,6 +10,7 @@ use std::{ }; use anyhow::{anyhow, bail, ensure, Result}; +use objdiff_core::obj::split_meta::SplitMeta; pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations}; pub use sections::{ObjSection, ObjSectionKind, ObjSections}; pub use splits::{ObjSplit, ObjSplits}; @@ -55,6 +56,7 @@ pub struct ObjInfo { pub sections: ObjSections, pub entry: Option, pub mw_comment: Option, + pub split_meta: Option, // Linker generated pub sda2_base: Option, @@ -94,6 +96,7 @@ impl ObjInfo { sections: ObjSections::new(kind, sections), entry: None, mw_comment: Default::default(), + split_meta: None, sda2_base: None, sda_base: None, stack_address: None, diff --git a/src/obj/sections.rs b/src/obj/sections.rs index 59a90e0..6c0f4bf 100644 --- a/src/obj/sections.rs +++ b/src/obj/sections.rs @@ -28,7 +28,7 @@ pub struct ObjSection { /// REL files reference the original ELF section indices pub elf_index: usize, pub relocations: ObjRelocations, - pub original_address: u64, + pub virtual_address: Option, pub file_offset: u64, pub section_known: bool, pub splits: ObjSplits, diff --git a/src/util/asm.rs b/src/util/asm.rs index 5d125ba..3618f3e 100644 --- a/src/util/asm.rs +++ b/src/util/asm.rs @@ -89,7 +89,7 @@ where W: Write + ?Sized { .or_else(|| vec.iter().find(|e| e.kind == SymbolEntryKind::Start)) .map(|e| e.index); if target_symbol_idx.is_none() { - let display_address = address as u64 + section.original_address; + let display_address = address as u64 + section.virtual_address.unwrap_or(0); let symbol_idx = symbols.len(); symbols.push(ObjSymbol { name: format!(".L_{display_address:08X}"), @@ -148,7 +148,7 @@ where W: Write + ?Sized { .iter() .any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start) { - let display_address = address + target_section.original_address; + let display_address = address + target_section.virtual_address.unwrap_or(0); let symbol_idx = symbols.len(); symbols.push(ObjSymbol { name: format!(".L_{display_address:08X}"), @@ -246,7 +246,7 @@ where for ins in disasm_iter(data, address) { let reloc = relocations.get(&ins.addr); let file_offset = section.file_offset + (ins.addr as u64 - section.address); - write_ins(w, symbols, ins, reloc, file_offset, section.original_address)?; + write_ins(w, symbols, ins, reloc, file_offset, section.virtual_address)?; } Ok(()) } @@ -257,7 +257,7 @@ fn write_ins( mut ins: Ins, reloc: Option<&ObjReloc>, file_offset: u64, - section_address: u64, + section_vaddr: Option, ) -> Result<()> where W: Write + ?Sized, @@ -265,7 +265,7 @@ where write!( w, "/* {:08X} {:08X} {:02X} {:02X} {:02X} {:02X} */\t", - ins.addr as u64 + section_address, + ins.addr as u64 + section_vaddr.unwrap_or(0), file_offset, (ins.code >> 24) & 0xFF, (ins.code >> 16) & 0xFF, @@ -466,7 +466,7 @@ where let dbg_symbols = vec.iter().map(|e| &symbols[e.index]).collect_vec(); bail!( "Unaligned symbol entry @ {:#010X}:\n\t{:?}", - section.original_address as u32 + sym_addr, + section.virtual_address.unwrap_or(0) as u32 + sym_addr, dbg_symbols ); } @@ -838,11 +838,12 @@ fn write_section_header( where W: Write + ?Sized, { + let section_virtual_address = section.virtual_address.unwrap_or(0); writeln!( w, "\n# {:#010X} - {:#010X}", - start as u64 + section.original_address, - end as u64 + section.original_address + start as u64 + section_virtual_address, + end as u64 + section_virtual_address )?; match section.name.as_str() { ".text" if subsection == 0 => { diff --git a/src/util/dol.rs b/src/util/dol.rs index a76e001..3d9e127 100644 --- a/src/util/dol.rs +++ b/src/util/dol.rs @@ -430,7 +430,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { align: 0, elf_index: 0, relocations: Default::default(), - original_address: 0, + virtual_address: Some(dol_section.address as u64), file_offset: dol_section.file_offset as u64, section_known: known, splits: Default::default(), @@ -460,7 +460,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { align: 0, elf_index: 0, relocations: Default::default(), - original_address: 0, + virtual_address: Some(addr as u64), file_offset: 0, section_known: false, splits: Default::default(), @@ -480,7 +480,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { align: 0, elf_index: 0, relocations: Default::default(), - original_address: 0, + virtual_address: Some(bss_section.address as u64), file_offset: 0, section_known: false, splits: Default::default(), @@ -507,7 +507,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { align: 0, elf_index: 0, relocations: Default::default(), - original_address: 0, + virtual_address: Some(bss_sections[0].0 as u64), file_offset: 0, section_known: false, splits: Default::default(), @@ -521,7 +521,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { align: 0, elf_index: 0, relocations: Default::default(), - original_address: 0, + virtual_address: Some(bss_sections[1].0 as u64), file_offset: 0, section_known: false, splits: Default::default(), diff --git a/src/util/elf.rs b/src/util/elf.rs index c450e7a..c23da5c 100644 --- a/src/util/elf.rs +++ b/src/util/elf.rs @@ -9,6 +9,7 @@ use anyhow::{anyhow, bail, ensure, Context, Result}; use cwdemangle::demangle; use flagset::Flags; use indexmap::IndexMap; +use objdiff_core::obj::split_meta::{SplitMeta, SHT_SPLITMETA, SPLITMETA_SECTION}; use object::{ elf, elf::{SHF_ALLOC, SHF_EXECINSTR, SHF_WRITE, SHT_NOBITS, SHT_PROGBITS}, @@ -95,7 +96,7 @@ where P: AsRef { align: section.align(), elf_index: section.index().0, relocations: Default::default(), - original_address: 0, // TODO load from abs symbol + virtual_address: None, // Loaded from section symbol file_offset: section.file_range().map(|(v, _)| v).unwrap_or_default(), section_known: true, splits: Default::default(), @@ -127,6 +128,26 @@ where P: AsRef { None }; + let split_meta = if let Some(split_meta_section) = obj_file.section_by_name(SPLITMETA_SECTION) { + let data = split_meta_section.uncompressed_data()?; + if data.is_empty() { + None + } else { + let mut reader = Cursor::new(&*data); + let metadata = + SplitMeta::from_reader(&mut reader, obj_file.endianness(), obj_file.is_64()) + .context("While reading .splitmeta section")?; + log::debug!("Loaded .splitmeta section"); + ensure!( + data.len() - reader.position() as usize == 0, + ".splitmeta section data not fully read" + ); + Some(metadata) + } + } else { + None + }; + let mut symbols: Vec = vec![]; let mut symbol_indexes: Vec> = vec![]; let mut section_starts = IndexMap::>::new(); @@ -209,6 +230,16 @@ where P: AsRef { let section_index = symbol .section_index() .ok_or_else(|| anyhow!("Section symbol without section"))?; + + // Resolve original address from split metadata + if let Some(addr) = split_meta + .as_ref() + .and_then(|m| m.virtual_addresses.as_ref()) + .and_then(|v| v.get(symbol.index().0).cloned()) + { + sections[section_index.0].virtual_address = Some(addr); + } + let section = obj_file.section_by_index(section_index)?; let section_name = section.name()?.to_string(); match &mut boundary_state { @@ -335,6 +366,7 @@ where P: AsRef { let mut obj = ObjInfo::new(kind, architecture, obj_name, symbols, sections); obj.entry = NonZeroU64::new(obj_file.entry()).map(|n| n.get()); obj.mw_comment = mw_comment.map(|(header, _)| header); + obj.split_meta = split_meta; obj.sda2_base = sda2_base; obj.sda_base = sda_base; obj.stack_address = stack_address; @@ -348,7 +380,7 @@ where P: AsRef { pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { let mut out_data = Vec::new(); - let mut writer = object::write::elf::Writer::new(Endianness::Big, false, &mut out_data); + let mut writer = Writer::new(Endianness::Big, false, &mut out_data); struct OutSection { index: SectionIndex, @@ -357,6 +389,7 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { rela_offset: usize, name: StringId, rela_name: Option, + virtual_address: Option, } struct OutSymbol { #[allow(dead_code)] @@ -376,6 +409,7 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { rela_offset: 0, name, rela_name: None, + virtual_address: section.virtual_address, }); } @@ -395,11 +429,12 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { writer.reserve_strtab_section_index(); writer.reserve_shstrtab_section_index(); - // Generate comment section + // Generate .comment section let mut comment_data = if let Some(mw_comment) = &obj.mw_comment { - let mut comment_data = Vec::::with_capacity(0x2C + obj.symbols.count() * 8); + // Reserve section let name = writer.add_section_name(".comment".as_bytes()); let index = writer.reserve_section_index(); + let out_section_idx = out_sections.len(); out_sections.push(OutSection { index, rela_index: None, @@ -407,12 +442,42 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { rela_offset: 0, name, rela_name: None, + virtual_address: None, }); + + // Generate .comment data + let mut comment_data = Vec::::with_capacity(0x2C + obj.symbols.count() * 8); mw_comment.to_writer_static(&mut comment_data, Endian::Big)?; // Null symbol CommentSym { align: 0, vis_flags: 0, active_flags: 0 } .to_writer_static(&mut comment_data, Endian::Big)?; - Some(comment_data) + Some((comment_data, out_section_idx)) + } else { + None + }; + + // Generate .splitmeta section + let mut split_meta = if let Some(metadata) = &obj.split_meta { + // Reserve section + let name = writer.add_section_name(SPLITMETA_SECTION.as_bytes()); + let index = writer.reserve_section_index(); + let out_section_idx = out_sections.len(); + out_sections.push(OutSection { + index, + rela_index: None, + offset: 0, + rela_offset: 0, + name, + rela_name: None, + virtual_address: None, + }); + + // Generate .splitmeta data + let mut out = metadata.clone(); + out.virtual_addresses = Some(vec![ + 0, // Null symbol + ]); + Some((out, out_section_idx)) } else { None }; @@ -449,10 +514,15 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { st_size: 0, }, }); - if let Some(comment_data) = &mut comment_data { + if let Some((comment_data, _)) = &mut comment_data { CommentSym { align: 1, vis_flags: 0, active_flags: 0 } .to_writer_static(comment_data, Endian::Big)?; } + if let Some(virtual_addresses) = + split_meta.as_mut().and_then(|(m, _)| m.virtual_addresses.as_mut()) + { + virtual_addresses.push(0); + } section_symbol_offset += 1; } @@ -472,10 +542,15 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { }; num_local = writer.symbol_count(); out_symbols.push(OutSymbol { index, sym }); - if let Some(comment_data) = &mut comment_data { + if let Some((comment_data, _)) = &mut comment_data { CommentSym { align: section.align as u32, vis_flags: 0, active_flags: 0 } .to_writer_static(comment_data, Endian::Big)?; } + if let Some(virtual_addresses) = + split_meta.as_mut().and_then(|(m, _)| m.virtual_addresses.as_mut()) + { + virtual_addresses.push(section.virtual_address.unwrap_or(0)); + } } } @@ -495,7 +570,8 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { continue; } - let section_index = symbol.section.and_then(|idx| out_sections.get(idx)).map(|s| s.index); + let section = symbol.section.and_then(|idx| out_sections.get(idx)); + let section_index = section.map(|s| s.index); let index = writer.reserve_symbol_index(section_index); let name_index = if symbol.name.is_empty() { None @@ -539,9 +615,18 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { } out_symbols.push(OutSymbol { index, sym }); symbol_map[symbol_index] = Some(index.0); - if let Some(comment_data) = &mut comment_data { + if let Some((comment_data, _)) = &mut comment_data { CommentSym::from(symbol, export_all).to_writer_static(comment_data, Endian::Big)?; } + if let Some(virtual_addresses) = + split_meta.as_mut().and_then(|(m, _)| m.virtual_addresses.as_mut()) + { + if let Some(section_vaddr) = section.and_then(|s| s.virtual_address) { + virtual_addresses.push(section_vaddr + symbol.address); + } else { + virtual_addresses.push(0); + } + } } writer.reserve_file_header(); @@ -576,12 +661,18 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { writer.reserve_strtab(); writer.reserve_shstrtab(); - // Reserve comment section - if let Some(comment_data) = &comment_data { - let out_section = out_sections.last_mut().unwrap(); + // Reserve .comment section + if let Some((comment_data, idx)) = &comment_data { + let out_section = &mut out_sections[*idx]; out_section.offset = writer.reserve(comment_data.len(), 32); } + // Reserve .splitmeta section + if let Some((metadata, idx)) = &split_meta { + let out_section = &mut out_sections[*idx]; + out_section.offset = writer.reserve(metadata.write_size(false), 32); + } + writer.reserve_section_headers(); writer.write_file_header(&object::write::elf::FileHeader { @@ -688,13 +779,24 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { writer.write_shstrtab(); // Write comment section - if let Some(comment_data) = &comment_data { - let out_section = out_sections.last().unwrap(); + if let Some((comment_data, idx)) = &comment_data { + let out_section = &out_sections[*idx]; writer.write_align(32); ensure!(writer.len() == out_section.offset); writer.write(comment_data); } + // Write .splitmeta section + if let Some((metadata, idx)) = &split_meta { + let out_section = &out_sections[*idx]; + writer.write_align(32); + ensure!(writer.len() == out_section.offset); + // object::write::elf::Writer doesn't implement std::io::Write... + let mut data = Vec::with_capacity(metadata.write_size(false)); + metadata.to_writer(&mut data, object::BigEndian, false)?; + writer.write(&data); + } + writer.write_null_section_header(); for ((_, section), out_section) in obj.sections.iter().zip(&out_sections) { writer.write_section_header(&SectionHeader { @@ -737,9 +839,9 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { writer.write_strtab_section_header(); writer.write_shstrtab_section_header(); - // Write comment section header - if let Some(comment_data) = &comment_data { - let out_section = out_sections.last().unwrap(); + // Write .comment section header + if let Some((comment_data, idx)) = &comment_data { + let out_section = &out_sections[*idx]; writer.write_section_header(&SectionHeader { name: Some(out_section.name), sh_type: SHT_PROGBITS, @@ -754,6 +856,23 @@ pub fn write_elf(obj: &ObjInfo, export_all: bool) -> Result> { }); } + // Write .splitmeta section header + if let Some((metadata, idx)) = &split_meta { + let out_section = &out_sections[*idx]; + writer.write_section_header(&SectionHeader { + name: Some(out_section.name), + sh_type: SHT_SPLITMETA, + sh_flags: 0, + sh_addr: 0, + sh_offset: out_section.offset as u64, + sh_size: metadata.write_size(false) as u64, + sh_link: 0, + sh_info: 0, + sh_addralign: 1, + sh_entsize: 1, + }); + } + ensure!(writer.reserved_len() == writer.len()); Ok(out_data) } diff --git a/src/util/rel.rs b/src/util/rel.rs index 51d8175..09ff650 100644 --- a/src/util/rel.rs +++ b/src/util/rel.rs @@ -420,7 +420,7 @@ where R: Read + Seek + ?Sized { .unwrap_or_default() as u64, elf_index: idx, relocations: Default::default(), - original_address: 0, + virtual_address: None, // TODO option to set? file_offset: offset as u64, section_known, splits: Default::default(), diff --git a/src/util/rso.rs b/src/util/rso.rs index e55c79b..a369ee7 100644 --- a/src/util/rso.rs +++ b/src/util/rso.rs @@ -410,7 +410,7 @@ where R: Read + Seek + ?Sized { align: 0, elf_index: idx as usize, relocations: Default::default(), - original_address: 0, + virtual_address: None, // TODO option to set? file_offset: offset as u64, section_known: false, splits: Default::default(), diff --git a/src/util/split.rs b/src/util/split.rs index 94d0f62..7b66700 100644 --- a/src/util/split.rs +++ b/src/util/split.rs @@ -5,6 +5,7 @@ use std::{ use anyhow::{anyhow, bail, ensure, Context, Result}; use itertools::Itertools; +use objdiff_core::obj::split_meta::SplitMeta; use petgraph::{graph::NodeIndex, Graph}; use sanitise_file_name::sanitize_with_options; use tracing_attributes::instrument; @@ -882,7 +883,7 @@ fn resolve_link_order(obj: &ObjInfo) -> Result> { /// Split an object into multiple relocatable objects. #[instrument(level = "debug", skip(obj))] -pub fn split_obj(obj: &ObjInfo) -> Result> { +pub fn split_obj(obj: &ObjInfo, module_name: Option<&str>) -> Result> { let mut objects: Vec = vec![]; let mut object_symbols: Vec>> = vec![]; let mut name_to_obj: HashMap = HashMap::new(); @@ -903,6 +904,12 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { } else { split_obj.mw_comment = obj.mw_comment.clone(); } + split_obj.split_meta = Some(SplitMeta { + generator: Some(format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))), + module_name: module_name.map(str::to_string), + module_id: Some(obj.module_id), + virtual_addresses: None, + }); objects.push(split_obj); } @@ -1083,7 +1090,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { align, elf_index: out_section_idx + 1, relocations: ObjRelocations::new(out_relocations)?, - original_address: current_address.address as u64, + virtual_address: Some(current_address.address as u64), file_offset: section.file_offset + (current_address.address as u64 - section.address), section_known: true, @@ -1148,7 +1155,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { else { bail!( "Bad extabindex relocation @ {:#010X}", - reloc_address as u64 + section.original_address + reloc_address as u64 + section.virtual_address.unwrap_or(0) ); }; let target_section = &obj.sections.at_address(target_addr)?.1.name; @@ -1158,9 +1165,9 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { \tTarget object: {}:{:#010X} ({})\n\ \tTarget symbol: {:#010X} ({})\n\ This will cause the linker to crash.\n", - reloc_address as u64 + section.original_address, + reloc_address as u64 + section.virtual_address.unwrap_or(0), section.name, - section.original_address, + section.virtual_address.unwrap_or(0), out_obj.name, target_section, target_addr,