1mod reloc;
2mod symtab;
3
4use crate::elf::{
5 reloc::{rel_size, ElfRelocationUpdate},
6 symtab::{sym_size, ElfSymbolTableUpdate},
7};
8use anyhow::{anyhow, bail, Result};
9use goblin::{
10 container::{Container, Ctx},
11 elf::{
12 section_header::{
13 section_header32, section_header64, SHT_GNU_HASH, SHT_GNU_VERSYM, SHT_HASH, SHT_NULL, SHT_RELA,
14 SHT_SYMTAB_SHNDX,
15 },
16 Elf, Header, SectionHeaders, Sym,
17 },
18 elf32::section_header::SHT_GROUP,
19 strtab::Strtab,
20};
21use regex::Regex;
22use scroll::{ctx::IntoCtx, Pread, Pwrite};
23use std::collections::HashMap;
24
25const GRP_COMDAT: u32 = 1; pub fn localize_elf_symbols(data: Vec<u8>, keep_regexes: &[Regex]) -> Result<Vec<u8>> {
28 let elf = Elf::parse(&data)?;
29 let container = elf.header.container()?;
30 let endianness = elf.header.endianness()?;
31 let ctx = Ctx::new(container, endianness);
32 let section_headers = elf.section_headers.clone();
33
34 let new_symtabs = symtab::localize_elf_symbols(&elf, ctx, &data, keep_regexes)?;
35 let new_relocs = reloc::process_elf_relocations(&elf, ctx, &data, &new_symtabs);
36
37 patch_new_elf_symbols(elf.header, section_headers, ctx, data, new_symtabs, new_relocs)
38}
39
40fn patch_new_elf_symbols(
41 elf_header: Header,
42 section_headers: SectionHeaders,
43 ctx: Ctx,
44 mut data: Vec<u8>,
45 mut new_symtabs: HashMap<usize, ElfSymbolTableUpdate>,
46 mut new_relocs: HashMap<usize, ElfRelocationUpdate>,
47) -> Result<Vec<u8>> {
48 let shoff = elf_header.e_shoff as usize;
49 let sym_size = sym_size(&ctx);
50 let header_size = match ctx.container {
51 Container::Little => section_header32::SIZEOF_SHDR,
52 Container::Big => section_header64::SIZEOF_SHDR,
53 };
54 let mut shndx_to_symtab_map = HashMap::<usize, HashMap<usize, usize>>::new();
55
56 for (sh_idx, header) in section_headers.iter().enumerate() {
57 if header.sh_type == SHT_SYMTAB_SHNDX {
58 if header.sh_info != 0 {
59 bail!("SYMTAB_SHNDX ELF section has invalid non-zero sh_info");
60 }
61 match new_symtabs.get_mut(&(header.sh_link as usize)) {
62 None => bail!("SYMTAB_SHNDX ELF section references invalid symtab in sh_link"),
63 Some(symtab) => shndx_to_symtab_map.insert(sh_idx, std::mem::take(&mut symtab.sym_idx_map)),
64 };
65 }
66 }
67
68 for (sh_idx, mut header) in section_headers.into_iter().enumerate() {
69 if header.sh_type == SHT_HASH || header.sh_type == SHT_GNU_HASH {
71 header.sh_type = SHT_NULL;
72 } else if header.sh_type == SHT_SYMTAB_SHNDX {
73 let symtab_idx_map = shndx_to_symtab_map.get(&sh_idx).unwrap();
75 let shndx_range = header.file_range().expect("Symtab SHNDX without file range");
76 let shndx_data = &mut data[shndx_range.start..shndx_range.end];
77 for (old_idx, new_idx) in symtab_idx_map {
78 if old_idx < new_idx {
79 let old_entry: u32 = shndx_data.pread_with(old_idx * 4, ctx.le).unwrap();
80 let new_entry: u32 = shndx_data.pread_with(new_idx * 4, ctx.le).unwrap();
81 shndx_data.pwrite_with(new_entry, old_idx * 4, ctx.le)?;
82 shndx_data.pwrite_with(old_entry, new_idx * 4, ctx.le)?;
83 }
84 }
85 } else if header.sh_type == SHT_GNU_VERSYM {
86 return Err(anyhow!("Cannot handle GNU_VERSYM ELF sections"));
89 } else if let Some(new_symtab) = new_symtabs.remove(&sh_idx) {
90 for (sym_idx, sym) in new_symtab.syms.into_iter().enumerate() {
91 let offset = new_symtab.header.sh_offset as usize + sym_idx * sym_size;
92 sym.into_ctx(&mut data[offset..], ctx);
93 }
94 header = new_symtab.header;
95 } else if let Some(new_rel) = new_relocs.remove(&sh_idx) {
96 let is_rela = new_rel.header.sh_type == SHT_RELA;
97 let rel_size = rel_size(ctx, &new_rel.header);
98 for (rel_idx, rel) in new_rel.rels.into_iter().enumerate() {
99 let offset = new_rel.header.sh_offset as usize + rel_idx * rel_size;
100 rel.into_ctx(&mut data[offset..], (is_rela, ctx));
101 }
102 }
103
104 let offset = shoff + sh_idx * header_size;
105 header.into_ctx(&mut data[offset..], ctx);
106 }
107
108 Ok(data)
109}
110
111pub fn demote_comdat_groups(mut data: Vec<u8>, keep_regexes: &[Regex]) -> Result<Vec<u8>> {
112 let elf = Elf::parse(&data)?;
113 let container = elf.header.container()?;
114 let endianness = elf.header.endianness()?;
115 let section_headers = elf.section_headers;
116 let ctx = Ctx::new(container, endianness);
117
118 'next_section: for header in section_headers.iter() {
119 if header.sh_type != SHT_GROUP || header.sh_flags != 0 {
121 continue;
122 }
123
124 let group_range = header.file_range().expect("Section header without file range");
125 let group_data = &data[group_range.start..group_range.end];
126 if group_data.len() < 4 {
127 continue; }
129 let group_flags: u32 = group_data.pread_with(0, endianness).unwrap();
130 if group_flags & GRP_COMDAT == 0 {
131 continue;
132 }
133 if group_flags != GRP_COMDAT {
134 return Err(anyhow!(
138 "COMDAT section group also contains unknown flags ({}), refusing to continue",
139 group_flags
140 ));
141 }
142
143 let symtab_idx = header.sh_link as usize;
147 let sym_idx = header.sh_info as usize;
148
149 let symtab = match section_headers.get(symtab_idx) {
150 Some(symtab) => symtab,
151 None => {
152 return Err(anyhow!(
153 "Section group references invalid symbol table index: {}",
154 symtab_idx
155 ));
156 },
157 };
158 let symtab_range = symtab.file_range().expect("Symtab section without file range");
159 let symtab_data = &data[symtab_range.start..symtab_range.end];
160 let sym_size = sym_size(&ctx);
161 let name_sym: Sym = symtab_data.pread_with(sym_idx * sym_size, ctx).unwrap();
162 let strtab_idx = symtab.sh_link as usize;
163
164 let strtab = if strtab_idx >= section_headers.len() {
165 return Err(anyhow!(
166 "Section group symbol references invalid string table index: {}",
167 strtab_idx
168 ));
169 } else {
170 let shdr = §ion_headers[strtab_idx];
171 shdr.check_size(data.len())?;
172 Strtab::parse(&data, shdr.sh_offset as usize, shdr.sh_size as usize, 0x0)
173 }?;
174
175 if let Some(name) = strtab.get_at(name_sym.st_name) {
176 for regex in keep_regexes {
177 if regex.is_match(name) {
178 continue 'next_section;
179 }
180 }
181 } else {
182 continue 'next_section;
183 }
184
185 let demoted_flags = group_flags & !GRP_COMDAT;
186 data.pwrite_with(demoted_flags, group_range.start, endianness)?;
187 }
188
189 Ok(data)
190}