//! # Fast ELF editing library.
//!
//! ELF is the main format of library and executable files on most
//! Unix systems, with the notable exceptions of Apple's various
//! platforms.
//!
//! This library does the same as the existing `patchelf` tool, but
//! with a time and space complexity that do not depend on the file
//! size.
//!
//! # Use of CStr in this crate
//!
//! This library tries to avoid doing things more than once. In
//! particular, ELF files comprise many strings represented as a Rust
//! `CStr`, i.e. a string ended with a 0 byte. We usually convert
//! these to the more efficient `&[u8]` type as soon as
//! possible. Moreover, we keep the zero byte itself in the `&[u8]`
//! while doing so in order to make our code smaller (and avoid some
//! edge cases) when writing the strings.
//!
//! This has the advantage of speed, but the inconvenience that you
//! have to provide the 0 byte in your slices when interacting with
//! this crate. See the example below for examples.
//!
//! # Example
//!
//! In this example, we modify the binary from the GNU Hello package
//! from Ubuntu "Noble Numbat" (24.04). Note that this makes our tests
//! GPL 3, since the binary of GNU Hello is included in the test
//! binary.
//!
//! ```
//! use std::io::Write;
//! use elfedit::*;
//! let mut p = tempfile::tempfile().unwrap();
//! p.write_all(include_bytes!("hello")).unwrap();
//! let mut elf = Elf::open(&p).unwrap();
//!
//! let new_interp = b"/nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/ld-linux-x86-64.so.2\0";
//!
//! let new_needed = b"ma.bibliotheque.so.19\0";
//! let new_needed2 = b"ma.bibliotheque.so.24\0";
//! let runpath = b"a:b:c\0";
//! elf.set_runpath(runpath)
//!     .set_interpreter(new_interp)
//!     .add_needed(new_needed).unwrap()
//!     .add_needed(new_needed2).unwrap();
//! elf.update().unwrap();
//!
//! let elf = Elf::open(&p).unwrap();
//! let p = elf.parse().unwrap();
//! assert_eq!(p.interpreter().unwrap().unwrap().to_bytes_with_nul(), new_interp);
//! assert_eq!(p.runpath().unwrap().unwrap().to_bytes_with_nul(), runpath);
//! let needed: Result<Vec<_>, _> = p.needed().map(|x| x.map(|x| x.to_bytes_with_nul())).collect();
//! let needed = needed.unwrap();
//! assert_eq!(
//!     needed,
//!     &[
//!         &b"libc.so.6\0"[..],
//!         &b"ma.bibliotheque.so.19\0"[..],
//!         &b"ma.bibliotheque.so.24\0"[..]
//!     ]
//! );
//! ```

use std::collections::HashMap;
use std::ffi::CStr;
use thiserror::*;

#[derive(Debug, Error)]
pub enum Error {
    #[error(transparent)]
    IO(#[from] std::io::Error),
    #[error(transparent)]
    FromBytesWithNul(#[from] std::ffi::FromBytesWithNulError),
    #[error(transparent)]
    FromBytesUntilNul(#[from] std::ffi::FromBytesUntilNulError),
    #[error("Section not found: {name:?}")]
    SectionNotFound { name: Vec<u8> },
    #[error("Wrong file type")]
    WrongMagic,
    #[error("No dynamic section")]
    NoDynamicSection,
    #[error("No section table")]
    NoSectionTable,
}

#[derive(Debug, Copy, Clone, PartialEq)]
struct Bits(u8);

impl Bits {
    const B32: Self = Bits(1);
    const B64: Self = Bits(2);
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Endianness(u8);

impl Endianness {
    const LITTLE: Self = Endianness(1);
    const BIG: Self = Endianness(2);
}

#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
struct ABI(u8);

fn align_next(x: usize, mul: usize) -> usize {
    (x + mul - 1) & !(mul - 1)
}

#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
struct Type(u8);

#[allow(dead_code)]
impl Type {
    const NONE: Self = Type(0);
    const REPOSITIONABLE: Self = Type(1);
    const EXECUTABLE: Self = Type(2);
    const SHARED_OBJECT: Self = Type(3);
    const CORE: Self = Type(4);
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct Id {
    magic: [u8; 4],
    class: Bits,
    pub data: Endianness,
    version: u8,
    osabi: ABI,
    abi_version: u8,
    padding: [u8; 7],
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Endian<U>(U);

trait EndianT: Sized {
    fn endian(e: Endian<Self>, id: Endianness) -> Self;
}

impl EndianT for u16 {
    fn endian(e: Endian<Self>, id: Endianness) -> Self {
        if id == Endianness::LITTLE {
            u16::from_le(e.0)
        } else if id == Endianness::BIG {
            u16::from_be(e.0)
        } else {
            panic!()
        }
    }
}

impl EndianT for u32 {
    fn endian(e: Endian<Self>, id: Endianness) -> Self {
        if id == Endianness::LITTLE {
            u32::from_le(e.0)
        } else if id == Endianness::BIG {
            u32::from_be(e.0)
        } else {
            panic!()
        }
    }
}

impl EndianT for u64 {
    fn endian(e: Endian<Self>, id: Endianness) -> Self {
        if id == Endianness::LITTLE {
            u64::from_le(e.0)
        } else if id == Endianness::BIG {
            u64::from_be(e.0)
        } else {
            panic!()
        }
    }
}

impl EndianT for Sht {
    fn endian(e: Endian<Self>, id: Endianness) -> Sht {
        if id == Endianness::LITTLE {
            Sht(u32::from_le(e.0 .0))
        } else if id == Endianness::BIG {
            Sht(u32::from_be(e.0 .0))
        } else {
            panic!()
        }
    }
}

impl Endian<u16> {
    fn from(id: Endianness, u: u16) -> Self {
        if id == Endianness::LITTLE {
            Endian(u.to_le())
        } else if id == Endianness::BIG {
            Endian(u.to_be())
        } else {
            panic!()
        }
    }
}

impl Endian<u32> {
    fn from(id: Endianness, u: u32) -> Self {
        if id == Endianness::LITTLE {
            Endian(u.to_le())
        } else if id == Endianness::BIG {
            Endian(u.to_be())
        } else {
            panic!()
        }
    }
}

impl Endian<u64> {
    fn from(id: Endianness, u: u64) -> Self {
        if id == Endianness::LITTLE {
            Endian(u.to_le())
        } else if id == Endianness::BIG {
            Endian(u.to_be())
        } else {
            panic!()
        }
    }
}

impl Endian<Pht> {
    fn from(id: Endianness, u: Pht) -> Self {
        if id == Endianness::LITTLE {
            Endian(Pht(u.0.to_le()))
        } else if id == Endianness::BIG {
            Endian(Pht(u.0.to_be()))
        } else {
            panic!()
        }
    }
}

impl Endian<DynTag64> {
    fn from(id: Endianness, u: DynTag64) -> Self {
        if id == Endianness::LITTLE {
            Endian(DynTag64(u.0.to_le()))
        } else if id == Endianness::BIG {
            Endian(DynTag64(u.0.to_be()))
        } else {
            panic!()
        }
    }
}

impl Endian<DynTag32> {
    fn from(id: Endianness, u: DynTag32) -> Self {
        if id == Endianness::LITTLE {
            Endian(DynTag32(u.0.to_le()))
        } else if id == Endianness::BIG {
            Endian(DynTag32(u.0.to_be()))
        } else {
            panic!()
        }
    }
}

impl EndianT for Pht {
    fn endian(e: Endian<Self>, id: Endianness) -> Self {
        if id == Endianness::LITTLE {
            Pht(u32::from_le(e.0 .0))
        } else if id == Endianness::BIG {
            Pht(u32::from_be(e.0 .0))
        } else {
            panic!()
        }
    }
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
struct Header<Offset> {
    type_: Endian<u16>,
    machine: Endian<u16>,
    version: Endian<u32>,
    entry: Endian<Offset>,
    phoff: Endian<Offset>,
    shoff: Endian<Offset>,
    flags: Endian<u32>,
    ehsize: Endian<u16>,
    phentsize: Endian<u16>,
    phnum: Endian<u16>,
    shentsize: Endian<u16>,
    shnum: Endian<u16>,
    shstrndx: Endian<u16>,
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
struct SectionHeader<Offset> {
    name: Endian<u32>,
    type_: Endian<Sht>,
    flags: Endian<Offset>,
    addr: Endian<Offset>,
    offset: Endian<Offset>,
    size: Endian<Offset>,
    link: Endian<u32>,
    info: Endian<u32>,
    addralign: Endian<Offset>,
    entsize: Endian<Offset>,
}

#[derive(Debug)]
enum SectionHeader_<'a> {
    B32(&'a SectionHeader<u32>),
    B64(&'a SectionHeader<u64>),
}

#[derive(Debug)]
enum SectionHeaderMut_<'a> {
    B32(&'a mut SectionHeader<u32>),
    B64(&'a mut SectionHeader<u64>),
}

#[derive(Debug)]
enum ProgramHeaderBits<'a> {
    B32(&'a ProgramHeader<u32>),
    B64(&'a ProgramHeader<u64>),
}

#[derive(Debug)]
enum ProgramHeaderMut_<'a> {
    B32(&'a mut ProgramHeader<u32>),
    B64(&'a mut ProgramHeader<u64>),
}

impl<'a> SectionHeaderMut_<'a> {
    fn set_size(&mut self, h: Endianness, u: usize) {
        match self {
            SectionHeaderMut_::B32(s) => s.size = <Endian<u32>>::from(h, u as u32),
            SectionHeaderMut_::B64(s) => s.size = <Endian<u64>>::from(h, u as u64),
        }
    }
    pub fn set_offset(&mut self, h: Endianness, u: usize) {
        match self {
            SectionHeaderMut_::B32(s) => s.offset = <Endian<u32>>::from(h, u as u32),
            SectionHeaderMut_::B64(s) => s.offset = <Endian<u64>>::from(h, u as u64),
        }
    }
    pub fn set_addr(&mut self, h: Endianness, u: usize) {
        match self {
            SectionHeaderMut_::B32(s) => s.addr = <Endian<u32>>::from(h, u as u32),
            SectionHeaderMut_::B64(s) => s.addr = <Endian<u64>>::from(h, u as u64),
        }
    }
    pub fn offset(&self, h: Endianness) -> u64 {
        match self {
            SectionHeaderMut_::B32(s) => EndianT::endian(s.offset, h) as u64,
            SectionHeaderMut_::B64(s) => EndianT::endian(s.offset, h),
        }
    }
    pub fn addr(&self, h: Endianness) -> u64 {
        match self {
            SectionHeaderMut_::B32(s) => EndianT::endian(s.addr, h) as u64,
            SectionHeaderMut_::B64(s) => EndianT::endian(s.addr, h),
        }
    }
}

impl<'a> ProgramHeaderMut_<'a> {
    fn set_filesz(&mut self, h: Endianness, u: usize) {
        match self {
            ProgramHeaderMut_::B32(s) => s.filesz = <Endian<u32>>::from(h, u as u32),
            ProgramHeaderMut_::B64(s) => s.filesz = <Endian<u64>>::from(h, u as u64),
        }
    }
    pub fn set_vaddr(&mut self, h: Endianness, u: usize) {
        match self {
            ProgramHeaderMut_::B32(s) => s.vaddr = <Endian<u32>>::from(h, u as u32),
            ProgramHeaderMut_::B64(s) => s.vaddr = <Endian<u64>>::from(h, u as u64),
        }
        // For now we set them to be equal.
        self.set_paddr(h, u);
    }
    pub fn set_paddr(&mut self, h: Endianness, u: usize) {
        match self {
            ProgramHeaderMut_::B32(s) => s.paddr = <Endian<u32>>::from(h, u as u32),
            ProgramHeaderMut_::B64(s) => s.paddr = <Endian<u64>>::from(h, u as u64),
        }
    }
    fn set_memsz(&mut self, h: Endianness, u: usize) {
        match self {
            ProgramHeaderMut_::B32(s) => s.memsz = <Endian<u32>>::from(h, u as u32),
            ProgramHeaderMut_::B64(s) => s.memsz = <Endian<u64>>::from(h, u as u64),
        }
    }
    pub fn set_offset(&mut self, h: Endianness, u: usize) {
        match self {
            ProgramHeaderMut_::B32(s) => s.offset = <Endian<u32>>::from(h, u as u32),
            ProgramHeaderMut_::B64(s) => s.offset = <Endian<u64>>::from(h, u as u64),
        }
    }
    pub fn set_type(&mut self, h: Endianness, u: Pht) {
        match self {
            ProgramHeaderMut_::B32(s) => s.type_ = <Endian<Pht>>::from(h, u),
            ProgramHeaderMut_::B64(s) => s.type_ = <Endian<Pht>>::from(h, u),
        }
    }
    pub fn set_flags(&mut self, h: Endianness, u: u32) {
        match self {
            ProgramHeaderMut_::B32(s) => s.flags32 = <Endian<u32>>::from(h, u),
            ProgramHeaderMut_::B64(s) => s.flags64 = <Endian<u32>>::from(h, u),
        }
    }
    pub fn set_align(&mut self, h: Endianness, u: u64) {
        match self {
            ProgramHeaderMut_::B32(s) => s.align = <Endian<u32>>::from(h, u as u32),
            ProgramHeaderMut_::B64(s) => s.align = <Endian<u64>>::from(h, u),
        }
    }

    fn type_(&self, id: Endianness) -> Pht {
        match self {
            ProgramHeaderMut_::B32(h) => EndianT::endian(h.type_, id),
            ProgramHeaderMut_::B64(h) => EndianT::endian(h.type_, id),
        }
    }
    pub fn filesz(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderMut_::B32(s) => EndianT::endian(s.filesz, h) as u64,
            ProgramHeaderMut_::B64(s) => EndianT::endian(s.filesz, h),
        }
    }
    pub fn memsz(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderMut_::B32(s) => EndianT::endian(s.memsz, h) as u64,
            ProgramHeaderMut_::B64(s) => EndianT::endian(s.memsz, h),
        }
    }
}

impl<'a> ProgramHeaderBits<'a> {
    fn offset(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderBits::B32(s) => EndianT::endian(s.offset, h) as u64,
            ProgramHeaderBits::B64(s) => EndianT::endian(s.offset, h),
        }
    }

    fn type_(&self, id: Endianness) -> Pht {
        match self {
            ProgramHeaderBits::B32(h) => EndianT::endian(h.type_, id),
            ProgramHeaderBits::B64(h) => EndianT::endian(h.type_, id),
        }
    }
    fn filesz(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderBits::B32(s) => EndianT::endian(s.filesz, h) as u64,
            ProgramHeaderBits::B64(s) => EndianT::endian(s.filesz, h),
        }
    }
    fn flags(&self, h: Endianness) -> u32 {
        match self {
            ProgramHeaderBits::B32(s) => EndianT::endian(s.flags32, h),
            ProgramHeaderBits::B64(s) => EndianT::endian(s.flags64, h),
        }
    }
    fn vaddr(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderBits::B32(s) => EndianT::endian(s.vaddr, h) as u64,
            ProgramHeaderBits::B64(s) => EndianT::endian(s.vaddr, h),
        }
    }
    fn memsz(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderBits::B32(s) => EndianT::endian(s.memsz, h) as u64,
            ProgramHeaderBits::B64(s) => EndianT::endian(s.memsz, h),
        }
    }
    pub fn align(&self, h: Endianness) -> u64 {
        match self {
            ProgramHeaderBits::B32(s) => EndianT::endian(s.align, h) as u64,
            ProgramHeaderBits::B64(s) => EndianT::endian(s.align, h),
        }
    }
}

impl<'a> SectionHeader_<'a> {
    fn offset(&self, h: Endianness) -> u64 {
        match self {
            SectionHeader_::B32(s) => EndianT::endian(s.offset, h) as u64,
            SectionHeader_::B64(s) => EndianT::endian(s.offset, h),
        }
    }
    fn size(&self, h: Endianness) -> u64 {
        match self {
            SectionHeader_::B32(s) => EndianT::endian(s.size, h) as u64,
            SectionHeader_::B64(s) => EndianT::endian(s.size, h),
        }
    }
    pub fn addr(&self, h: Endianness) -> u64 {
        match self {
            SectionHeader_::B32(s) => EndianT::endian(s.addr, h) as u64,
            SectionHeader_::B64(s) => EndianT::endian(s.addr, h),
        }
    }

    fn name(&self, id: Endianness) -> u32 {
        match self {
            SectionHeader_::B32(h) => EndianT::endian(h.name, id),
            SectionHeader_::B64(h) => EndianT::endian(h.name, id),
        }
    }

    fn type_(&self, id: Endianness) -> Sht {
        match self {
            SectionHeader_::B32(h) => EndianT::endian(h.type_, id),
            SectionHeader_::B64(h) => EndianT::endian(h.type_, id),
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
struct Pht(u32);

impl Pht {
    const LOAD: Self = Pht(1);
    const PHDR: Self = Pht(6);
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
struct ProgramHeader<Offset: Flags> {
    type_: Endian<Pht>,
    flags64: Endian<Offset::F64>,
    offset: Endian<Offset>,
    vaddr: Endian<Offset>,
    paddr: Endian<Offset>,
    filesz: Endian<Offset>,
    memsz: Endian<Offset>,
    flags32: Endian<Offset::F32>,
    align: Endian<Offset>,
}

/// An open ELF file.
///
/// Most operations on this type follow a "builder pattern", meaning
/// that they don't actually update the underlying file until the
/// `.update()` method is called.
pub struct Elf<'a> {
    map: memmap::MmapMut,
    f: &'a std::fs::File,
    new_needed: Option<Vec<Vec<u8>>>,
    new_runpath: Vec<u8>,
    new_interp: Vec<u8>,
}

impl<'f> Elf<'f> {
    /// Create a builder around the provided file. Maps `file` in memory.
    pub fn open(file: &'f std::fs::File) -> Result<Self, Error> {
        let map = unsafe { memmap::MmapOptions::new().map_mut(&file)? };
        let e = Elf {
            map,
            f: file,
            new_needed: None,
            new_runpath: Vec::new(),
            new_interp: Vec::new(),
        };

        if e.map.len() >= std::mem::size_of::<Header<u32>>()
            && e.id().magic == [0x7f, 0x45, 0x4c, 0x46]
        {
            Ok(e)
        } else {
            Err(Error::WrongMagic)
        }
    }

    pub fn dynsym(&self) -> Dynsym {
        let header = self.header();
        let id = self.id();
        let names = self.names(id.data, &header).unwrap();
        for i in 0..header.shnum(id.data) {
            let s = self.section(i as usize).unwrap();
            let name = names.name(s.name(id.data) as usize).unwrap();
            if name.to_bytes() == b".dynsym" {
                let offset = s.offset(id.data) as usize;
                let size = s.size(id.data) as usize;
                if id.class == Bits::B32 {
                    return Dynsym::B32(unsafe {
                        std::slice::from_raw_parts(
                            self.map.as_ptr().add(offset) as *const u8 as *const Elf32Sym,
                            size / std::mem::size_of::<Elf32Sym>(),
                        )
                    });
                } else if id.class == Bits::B64 {
                    return Dynsym::B64(unsafe {
                        std::slice::from_raw_parts(
                            self.map.as_ptr().add(offset) as *const u8 as *const Elf64Sym,
                            size / std::mem::size_of::<Elf64Sym>(),
                        )
                    });
                }
            }
        }
        Dynsym::B64(&[])
    }

    pub fn verneed(&self) -> Vec<(Verneed, Vec<Vernaux>)> {
        let header = self.header();
        let id = self.id();
        let names = self.names(id.data, &header).unwrap();
        let mut result = Vec::new();
        for i in 0..header.shnum(id.data) {
            let s = self.section(i as usize).unwrap();
            let name = names.name(s.name(id.data) as usize).unwrap();
            if name.to_bytes() == b".gnu.version_r" {
                let offset = s.offset(id.data) as usize;
                let size = s.size(id.data) as usize;
                unsafe {
                    let mut current = self.map.as_ptr().add(offset) as *const u8;
                    loop {
                        assert!((current as usize) < offset + size);
                        let mut aux = Vec::new();
                        let mut aux_ptr =
                            current.add((&*(current as *const Verneed)).aux as usize) as *const u8;
                        loop {
                            assert!((aux_ptr as usize) < offset + size);
                            aux.push(*(aux_ptr as *const Vernaux));
                            let next = (&*(aux_ptr as *const Vernaux)).next as usize;
                            if next == 0 {
                                break;
                            }
                            aux_ptr = aux_ptr.add(next);
                        }
                        result.push((*(current as *const Verneed), aux));
                        let next = (&*(current as *const Vernaux)).next as usize;
                        if next == 0 {
                            break;
                        }
                        current = current.add(next);
                    }
                }
            }
        }
        result
    }

    pub fn debug_svg(&self) -> String {
        use std::fmt::Write;
        let mut svg = String::new();
        let mut x = 0;
        writeln!(
            svg,
            r#"<rect x="0" y="0" width="{}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">File id</text>"#,
            std::mem::size_of::<Id>(),
            std::mem::size_of::<Id>()/2
        )
        .unwrap();
        x += std::mem::size_of::<Id>();

        let id = self.id();
        let header_w = if let Bits::B64 = id.class {
            std::mem::size_of::<Header<u64>>()
        } else {
            std::mem::size_of::<Header<u32>>()
        };
        writeln!(
            svg,
            r#"<rect x="{x}" y="0" width="{}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">File header</text>"#,
            header_w,
            x + header_w / 2,
        )
            .unwrap();
        x += header_w;

        let h = self.header();
        let shoff = h.shoff(id.data);
        let shw = h.shnum(id.data) * h.shentsize(id.data);

        writeln!(
            svg,
            r#"<rect x="{shoff}" y="0" width="{shw}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">Section headers</text>"#,
            shoff as usize + shw as usize/2,
        )
            .unwrap();
        x = x.max(shoff as usize + shw as usize);

        let phoff = h.phoff(id.data);
        let phw = h.phnum(id.data) * h.phentsize(id.data);

        writeln!(
            svg,
            r#"<rect x="{phoff}" y="0" width="{phw}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">Program headers</text>"#,
            phoff as usize + phw as usize/2,
        )
            .unwrap();
        x = x.max(phoff as usize + phw as usize);

        let mut y = 10;
        for i in 0..h.shnum(id.data) {
            let s = self.section(i as usize).unwrap();
            let off = s.offset(id.data);
            let sz = s.size(id.data);
            let names = self.names(id.data, &h).unwrap();
            let name = names.name(s.name(id.data) as usize).unwrap();
            let xoff = off + sz / 2;
            x = x.max(off as usize + sz as usize);
            writeln!(
                svg,
                r#"<rect x="{off}" y="{y}" width="{sz}" height="10" fill="none" stroke="blue" /><text text-anchor="middle" x="{xoff}" y="{y}" transform="rotate(90, {}, {y})">s{}: {}</text>"#,
                xoff,
                i,
                name.to_str().unwrap()
            )
                .unwrap();
            y += 10;
        }

        for i in 0..h.phnum(id.data) {
            let s = self.program(i as usize);
            let off = s.offset(id.data);
            let sz = s.filesz(id.data);
            let xoff = off + sz / 2;
            writeln!(
                svg,
                r#"<rect x="{off}" y="{y}" width="{sz}" height="10" fill="none" stroke="red" /><text text-anchor="middle" x="{xoff}" y="{}">p{}: {}</text>"#,
                y+10,
                i,
                s.flags(id.data)
            )
                .unwrap();
            y += 10;
        }

        for i in 0..h.phnum(id.data) {
            let s = self.program(i as usize);
            let off = s.vaddr(id.data);
            let sz = s.memsz(id.data);
            let xoff = off + sz / 2;
            x = x.max(off as usize + sz as usize);
            writeln!(
                svg,
                r#"<rect x="{off}" y="{y}" width="{sz}" height="10" fill="none" stroke="green" /><text text-anchor="middle" x="{xoff}" y="{}">p{}: {}</text>"#,
                y+10,
                i,
                s.flags(id.data),
            )
                .unwrap();
            y += 10;
        }

        let mut p = 4096;
        let yy = y + 50;
        let mut pages = String::new();
        while p < x {
            writeln!(
                pages,
                r#"<path d="M {p} -40 V {}" stroke="gray" stroke-dasharray="4" /><text text-anchor="middle" x="{p}" y="0">{p}</text>"#,
                y + 10,
            )
            .unwrap();
            p += 4096;
        }

        format!(
            r#"<svg viewBox="0 -40 {x} {yy}" xmlns="http://www.w3.org/2000/svg">{pages}{svg}</svg>"#,
        )
    }

    fn section<'a>(&'a self, n: usize) -> Result<SectionHeader_<'a>, Error> {
        let h = self.header();
        let id = self.id().data;
        assert!(n < h.shnum(id) as usize);
        let ph = h.shoff(id) as usize + n * h.shentsize(id) as usize;
        match h {
            Header_::B32(_) => {
                if ph + std::mem::size_of::<SectionHeader<u32>>() > self.map.len() {
                    return Err(Error::NoSectionTable);
                }
                assert_eq!(ph % std::mem::align_of::<SectionHeader<u32>>(), 0);
                Ok(SectionHeader_::B32(unsafe {
                    &*((self.map.as_ptr()).add(ph) as *const u8 as *const SectionHeader<u32>)
                }))
            }
            Header_::B64(_) => {
                if ph + std::mem::size_of::<SectionHeader<u64>>() > self.map.len() {
                    return Err(Error::NoSectionTable);
                }
                assert_eq!(ph % std::mem::align_of::<SectionHeader<u64>>(), 0);
                Ok(SectionHeader_::B64(unsafe {
                    &*((self.map.as_ptr()).add(ph) as *const u8 as *const SectionHeader<u64>)
                }))
            }
        }
    }

    fn sections<U>(&self) -> &[SectionHeader<U>] {
        let h = self.header();
        let id = self.id().data;
        if self.id().class == Bits::B64 {
            assert_eq!(std::mem::size_of::<U>(), 8)
        } else {
            assert_eq!(std::mem::size_of::<U>(), 4)
        }
        let ph = h.shoff(id) as usize;
        let shnum = h.shnum(id) as usize;

        assert!(ph + std::mem::size_of::<SectionHeader<U>>() <= self.map.len());
        assert_eq!(ph % std::mem::align_of::<SectionHeader<U>>(), 0);
        unsafe {
            std::slice::from_raw_parts(
                self.map.as_ptr().add(ph) as *const u8 as *const SectionHeader<U>,
                shnum,
            )
        }
    }

    fn program<'a>(&'a self, n: usize) -> ProgramHeaderBits<'a> {
        let h = self.header();
        let id = self.id().data;
        assert!(n < h.phnum(id) as usize);
        let ph = h.phoff(id) as usize + n * h.phentsize(id) as usize;
        match h {
            Header_::B32(_) => {
                assert!(ph + std::mem::size_of::<ProgramHeader<u32>>() <= self.map.len());
                assert_eq!(ph % std::mem::align_of::<ProgramHeader<u32>>(), 0);
                ProgramHeaderBits::B32(unsafe {
                    &*((self.map.as_ptr()).add(ph) as *const u8 as *const ProgramHeader<u32>)
                })
            }
            Header_::B64(_) => {
                assert!(ph + std::mem::size_of::<ProgramHeader<u64>>() <= self.map.len());
                assert_eq!(ph % std::mem::align_of::<ProgramHeader<u32>>(), 0);
                ProgramHeaderBits::B64(unsafe {
                    &*((self.map.as_ptr()).add(ph) as *const u8 as *const ProgramHeader<u64>)
                })
            }
        }
    }

    fn program_mut<'a>(&'a mut self, n: usize) -> ProgramHeaderMut_<'a> {
        let h = self.header();
        let id = self.id().data;
        assert!(n < h.phnum(id) as usize);
        let ph = h.phoff(id) as usize + n * h.phentsize(id) as usize;
        match h {
            Header_::B32(_) => {
                assert!(ph + std::mem::size_of::<ProgramHeader<u32>>() <= self.map.len());
                assert_eq!(ph % std::mem::align_of::<ProgramHeader<u32>>(), 0);
                ProgramHeaderMut_::B32(unsafe {
                    &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut ProgramHeader<u32>)
                })
            }
            Header_::B64(_) => {
                assert!(ph + std::mem::size_of::<ProgramHeader<u64>>() <= self.map.len());
                assert_eq!(ph % std::mem::align_of::<ProgramHeader<u32>>(), 0);
                ProgramHeaderMut_::B64(unsafe {
                    &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut ProgramHeader<u64>)
                })
            }
        }
    }

    fn section_mut<'a>(&'a mut self, n: usize) -> SectionHeaderMut_<'a> {
        let h = self.header();
        let id = self.id().data;
        assert!(n < h.shnum(id) as usize);
        let ph = h.shoff(id) as usize + n * h.shentsize(id) as usize;
        match h {
            Header_::B32(_) => {
                assert!(ph + std::mem::size_of::<SectionHeader<u32>>() <= self.map.len());
                assert_eq!(ph % std::mem::align_of::<SectionHeader<u32>>(), 0);
                SectionHeaderMut_::B32(unsafe {
                    &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut SectionHeader<u32>)
                })
            }
            Header_::B64(_) => {
                assert!(ph + std::mem::size_of::<SectionHeader<u64>>() <= self.map.len());
                assert_eq!(ph % std::mem::align_of::<SectionHeader<u64>>(), 0);
                SectionHeaderMut_::B64(unsafe {
                    &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut SectionHeader<u64>)
                })
            }
        }
    }

    fn names<'a>(&'a self, id: Endianness, header: &Header_) -> Result<Names<'a>, Error> {
        let s = self.section(header.shstrndx(id) as usize)?;
        let offset = s.offset(id) as usize;
        let size = s.size(id) as usize;
        Ok(Names(&self.map[offset..offset + size]))
    }

    pub fn id(&self) -> &Id {
        assert!(self.map.len() >= std::mem::size_of::<Header<u32>>());
        unsafe { &*(self.map.as_ptr() as *const u8 as *const Id) }
    }

    fn header(&self) -> Header_ {
        let id = self.id();
        // The aligment is guaranteed here by design of the ELF
        // file. The size isn't, however, so we need to check it.
        match id.class {
            Bits::B32 => {
                assert!(
                    std::mem::size_of::<Id>() + std::mem::size_of::<Header<u32>>()
                        <= self.map.len()
                );

                let header_: &Header<u32> = unsafe {
                    &*(self.map.as_ptr().add(std::mem::size_of::<Id>()) as *const u8
                        as *const Header<u32>)
                };
                Header_::B32(header_)
            }
            Bits::B64 => {
                assert!(
                    std::mem::size_of::<Id>() + std::mem::size_of::<Header<u64>>()
                        <= self.map.len()
                );
                let header_: &Header<u64> = unsafe {
                    &*(self.map.as_ptr().add(std::mem::size_of::<Id>()) as *const u8
                        as *const Header<u64>)
                };
                Header_::B64(header_)
            }
            _ => panic!(),
        }
    }

    fn header_mut(&mut self) -> HeaderMut_ {
        let id = self.id();
        match id.class {
            Bits::B32 => {
                assert!(
                    std::mem::size_of::<Id>() + std::mem::size_of::<Header<u32>>()
                        <= self.map.len()
                );
                let header_: &mut Header<u32> = unsafe {
                    &mut *(self.map.as_mut_ptr().add(std::mem::size_of::<Id>()) as *mut u8
                        as *mut Header<u32>)
                };
                HeaderMut_::B32(header_)
            }
            Bits::B64 => {
                assert!(
                    std::mem::size_of::<Id>() + std::mem::size_of::<Header<u64>>()
                        <= self.map.len()
                );
                let header_: &mut Header<u64> = unsafe {
                    &mut *(self.map.as_mut_ptr().add(std::mem::size_of::<Id>()) as *mut u8
                        as *mut Header<u64>)
                };
                HeaderMut_::B64(header_)
            }
            _ => panic!(),
        }
    }

    /// Parses the sections relevant for linking (.interp, .dynstr, .dynamic).
    ///
    /// The sections are stored in an unordered array in the ELF file,
    /// so this avoids going through the array for each query.
    pub fn parse(&self) -> Result<Option<Parsed>, Error> {
        let id = self.id();
        let header = self.header();
        let names = match self.names(id.data, &header) {
            Ok(names) => names,
            Err(Error::NoSectionTable) => return Ok(None),
            Err(e) => return Err(e),
        };
        let mut dynstr = Names(&[][..]);
        let mut dynamic = None;
        let mut interp = None;
        for i in 0..header.shnum(id.data) {
            let s = self.section(i as usize)?;
            let name = names.name(s.name(id.data) as usize)?;
            if name.to_bytes() == b".interp" {
                let offset = s.offset(id.data) as usize;
                let size = s.size(id.data) as usize;
                interp = Some(&self.map[offset..offset + size]);
            }

            if name.to_bytes() == b".dynstr" {
                let offset = s.offset(id.data) as usize;
                let size = s.size(id.data) as usize;
                dynstr = Names(&self.map[offset..offset + size]);
            }

            if s.type_(id.data) == Sht::DYNAMIC {
                let offset = s.offset(id.data) as usize;
                let size = s.size(id.data) as usize;
                assert!(offset + size <= self.map.len());
                dynamic = Some(unsafe {
                    dynamic_from_raw(id.class, self.map.as_ptr().add(offset), size)
                });
            }
        }
        if let Some(dynamic) = dynamic {
            Ok(Some(Parsed {
                data: id.data,
                dynstr,
                dynamic,
                interp,
            }))
        } else {
            Ok(None)
        }
    }

    pub fn section_bytes(&self, name_: &[u8]) -> Result<Option<&[u8]>, Error> {
        let id = self.id();
        let header = self.header();
        let names = self.names(id.data, &header)?;
        for i in 0..header.shnum(id.data) {
            let s = self.section(i as usize)?;
            let name = names.name(s.name(id.data) as usize)?;
            if name.to_bytes() == name_ {
                let offset = s.offset(id.data) as usize;
                let size = s.size(id.data) as usize;
                return Ok(Some(&self.map[offset..offset + size]));
            }
        }
        Ok(None)
    }

    fn find_section<U>(&self, name: &[u8]) -> Result<Option<usize>, Error> {
        let id = self.id().data;
        let header = self.header();
        let names = self.names(id, &header)?;
        for (i, sec) in self.sections::<U>().into_iter().enumerate() {
            let n = names
                .name(EndianT::endian(sec.name, id) as usize)?
                .to_bytes();
            if n == name {
                return Ok(Some(i));
            }
        }
        Ok(None)
    }

    fn find_next_off<U: EndianT + Copy + Into<u64>>(&self, off: u64) -> u64 {
        let id = self.id().data;
        let header = self.header();
        let mut next_off = self.f.metadata().unwrap().len();
        for s in self
            .sections::<U>()
            .iter()
            .map(|sec| EndianT::endian(sec.offset, id).into())
            .chain(std::iter::once(header.shoff(id)))
        {
            if s > off && s < next_off {
                next_off = s;
            }
        }
        next_off
    }

    /// Updates a section. This is a low-level function and doesn't
    /// normally need to be called.
    fn update_section(&mut self, name: &[u8], new: &[u8]) -> Result<(), Error> {
        let id = self.id().data;
        let class = self.id().class;

        let interp_i = if class == Bits::B64 {
            self.find_section::<u64>(name)?
        } else {
            self.find_section::<u32>(name)?
        };
        let Some(interp_i) = interp_i else {
            return Err(Error::SectionNotFound {
                name: name.to_vec(),
            });
        };

        let off = self.section(interp_i as usize)?.offset(id);
        let next_off = if class == Bits::B64 {
            self.find_next_off::<u64>(off) as u64
        } else {
            self.find_next_off::<u32>(off) as u64
        };

        let new_len = new.len();

        if new_len <= (next_off - off) as usize {
            // Easy
            let off = self.section(interp_i as usize)?.offset(id);
            assert!(off as usize + new.len() <= self.map.len());
            unsafe {
                self.map
                    .as_mut_ptr()
                    .add(off as usize)
                    .write_bytes(0, (next_off - off) as usize);
                std::ptr::copy(
                    new.as_ptr() as *const i8,
                    self.map.as_mut_ptr().add(off as usize) as *mut i8,
                    new.len(),
                );
            }
            self.section_mut(interp_i as usize).set_size(id, new_len);
            Ok(())
        } else {
            Ok(self.update_section_allocate(id, new, interp_i))
        }
    }

    fn update_section_allocate(&mut self, id: Endianness, new: &[u8], interp_i: usize) {
        let new_len = new.len();
        let h = self.header();

        let mut sections: Vec<_> = (0..h.shnum(id))
            .map(|p_| {
                let sh = self.section(p_ as usize).unwrap();
                (
                    sh.offset(id) as usize,
                    sh.size(id) as usize,
                    sh.type_(id),
                    p_ as isize,
                )
            })
            .collect();
        sections.push((
            h.shoff(id) as usize,
            h.shentsize(id) as usize * h.shnum(id) as usize,
            Sht::NULL,
            -1,
        ));
        sections.sort_by_key(|(o, _, _, _)| *o);

        let s = sections
            .iter()
            .position(|(_, _, _, i)| *i == interp_i as isize)
            .unwrap();

        let mut last_vaddr = 0;
        let mut page_size = 0;
        let mut programs: Vec<_> = (0..h.phnum(id))
            .map(|p_| {
                let sh = self.program(p_ as usize);
                last_vaddr = last_vaddr.max((sh.vaddr(id) + sh.memsz(id)) as usize);
                let type_ = sh.type_(id);
                if type_ == Pht::LOAD {
                    page_size = sh.align(id) as usize;
                }
                (
                    sh.offset(id) as usize,
                    sh.filesz(id) as usize,
                    type_,
                    sh.flags(id),
                    p_ as isize,
                )
            })
            .collect();

        let mut rounded_last_vaddr = align_next(last_vaddr as usize, page_size);

        programs.sort_by_key(|(o, _, _, _, _)| *o);

        let s_off = sections[s].0;
        let p = programs
            .iter()
            .position(|(o, e, t, _, _)| *o <= s_off && *o + *e > s_off && *t == Pht::LOAD)
            .expect("Section isn't loaded, no need to do anything");

        let s_len = sections[s].1;

        let end0 = s_off + s_len;
        let mut end = end0;
        let shoff = h.shoff(id) as usize;
        let mut new_offset = shoff;

        for (off, len, _, _) in sections.iter().skip(s + 1) {
            if *off >= end + new_len {
                // Is this the same segment ?
                if end <= programs[p].0 + programs[p].1 {
                    new_offset = end;
                    break;
                }
            }
            end = end.max(*off + *len)
        }

        if new_offset < shoff {
            // Just write the section here and return.
            unsafe {
                std::ptr::copy(
                    new.as_ptr(),
                    self.map.as_mut_ptr().add(new_offset),
                    new.len(),
                )
            }
            let mut sec = self.section_mut(interp_i);
            let addr = new_offset + sec.addr(id) as usize - sec.offset(id) as usize;
            sec.set_addr(id, addr);
            sec.set_offset(id, new_offset);
            sec.set_size(id, new.len());
            if new_offset == programs[p].0 + programs[p].1 {
                // Extend segment
                let mut p = self.program_mut(programs[p].4 as usize);
                p.set_filesz(id, p.filesz(id) as usize + new.len());
                p.set_memsz(id, p.memsz(id) as usize + new.len());
            }
            // If the section has its own segment, update that too.
            for (o, l, _, _, p) in &programs {
                if *o == s_off && *l == s_len {
                    let mut p = self.program_mut(*p as usize);
                    p.set_offset(id, new_offset);
                    p.set_filesz(id, new.len());
                    p.set_memsz(id, new.len());
                    p.set_vaddr(id, addr);
                }
            }
        } else {
            // Add a segment at the end, possibly moving the first
            // section after the section header table.
            let pht_end = h.phoff(id) as usize + h.phentsize(id) as usize * h.phnum(id) as usize;

            let first_section_after_pht = sections
                .iter()
                .filter_map(|(o, _, _, _)| {
                    if *o >= pht_end as usize {
                        Some(*o)
                    } else {
                        None
                    }
                })
                .min()
                .unwrap_or(0);

            let new_offset;

            // Can we add a new LOAD segment (i.e. is there space
            // immediately after the program header table?)
            if first_section_after_pht < pht_end + h.phentsize(id) as usize {
                // No space. We will move enough sections to the end
                // of the file, in order to make room.
                //
                // In the worst global case of patching, we need 3 new
                // LOAD segments:
                //
                // - two new ones (interp + dynstr)
                // - possibly an extra one for the initial segments we
                // need to move

                let new_segments = 3;
                let limit = pht_end + new_segments * h.phentsize(id) as usize;
                let mut init = 0;
                let mut end = 0;
                let mut init_off = 0;
                let mut end_off = 0;
                for (n, (o, _, _, _)) in sections.iter().enumerate() {
                    if *o >= pht_end && init == 0 {
                        init = n;
                        init_off = *o;
                    }
                    if *o >= limit && end == 0 {
                        end = n;
                        end_off = *o;
                    }
                }
                // Max between the length of the file and the max of
                // the sections' span.
                let current_len = sections
                    .iter()
                    .map(|(o, l, t, _)| if *t != Sht::NOBITS { o + l } else { 0 })
                    .max()
                    .unwrap_or(self.f.metadata().unwrap().len() as usize);
                let rounded_len = align_next(current_len, page_size);
                let rounded_moved = align_next(end_off - init_off, page_size as usize);

                new_offset = rounded_len as usize + rounded_moved;
                // New pages for the moved + new pages for new.
                self.f
                    .set_len((rounded_len + rounded_moved + new.len()) as u64)
                    .unwrap();
                self.map = unsafe { memmap::MmapOptions::new().map_mut(self.f).unwrap() };
                unsafe {
                    std::ptr::copy(
                        self.map.as_ptr().add(init_off),
                        self.map.as_mut_ptr().add(rounded_len as usize),
                        end_off - init_off,
                    );
                }
                let mut pi = 0;
                for s in init..end {
                    let sec_n = sections[s].3;
                    let mut sec = self.section_mut(sec_n as usize);
                    let new_offset = rounded_len as usize + sec.offset(id) as usize - init_off;
                    sec.set_offset(id, new_offset);
                    sec.set_addr(id, new_offset + rounded_last_vaddr - rounded_len as usize);
                    while pi < programs.len() && programs[pi].0 <= sections[s].0 {
                        if programs[pi].0 == sections[s].0 && programs[pi].2 != Pht::LOAD {
                            let mut p = self.program_mut(programs[pi].4 as usize);
                            p.set_offset(id, new_offset);
                            p.set_vaddr(id, new_offset + rounded_last_vaddr - rounded_len as usize);
                            // Since we may be trying to update this
                            // very section + segment (example: if the
                            // interpreter is the first section),
                            // update the `programs` array.
                            programs[pi].0 = new_offset;
                        }
                        pi += 1;
                    }
                }

                self.add_load_segment(
                    id,
                    rounded_len as usize,
                    end_off - init_off,
                    rounded_last_vaddr,
                    4,
                    page_size,
                );
                rounded_last_vaddr = rounded_last_vaddr + rounded_moved as usize;
            } else {
                // There is space after the program header table, no
                // need to move anything.
                let current_len = sections
                    .iter()
                    .map(|(o, l, t, _)| if *t != Sht::NOBITS { o + l } else { 0 })
                    .max()
                    .unwrap_or(self.f.metadata().unwrap().len() as usize);
                let rounded_len = align_next(current_len, page_size);
                new_offset = rounded_len;
                // New pages for new.
                self.f.set_len((rounded_len + new.len()) as u64).unwrap();
                self.map = unsafe { memmap::MmapOptions::new().map_mut(self.f).unwrap() };
            }

            assert_eq!(new_offset % 4096, rounded_last_vaddr % 4096);

            let mut sec = self.section_mut(interp_i);
            let old_offset = sec.offset(id) as usize;
            sec.set_offset(id, new_offset);
            sec.set_size(id, new.len());
            sec.set_addr(id, rounded_last_vaddr as usize);

            // Update the corresponding segment, which may have been
            // moved just above (hence the resetting of programs[..]
            // above).
            for (o, _, ty, _, p) in &programs {
                if *o == old_offset && *ty != Pht::LOAD {
                    let mut p = self.program_mut(*p as usize);
                    p.set_offset(id, new_offset);
                    p.set_filesz(id, new.len());
                    p.set_memsz(id, new.len());
                    p.set_vaddr(id, rounded_last_vaddr as usize);
                }
            }

            unsafe {
                std::ptr::copy(
                    new.as_ptr(),
                    self.map.as_mut_ptr().add(new_offset),
                    new.len(),
                )
            }

            self.add_load_segment(
                id,
                new_offset,
                new.len(),
                rounded_last_vaddr as usize,
                6,
                page_size,
            );
        }
    }

    /// Beware: the last segment of the file must be RW.
    fn add_load_segment(
        &mut self,
        id: Endianness,
        offset: usize,
        len: usize,
        vaddr: usize,
        flags: u32,
        align: usize,
    ) {
        let mut hh = self.header_mut();
        let phentsize = hh.phentsize(id) as usize;
        let phnum = hh.phnum(id);
        hh.set_phnum(id, phnum + 1);
        let mut p = self.program_mut(phnum as usize);
        p.set_offset(id, offset);
        p.set_memsz(id, len);
        p.set_filesz(id, len);
        p.set_vaddr(id, vaddr);
        p.set_type(id, Pht::LOAD);
        p.set_flags(id, flags);
        p.set_align(id, align as u64);
        for p in 0..phnum {
            let mut p = self.program_mut(p as usize);
            if p.type_(id) == Pht::PHDR {
                p.set_filesz(id, p.filesz(id) as usize + phentsize);
                p.set_memsz(id, p.memsz(id) as usize + phentsize);
                break;
            }
        }
    }

    /// Sets the runpath. Note that this doesn't modify the underlying
    /// file yet, and can be changed at any later time.
    pub fn set_runpath(&mut self, runpath: &[u8]) -> &mut Self {
        self.new_runpath.clear();
        self.new_runpath.extend(runpath);
        self
    }

    /// Add a needed library. This is useful for example for dynamic
    /// libraries loaded using `dlopen`.
    pub fn add_needed(&mut self, needed: &[u8]) -> Result<&mut Self, Error> {
        if self.new_needed.is_none() {
            let Some(parsed) = self.parse()? else {
                return Err(Error::NoDynamicSection);
            };
            let owned = parsed.current_needed_owned()?;
            self.new_needed = Some(owned)
        }
        if let Some(ref mut n) = self.new_needed {
            if n.iter().all(|x| x != needed) {
                n.push(needed.to_vec().into());
            }
        }
        Ok(self)
    }

    /// Remove multiple needed libraries in a single pass. This
    /// function works like `Vec::retain`: the `retain` function is
    /// passed nedeed library names in order, and these items are kept
    /// if and only if `retain` returns `true`.
    pub fn retain_needed<F: FnMut(&[u8]) -> bool>(
        &mut self,
        mut retain: F,
    ) -> Result<&mut Self, Error> {
        if self.new_needed.is_none() {
            let Some(parsed) = self.parse()? else {
                return Err(Error::NoDynamicSection);
            };
            let owned = parsed.current_needed_owned()?;
            self.new_needed = Some(owned);
        }
        if let Some(ref mut n) = self.new_needed {
            n.retain(|x| retain(&*x));
        }
        Ok(self)
    }

    fn update_sections(
        &mut self,
        data: Endianness,
        dynstr: &mut Vec<u8>,
        dynamic: &mut OwnedDynamic,
        runpath: Option<&[u8]>,
    ) -> Result<(), Error> {
        // Start by saving the current runpath
        let initial_dynamic_len = match dynamic {
            OwnedDynamic::B32(d) => d.len(),
            OwnedDynamic::B64(d) => d.len(),
        };
        remove_trailing_needed_runpath(data, dynstr, dynamic);
        let id = self.id().data;
        if self.new_runpath.is_empty() {
            if let Some(runpath) = runpath {
                set_runpath(id, dynstr, dynamic, &runpath)
            }
        } else {
            set_runpath(id, dynstr, dynamic, &self.new_runpath)
        }

        let id = self.id().data;
        if let Some(ref mut needed) = self.new_needed {
            retain_needed(id, dynstr, dynamic, needed);
        }

        match dynamic {
            OwnedDynamic::B32(d) => {
                while d.len() < initial_dynamic_len {
                    d.push(u32::dyn_(id, u32::tag_nul(), 0));
                }
            }
            OwnedDynamic::B64(d) => {
                while d.len() < initial_dynamic_len {
                    d.push(u64::dyn_(id, u64::tag_nul(), 0));
                }
            }
        }
        Ok(())
    }

    /// Run the updates, potentially extending the underlying ELF file.
    pub fn update(mut self, into: Option<&std::path::Path>) -> Result<bool, Error> {
        let Some(p) = self.parse()? else {
            return Ok(false);
        };
        let mut dynstr_owned = p.dynstr.0.to_vec();
        let mut dynamic_owned = p.dynamic.to_owned();
        let interp_owned = p.interp.to_owned();

        // Avoid a reparsing in `update_sections`.
        let p = Parsed {
            data: p.data,
            dynamic: dynamic_owned.as_ref(),
            dynstr: Names(&dynstr_owned),
            interp: interp_owned.as_deref(),
        };
        let runpath = p.runpath()?.map(|x| x.to_bytes_with_nul().to_vec());

        self.update_sections(
            p.data,
            &mut dynstr_owned,
            &mut dynamic_owned,
            runpath.as_deref(),
        )?;
        let new_interp = std::mem::replace(&mut self.new_interp, Vec::new());
        let new_runpath = std::mem::replace(&mut self.new_runpath, Vec::new());
        let new_needed = self.new_needed.take();
        let id = self.id().data;
        if !new_runpath.is_empty() {
            set_runpath(id, &mut dynstr_owned, &mut dynamic_owned, &new_runpath)
        }

        let into_file = if let Some(into) = into {
            let mut f = std::fs::OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                .open(&into)?;
            std::fs::set_permissions(into, self.f.metadata()?.permissions())?;
            std::io::copy(&mut self.f, &mut f)?;
            Some(f)
        } else {
            None
        };

        let mut elf = if let Some(ref file) = into_file {
            Elf::open(&file)?
        } else {
            self
        };

        if !new_interp.is_empty() {
            elf.update_section(b".interp", &new_interp).unwrap()
        }
        if new_needed.is_some() || !new_runpath.is_empty() {
            elf.update_section(b".dynstr", &dynstr_owned).unwrap();
            elf.update_section(b".dynamic", dynamic_owned.as_bytes())
                .unwrap();
        }

        let id = elf.id().data;

        match dynamic_owned {
            OwnedDynamic::B32(ref mut v) => {
                for t in v.iter_mut() {
                    elf.update_dynamic::<u32>(id, t)?
                }
            }
            OwnedDynamic::B64(ref mut v) => {
                for t in v.iter_mut() {
                    elf.update_dynamic::<u64>(id, t)?
                }
            }
        }
        elf.update_section(b".dynamic", dynamic_owned.as_bytes())
            .unwrap();

        Ok(true)
    }

    fn update_dynamic<F: Flags>(&self, id: Endianness, f: &mut F::Dyn) -> Result<(), Error> {
        if F::tag(id, *f) == F::dt_strtab() {
            let s = self.find_section::<F>(b".dynstr").unwrap().unwrap();
            let sec = self.section(s)?;
            F::set_d_un(id, f, sec.addr(id) as usize)
        } else if F::tag(id, *f) == F::dt_strsz() {
            let s = self.find_section::<F>(b".dynstr").unwrap().unwrap();
            let sec = self.section(s)?;
            F::set_d_un(id, f, sec.size(id) as usize)
        } else if F::tag(id, *f) == F::dt_gnuhash() {
            let s = self.find_section::<F>(b".gnu.hash").unwrap().unwrap();
            let sec = self.section(s)?;
            F::set_d_un(id, f, sec.addr(id) as usize)
        }
        Ok(())
    }

    pub fn set_interpreter(&mut self, new: &[u8]) -> &mut Self {
        self.new_interp = new.to_vec();
        self
    }
}

/// An index to some of the sections of the file.
///
/// This is used to avoid going through the list of sections for each
/// request. There are rarely many sections, but since the underlying
/// ELF file is memory-mapped, merely loading the list could incur
/// unnecessary IO.
#[derive(Debug)]
pub struct Parsed<'a> {
    data: Endianness,
    dynamic: Dynamic<'a>,
    dynstr: Names<'a>,
    interp: Option<&'a [u8]>,
}

#[derive(Debug, Clone)]
enum OwnedDynamic {
    B32(Vec<Elf32Dyn>),
    B64(Vec<Elf64Dyn>),
}

impl OwnedDynamic {
    fn as_ref(&self) -> Dynamic {
        match self {
            OwnedDynamic::B32(d) => Dynamic::B32(d),
            OwnedDynamic::B64(d) => Dynamic::B64(d),
        }
    }

    fn as_bytes(&self) -> &[u8] {
        // The `unsafe` here are always satisfied since the inputs to
        // `from_raw_parts` are taken from valid `Vec`s.
        match self {
            OwnedDynamic::B32(x) => unsafe {
                std::slice::from_raw_parts(
                    x.as_ptr() as *const u8,
                    x.len() * std::mem::size_of::<Elf32Dyn>(),
                )
            },
            OwnedDynamic::B64(x) => unsafe {
                std::slice::from_raw_parts(
                    x.as_ptr() as *const u8,
                    x.len() * std::mem::size_of::<Elf64Dyn>(),
                )
            },
        }
    }
}

impl<'a> Dynamic<'a> {
    fn to_owned(&self) -> OwnedDynamic {
        match self {
            Dynamic::B32(x) => OwnedDynamic::B32(x.to_vec()),
            Dynamic::B64(x) => OwnedDynamic::B64(x.to_vec()),
        }
    }
}

/// If the "needed" and/or "runpath" fields in the .dynamic section
/// point to the end of the .dynstr section, we can optimise the
/// .dynstr section by removing them first, and then adding them.
///
/// Otherwise, we will (in other functions) leave these fields where
/// they are, add new ones at the end and ignore the previous values.
fn remove_trailing_needed_runpath_<F: Flags>(
    id: Endianness,
    dynstr: &mut Vec<u8>,
    dynamic: &mut Vec<F::Dyn>,
) {
    let mut off: Vec<F::Dyn> = dynamic
        .iter()
        .filter_map(|x| {
            if F::tag(id, *x) == F::dt_needed() || F::tag(id, *x) == F::dt_runpath() {
                Some(*x)
            } else {
                None
            }
        })
        .collect();
    off.sort_by_key(|x| F::d_un(id, *x));
    let mut removed = std::collections::HashSet::new();
    while let Some(p) = off.pop() {
        let c = CStr::from_bytes_until_nul(&dynstr[F::d_un(id, p) as usize..]).unwrap();
        let len = c.count_bytes() + 1;
        if F::d_un(id, p) as usize + len >= dynstr.len() {
            dynstr.truncate(F::d_un(id, p) as usize);
            removed.insert(p);
        } else {
            off.push(p);
            break;
        }
    }

    dynamic.retain(|x| !removed.contains(x))
}

fn remove_trailing_needed_runpath(
    id: Endianness,
    dynstr: &mut Vec<u8>,
    dynamic: &mut OwnedDynamic,
) {
    match dynamic {
        OwnedDynamic::B32(b) => remove_trailing_needed_runpath_::<u32>(id, dynstr, b),
        OwnedDynamic::B64(b) => remove_trailing_needed_runpath_::<u64>(id, dynstr, b),
    }
}

fn set_runpath_<F: Flags>(
    id: Endianness,
    dynstr: &mut Vec<u8>,
    dynamic: &mut Vec<F::Dyn>,
    rpath: &[u8],
) {
    for x in dynamic.iter_mut() {
        if F::tag(id, *x) == F::dt_runpath() {
            // Test for rpath size
            let current = &mut dynstr[F::d_un(id, *x)..];
            let pos = current.iter().position(|x| *x == 0).unwrap();
            if pos + 1 >= rpath.len() {
                // If the current CStr representing the rpath is at least
                // as long as the new rpath, replace
                let current = &mut current[..rpath.len()];
                current.clone_from_slice(rpath);
            } else {
                // Else, insert at the end.
                let offset = dynstr.len();
                dynstr.extend(rpath);
                F::set_d_un(id, x, offset);
            }
            return;
        }
    }
    let offset = dynstr.len();
    dynstr.extend(rpath);
    // If the dynamic array ends with two DT_NULL, pop one.
    if dynamic.len() >= 2 && F::tag(id, dynamic[dynamic.len() - 2]) == F::tag_nul() {
        dynamic.pop();
    }
    dynamic.insert(0, F::dyn_(id, F::dt_runpath(), offset));
}

fn retain_needed(
    id: Endianness,
    dynstr: &mut Vec<u8>,
    dynamic: &mut OwnedDynamic,
    needed: &mut Vec<Vec<u8>>,
) {
    match dynamic {
        OwnedDynamic::B32(b) => retain_needed_::<u32>(id, dynstr, b, needed),
        OwnedDynamic::B64(b) => retain_needed_::<u64>(id, dynstr, b, needed),
    }
}

fn retain_needed_<F: Flags>(
    id: Endianness,
    dynstr: &mut Vec<u8>,
    dynamic: &mut Vec<F::Dyn>,
    needed: &[Vec<u8>],
) {
    // Try to reuse existing strings from needed libs.
    let initial_dynamic_len = dynamic.len();
    let mut offsets = HashMap::new();
    for d in dynamic.iter() {
        if F::tag(id, *d) == F::dt_needed() {
            let off = F::d_un(id, *d);
            if off < dynstr.len() {
                if let Ok(c) = CStr::from_bytes_with_nul(&dynstr[off..]) {
                    offsets.insert(c.to_bytes_with_nul(), off);
                }
            }
        }
    }

    let mut new_dynamic = Vec::with_capacity(dynamic.len() + needed.len());
    let mut needed_it = needed.iter();
    let mut extra_dynstr = Vec::with_capacity(needed.len() * 20);
    for mut d in dynamic.drain(..) {
        if F::tag(id, d) == F::tag_nul() {
            break;
        } else if F::tag(id, d) != F::dt_needed() {
            new_dynamic.push(d);
        } else if let Some(needed) = needed_it.next() {
            if let Some(off) = offsets.get(needed.as_slice()) {
                F::set_d_un(id, &mut d, *off);
                new_dynamic.push(d)
            } else {
                F::set_d_un(id, &mut d, dynstr.len() + extra_dynstr.len());
                extra_dynstr.extend(needed.as_slice());
                new_dynamic.push(d)
            }
        }
    }

    for needed in needed_it {
        if let Some(off) = offsets.get(needed.as_slice()) {
            new_dynamic.push(F::dyn_(id, F::dt_needed(), *off))
        } else {
            new_dynamic.push(F::dyn_(
                id,
                F::dt_needed(),
                dynstr.len() + extra_dynstr.len(),
            ));
            extra_dynstr.extend(needed.as_slice());
        }
    }

    dynstr.extend(&extra_dynstr);
    new_dynamic.push(F::dyn_(id, F::tag_nul(), 0));
    while new_dynamic.len() < initial_dynamic_len {
        // Make the length even. I couldn't find it in the spec, but
        // ld seems to do that.
        new_dynamic.push(F::dyn_(id, F::tag_nul(), 0));
    }
    *dynamic = new_dynamic;
}

fn set_runpath(id: Endianness, dynstr: &mut Vec<u8>, dynamic: &mut OwnedDynamic, rpath: &[u8]) {
    match dynamic {
        OwnedDynamic::B32(b) => set_runpath_::<u32>(id, dynstr, b, rpath),
        OwnedDynamic::B64(b) => set_runpath_::<u64>(id, dynstr, b, rpath),
    }
}

enum Either<I, A: Iterator<Item = I>, B: Iterator<Item = I>> {
    A(A),
    B(B),
}

impl<I, A: Iterator<Item = I>, B: Iterator<Item = I>> Iterator for Either<I, A, B> {
    type Item = I;
    fn next(&mut self) -> Option<I> {
        match self {
            Either::A(a) => a.next(),
            Either::B(b) => b.next(),
        }
    }
}

impl<'a> Parsed<'a> {
    /// Return the runpath of the ELF file.
    ///
    /// Note that we actually mean "runpath" and not "rpath", which
    /// was used in the past with a more complex and less flexible
    /// semantics. This crate doesn't handle the rpath field at the
    /// moment. More information <a
    /// href="https://en.wikipedia.org/wiki/Rpath">on Wikipedia</a>.
    ///
    /// The error case of this function comes from invalid ELF file.
    pub fn runpath(&self) -> Result<Option<&'a CStr>, Error> {
        let mut rpath = None;
        match self.dynamic {
            Dynamic::B64(dyn_) => {
                for d in dyn_ {
                    if EndianT::endian(d.tag, self.data) == <u64 as Flags>::dt_runpath() {
                        rpath = Some(
                            self.dynstr
                                .name(EndianT::endian(d.d_un, self.data) as usize)?,
                        );
                    }
                }
            }
            Dynamic::B32(dyn_) => {
                for d in dyn_ {
                    if EndianT::endian(d.tag, self.data) == <u32 as Flags>::dt_runpath() {
                        rpath = Some(
                            self.dynstr
                                .name(EndianT::endian(d.d_un, self.data) as usize)?,
                        );
                    }
                }
            }
        }
        Ok(rpath)
    }

    /// Iterator on the needed libraries.
    ///
    /// When launching a binary, the "interpreter" will typically
    /// resolve these from the runpath and load them in memory before
    /// start.
    ///
    /// The error case of this function comes from invalid ELF file.
    pub fn needed<'b>(&'b self) -> impl Iterator<Item = Result<&'a CStr, Error>> + 'b {
        match self.dynamic {
            Dynamic::B64(dyn_) => Either::A(dyn_.iter().filter_map(|d| {
                if EndianT::endian(d.tag, self.data) == <u64 as Flags>::dt_needed() {
                    Some(
                        self.dynstr
                            .name(EndianT::endian(d.d_un, self.data) as usize),
                    )
                } else {
                    None
                }
            })),
            Dynamic::B32(dyn_) => Either::B(dyn_.iter().filter_map(|d| {
                if EndianT::endian(d.tag, self.data) == <u32 as Flags>::dt_needed() {
                    Some(
                        self.dynstr
                            .name(EndianT::endian(d.d_un, self.data) as usize),
                    )
                } else {
                    None
                }
            })),
        }
    }

    fn current_needed_owned(&self) -> Result<Vec<Vec<u8>>, Error> {
        self.needed()
            .map(|x| x.map(|x| x.to_bytes_with_nul().to_vec()))
            .collect()
    }

    /// Intepreter of the ELF file.
    ///
    /// The interpreter (often found at `/lib/ld.so`) is the program
    /// that resolves the dynamic libraries "needed" by the ELF, using
    /// the runpath variable.
    ///
    /// The error case of this function comes from invalid ELF file.
    pub fn interpreter(&self) -> Result<Option<&'a CStr>, Error> {
        Ok(self.interp.map(|x| CStr::from_bytes_until_nul(x).unwrap()))
    }
}

#[derive(Debug)]
enum Dynamic<'a> {
    B32(&'a [Elf32Dyn]),
    B64(&'a [Elf64Dyn]),
}

#[derive(Debug)]
struct Names<'a>(&'a [u8]);

impl<'a> Names<'a> {
    fn name(&self, n: usize) -> Result<&'a CStr, Error> {
        Ok(CStr::from_bytes_until_nul(&self.0[n..])?)
    }
}

trait Flags {
    type F32: std::fmt::Debug;
    type F64: std::fmt::Debug;
    type DynTag: std::fmt::Debug + std::convert::From<u64> + PartialEq;
    type Dyn: Copy + std::fmt::Debug + PartialEq + Eq + std::hash::Hash;

    fn dyn_(id: Endianness, tag: Self::DynTag, d_un: usize) -> Self::Dyn;

    fn d_un(id: Endianness, _: Self::Dyn) -> usize;
    fn set_d_un(id: Endianness, _: &mut Self::Dyn, _: usize);
    fn tag(id: Endianness, _: Self::Dyn) -> Self::DynTag;
    fn tag_nul() -> Self::DynTag;

    fn dt_needed() -> Self::DynTag {
        Self::DynTag::from(1)
    }

    fn dt_strsz() -> Self::DynTag {
        Self::DynTag::from(0x0a)
    }

    fn dt_gnuhash() -> Self::DynTag {
        Self::DynTag::from(0x000000006ffffef5)
    }

    fn dt_strtab() -> Self::DynTag {
        Self::DynTag::from(0x5)
    }

    fn dt_runpath() -> Self::DynTag {
        Self::DynTag::from(0x1d)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct DynTag32(u32);

impl EndianT for DynTag32 {
    fn endian(e: Endian<Self>, id: Endianness) -> Self {
        DynTag32(EndianT::endian(Endian(e.0 .0), id))
    }
}

impl std::convert::From<u64> for DynTag32 {
    fn from(e: u64) -> Self {
        Self(e as u32)
    }
}

impl EndianT for DynTag64 {
    fn endian(e: Endian<Self>, id: Endianness) -> Self {
        DynTag64(EndianT::endian(Endian(e.0 .0), id))
    }
}

impl std::convert::From<u64> for DynTag64 {
    fn from(e: u64) -> Self {
        Self(e)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct DynTag64(u64);

impl Flags for u32 {
    type F32 = u32;
    type F64 = ();
    type DynTag = DynTag32;
    type Dyn = Elf32Dyn;

    fn dyn_(id: Endianness, tag: Self::DynTag, d_un: usize) -> Self::Dyn {
        Elf32Dyn {
            tag: <Endian<DynTag32>>::from(id, tag),
            d_un: <Endian<u32>>::from(id, d_un as u32),
        }
    }
    fn set_d_un(id: Endianness, x: &mut Self::Dyn, val: usize) {
        x.d_un = <Endian<u32>>::from(id, val as u32)
    }
    fn tag_nul() -> Self::DynTag {
        DynTag32(0)
    }

    fn d_un(id: Endianness, x: Self::Dyn) -> usize {
        EndianT::endian(x.d_un, id) as usize
    }
    fn tag(id: Endianness, x: Self::Dyn) -> Self::DynTag {
        EndianT::endian(x.tag, id)
    }
}

impl Flags for u64 {
    type F32 = ();
    type F64 = u32;
    type DynTag = DynTag64;
    type Dyn = Elf64Dyn;

    fn dyn_(e: Endianness, tag: Self::DynTag, d_un: usize) -> Self::Dyn {
        Elf64Dyn {
            tag: <Endian<DynTag64>>::from(e, tag),
            d_un: <Endian<u64>>::from(e, d_un as u64),
        }
    }
    fn set_d_un(id: Endianness, x: &mut Self::Dyn, val: usize) {
        x.d_un = <Endian<u64>>::from(id, val as u64)
    }
    fn tag_nul() -> Self::DynTag {
        DynTag64(0)
    }

    fn d_un(id: Endianness, x: Self::Dyn) -> usize {
        EndianT::endian(x.d_un, id) as usize
    }
    fn tag(id: Endianness, x: Self::Dyn) -> Self::DynTag {
        EndianT::endian(x.tag, id)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
struct Elf32Dyn {
    tag: Endian<DynTag32>,
    d_un: Endian<u32>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
struct Elf64Dyn {
    tag: Endian<DynTag64>,
    d_un: Endian<u64>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Elf32Sym {
    name: Endian<u32>,
    value: Endian<u32>,
    size: Endian<u32>,
    info: u8,
    other: u8,
    shndx: Endian<u16>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Elf64Sym {
    name: Endian<u32>,
    info: u8,
    other: u8,
    shndx: Endian<u16>,
    value: Endian<u64>,
    size: Endian<u64>,
}

#[derive(Debug)]
pub enum Dynsym<'a> {
    B32(&'a [Elf32Sym]),
    B64(&'a [Elf64Sym]),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Verneed {
    version: u16,
    cnt: u16,
    file: u32,
    aux: u32,
    next: u32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Vernaux {
    hash: u32,
    flags: u16,
    other: u16,
    name: u32,
    next: u32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Sht(u32);

#[allow(dead_code)]
impl Sht {
    const NULL: Self = Sht(0);
    const PROGBITS: Self = Sht(1);
    const SYMTAB: Self = Sht(2);
    const STRTAB: Self = Sht(3);
    const RELA: Self = Sht(4);
    const HASH: Self = Sht(5);
    const DYNAMIC: Self = Sht(6);
    const NOTE: Self = Sht(7);
    const NOBITS: Self = Sht(8);
}

#[derive(Debug, Clone, Copy)]
enum Header_<'a> {
    B32(&'a Header<u32>),
    B64(&'a Header<u64>),
}

#[derive(Debug)]
enum HeaderMut_<'a> {
    B32(&'a mut Header<u32>),
    B64(&'a mut Header<u64>),
}

impl<'a> HeaderMut_<'a> {
    pub fn set_phnum(&mut self, id: Endianness, off: u16) {
        match self {
            HeaderMut_::B32(h) => h.phnum = <Endian<u16>>::from(id, off),
            HeaderMut_::B64(h) => h.phnum = <Endian<u16>>::from(id, off),
        }
    }

    pub fn phnum(&self, id: Endianness) -> u16 {
        match self {
            HeaderMut_::B32(h) => EndianT::endian(h.phnum, id),
            HeaderMut_::B64(h) => EndianT::endian(h.phnum, id),
        }
    }
    pub fn phentsize(&self, id: Endianness) -> u16 {
        match self {
            HeaderMut_::B32(h) => EndianT::endian(h.phentsize, id),
            HeaderMut_::B64(h) => EndianT::endian(h.phentsize, id),
        }
    }
}

impl<'a> Header_<'a> {
    fn shstrndx(&self, id: Endianness) -> u16 {
        match self {
            Header_::B32(h) => EndianT::endian(h.shstrndx, id),
            Header_::B64(h) => EndianT::endian(h.shstrndx, id),
        }
    }

    fn shnum(&self, id: Endianness) -> u16 {
        match self {
            Header_::B32(h) => EndianT::endian(h.shnum, id),
            Header_::B64(h) => EndianT::endian(h.shnum, id),
        }
    }

    pub fn shoff(&self, id: Endianness) -> u64 {
        match self {
            Header_::B32(h) => EndianT::endian(h.shoff, id) as u64,
            Header_::B64(h) => EndianT::endian(h.shoff, id),
        }
    }

    fn shentsize(&self, id: Endianness) -> u16 {
        match self {
            Header_::B32(h) => EndianT::endian(h.shentsize, id),
            Header_::B64(h) => EndianT::endian(h.shentsize, id),
        }
    }

    fn phentsize(&self, id: Endianness) -> u16 {
        match self {
            Header_::B32(h) => EndianT::endian(h.phentsize, id),
            Header_::B64(h) => EndianT::endian(h.phentsize, id),
        }
    }

    pub fn phnum(&self, id: Endianness) -> u16 {
        match self {
            Header_::B32(h) => EndianT::endian(h.phnum, id),
            Header_::B64(h) => EndianT::endian(h.phnum, id),
        }
    }

    fn phoff(&self, id: Endianness) -> u64 {
        match self {
            Header_::B32(h) => EndianT::endian(h.phoff, id) as u64,
            Header_::B64(h) => EndianT::endian(h.phoff, id),
        }
    }
}

unsafe fn dynamic_from_raw<'a>(class: Bits, ptr: *const u8, size: usize) -> Dynamic<'a> {
    match class {
        Bits::B32 => {
            assert_eq!((ptr as usize) % std::mem::align_of::<Elf32Dyn>(), 0);
            Dynamic::B32(unsafe {
                std::slice::from_raw_parts(
                    ptr as *const Elf32Dyn,
                    size / std::mem::size_of::<Elf32Dyn>(),
                )
            })
        }
        Bits::B64 => {
            assert_eq!((ptr as usize) % std::mem::align_of::<Elf64Dyn>(), 0);
            Dynamic::B64(unsafe {
                std::slice::from_raw_parts(
                    ptr as *const Elf64Dyn,
                    size / std::mem::size_of::<Elf64Dyn>(),
                )
            })
        }
        _ => panic!(),
    }
}