Skip to content

Commit

Permalink
Insert a debuginfo.elf file in the RomFs when running cargo nro
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
roblabla committed Sep 15, 2018
1 parent a4fb879 commit 3c25712
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 7 deletions.
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
115 changes: 109 additions & 6 deletions src/bin/cargo-nro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -69,6 +76,106 @@ fn get_metadata(manifest_path: &Path, package_id: &str, target_name: &str) -> (P
(package, package_metadata)
}

trait BetterIOWrite<Ctx: Copy>: IOwrite<Ctx> {
fn iowrite_with_try<N: scroll::ctx::SizeWith<Ctx, Units = usize> + scroll::ctx::TryIntoCtx<Ctx>>(&mut self, n: N, ctx: Ctx)
-> Result<(), N::Error>
where
N::Error: From<std::io::Error>
{
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<Ctx: Copy, W: IOwrite<Ctx> + ?Sized> BetterIOWrite<Ctx> for W {}

fn generate_debuginfo_romfs<P: AsRef<Path>>(elf_path: &Path, romfs: Option<P>) -> goblin::error::Result<RomFs> {
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::<u64>();

// Write ELF header
// TODO: Anything else?
header.e_phoff = ::std::mem::size_of::<ElfHeader>() 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<String>,
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 3c25712

Please sign in to comment.