1use alloc::vec::Vec;
2
3use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable};
4use crate::{elf, endian};
5
6use super::FileHeader;
7
8/// A version index.
9#[derive(Debug, Default, Clone, Copy)]
10pub struct VersionIndex(pub u16);
11
12impl VersionIndex {
13 /// Return the version index.
14 pub fn index(&self) -> u16 {
15 self.0 & elf::VERSYM_VERSION
16 }
17
18 /// Return true if it is the local index.
19 pub fn is_local(&self) -> bool {
20 self.index() == elf::VER_NDX_LOCAL
21 }
22
23 /// Return true if it is the global index.
24 pub fn is_global(&self) -> bool {
25 self.index() == elf::VER_NDX_GLOBAL
26 }
27
28 /// Return the hidden flag.
29 pub fn is_hidden(&self) -> bool {
30 self.0 & elf::VERSYM_HIDDEN != 0
31 }
32}
33
34/// A version definition or requirement.
35///
36/// This is derived from entries in the [`elf::SHT_GNU_VERDEF`] and [`elf::SHT_GNU_VERNEED`] sections.
37#[derive(Debug, Default, Clone, Copy)]
38pub struct Version<'data> {
39 name: &'data [u8],
40 hash: u32,
41 // Used to keep track of valid indices in `VersionTable`.
42 valid: bool,
43}
44
45impl<'data> Version<'data> {
46 /// Return the version name.
47 pub fn name(&self) -> &'data [u8] {
48 self.name
49 }
50
51 /// Return hash of the version name.
52 pub fn hash(&self) -> u32 {
53 self.hash
54 }
55}
56
57/// A table of version definitions and requirements.
58///
59/// It allows looking up the version information for a given symbol index.
60///
61/// This is derived from entries in the [`elf::SHT_GNU_VERSYM`], [`elf::SHT_GNU_VERDEF`]
62/// and [`elf::SHT_GNU_VERNEED`] sections.
63///
64/// Returned by [`SectionTable::versions`](super::SectionTable::versions).
65#[derive(Debug, Clone)]
66pub struct VersionTable<'data, Elf: FileHeader> {
67 symbols: &'data [elf::Versym<Elf::Endian>],
68 versions: Vec<Version<'data>>,
69}
70
71impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> {
72 fn default() -> Self {
73 VersionTable {
74 symbols: &[],
75 versions: Vec::new(),
76 }
77 }
78}
79
80impl<'data, Elf: FileHeader> VersionTable<'data, Elf> {
81 /// Parse the version sections.
82 pub fn parse<R: ReadRef<'data>>(
83 endian: Elf::Endian,
84 versyms: &'data [elf::Versym<Elf::Endian>],
85 verdefs: Option<VerdefIterator<'data, Elf>>,
86 verneeds: Option<VerneedIterator<'data, Elf>>,
87 strings: StringTable<'data, R>,
88 ) -> Result<Self> {
89 let mut max_index = 0;
90 if let Some(mut verdefs) = verdefs.clone() {
91 while let Some((verdef, _)) = verdefs.next()? {
92 if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 {
93 continue;
94 }
95 let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION;
96 if max_index < index {
97 max_index = index;
98 }
99 }
100 }
101 if let Some(mut verneeds) = verneeds.clone() {
102 while let Some((_, mut vernauxs)) = verneeds.next()? {
103 while let Some(vernaux) = vernauxs.next()? {
104 let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION;
105 if max_index < index {
106 max_index = index;
107 }
108 }
109 }
110 }
111
112 // Indices should be sequential, but this could be up to
113 // 32k * size_of::<Version>() if max_index is bad.
114 let mut versions = vec![Version::default(); max_index as usize + 1];
115
116 if let Some(mut verdefs) = verdefs {
117 while let Some((verdef, mut verdauxs)) = verdefs.next()? {
118 if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 {
119 continue;
120 }
121 let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION;
122 if index <= elf::VER_NDX_GLOBAL {
123 // TODO: return error?
124 continue;
125 }
126 if let Some(verdaux) = verdauxs.next()? {
127 versions[usize::from(index)] = Version {
128 name: verdaux.name(endian, strings)?,
129 hash: verdef.vd_hash.get(endian),
130 valid: true,
131 };
132 }
133 }
134 }
135 if let Some(mut verneeds) = verneeds {
136 while let Some((_, mut vernauxs)) = verneeds.next()? {
137 while let Some(vernaux) = vernauxs.next()? {
138 let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION;
139 if index <= elf::VER_NDX_GLOBAL {
140 // TODO: return error?
141 continue;
142 }
143 versions[usize::from(index)] = Version {
144 name: vernaux.name(endian, strings)?,
145 hash: vernaux.vna_hash.get(endian),
146 valid: true,
147 };
148 }
149 }
150 }
151
152 Ok(VersionTable {
153 symbols: versyms,
154 versions,
155 })
156 }
157
158 /// Return true if the version table is empty.
159 pub fn is_empty(&self) -> bool {
160 self.symbols.is_empty()
161 }
162
163 /// Return version index for a given symbol index.
164 pub fn version_index(&self, endian: Elf::Endian, index: usize) -> VersionIndex {
165 let version_index = match self.symbols.get(index) {
166 Some(x) => x.0.get(endian),
167 // Ideally this would be VER_NDX_LOCAL for undefined symbols,
168 // but currently there are no checks that need this distinction.
169 None => elf::VER_NDX_GLOBAL,
170 };
171 VersionIndex(version_index)
172 }
173
174 /// Return version information for a given symbol version index.
175 ///
176 /// Returns `Ok(None)` for local and global versions.
177 /// Returns `Err(_)` if index is invalid.
178 pub fn version(&self, index: VersionIndex) -> Result<Option<&Version<'data>>> {
179 if index.index() <= elf::VER_NDX_GLOBAL {
180 return Ok(None);
181 }
182 self.versions
183 .get(usize::from(index.index()))
184 .filter(|version| version.valid)
185 .read_error("Invalid ELF symbol version index")
186 .map(Some)
187 }
188
189 /// Return true if the given symbol index satisfies the requirements of `need`.
190 ///
191 /// Returns false for any error.
192 ///
193 /// Note: this function hasn't been fully tested and is likely to be incomplete.
194 pub fn matches(&self, endian: Elf::Endian, index: usize, need: Option<&Version<'_>>) -> bool {
195 let version_index = self.version_index(endian, index);
196 let def = match self.version(version_index) {
197 Ok(def) => def,
198 Err(_) => return false,
199 };
200 match (def, need) {
201 (Some(def), Some(need)) => need.hash == def.hash && need.name == def.name,
202 (None, Some(_need)) => {
203 // Version must be present if needed.
204 false
205 }
206 (Some(_def), None) => {
207 // For a dlsym call, use the newest version.
208 // TODO: if not a dlsym call, then use the oldest version.
209 !version_index.is_hidden()
210 }
211 (None, None) => true,
212 }
213 }
214}
215
216/// An iterator for the entries in an ELF [`elf::SHT_GNU_VERDEF`] section.
217#[derive(Debug, Clone)]
218pub struct VerdefIterator<'data, Elf: FileHeader> {
219 endian: Elf::Endian,
220 data: Bytes<'data>,
221}
222
223impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> {
224 pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self {
225 VerdefIterator {
226 endian,
227 data: Bytes(data),
228 }
229 }
230
231 /// Return the next `Verdef` entry.
232 pub fn next(
233 &mut self,
234 ) -> Result<Option<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>> {
235 if self.data.is_empty() {
236 return Ok(None);
237 }
238
239 let verdef = self
240 .data
241 .read_at::<elf::Verdef<_>>(0)
242 .read_error("ELF verdef is too short")?;
243
244 let mut verdaux_data = self.data;
245 verdaux_data
246 .skip(verdef.vd_aux.get(self.endian) as usize)
247 .read_error("Invalid ELF vd_aux")?;
248 let verdaux =
249 VerdauxIterator::new(self.endian, verdaux_data.0, verdef.vd_cnt.get(self.endian));
250
251 let next = verdef.vd_next.get(self.endian);
252 if next != 0 {
253 self.data
254 .skip(next as usize)
255 .read_error("Invalid ELF vd_next")?;
256 } else {
257 self.data = Bytes(&[]);
258 }
259 Ok(Some((verdef, verdaux)))
260 }
261}
262
263/// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERDEF`] section.
264#[derive(Debug, Clone)]
265pub struct VerdauxIterator<'data, Elf: FileHeader> {
266 endian: Elf::Endian,
267 data: Bytes<'data>,
268 count: u16,
269}
270
271impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> {
272 pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self {
273 VerdauxIterator {
274 endian,
275 data: Bytes(data),
276 count,
277 }
278 }
279
280 /// Return the next `Verdaux` entry.
281 pub fn next(&mut self) -> Result<Option<&'data elf::Verdaux<Elf::Endian>>> {
282 if self.count == 0 {
283 return Ok(None);
284 }
285
286 let verdaux = self
287 .data
288 .read_at::<elf::Verdaux<_>>(0)
289 .read_error("ELF verdaux is too short")?;
290
291 self.data
292 .skip(verdaux.vda_next.get(self.endian) as usize)
293 .read_error("Invalid ELF vda_next")?;
294 self.count -= 1;
295 Ok(Some(verdaux))
296 }
297}
298
299/// An iterator for the entries in an ELF [`elf::SHT_GNU_VERNEED`] section.
300#[derive(Debug, Clone)]
301pub struct VerneedIterator<'data, Elf: FileHeader> {
302 endian: Elf::Endian,
303 data: Bytes<'data>,
304}
305
306impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> {
307 pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self {
308 VerneedIterator {
309 endian,
310 data: Bytes(data),
311 }
312 }
313
314 /// Return the next `Verneed` entry.
315 pub fn next(
316 &mut self,
317 ) -> Result<
318 Option<(
319 &'data elf::Verneed<Elf::Endian>,
320 VernauxIterator<'data, Elf>,
321 )>,
322 > {
323 if self.data.is_empty() {
324 return Ok(None);
325 }
326
327 let verneed = self
328 .data
329 .read_at::<elf::Verneed<_>>(0)
330 .read_error("ELF verneed is too short")?;
331
332 let mut vernaux_data = self.data;
333 vernaux_data
334 .skip(verneed.vn_aux.get(self.endian) as usize)
335 .read_error("Invalid ELF vn_aux")?;
336 let vernaux =
337 VernauxIterator::new(self.endian, vernaux_data.0, verneed.vn_cnt.get(self.endian));
338
339 let next = verneed.vn_next.get(self.endian);
340 if next != 0 {
341 self.data
342 .skip(next as usize)
343 .read_error("Invalid ELF vn_next")?;
344 } else {
345 self.data = Bytes(&[]);
346 }
347 Ok(Some((verneed, vernaux)))
348 }
349}
350
351/// An iterator for the auxiliary records for an entry in an ELF [`elf::SHT_GNU_VERNEED`] section.
352#[derive(Debug, Clone)]
353pub struct VernauxIterator<'data, Elf: FileHeader> {
354 endian: Elf::Endian,
355 data: Bytes<'data>,
356 count: u16,
357}
358
359impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> {
360 pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self {
361 VernauxIterator {
362 endian,
363 data: Bytes(data),
364 count,
365 }
366 }
367
368 /// Return the next `Vernaux` entry.
369 pub fn next(&mut self) -> Result<Option<&'data elf::Vernaux<Elf::Endian>>> {
370 if self.count == 0 {
371 return Ok(None);
372 }
373
374 let vernaux = self
375 .data
376 .read_at::<elf::Vernaux<_>>(0)
377 .read_error("ELF vernaux is too short")?;
378
379 self.data
380 .skip(vernaux.vna_next.get(self.endian) as usize)
381 .read_error("Invalid ELF vna_next")?;
382 self.count -= 1;
383 Ok(Some(vernaux))
384 }
385}
386
387impl<Endian: endian::Endian> elf::Verdaux<Endian> {
388 /// Parse the version name from the string table.
389 pub fn name<'data, R: ReadRef<'data>>(
390 &self,
391 endian: Endian,
392 strings: StringTable<'data, R>,
393 ) -> Result<&'data [u8]> {
394 stringsResult<&[u8], ()>
395 .get(self.vda_name.get(endian))
396 .read_error("Invalid ELF vda_name")
397 }
398}
399
400impl<Endian: endian::Endian> elf::Verneed<Endian> {
401 /// Parse the file from the string table.
402 pub fn file<'data, R: ReadRef<'data>>(
403 &self,
404 endian: Endian,
405 strings: StringTable<'data, R>,
406 ) -> Result<&'data [u8]> {
407 stringsResult<&[u8], ()>
408 .get(self.vn_file.get(endian))
409 .read_error("Invalid ELF vn_file")
410 }
411}
412
413impl<Endian: endian::Endian> elf::Vernaux<Endian> {
414 /// Parse the version name from the string table.
415 pub fn name<'data, R: ReadRef<'data>>(
416 &self,
417 endian: Endian,
418 strings: StringTable<'data, R>,
419 ) -> Result<&'data [u8]> {
420 stringsResult<&[u8], ()>
421 .get(self.vna_name.get(endian))
422 .read_error("Invalid ELF vna_name")
423 }
424}
425