elf_utilities/section/
elf64.rs

1//! Type definitions for 64-bit ELF binaries.
2
3use std::collections::HashSet;
4
5use crate::section;
6use crate::*;
7
8use serde::{Deserialize, Serialize};
9
10use super::StrTabEntry;
11
12#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
13pub enum Contents64 {
14    /// almost section's data
15    Raw(Vec<u8>),
16    /// symbol table
17    Symbols(Vec<symbol::Symbol64>),
18    /// relocation symbol table
19    RelaSymbols(Vec<relocation::Rela64>),
20    /// dynamic information
21    Dynamics(Vec<dynamic::Dyn64>),
22    /// String Table
23    StrTab(Vec<StrTabEntry>),
24}
25
26#[derive(Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
27pub struct Section64 {
28    pub name: String,
29    pub header: Shdr64,
30
31    pub contents: Contents64,
32}
33
34#[derive(Debug, Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
35#[repr(C)]
36pub struct Shdr64 {
37    /// Section name, index in string tbl
38    pub sh_name: Elf64Word,
39    /// Type of section
40    pub sh_type: Elf64Word,
41    /// Miscellaneous section attributes
42    pub sh_flags: Elf64Xword,
43    ///  Section virtual addr at execution
44    pub sh_addr: Elf64Addr,
45    /// Section file offset
46    pub sh_offset: Elf64Off,
47    /// Size of section in bytes
48    pub sh_size: Elf64Xword,
49    /// Index of another section
50    pub sh_link: Elf64Word,
51    /// Additional section information
52    pub sh_info: Elf64Word,
53    /// Section alignment
54    pub sh_addralign: Elf64Xword,
55    /// Entry size if section holds table
56    pub sh_entsize: Elf64Xword,
57}
58
59/// A `Shdr64` builder
60///
61/// # Examples
62///
63/// ```
64/// use elf_utilities::section;
65/// let shdr: section::Shdr64 = section::ShdrPreparation64::default()
66///            .ty(section::Type::ProgBits)
67///            .flags(vec![section::Flag::Alloc, section::Flag::Write].iter())
68///            .into();
69///
70/// assert_eq!(section::Type::ProgBits, shdr.get_type());
71/// assert!(shdr.get_flags().contains(&section::Flag::Alloc));
72/// assert!(shdr.get_flags().contains(&section::Flag::Write));
73/// ```
74#[derive(Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)]
75#[repr(C)]
76pub struct ShdrPreparation64 {
77    /// Type of section
78    pub sh_type: section::Type,
79    /// Miscellaneous section attributes
80    pub sh_flags: Elf64Xword,
81    /// Index of another section
82    pub sh_link: Elf64Word,
83    /// Additional section information
84    pub sh_info: Elf64Word,
85    /// Section alignment
86    pub sh_addralign: Elf64Xword,
87}
88
89impl Default for Shdr64 {
90    fn default() -> Self {
91        Self {
92            sh_name: 0,
93            sh_type: 0,
94            sh_flags: 0,
95            sh_addr: 0,
96            sh_offset: 0,
97            sh_size: 0,
98            sh_link: 0,
99            sh_info: 0,
100            sh_addralign: 0,
101            sh_entsize: 0,
102        }
103    }
104}
105
106#[allow(dead_code)]
107impl Shdr64 {
108    pub const SIZE: usize = 0x40;
109
110    // getter
111    pub fn get_type(&self) -> section::Type {
112        section::Type::from(self.sh_type)
113    }
114    pub fn get_flags(&self) -> HashSet<section::Flag> {
115        let mut mask: Elf64Xword = 0b1;
116        let mut flags = HashSet::new();
117        loop {
118            if mask == 0 {
119                break;
120            }
121            if self.sh_flags & mask != 0 {
122                flags.insert(section::Flag::from(mask));
123            }
124            mask <<= 1;
125        }
126
127        flags
128    }
129
130    // setter
131    pub fn set_type(&mut self, ty: section::Type) {
132        self.sh_type = ty.into();
133    }
134    pub fn set_flags<'a, I>(&mut self, flags: I)
135    where
136        I: Iterator<Item = &'a section::Flag>,
137    {
138        for flag in flags {
139            self.sh_flags = self.sh_flags | Into::<Elf64Xword>::into(*flag);
140        }
141    }
142
143    /// Create Vec<u8> from this.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use elf_utilities::section::Shdr64;
149    /// let null_sct : Shdr64 = Default::default();
150    ///
151    /// assert_eq!([0].repeat(Shdr64::SIZE), null_sct.to_le_bytes());
152    /// ```
153    pub fn to_le_bytes(&self) -> Vec<u8> {
154        bincode::serialize(self).unwrap()
155    }
156}
157
158impl Section64 {
159    pub fn new_null_section() -> Self {
160        Self {
161            contents: Contents64::Raw(Default::default()),
162            header: Default::default(),
163            name: Default::default(),
164        }
165    }
166
167    pub fn new(name: String, hdr: ShdrPreparation64, contents: Contents64) -> Self {
168        Self {
169            contents,
170            name,
171            header: hdr.into(),
172        }
173    }
174
175    /// create binary without header
176    pub fn to_le_bytes(&self) -> Vec<u8> {
177        match &self.contents {
178            Contents64::Raw(bytes) => bytes.clone(),
179            Contents64::StrTab(strs) => {
180                // ELFの文字列テーブルは null-byte + (name + null-byte) * n という形状に
181                // それに合うようにバイト列を構築.
182                let mut string_table: Vec<u8> = vec![0x00];
183
184                for st in strs {
185                    for byte in st.v.as_bytes() {
186                        string_table.push(*byte);
187                    }
188                    string_table.push(0x00);
189                }
190
191                string_table
192            }
193            Contents64::Symbols(syms) => {
194                let mut bytes = Vec::new();
195                for sym in syms.iter() {
196                    bytes.append(&mut sym.to_le_bytes());
197                }
198                bytes
199            }
200            Contents64::RelaSymbols(rela_syms) => {
201                let mut bytes = Vec::new();
202                for sym in rela_syms.iter() {
203                    bytes.append(&mut sym.to_le_bytes());
204                }
205                bytes
206            }
207            Contents64::Dynamics(dynamics) => {
208                let mut bytes = Vec::new();
209                for sym in dynamics.iter() {
210                    bytes.append(&mut sym.to_le_bytes());
211                }
212                bytes
213            }
214        }
215    }
216}
217
218impl ShdrPreparation64 {
219    pub fn ty(mut self, t: section::Type) -> Self {
220        self.sh_type = t;
221        self
222    }
223
224    pub fn flags<'a, I>(mut self, flags: I) -> Self
225    where
226        I: Iterator<Item = &'a section::Flag>,
227    {
228        for flag in flags {
229            self.sh_flags |= Into::<Elf64Xword>::into(*flag);
230        }
231
232        self
233    }
234
235    pub fn link(mut self, link: Elf64Word) -> Self {
236        self.sh_link = link;
237        self
238    }
239    pub fn info(mut self, info: Elf64Word) -> Self {
240        self.sh_info = info;
241        self
242    }
243}
244
245impl Default for ShdrPreparation64 {
246    fn default() -> Self {
247        Self {
248            sh_type: section::Type::Null,
249            sh_flags: 0,
250            sh_link: 0,
251            sh_info: 0,
252            sh_addralign: 0,
253        }
254    }
255}
256
257impl Into<Shdr64> for ShdrPreparation64 {
258    fn into(self) -> Shdr64 {
259        Shdr64 {
260            sh_name: 0,
261            sh_type: self.sh_type.into(),
262            sh_flags: self.sh_flags,
263            sh_addr: 0,
264            sh_offset: 0,
265            sh_size: 0,
266            sh_link: self.sh_link,
267            sh_info: self.sh_info,
268            sh_addralign: self.sh_addralign,
269            sh_entsize: 0,
270        }
271    }
272}
273
274impl Contents64 {
275    pub fn size(&self) -> usize {
276        match self {
277            Contents64::Raw(bytes) => bytes.len(),
278            Contents64::StrTab(strs) => {
279                // ELFの文字列テーブルは null-byte + (name + null-byte) * n という形状に
280                let total_len: usize = strs.iter().map(|s| s.v.len()).sum();
281                total_len + strs.len() + 1
282            }
283            Contents64::Symbols(syms) => symbol::Symbol64::SIZE * syms.len(),
284            Contents64::RelaSymbols(rela_syms) => {
285                relocation::Rela64::SIZE as usize * rela_syms.len()
286            }
287            Contents64::Dynamics(dyn_info) => dynamic::Dyn64::SIZE * dyn_info.len(),
288        }
289    }
290
291    pub fn new_string_table(strs: Vec<String>) -> Self {
292        let mut name_idx = 1;
293        let strs = strs
294            .iter()
295            .map(|s| {
296                let ent = StrTabEntry {
297                    v: s.clone(),
298                    idx: name_idx,
299                };
300                name_idx += s.len() + 1;
301                ent
302            })
303            .collect();
304
305        Contents64::StrTab(strs)
306    }
307}