From 3c25712e5ab70d361fc10a8580af3bbbd5a2c678 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sat, 15 Sep 2018 13:39:39 +0000 Subject: [PATCH] Insert a debuginfo.elf file in the RomFs when running cargo nro This file is an ELF containing only the debug section of the original ELF. It should be similar to having ran objcopy --keep-only-debug on the original elf (although it is reimplemented in rust, and is not yet perfect). --- Cargo.lock | 48 ++++++++++++++++++ Cargo.toml | 4 +- src/bin/cargo-nro.rs | 115 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac34a57..2cbeb74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,6 +198,16 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "goblin" +version = "0.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.5" @@ -226,7 +236,9 @@ dependencies = [ "cargo_metadata 0.6.0 (git+https://github.com/roblabla/cargo_metadata)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "lz4 1.22.0 (git+https://github.com/bozaro/lz4-rs.git)", + "scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -234,6 +246,14 @@ dependencies = [ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "log" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lz4" version = "1.22.0" @@ -263,6 +283,11 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.9" @@ -331,6 +356,24 @@ dependencies = [ "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "scroll" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scroll_derive 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scroll_derive" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.9.0" @@ -546,13 +589,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum goblin 0.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5911d7df7b8f65ab676c5327b50acea29d3c6a1a4ad05e444cf5dce321b26db2" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" "checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" "checksum lz4 1.22.0 (git+https://github.com/bozaro/lz4-rs.git)" = "" "checksum lz4-sys 1.8.0 (git+https://github.com/bozaro/lz4-rs.git)" = "" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum quote 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b71f9f575d55555aa9c06188be9d4e2bfc83ed02537948ac0d520c24d0419f1a" @@ -562,6 +608,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" +"checksum scroll 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66f024a8cc5e456eb870f688dbd899c84f61190c82c7a911e40f926941969074" +"checksum scroll_derive 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f9a353f5dd99e42ff097d5a61db3257aa2c7127d76a3fa8287b642ef9ae0f7c5" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920" diff --git a/Cargo.toml b/Cargo.toml index e6e0839..7b5ce13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,13 @@ byteorder = "1" lz4 = { git = "https://github.com/bozaro/lz4-rs.git"} clap = {version = "2", optional = true} sha2 = "0.7.1" +scroll = { version = "0.9.0", optional = true } serde = "1" serde_derive = "1" serde_json = "1" cargo_metadata = { git = "https://github.com/roblabla/cargo_metadata", optional = true } url = "1.7.1" +goblin = { version = "0.0.17", optional = true } [features] -binaries = ["clap", "cargo_metadata"] +binaries = ["clap", "cargo_metadata", "scroll", "goblin"] diff --git a/src/bin/cargo-nro.rs b/src/bin/cargo-nro.rs index 18a8720..93bf1c6 100644 --- a/src/bin/cargo-nro.rs +++ b/src/bin/cargo-nro.rs @@ -7,15 +7,22 @@ extern crate serde_json; #[macro_use] extern crate serde_derive; extern crate cargo_metadata; +extern crate goblin; +extern crate scroll; use std::env::{self, VarError}; use std::process::{Command, Stdio}; use std::path::{Path, PathBuf}; use std::fs::File; +use std::io::{Write, Read}; +use scroll::IOwrite; + use linkle::format::{romfs::RomFs, nxo::NxoFile, nacp::NacpFile}; use cargo_metadata::{Package, Message}; use clap::{Arg, App}; use url::Url; +use goblin::elf::{Elf, Header as ElfHeader, ProgramHeader}; +use goblin::elf::section_header::{SHT_NOBITS, SHT_SYMTAB, SHT_STRTAB}; fn find_project_root(path: &Path) -> Option<&Path> { for parent in path.ancestors() { @@ -69,6 +76,106 @@ fn get_metadata(manifest_path: &Path, package_id: &str, target_name: &str) -> (P (package, package_metadata) } +trait BetterIOWrite: IOwrite { + fn iowrite_with_try + scroll::ctx::TryIntoCtx>(&mut self, n: N, ctx: Ctx) + -> Result<(), N::Error> + where + N::Error: From + { + let mut buf = [0u8; 256]; + let size = N::size_with(&ctx); + let buf = &mut buf[0..size]; + n.try_into_ctx(buf, ctx)?; + self.write_all(buf)?; + Ok(()) + } +} + +impl + ?Sized> BetterIOWrite for W {} + +fn generate_debuginfo_romfs>(elf_path: &Path, romfs: Option

) -> goblin::error::Result { + let mut elf_file = File::open(elf_path)?; + let mut buffer = Vec::new(); + elf_file.read_to_end(&mut buffer)?; + let elf = goblin::elf::Elf::parse(&buffer)?; + let new_file = { + let mut new_path = PathBuf::from(elf_path); + new_path.set_extension("debug"); + let mut file = File::create(&new_path)?; + let Elf { + mut header, + program_headers, + mut section_headers, + is_64, + little_endian, + .. + } = elf; + + let ctx = goblin::container::Ctx { + container: if is_64 { goblin::container::Container::Big } else { goblin::container::Container::Little }, + le: if little_endian { goblin::container::Endian::Little } else { goblin::container::Endian::Big } + }; + + for section in section_headers.iter_mut() { + if section.sh_type == SHT_NOBITS || section.sh_type == SHT_SYMTAB || section.sh_type == SHT_STRTAB { + continue; + } + if let Some(Ok(s)) = elf.shdr_strtab.get(section.sh_name) { + if !(s.starts_with(".debug") || s == ".comment") { + section.sh_type = SHT_NOBITS; + } + } + } + + // Calculate section data length + elf/program headers + let data_off = ElfHeader::size(&ctx) + ProgramHeader::size(&ctx) * program_headers.len(); + let shoff = data_off as u64 + section_headers.iter().map(|v| { + if v.sh_type != SHT_NOBITS { + v.sh_size + } else { + 0 + } + }).sum::(); + + // Write ELF header + // TODO: Anything else? + header.e_phoff = ::std::mem::size_of::() as u64; + header.e_shoff = shoff; + file.iowrite_with(header, ctx)?; + + // Write program headers + for phdr in program_headers { + file.iowrite_with_try(phdr, ctx)?; + } + + // Write section data + let mut cur_idx = data_off; + for section in section_headers.iter_mut().filter(|v| v.sh_type != SHT_NOBITS) { + file.write_all(&buffer[section.sh_offset as usize..(section.sh_offset + section.sh_size) as usize])?; + section.sh_offset = cur_idx as u64; + cur_idx += section.sh_size as usize; + } + + // Write section headers + for section in section_headers { + file.iowrite_with(section, ctx)?; + } + + file.sync_all()?; + new_path + }; + + let mut romfs = if let Some(romfs) = romfs { + RomFs::from_directory(romfs.as_ref())? + } else { + RomFs::empty() + }; + + romfs.push_file(&new_file, String::from("debug_info.elf"))?; + + Ok(romfs) +} + #[derive(Debug, Serialize, Deserialize, Default)] struct PackageMetadata { romfs: Option, @@ -178,18 +285,14 @@ fn main() { nacp.title_id = target_metadata.title_id; } - let romfs = if let Some(romfs) = romfs { - Some(RomFs::from_directory(&romfs).unwrap()) - } else { - None - }; + let romfs = generate_debuginfo_romfs(Path::new(&artifact.filenames[0]), romfs).unwrap(); let mut new_name = PathBuf::from(artifact.filenames[0].clone()); assert!(new_name.set_extension("nro")); NxoFile::from_elf(&artifact.filenames[0]).unwrap() .write_nro(&mut File::create(new_name.clone()).unwrap(), - romfs, + Some(romfs), icon_file, Some(nacp) ).unwrap();