1 use goblin::elf::section_header::SHT_GNU_HASH;
2 use goblin::elf::sym::Sym;
3 use goblin::elf::Elf;
4 use goblin::elf32::gnu_hash::GnuHash as GnuHash32;
5 use goblin::elf64::gnu_hash::GnuHash as GnuHash64;
6
7 #[repr(C)]
8 #[repr(align(64))] // Align to cache lines
9 pub struct AlignedData<T: ?Sized>(T);
10
parse_gnu_hash_section(base: &[u8], symbol_name: &str) -> Result<Sym, &'static str>11 fn parse_gnu_hash_section(base: &[u8], symbol_name: &str) -> Result<Sym, &'static str> {
12 let obj = Elf::parse(base).map_err(|_| "cannot parse ELF file")?;
13 let hash_section = obj
14 .section_headers
15 .iter()
16 .find(|s| s.sh_type == SHT_GNU_HASH)
17 .ok_or("object does not contain .gnu.hash section")?;
18 let (start, length) = (
19 hash_section.sh_offset as usize,
20 hash_section.sh_size as usize,
21 );
22 let hashtab: &[u8] = &base[start..(start + length)];
23 let dynsyms = obj.dynsyms.to_vec();
24 let section = unsafe {
25 if obj.is_64 {
26 GnuHash64::from_raw_table(hashtab, &dynsyms)?.find(symbol_name, &obj.dynstrtab)
27 } else {
28 GnuHash32::from_raw_table(hashtab, &dynsyms)?.find(symbol_name, &obj.dynstrtab)
29 }
30 };
31 section.copied().ok_or("cannot find symbol")
32 }
33
34 // Use lazy_parse and assembles the Elf with only parts we care
parse_text_section_size_lazy(base: &[u8]) -> Result<u64, &'static str>35 fn parse_text_section_size_lazy(base: &[u8]) -> Result<u64, &'static str> {
36 let header = Elf::parse_header(base).map_err(|_| "parse elf header error")?;
37 // dummy Elf with only header
38 let mut obj = Elf::lazy_parse(header).map_err(|_| "cannot parse ELF file")?;
39
40 use goblin::container::{Container, Ctx};
41 use goblin::elf::SectionHeader;
42 use goblin::strtab::Strtab;
43
44 let ctx = Ctx {
45 le: scroll::Endian::Little,
46 container: Container::Big,
47 };
48
49 obj.section_headers =
50 SectionHeader::parse(base, header.e_shoff as usize, header.e_shnum as usize, ctx)
51 .map_err(|_| "parse section headers error")?;
52
53 let strtab_idx = header.e_shstrndx as usize;
54 let strtab_shdr = &obj.section_headers[strtab_idx];
55 let strtab = Strtab::parse(
56 base,
57 strtab_shdr.sh_offset as usize,
58 strtab_shdr.sh_size as usize,
59 0x0,
60 )
61 .map_err(|_| "parse string table error")?;
62 for (_, section) in obj.section_headers.iter().enumerate() {
63 let section_name = strtab.get_at(section.sh_name).unwrap();
64 if section_name == ".text" {
65 return Ok(section.sh_size);
66 }
67 }
68
69 Err("Didn't find text section")
70 }
71
72 #[test]
test_parse_gnu_hash_section_64bit()73 fn test_parse_gnu_hash_section_64bit() {
74 static ALIGNED_DATA: &AlignedData<[u8]> =
75 &AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so"));
76
77 assert_eq!(
78 parse_gnu_hash_section(&ALIGNED_DATA.0, "helloWorld"),
79 Ok(Sym {
80 st_name: 97,
81 st_info: 0x12,
82 st_other: 0,
83 st_shndx: 12,
84 st_value: 0x65a,
85 st_size: 33,
86 })
87 );
88 assert_eq!(
89 parse_gnu_hash_section(&ALIGNED_DATA.0, "_edata"),
90 Ok(Sym {
91 st_name: 130,
92 st_info: 0x10,
93 st_other: 0,
94 st_shndx: 22,
95 st_value: 0x0020_1030,
96 st_size: 0,
97 })
98 );
99 assert_eq!(
100 parse_gnu_hash_section(&ALIGNED_DATA.0, "__gmon_start__"),
101 Err("cannot find symbol"),
102 );
103 }
104
105 #[test]
test_parse_gnu_hash_section_32bit()106 fn test_parse_gnu_hash_section_32bit() {
107 static ALIGNED_DATA: &AlignedData<[u8]> =
108 &AlignedData(*include_bytes!("bins/elf/gnu_hash/hello32.so"));
109
110 assert_eq!(
111 parse_gnu_hash_section(&ALIGNED_DATA.0, "helloWorld"),
112 Ok(Sym {
113 st_name: 97,
114 st_info: 0x12,
115 st_other: 0,
116 st_shndx: 12,
117 st_value: 0x4ed,
118 st_size: 49,
119 })
120 );
121 assert_eq!(
122 parse_gnu_hash_section(&ALIGNED_DATA.0, "_edata"),
123 Ok(Sym {
124 st_name: 130,
125 st_info: 0x10,
126 st_other: 0,
127 st_shndx: 22,
128 st_value: 0x2018,
129 st_size: 0,
130 })
131 );
132 assert_eq!(
133 parse_gnu_hash_section(&ALIGNED_DATA.0, "__gmon_start__"),
134 Err("cannot find symbol"),
135 );
136 }
137
138 #[test]
test_parse_text_section_size_lazy()139 fn test_parse_text_section_size_lazy() {
140 static ALIGNED_DATA: &AlignedData<[u8]> =
141 &AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so"));
142 assert_eq!(parse_text_section_size_lazy(&ALIGNED_DATA.0), Ok(0x126));
143 }
144
145 #[test]
test_oom()146 fn test_oom() {
147 use goblin::container::{Container, Ctx};
148 use scroll::Pwrite;
149
150 fn test_oom(data: &mut [u8]) {
151 let mut modified_data = data.to_vec();
152 let mut elf = Elf::parse(&data).unwrap();
153 let endianness = elf.header.endianness().unwrap();
154 let ctx = Ctx::new(
155 if elf.is_64 {
156 Container::Big
157 } else {
158 Container::Little
159 },
160 endianness,
161 );
162 let original_e_phnum = elf.header.e_phnum;
163 let original_e_shnum = elf.header.e_shnum;
164
165 // Way too many program headers
166 elf.header.e_phnum = 1000;
167 modified_data
168 .pwrite_with(elf.header, 0, endianness)
169 .unwrap();
170 assert!(Elf::parse(&modified_data).is_err());
171
172 // Possible overflow of program headers
173 elf.header.e_phnum = std::u16::MAX;
174 modified_data
175 .pwrite_with(elf.header, 0, endianness)
176 .unwrap();
177 assert!(Elf::parse(&modified_data).is_err());
178
179 // Back to original
180 elf.header.e_phnum = original_e_phnum;
181 modified_data
182 .pwrite_with(elf.header, 0, endianness)
183 .unwrap();
184 assert!(Elf::parse(&modified_data).is_ok());
185
186 // Way too many section headers
187 elf.header.e_shnum = 1000;
188 modified_data
189 .pwrite_with(elf.header, 0, endianness)
190 .unwrap();
191 assert!(Elf::parse(&modified_data).is_err());
192
193 // Fallback to large empty section header's sh_size
194 elf.header.e_shnum = 0;
195 elf.section_headers[0].sh_size = std::u64::MAX;
196 modified_data
197 .pwrite_with(elf.header, 0, endianness)
198 .unwrap();
199 modified_data
200 .pwrite_with(
201 elf.section_headers[0].clone(),
202 elf.header.e_shoff as usize,
203 ctx,
204 )
205 .unwrap();
206 assert!(Elf::parse(&modified_data).is_err());
207
208 // Possible overflow of section headers
209 elf.header.e_shnum = std::u16::MAX;
210 modified_data
211 .pwrite_with(elf.header, 0, endianness)
212 .unwrap();
213 assert!(Elf::parse(&modified_data).is_err());
214
215 // Back to original
216 elf.header.e_shnum = original_e_shnum;
217 modified_data
218 .pwrite_with(elf.header, 0, endianness)
219 .unwrap();
220 assert!(Elf::parse(&modified_data).is_ok());
221 }
222
223 let aligned_data: &mut AlignedData<[u8]> =
224 &mut AlignedData(*include_bytes!("bins/elf/gnu_hash/hello32.so"));
225 test_oom(&mut aligned_data.0);
226
227 let aligned_data: &mut AlignedData<[u8]> =
228 &mut AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so"));
229 test_oom(&mut aligned_data.0);
230 }
231