Skip to content

Commit 7ee0d77

Browse files
committed
dtk dol strings command
This dumps a series of symbol definitions for strings in an elf, starting at a given address. This is intended for games where mwerks is configured to emit each string as a separate symbol.
1 parent e4219e7 commit 7ee0d77

2 files changed

Lines changed: 129 additions & 30 deletions

File tree

src/cmd/dol.rs

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{
22
cmp::min,
33
collections::{BTreeMap, HashMap, btree_map::Entry, hash_map},
4+
ffi::CStr,
45
fs,
56
fs::DirBuilder,
67
io::{Cursor, Seek, Write},
@@ -13,6 +14,7 @@ use anyhow::{Context, Result, anyhow, bail};
1314
use argp::FromArgs;
1415
use cwdemangle::demangle;
1516
use itertools::Itertools;
17+
use object::ObjectSection;
1618
use rayon::prelude::*;
1719
use serde::{Deserialize, Serialize};
1820
use tracing::{debug, info, info_span};
@@ -43,7 +45,7 @@ use crate::{
4345
comment::MWComment,
4446
config::{
4547
SectionAddressRef, apply_splits_file, apply_symbols_file, is_auto_symbol,
46-
signed_hex_serde, write_splits_file, write_symbols_file,
48+
section_addr_ref_from_str, signed_hex_serde, write_splits_file, write_symbols_file,
4749
},
4850
dep::DepFile,
4951
diff::{calc_diff_ranges, print_diff, process_code},
@@ -80,6 +82,7 @@ enum SubCommand {
8082
Diff(DiffArgs),
8183
Apply(ApplyArgs),
8284
Config(ConfigArgs),
85+
Strings(StringsArgs),
8386
}
8487

8588
#[derive(FromArgs, PartialEq, Eq, Debug)]
@@ -139,6 +142,21 @@ pub struct ApplyArgs {
139142
full: bool,
140143
}
141144

145+
#[derive(FromArgs, PartialEq, Eq, Debug)]
146+
/// Applies updated symbols from a linked ELF to the project configuration.
147+
#[argp(subcommand, name = "strings")]
148+
pub struct StringsArgs {
149+
#[argp(positional, from_str_fn(native_path))]
150+
/// linked ELF
151+
elf_file: Utf8NativePathBuf,
152+
#[argp(positional, from_str_fn(section_addr_ref_from_str))]
153+
/// position of string table to split
154+
address: SectionAddressRef,
155+
/// Max count strings to split
156+
#[argp(option, default = "20")]
157+
count: u32,
158+
}
159+
142160
#[derive(FromArgs, PartialEq, Eq, Debug)]
143161
/// Generates a project configuration file from a DOL (& RELs).
144162
#[argp(subcommand, name = "config")]
@@ -468,6 +486,7 @@ pub fn run(args: Args) -> Result<()> {
468486
SubCommand::Diff(c_args) => diff(c_args),
469487
SubCommand::Apply(c_args) => apply(c_args),
470488
SubCommand::Config(c_args) => config(c_args),
489+
SubCommand::Strings(c_args) => strings(c_args),
471490
}
472491
}
473492

@@ -525,19 +544,19 @@ fn apply_selfile(obj: &mut ObjInfo, buf: &[u8]) -> Result<()> {
525544
if let Some((existing_symbol_idx, existing_symbol)) = existing_symbol {
526545
log::debug!("Mapping symbol {} to {}", symbol.name, existing_symbol.name);
527546
obj.symbols.replace(existing_symbol_idx, ObjSymbol {
528-
name: symbol.name.clone(),
529-
demangled_name: symbol.demangled_name.clone(),
530-
address: address as u64,
531-
section,
532-
size: existing_symbol.size,
533-
size_known: existing_symbol.size_known,
534-
flags: ObjSymbolFlagSet(existing_symbol.flags.0 | ObjSymbolFlags::Exported),
535-
kind: existing_symbol.kind,
536-
align: existing_symbol.align,
537-
data_kind: existing_symbol.data_kind,
538-
name_hash: existing_symbol.name_hash,
539-
demangled_name_hash: existing_symbol.demangled_name_hash,
540-
})?;
547+
name: symbol.name.clone(),
548+
demangled_name: symbol.demangled_name.clone(),
549+
address: address as u64,
550+
section,
551+
size: existing_symbol.size,
552+
size_known: existing_symbol.size_known,
553+
flags: ObjSymbolFlagSet(existing_symbol.flags.0 | ObjSymbolFlags::Exported),
554+
kind: existing_symbol.kind,
555+
align: existing_symbol.align,
556+
data_kind: existing_symbol.data_kind,
557+
name_hash: existing_symbol.name_hash,
558+
demangled_name_hash: existing_symbol.demangled_name_hash,
559+
})?;
541560
} else {
542561
log::debug!("Creating symbol {} at {:#010X}", symbol.name, address);
543562
obj.symbols.add(
@@ -2143,10 +2162,10 @@ fn config(args: ConfigArgs) -> Result<()> {
21432162
let header = process_rel_header(&mut entry)?;
21442163
entry.rewind()?;
21452164
modules.push((header.module_id, ModuleConfig {
2146-
object: path.with_unix_encoding(),
2147-
hash: Some(file_sha1_string(&mut entry)?),
2148-
..Default::default()
2149-
}));
2165+
object: path.with_unix_encoding(),
2166+
hash: Some(file_sha1_string(&mut entry)?),
2167+
..Default::default()
2168+
}));
21502169
}
21512170
"sel" => {
21522171
config.selfile = Some(path.with_unix_encoding());
@@ -2218,10 +2237,10 @@ fn apply_add_relocations(obj: &mut ObjInfo, relocations: &[AddRelocationConfig])
22182237
}
22192238
};
22202239
obj.sections[section].relocations.replace(address, ObjReloc {
2221-
kind: reloc.kind,
2222-
target_symbol,
2223-
addend: reloc.addend,
2224-
module: None,
2240+
kind: reloc.kind,
2241+
target_symbol,
2242+
addend: reloc.addend,
2243+
module: None,
22252244
});
22262245
}
22272246
Ok(())
@@ -2398,6 +2417,39 @@ fn extracted_path(target_dir: &Utf8NativePath, path: &Utf8UnixPath) -> Utf8Nativ
23982417
target_path
23992418
}
24002419

2420+
fn strings(args: StringsArgs) -> Result<()> {
2421+
let mut file = open_file(&args.elf_file, true)?;
2422+
let data = file.map()?;
2423+
2424+
let obj_file = object::read::File::parse(data)?;
2425+
let section = args.address.resolve_in_file(&obj_file)?;
2426+
let section_name = section.name()?;
2427+
2428+
let mut relative = (args.address.address as u64 - section.address()) as usize;
2429+
let section_data = section.data()?;
2430+
2431+
for _ in 0..args.count {
2432+
let symbol_data = &section_data[relative..];
2433+
let str = match CStr::from_bytes_until_nul(symbol_data) {
2434+
Ok(v) => v,
2435+
Err(_) => break,
2436+
};
2437+
2438+
let size = str.count_bytes() + 1;
2439+
2440+
println!(
2441+
"str_{0:08X} = {1}:0x{0:08X}; // type:object size:0x{2:X} scope:local data:string",
2442+
relative as u64 + section.address(),
2443+
section_name,
2444+
size
2445+
);
2446+
2447+
relative += size;
2448+
}
2449+
2450+
Ok(())
2451+
}
2452+
24012453
#[cfg(test)]
24022454
mod test {
24032455
use super::*;

src/util/config.rs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
use anyhow::{Context, Result, anyhow, bail, ensure};
99
use cwdemangle::{DemangleOptions, demangle};
1010
use filetime::FileTime;
11+
use object::{Object, ObjectSection};
1112
use once_cell::sync::Lazy;
1213
use regex::{Captures, Regex};
1314
use tracing::{debug, info, warn};
@@ -800,14 +801,14 @@ where R: BufRead + ?Sized {
800801
end
801802
);
802803
section.splits.push(start, ObjSplit {
803-
unit: unit.clone(),
804-
end,
805-
align,
806-
common,
807-
autogenerated: false,
808-
skip,
809-
rename,
810-
});
804+
unit: unit.clone(),
805+
end,
806+
align,
807+
common,
808+
autogenerated: false,
809+
skip,
810+
rename,
811+
});
811812
}
812813
_ => {}
813814
}
@@ -928,6 +929,29 @@ impl SectionAddressRef {
928929
);
929930
Ok(SectionAddress::new(section_index, self.address))
930931
}
932+
933+
pub fn resolve_in_file<'data, O: Object<'data>>(
934+
&self,
935+
obj: &'data O,
936+
) -> Result<O::Section<'data>> {
937+
if let Some(name) = self.section.as_ref() {
938+
let section = obj
939+
.section_by_name(name)
940+
.ok_or_else(|| anyhow!("Unable to locate section '{}'", name))?;
941+
942+
if self.address as u64 - section.address() >= section.size() {
943+
return Err(anyhow!("Address {:#X} not in section '{}'", self.address, name));
944+
}
945+
946+
Ok(section)
947+
} else {
948+
obj.sections()
949+
.find(|s| (s.address()..(s.address() + s.size())).contains(&(self.address as u64)))
950+
.ok_or_else(|| {
951+
anyhow!("Unable to locate section containing address {:#X}", self.address)
952+
})
953+
}
954+
}
931955
}
932956

933957
impl<'de> serde::Deserialize<'de> for SectionAddressRef {
@@ -973,3 +997,26 @@ impl serde::Serialize for SectionAddressRef {
973997
}
974998
}
975999
}
1000+
1001+
fn hex_address(mut value: &str) -> Result<u32, String> {
1002+
if value.starts_with("0x") {
1003+
value = &value[2..];
1004+
}
1005+
u32::from_str_radix(value, 16).map_err(|e| e.to_string())
1006+
}
1007+
1008+
pub fn section_addr_ref_from_str(s: &str) -> std::result::Result<SectionAddressRef, String> {
1009+
let (section, offset) = match s.split_once(':') {
1010+
Some((section, offset)) => {
1011+
if section.is_empty() {
1012+
return Err("Section name is empty!".into());
1013+
}
1014+
1015+
(Some(section.to_owned()), offset)
1016+
}
1017+
None => (None, s),
1018+
};
1019+
1020+
let offset = hex_address(offset)?;
1021+
Ok(SectionAddressRef { section, address: offset })
1022+
}

0 commit comments

Comments
 (0)