Skip to content

Commit

Permalink
Merge pull request #23 from roblabla/master
Browse files Browse the repository at this point in the history
Inject debuginfo ELF into romfs
  • Loading branch information
roblabla committed Sep 20, 2018
2 parents 6b224ec + 3c25712 commit 3446a71
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 61 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"]
117 changes: 112 additions & 5 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 linkle::format::{nxo::NxoFile, nacp::NacpFile};
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 @@ -154,9 +261,6 @@ fn main() {
None
};

let romfs = romfs.map(|v| v.to_string_lossy().into_owned());
let romfs = romfs.as_ref().map(|v: &String| v.as_ref());

let icon_file = if let Some(icon) = target_metadata.icon {
let icon_path = root.join(icon);
if !icon_path.is_file() {
Expand All @@ -181,11 +285,14 @@ fn main() {
nacp.title_id = target_metadata.title_id;
}

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
11 changes: 8 additions & 3 deletions src/bin/linkle_clap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ extern crate linkle;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};

use std::fs::OpenOptions;
use std::path::Path;
use std::process;

fn create_nxo(format: &str, matches: &ArgMatches) -> std::io::Result<()> {
let input_file = matches.value_of("INPUT_FILE").unwrap();
let output_file = matches.value_of("OUTPUT_FILE").unwrap();
let romfs_dir = matches.value_of("ROMFS_PATH");
let icon_file = matches.value_of("ICON_PATH");
let romfs_dir = if let Some(romfs_path) = matches.value_of_os("ROMFS_PATH") {
Some(linkle::format::romfs::RomFs::from_directory(Path::new(romfs_path))?)
} else {
None
};
let nacp_file = if let Some(nacp_path) = matches.value_of("NACP_FILE") {
Some(linkle::format::nacp::NacpFile::from_file(nacp_path)?)
} else {
Expand Down Expand Up @@ -50,9 +55,9 @@ fn create_nacp(matches: &ArgMatches) -> std::io::Result<()> {
}

fn create_romfs(matches: &ArgMatches) -> std::io::Result<()> {
let input_directory = matches.value_of("INPUT_DIRECTORY").unwrap();
let input_directory = matches.value_of_os("INPUT_DIRECTORY").unwrap();
let output_file = matches.value_of("OUTPUT_FILE").unwrap();
let romfs = linkle::format::romfs::RomFs::from_directory(input_directory)?;
let romfs = linkle::format::romfs::RomFs::from_directory(Path::new(input_directory))?;
let mut option = OpenOptions::new();
let output_option = option.write(true).create(true).truncate(true);
romfs.write(&mut output_option.open(output_file)?)?;
Expand Down
18 changes: 16 additions & 2 deletions src/format/nacp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl NacpFile {
let default_lang_entry = NacpLangEntry { name, author };
match lang_entries {
None => {
for _ in 0..15 {
for _ in 0..16 {
self.write_lang_entry(output_writter, &default_lang_entry)?;
}
}
Expand Down Expand Up @@ -234,10 +234,12 @@ impl NacpFile {
self.write_lang_entry(
output_writter,
&lang_entries
.zh_tw
.zh_cn
.clone()
.unwrap_or(default_lang_entry.clone()),
)?;
// There are 16 entries. One is missing :eyes:
self.write_lang_entry(output_writter, &default_lang_entry)?;
}
}

Expand Down Expand Up @@ -279,3 +281,15 @@ impl NacpFile {
Ok(())
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn nacp_is_4000_size() {
let mut buf = Vec::new();
NacpFile::default().write(&mut buf).unwrap();
assert_eq!(buf.len(), 0x4000, "Nacp length is wrong");
}
}
9 changes: 3 additions & 6 deletions src/format/nxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl NxoFile {
})
}

pub fn write_nro<T>(&mut self, output_writter: &mut T, romfs: Option<&str>, icon: Option<&str>, nacp: Option<NacpFile>) -> std::io::Result<()>
pub fn write_nro<T>(&mut self, output_writter: &mut T, romfs: Option<RomFs>, icon: Option<&str>, nacp: Option<NacpFile>) -> std::io::Result<()>
where
T: Write,
{
Expand Down Expand Up @@ -234,7 +234,7 @@ impl NxoFile {
output_writter.write_u32::<LittleEndian>(0)?; // version

// Offset to the next available region.
let mut offset = total_len as u64 + 8 + 16 + 16 + 16;
let mut offset = 8 + 16 + 16 + 16;

let icon_len = if let Some(icon) = &icon {
// TODO: Check if icon is a 256x256 JPEG. Convert it if it isn't?
Expand Down Expand Up @@ -263,15 +263,12 @@ impl NxoFile {

offset += nacp_len;

let romfs = if let Some(romfs) = &romfs {
let romfs = RomFs::from_directory(romfs)?;
if let Some(romfs) = &romfs {
output_writter.write_u64::<LittleEndian>(offset)?;
output_writter.write_u64::<LittleEndian>(romfs.len() as u64)?;
Some(romfs)
} else {
output_writter.write_u64::<LittleEndian>(0)?;
output_writter.write_u64::<LittleEndian>(0)?;
None
};

if let Some(icon) = icon {
Expand Down
Loading

0 comments on commit 3446a71

Please sign in to comment.