|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
2 | 3 |
|
3 | 4 | //! Contains structures and functions dedicated to the parsing, building and patching of firmwares |
4 | 5 | //! to be loaded into a given execution unit. |
@@ -467,65 +468,122 @@ mod elf { |
467 | 468 | transmute::FromBytes, // |
468 | 469 | }; |
469 | 470 |
|
| 471 | + /// Trait to abstract over ELF header differences. |
| 472 | + trait ElfHeader: FromBytes { |
| 473 | + fn shnum(&self) -> u16; |
| 474 | + fn shoff(&self) -> u64; |
| 475 | + fn shstrndx(&self) -> u16; |
| 476 | + } |
| 477 | + |
| 478 | + /// Trait to abstract over ELF section-header differences. |
| 479 | + trait ElfSectionHeader: FromBytes { |
| 480 | + fn name(&self) -> u32; |
| 481 | + fn offset(&self) -> u64; |
| 482 | + fn size(&self) -> u64; |
| 483 | + } |
| 484 | + |
| 485 | + /// Trait describing a matching ELF header and section-header format. |
| 486 | + trait ElfFormat { |
| 487 | + type Header: ElfHeader; |
| 488 | + type SectionHeader: ElfSectionHeader; |
| 489 | + } |
| 490 | + |
470 | 491 | /// Newtype to provide a [`FromBytes`] implementation. |
471 | 492 | #[repr(transparent)] |
472 | 493 | struct Elf64Hdr(bindings::elf64_hdr); |
473 | 494 | // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. |
474 | 495 | unsafe impl FromBytes for Elf64Hdr {} |
475 | 496 |
|
| 497 | + impl ElfHeader for Elf64Hdr { |
| 498 | + fn shnum(&self) -> u16 { |
| 499 | + self.0.e_shnum |
| 500 | + } |
| 501 | + |
| 502 | + fn shoff(&self) -> u64 { |
| 503 | + self.0.e_shoff |
| 504 | + } |
| 505 | + |
| 506 | + fn shstrndx(&self) -> u16 { |
| 507 | + self.0.e_shstrndx |
| 508 | + } |
| 509 | + } |
| 510 | + |
476 | 511 | #[repr(transparent)] |
477 | 512 | struct Elf64SHdr(bindings::elf64_shdr); |
478 | 513 | // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. |
479 | 514 | unsafe impl FromBytes for Elf64SHdr {} |
480 | 515 |
|
| 516 | + impl ElfSectionHeader for Elf64SHdr { |
| 517 | + fn name(&self) -> u32 { |
| 518 | + self.0.sh_name |
| 519 | + } |
| 520 | + |
| 521 | + fn offset(&self) -> u64 { |
| 522 | + self.0.sh_offset |
| 523 | + } |
| 524 | + |
| 525 | + fn size(&self) -> u64 { |
| 526 | + self.0.sh_size |
| 527 | + } |
| 528 | + } |
| 529 | + |
| 530 | + struct Elf64Format; |
| 531 | + |
| 532 | + impl ElfFormat for Elf64Format { |
| 533 | + type Header = Elf64Hdr; |
| 534 | + type SectionHeader = Elf64SHdr; |
| 535 | + } |
| 536 | + |
481 | 537 | /// Returns a NULL-terminated string from the ELF image at `offset`. |
482 | 538 | fn elf_str(elf: &[u8], offset: u64) -> Option<&str> { |
483 | 539 | let idx = usize::try_from(offset).ok()?; |
484 | 540 | let bytes = elf.get(idx..)?; |
485 | 541 | CStr::from_bytes_until_nul(bytes).ok()?.to_str().ok() |
486 | 542 | } |
487 | 543 |
|
488 | | - /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it. |
489 | | - pub(super) fn elf64_section<'a, 'b>(elf: &'a [u8], name: &'b str) -> Option<&'a [u8]> { |
490 | | - let hdr = &elf |
491 | | - .get(0..size_of::<bindings::elf64_hdr>()) |
492 | | - .and_then(Elf64Hdr::from_bytes)? |
493 | | - .0; |
494 | | - |
495 | | - // Get all the section headers. |
496 | | - let mut shdr = { |
497 | | - let shdr_num = usize::from(hdr.e_shnum); |
498 | | - let shdr_start = usize::try_from(hdr.e_shoff).ok()?; |
499 | | - let shdr_end = shdr_num |
500 | | - .checked_mul(size_of::<Elf64SHdr>()) |
501 | | - .and_then(|v| v.checked_add(shdr_start))?; |
502 | | - |
503 | | - elf.get(shdr_start..shdr_end) |
504 | | - .map(|slice| slice.chunks_exact(size_of::<Elf64SHdr>()))? |
505 | | - }; |
| 544 | + fn elf_section_generic<'a, F>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> |
| 545 | + where |
| 546 | + F: ElfFormat, |
| 547 | + { |
| 548 | + let hdr = F::Header::from_bytes(elf.get(0..size_of::<F::Header>())?)?; |
| 549 | + |
| 550 | + let shdr_num = usize::from(hdr.shnum()); |
| 551 | + let shdr_start = usize::try_from(hdr.shoff()).ok()?; |
| 552 | + let shdr_end = shdr_num |
| 553 | + .checked_mul(size_of::<F::SectionHeader>()) |
| 554 | + .and_then(|v| v.checked_add(shdr_start))?; |
| 555 | + |
| 556 | + // Get all the section headers as an iterator over byte chunks. |
| 557 | + let shdr_bytes = elf.get(shdr_start..shdr_end)?; |
| 558 | + let mut shdr_iter = shdr_bytes.chunks_exact(size_of::<F::SectionHeader>()); |
506 | 559 |
|
507 | 560 | // Get the strings table. |
508 | | - let strhdr = shdr |
| 561 | + let strhdr = shdr_iter |
509 | 562 | .clone() |
510 | | - .nth(usize::from(hdr.e_shstrndx)) |
511 | | - .and_then(Elf64SHdr::from_bytes)?; |
| 563 | + .nth(usize::from(hdr.shstrndx())) |
| 564 | + .and_then(F::SectionHeader::from_bytes)?; |
512 | 565 |
|
513 | 566 | // Find the section which name matches `name` and return it. |
514 | | - shdr.find_map(|sh| { |
515 | | - let hdr = Elf64SHdr::from_bytes(sh)?; |
516 | | - let name_offset = strhdr.0.sh_offset.checked_add(u64::from(hdr.0.sh_name))?; |
| 567 | + shdr_iter.find_map(|sh_bytes| { |
| 568 | + let sh = F::SectionHeader::from_bytes(sh_bytes)?; |
| 569 | + let name_offset = strhdr.offset().checked_add(u64::from(sh.name()))?; |
517 | 570 | let section_name = elf_str(elf, name_offset)?; |
518 | 571 |
|
519 | 572 | if section_name != name { |
520 | 573 | return None; |
521 | 574 | } |
522 | 575 |
|
523 | | - let start = usize::try_from(hdr.0.sh_offset).ok()?; |
524 | | - let end = usize::try_from(hdr.0.sh_size) |
| 576 | + let start = usize::try_from(sh.offset()).ok()?; |
| 577 | + let end = usize::try_from(sh.size()) |
525 | 578 | .ok() |
526 | | - .and_then(|sh_size| start.checked_add(sh_size))?; |
| 579 | + .and_then(|sz| start.checked_add(sz))?; |
527 | 580 |
|
528 | 581 | elf.get(start..end) |
529 | 582 | }) |
530 | 583 | } |
| 584 | + |
| 585 | + /// Tries to extract section with name `name` from the ELF64 image `elf`, and returns it. |
| 586 | + pub(super) fn elf64_section<'a>(elf: &'a [u8], name: &str) -> Option<&'a [u8]> { |
| 587 | + elf_section_generic::<Elf64Format>(elf, name) |
| 588 | + } |
531 | 589 | } |
0 commit comments