elf_utilities/file/
elf64.rs

1use section::Section64;
2use segment::Segment64;
3
4use crate::{
5    header,
6    section::{self, Contents64, StrTabEntry},
7    segment,
8};
9
10const SHSTRTAB_INITIAL_SIZE: usize = 0xb;
11
12#[derive(Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
13#[repr(C)]
14pub struct ELF64 {
15    pub ehdr: header::Ehdr64,
16    pub sections: Vec<section::Section64>,
17    pub segments: Vec<segment::Segment64>,
18}
19
20impl Default for ELF64 {
21    fn default() -> Self {
22        Self {
23            ehdr: {
24                let mut hdr = header::Ehdr64::default();
25                hdr.e_shnum = 2;
26                hdr.e_shstrndx = 1;
27                hdr.e_shoff += SHSTRTAB_INITIAL_SIZE as u64;
28
29                hdr
30            },
31            sections: {
32                let mut scts = Vec::with_capacity(50);
33                scts.push(section::Section64::new_null_section());
34
35                let shstrtab_contents = Contents64::new_string_table(vec![".shstrtab".to_string()]);
36                scts.push(section::Section64 {
37                    name: ".shstrtab".to_string(),
38                    header: section::Shdr64 {
39                        sh_name: 1,
40                        sh_type: section::Type::StrTab.into(),
41                        sh_flags: 0,
42                        sh_addr: 0,
43                        sh_offset: header::Ehdr64::SIZE as u64,
44                        sh_size: shstrtab_contents.size() as u64,
45                        sh_link: 0,
46                        sh_info: 0,
47                        sh_addralign: 1,
48                        sh_entsize: 0,
49                    },
50                    contents: shstrtab_contents,
51                });
52                scts
53            },
54            segments: Vec::with_capacity(10),
55        }
56    }
57}
58
59impl ELF64 {
60    /// add a section with creating new entry of section table and etc.
61    pub fn add_section(&mut self, mut sct: Section64) {
62        // 新しいセクションのsh_name等を計算する為に
63        // 現在の末尾のセクションを取得する
64        // 本当の末尾には.shstrtabが存在するので,その一つ前
65        let last_sct_idx = self.sections.len() - 2;
66
67        self.fill_elf_info(&mut sct, last_sct_idx);
68
69        // セクションの追加 => SHTの開始オフセットが変更される
70        self.ehdr.e_shoff += sct.header.sh_size;
71        self.ehdr.e_shnum += 1;
72        self.ehdr.e_shstrndx += 1;
73
74        self.sections.insert(self.sections.len() - 1, sct);
75    }
76
77    pub fn add_segment(&mut self, sgt: Segment64) {
78        // PHTに追加される => SHTのオフセットと各セクションのオフセットが変更される
79        self.ehdr.e_shoff += segment::Phdr64::SIZE as u64;
80        for sct in self.sections.iter_mut() {
81            sct.header.sh_offset += segment::Phdr64::SIZE as u64;
82        }
83        self.ehdr.e_phnum += 1;
84
85        self.segments.push(sgt);
86    }
87
88    /// get section index if predicate returns true.
89    pub fn first_shidx_by<P>(&self, predicate: P) -> Option<usize>
90    where
91        P: Fn(&section::Section64) -> bool,
92    {
93        for (i, sct) in self.sections.iter().enumerate() {
94            if predicate(sct) {
95                return Some(i);
96            }
97        }
98
99        None
100    }
101
102    /// get a section if predicate returns true.
103    pub fn first_section_by<P>(&self, predicate: P) -> Option<&section::Section64>
104    where
105        P: Fn(&section::Section64) -> bool,
106    {
107        match self.first_shidx_by(predicate) {
108            Some(idx) => Some(&self.sections[idx]),
109            None => None,
110        }
111    }
112    /// get a mutable section if predicate returns true.
113    pub fn first_mut_section_by<P>(&mut self, predicate: P) -> Option<&mut section::Section64>
114    where
115        P: Fn(&section::Section64) -> bool,
116    {
117        match self.first_shidx_by(predicate) {
118            Some(idx) => Some(&mut self.sections[idx]),
119            None => None,
120        }
121    }
122
123    pub fn to_le_bytes(&self) -> Vec<u8> {
124        let mut file_binary: Vec<u8> = Vec::new();
125
126        let mut header_binary = self.ehdr.to_le_bytes();
127        file_binary.append(&mut header_binary);
128
129        for seg in self.segments.iter() {
130            let mut phdr_binary = seg.header.to_le_bytes();
131            file_binary.append(&mut phdr_binary);
132        }
133
134        let mut sections = self.sections.clone();
135        sections.sort_by_key(|sct| sct.header.sh_offset);
136        for sct in sections.iter() {
137            let mut section_binary = sct.to_le_bytes();
138
139            if sct.header.sh_addralign > 1 && file_binary.len() != sct.header.sh_offset as usize {
140                file_binary.append(&mut vec![0x00; sct.header.sh_offset as usize - file_binary.len()]);
141            }
142
143            file_binary.append(&mut section_binary);
144        }
145
146        if file_binary.len() < self.ehdr.e_shoff as usize {
147            file_binary.append(&mut vec![0x00; self.ehdr.e_shoff as usize - file_binary.len()]);
148        }
149
150        for sct in self.sections.iter() {
151            let mut shdr_binary = sct.header.to_le_bytes();
152            file_binary.append(&mut shdr_binary);
153        }
154        file_binary
155    }
156
157    /// sh_nameやsh_offset等の調整
158    fn fill_elf_info(&mut self, new_sct: &mut Section64, prev_sct_idx: usize) {
159        let shstrtab_len = self.sections[self.ehdr.e_shstrndx as usize].contents.size() as usize;
160        let prev_offset = self.sections[prev_sct_idx].header.sh_offset;
161        let prev_size = self.sections[prev_sct_idx].header.sh_size;
162
163        // <prev_section_name> の後に0x00が入るので,+1
164        new_sct.header.sh_name = shstrtab_len as u32 + 1;
165        // .shstrtabの更新
166        if let Contents64::StrTab(ref mut tab) =
167            self.sections[self.ehdr.e_shstrndx as usize].contents
168        {
169            tab.push(StrTabEntry {
170                v: new_sct.name.clone(),
171                idx: shstrtab_len + 1,
172            });
173        }
174
175        // NULLセクションのすぐ次に挿入する場合,
176        // sh_offsetはEhdr64::SIZE + PHT's SIZEという感じになる.
177        // .shstrtabが既に存在するがサイズは固定なので,その分足しておく
178        if prev_sct_idx == 0 {
179            new_sct.header.sh_offset = header::Ehdr64::SIZE as u64
180                + segment::Phdr64::SIZE as u64 * self.segments.len() as u64
181                + SHSTRTAB_INITIAL_SIZE as u64;
182        } else {
183            new_sct.header.sh_offset = prev_offset + prev_size;
184        }
185
186        new_sct.header.sh_size = new_sct.contents.size() as u64;
187    }
188}