Skip to main content

objdiff_core/obj/
split_meta.rs

1use alloc::{string::String, vec, vec::Vec};
2
3use anyhow::{Result, anyhow};
4use object::{Endian, ObjectSection, elf::SHT_NOTE};
5
6#[cfg(feature = "std")]
7use crate::util::align_data_to_4;
8use crate::util::align_size_to_4;
9
10pub const SPLITMETA_SECTION: &str = ".note.split";
11pub const SHT_SPLITMETA: u32 = SHT_NOTE;
12pub const ELF_NOTE_SPLIT: &[u8] = b"Split";
13
14/// This is used to store metadata about the source of an object file,
15/// such as the original virtual addresses and the tool that wrote it.
16#[derive(Debug, Default, Clone)]
17pub struct SplitMeta {
18    /// The tool that generated the object. Informational only.
19    pub generator: Option<String>,
20    /// The name of the source module. (e.g. the DOL or REL name)
21    pub module_name: Option<String>,
22    /// The ID of the source module. (e.g. the DOL or REL ID)
23    pub module_id: Option<u32>,
24    /// Original virtual addresses of each symbol in the object.
25    /// Index 0 is the ELF null symbol.
26    pub virtual_addresses: Option<Vec<u64>>,
27}
28
29const NT_SPLIT_GENERATOR: u32 = u32::from_be_bytes(*b"GENR");
30const NT_SPLIT_MODULE_NAME: u32 = u32::from_be_bytes(*b"MODN");
31const NT_SPLIT_MODULE_ID: u32 = u32::from_be_bytes(*b"MODI");
32const NT_SPLIT_VIRTUAL_ADDRESSES: u32 = u32::from_be_bytes(*b"VIRT");
33
34impl SplitMeta {
35    pub fn from_section<E>(section: object::Section, e: E, is_64: bool) -> Result<Self>
36    where E: Endian {
37        let mut result = SplitMeta::default();
38        let data = section.uncompressed_data().map_err(object_error)?;
39        let mut iter = NoteIterator::new(data.as_ref(), section.align(), e, is_64)?;
40        while let Some(note) = iter.next(e)? {
41            if note.name != ELF_NOTE_SPLIT {
42                continue;
43            }
44            match note.n_type {
45                NT_SPLIT_GENERATOR => {
46                    let string =
47                        String::from_utf8(note.desc.to_vec()).map_err(anyhow::Error::new)?;
48                    result.generator = Some(string);
49                }
50                NT_SPLIT_MODULE_NAME => {
51                    let string =
52                        String::from_utf8(note.desc.to_vec()).map_err(anyhow::Error::new)?;
53                    result.module_name = Some(string);
54                }
55                NT_SPLIT_MODULE_ID => {
56                    result.module_id = Some(e.read_u32(
57                        note.desc.try_into().map_err(|_| anyhow!("Invalid module ID size"))?,
58                    ));
59                }
60                NT_SPLIT_VIRTUAL_ADDRESSES => {
61                    let vec = if is_64 {
62                        let mut vec = vec![0u64; note.desc.len() / 8];
63                        for (i, v) in vec.iter_mut().enumerate() {
64                            *v = e.read_u64(note.desc[i * 8..(i + 1) * 8].try_into().unwrap());
65                        }
66                        vec
67                    } else {
68                        let mut vec = vec![0u64; note.desc.len() / 4];
69                        for (i, v) in vec.iter_mut().enumerate() {
70                            *v = e.read_u32(note.desc[i * 4..(i + 1) * 4].try_into().unwrap())
71                                as u64;
72                        }
73                        vec
74                    };
75                    result.virtual_addresses = Some(vec);
76                }
77                _ => {
78                    // Ignore unknown sections
79                }
80            }
81        }
82        Ok(result)
83    }
84
85    #[cfg(feature = "std")]
86    pub fn to_writer<E, W>(&self, writer: &mut W, e: E, is_64: bool) -> std::io::Result<()>
87    where
88        E: Endian,
89        W: std::io::Write + ?Sized,
90    {
91        if let Some(generator) = &self.generator {
92            write_note_header(writer, e, NT_SPLIT_GENERATOR, generator.len())?;
93            writer.write_all(generator.as_bytes())?;
94            align_data_to_4(writer, generator.len())?;
95        }
96        if let Some(module_name) = &self.module_name {
97            write_note_header(writer, e, NT_SPLIT_MODULE_NAME, module_name.len())?;
98            writer.write_all(module_name.as_bytes())?;
99            align_data_to_4(writer, module_name.len())?;
100        }
101        if let Some(module_id) = self.module_id {
102            write_note_header(writer, e, NT_SPLIT_MODULE_ID, 4)?;
103            writer.write_all(&e.write_u32(module_id))?;
104        }
105        if let Some(virtual_addresses) = &self.virtual_addresses {
106            let count = virtual_addresses.len();
107            let size = if is_64 { count * 8 } else { count * 4 };
108            write_note_header(writer, e, NT_SPLIT_VIRTUAL_ADDRESSES, size)?;
109            if is_64 {
110                for &addr in virtual_addresses {
111                    writer.write_all(&e.write_u64(addr))?;
112                }
113            } else {
114                for &addr in virtual_addresses {
115                    writer.write_all(&e.write_u32(addr as u32))?;
116                }
117            }
118        }
119        Ok(())
120    }
121
122    pub fn write_size(&self, is_64: bool) -> usize {
123        let mut size = 0;
124        if let Some(generator) = self.generator.as_deref() {
125            size += NOTE_HEADER_SIZE + generator.len();
126            size = align_size_to_4(size);
127        }
128        if let Some(module_name) = self.module_name.as_deref() {
129            size += NOTE_HEADER_SIZE + module_name.len();
130            size = align_size_to_4(size);
131        }
132        if self.module_id.is_some() {
133            size += NOTE_HEADER_SIZE + 4;
134            size = align_size_to_4(size);
135        }
136        if let Some(virtual_addresses) = self.virtual_addresses.as_deref() {
137            size += NOTE_HEADER_SIZE + if is_64 { 8 } else { 4 } * virtual_addresses.len();
138            size = align_size_to_4(size);
139        }
140        size
141    }
142}
143
144/// Convert an object::read::Error to a String.
145#[inline]
146fn object_error(err: object::read::Error) -> anyhow::Error { anyhow::Error::new(err) }
147
148/// An ELF note entry.
149struct Note<'data> {
150    n_type: u32,
151    name: &'data [u8],
152    desc: &'data [u8],
153}
154
155/// object::read::elf::NoteIterator is awkward to use generically,
156/// so wrap it in our own iterator.
157enum NoteIterator<'data, E>
158where E: Endian
159{
160    B32(object::read::elf::NoteIterator<'data, object::elf::FileHeader32<E>>),
161    B64(object::read::elf::NoteIterator<'data, object::elf::FileHeader64<E>>),
162}
163
164impl<'data, E> NoteIterator<'data, E>
165where E: Endian
166{
167    fn new(data: &'data [u8], align: u64, e: E, is_64: bool) -> Result<Self> {
168        Ok(if is_64 {
169            NoteIterator::B64(
170                object::read::elf::NoteIterator::new(e, align, data).map_err(object_error)?,
171            )
172        } else {
173            NoteIterator::B32(
174                object::read::elf::NoteIterator::new(e, align as u32, data)
175                    .map_err(object_error)?,
176            )
177        })
178    }
179
180    fn next(&mut self, e: E) -> Result<Option<Note<'data>>> {
181        match self {
182            NoteIterator::B32(iter) => Ok(iter.next().map_err(object_error)?.map(|note| Note {
183                n_type: note.n_type(e),
184                name: note.name(),
185                desc: note.desc(),
186            })),
187            NoteIterator::B64(iter) => Ok(iter.next().map_err(object_error)?.map(|note| Note {
188                n_type: note.n_type(e),
189                name: note.name(),
190                desc: note.desc(),
191            })),
192        }
193    }
194}
195
196// ELF note format:
197// Name Size | 4 bytes (integer)
198// Desc Size | 4 bytes (integer)
199// Type | 4 bytes (usually interpreted as an integer)
200// Name | variable size, padded to a 4 byte boundary
201// Desc | variable size, padded to a 4 byte boundary
202const NOTE_HEADER_SIZE: usize = 12 + ((ELF_NOTE_SPLIT.len() + 4) & !3);
203
204#[cfg(feature = "std")]
205fn write_note_header<E, W>(writer: &mut W, e: E, kind: u32, desc_len: usize) -> std::io::Result<()>
206where
207    E: Endian,
208    W: std::io::Write + ?Sized,
209{
210    writer.write_all(&e.write_u32(ELF_NOTE_SPLIT.len() as u32 + 1))?; // Name Size
211    writer.write_all(&e.write_u32(desc_len as u32))?; // Desc Size
212    writer.write_all(&e.write_u32(kind))?; // Type
213    writer.write_all(ELF_NOTE_SPLIT)?; // Name
214    writer.write_all(&[0; 1])?; // Null terminator
215    align_data_to_4(writer, ELF_NOTE_SPLIT.len() + 1)?;
216    Ok(())
217}