Attempts to generate a Fuzzware compatible config file from an ELF binary. Note: Unlike the original Fuzzware implementation, we rely entirely on the information included in the ELF file. We never attempt to manually identify sections.
(path: &Path)
| 100 | /// Note: Unlike the original Fuzzware implementation, we rely entirely on the information included |
| 101 | /// in the ELF file. We never attempt to manually identify sections. |
| 102 | pub fn from_elf(path: &Path) -> anyhow::Result<FirmwareConfig> { |
| 103 | let data = std::fs::read(path)?; |
| 104 | |
| 105 | // TODO: we parse the binary as a generic file, just to find names of sections we could avoid |
| 106 | // this by directly getting names specialized reader. |
| 107 | let obj_file = object::File::parse(data.as_slice())?; |
| 108 | |
| 109 | let elf = object::elf::FileHeader32::<Endianness>::parse(data.as_slice())?; |
| 110 | let endian = elf.endian()?; |
| 111 | |
| 112 | let file_name = path |
| 113 | .file_name() |
| 114 | .and_then(|x| x.to_str()) |
| 115 | .ok_or_else(|| anyhow::format_err!("unable to extract file name"))?; |
| 116 | |
| 117 | // Start with initial cortexm memory map. |
| 118 | let mut memory = cortexm_memory_map(); |
| 119 | let ram_range = memory.get("ram").map(|x| x.base_addr..x.base_addr + x.size); |
| 120 | |
| 121 | for section in obj_file.sections() { |
| 122 | tracing::info!( |
| 123 | "section \"{}\" address={:#x} size={:#x}", |
| 124 | section.name()?, |
| 125 | section.address(), |
| 126 | section.size() |
| 127 | ); |
| 128 | } |
| 129 | |
| 130 | // Determine what needs to be loaded by inspecting program headers |
| 131 | let program_headers = elf.program_headers(endian, data.as_slice())?; |
| 132 | |
| 133 | for (idx, segment) in program_headers.into_iter().enumerate() { |
| 134 | if segment.p_type(endian) != object::elf::PT_LOAD { |
| 135 | continue; |
| 136 | } |
| 137 | |
| 138 | let p_flags = segment.p_flags(endian); |
| 139 | let base_addr = segment.p_paddr(endian) as u64; |
| 140 | let vaddr = segment.p_vaddr(endian) as u64; |
| 141 | let in_memory_size = segment.p_memsz(endian) as u64; |
| 142 | |
| 143 | // Find a name to use for the segment by looking at section headers (note: segments don't |
| 144 | // have a name for ELF files). |
| 145 | let range = vaddr..vaddr + in_memory_size.max(1); |
| 146 | let mut name = |
| 147 | find_section_name(&obj_file, range).unwrap_or_else(|| format!("memory_{base_addr:#x}")); |
| 148 | |
| 149 | if let Some(suffix) = name.strip_prefix(".") { |
| 150 | name = suffix.into(); |
| 151 | } |
| 152 | |
| 153 | tracing::info!( |
| 154 | "PL_LOAD base_addr={base_addr:#x} (vaddr={vaddr:#x}) size={in_memory_size:#x} (name={name})" |
| 155 | ); |
| 156 | |
| 157 | if in_memory_size == 0 { |
| 158 | tracing::debug!("ignoring zero sized segment: Idx={idx}: {base_addr:#x} ({name})"); |
| 159 | continue; |
no test coverage detected