Bug 1946542 pt1 - Split the buildid_reader crate and ffi bindings r=gerard-majax
This makes `buildid_reader` usable without `nsstring`/`nserror` crates, as they rely on functions compiled into libxul. What was previously in `buildid_reader::reader` is now in `buildid_reader` (top-level), and the previous top-level of `buildid_reader` has been moved to `buildid_reader_ffi` (in the `ffi` subdirectory). Differential Revision: https://phabricator.services.mozilla.com/D237941
This commit is contained in:
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -615,9 +615,18 @@ dependencies = [
|
||||
"goblin 0.8.2",
|
||||
"libc",
|
||||
"log",
|
||||
"scroll",
|
||||
"thiserror 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "buildid_reader_ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"buildid_reader",
|
||||
"log",
|
||||
"nserror",
|
||||
"nsstring",
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2401,7 +2410,7 @@ dependencies = [
|
||||
"binary_http",
|
||||
"bitsdownload",
|
||||
"bookmark_sync",
|
||||
"buildid_reader",
|
||||
"buildid_reader_ffi",
|
||||
"cascade_bloom_filter",
|
||||
"cert_storage",
|
||||
"chardetng_c",
|
||||
|
||||
@@ -8,5 +8,4 @@ goblin = "^0.8.1"
|
||||
libc = "0.2"
|
||||
scroll = "0.12"
|
||||
log = "0.4"
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
thiserror = "2"
|
||||
|
||||
13
toolkit/library/buildid_reader/ffi/Cargo.toml
Normal file
13
toolkit/library/buildid_reader/ffi/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "buildid_reader_ffi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
buildid_reader = { path = ".." }
|
||||
log = "0.4"
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
45
toolkit/library/buildid_reader/ffi/lib.rs
Normal file
45
toolkit/library/buildid_reader/ffi/lib.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use buildid_reader::{result::Error, BuildIdReader};
|
||||
use log::{error, trace};
|
||||
use nserror::*;
|
||||
use nsstring::{nsAString, nsCString};
|
||||
use std::path::Path;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn read_toolkit_buildid_from_file(
|
||||
fname: &nsAString,
|
||||
nname: &nsCString,
|
||||
rv_build_id: &mut nsCString,
|
||||
) -> nsresult {
|
||||
let fname_str = fname.to_string();
|
||||
let path = Path::new(&fname_str);
|
||||
let note_name = nname.to_string();
|
||||
|
||||
trace!("read_toolkit_buildid_from_file {} {}", fname, nname);
|
||||
|
||||
match BuildIdReader::new(&path).and_then(|mut reader| reader.read_string_build_id(¬e_name)) {
|
||||
Ok(id) => {
|
||||
trace!("read_toolkit_buildid_from_file {}", id);
|
||||
rv_build_id.assign(&id);
|
||||
NS_OK
|
||||
}
|
||||
Err(err) => {
|
||||
error!("read_toolkit_buildid_from_file failed to read string build id from note {:?} with error {:?}", note_name, err);
|
||||
match err {
|
||||
Error::FailedToOpenFile { .. } => NS_ERROR_FILE_UNRECOGNIZED_PATH,
|
||||
Error::FailedToRead { .. } => NS_ERROR_OUT_OF_MEMORY,
|
||||
Error::StringFromBytesNulError { .. } | Error::StringFromBytesUtf8Error { .. } => {
|
||||
NS_ERROR_ILLEGAL_VALUE
|
||||
}
|
||||
Error::Goblin { .. } | Error::NotFatArchive => NS_ERROR_ILLEGAL_VALUE,
|
||||
Error::CopyBytes { .. } => NS_ERROR_OUT_OF_MEMORY,
|
||||
Error::NoteNotAvailable | Error::ArchNotAvailable => NS_ERROR_NOT_AVAILABLE,
|
||||
Error::InvalidNoteName => NS_ERROR_INVALID_ARG,
|
||||
Error::NotEnoughData { .. } => NS_ERROR_FAILURE,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
toolkit/library/buildid_reader/src/elf.rs
Normal file
84
toolkit/library/buildid_reader/src/elf.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::result::{Error, Result};
|
||||
|
||||
use super::BuildIdReader;
|
||||
use goblin::{elf, elf::note::Note, elf::section_header::SectionHeader};
|
||||
use scroll::ctx::TryFromCtx;
|
||||
|
||||
use log::trace;
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn get_build_id_bytes(&mut self, buffer: &[u8], note_name: &str) -> Result<Vec<u8>> {
|
||||
trace!("get_build_id_bytes: {}", note_name);
|
||||
let elf_head = elf::Elf::parse_header(buffer).map_err(|source| Error::Goblin {
|
||||
action: "parse Elf header",
|
||||
source,
|
||||
})?;
|
||||
let mut elf = elf::Elf::lazy_parse(elf_head).map_err(|source| Error::Goblin {
|
||||
action: "lazy parse Elf",
|
||||
source,
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", elf);
|
||||
let context = goblin::container::Ctx {
|
||||
container: elf.header.container().map_err(|source| Error::Goblin {
|
||||
action: "get Elf container",
|
||||
source,
|
||||
})?,
|
||||
le: elf.header.endianness().map_err(|source| Error::Goblin {
|
||||
action: "get Elf endianness",
|
||||
source,
|
||||
})?,
|
||||
};
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", context);
|
||||
let section_header_bytes = self.copy_bytes(
|
||||
elf_head.e_shoff as usize,
|
||||
(elf_head.e_shnum as usize) * (elf_head.e_shentsize as usize),
|
||||
)?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", section_header_bytes);
|
||||
elf.section_headers =
|
||||
SectionHeader::parse_from(§ion_header_bytes, 0, elf_head.e_shnum as usize, context)
|
||||
.map_err(|source| Error::Goblin {
|
||||
action: "parse section headers",
|
||||
source,
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", elf.section_headers);
|
||||
let shdr_strtab = &elf.section_headers[elf_head.e_shstrndx as usize];
|
||||
let shdr_strtab_bytes =
|
||||
self.copy_bytes(shdr_strtab.sh_offset as usize, shdr_strtab.sh_size as usize)?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", shdr_strtab_bytes);
|
||||
elf.shdr_strtab =
|
||||
goblin::strtab::Strtab::parse(&shdr_strtab_bytes, 0, shdr_strtab.sh_size as usize, 0x0)
|
||||
.map_err(|source| Error::Goblin {
|
||||
action: "parse section header string tab",
|
||||
source,
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", elf.shdr_strtab);
|
||||
let tk_note = elf
|
||||
.section_headers
|
||||
.iter()
|
||||
.find(|s| elf.shdr_strtab.get_at(s.sh_name) == Some(note_name))
|
||||
.ok_or(Error::NoteNotAvailable)?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", tk_note);
|
||||
let note_bytes = self.copy_bytes(tk_note.sh_offset as usize, tk_note.sh_size as usize)?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", note_bytes);
|
||||
let (note, _size) =
|
||||
Note::try_from_ctx(¬e_bytes, (4, context)).map_err(|source| Error::Goblin {
|
||||
action: "parse note",
|
||||
source,
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", note);
|
||||
Ok(note.desc.to_vec())
|
||||
}
|
||||
}
|
||||
@@ -2,43 +2,127 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::result::{Error, Result};
|
||||
use log::{error, trace};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Result as IOResult, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
use nserror::{nsresult, NS_OK};
|
||||
use nsstring::{nsAString, nsCString};
|
||||
pub mod result;
|
||||
|
||||
mod reader;
|
||||
use reader::BuildIdReader;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
use log::{error, trace};
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
mod macos;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn read_toolkit_buildid_from_file(
|
||||
fname: &nsAString,
|
||||
nname: &nsCString,
|
||||
rv_build_id: &mut nsCString,
|
||||
) -> nsresult {
|
||||
let fname_str = fname.to_string();
|
||||
let path = Path::new(&fname_str);
|
||||
let note_name = nname.to_string();
|
||||
// Target Android, Linux, *BSD, Solaris, ... Anything using ELF
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
|
||||
mod elf;
|
||||
|
||||
trace!("read_toolkit_buildid_from_file {} {}", fname, nname);
|
||||
pub struct BuildIdReader {
|
||||
file: File,
|
||||
}
|
||||
|
||||
match BuildIdReader::new(&path) {
|
||||
Ok(mut reader) => match reader.read_string_build_id(¬e_name) {
|
||||
Ok(id) => {
|
||||
trace!("read_toolkit_buildid_from_file {}", id);
|
||||
rv_build_id.assign(&id);
|
||||
NS_OK
|
||||
}
|
||||
Err(err) => {
|
||||
error!("read_toolkit_buildid_from_file failed to read string buiild id from note {:?} with error {:?}", note_name, err);
|
||||
#[cfg(target_os = "windows")]
|
||||
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::pe::header::Header>();
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::mach::header::Header64>();
|
||||
|
||||
// Target Android, Linux, *BSD, Solaris, ... Anything using ELF
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
|
||||
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::elf::header::Header>();
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn new(filename: &Path) -> Result<Self> {
|
||||
trace!("BuildIdReader::new {:?}", filename);
|
||||
let f = File::open(filename).map_err(|source| Error::FailedToOpenFile {
|
||||
path: filename.into(),
|
||||
source,
|
||||
})?;
|
||||
Ok(BuildIdReader { file: f })
|
||||
}
|
||||
|
||||
fn read_raw_build_id(&mut self, note_name: &str) -> Result<Vec<u8>> {
|
||||
trace!("BuildIdReader::read_raw_build_id {}", note_name);
|
||||
let mut buffer = [0; MAX_BUFFER_READ];
|
||||
let _ = self
|
||||
.file
|
||||
.read_exact(&mut buffer)
|
||||
.map_err(|source| Error::FailedToRead {
|
||||
size: MAX_BUFFER_READ,
|
||||
source,
|
||||
})?;
|
||||
|
||||
// This does actually depend on the platform, so it's not in this
|
||||
// impl nor source file but in the platform-dependant modules listed at
|
||||
// the end of this file
|
||||
self.get_build_id_bytes(&buffer, note_name)
|
||||
}
|
||||
|
||||
pub fn read_string_build_id(&mut self, note_name: &str) -> Result<String> {
|
||||
trace!("BuildIdReader::read_string_build_id {}", note_name);
|
||||
let b = self.read_raw_build_id(note_name).map_err(|err| {
|
||||
error!(
|
||||
"BuildIdReader::read_string_build_id failed to read raw build id with error {}",
|
||||
err
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("read_toolkit_buildid_from_file failed to build BuildIdReader for {:?} with error {:?}", path, err);
|
||||
);
|
||||
err
|
||||
})?;
|
||||
Self::string_from_bytes(&b)
|
||||
}
|
||||
|
||||
fn string_from_bytes(bytes: &[u8]) -> Result<String> {
|
||||
trace!("BuildIdReader::string_from_bytes {:?}", bytes);
|
||||
Ok(CStr::from_bytes_until_nul(bytes)?.to_str()?.to_string())
|
||||
}
|
||||
|
||||
fn copy_bytes_into(&mut self, offset: usize, buffer: &mut [u8]) -> IOResult<()> {
|
||||
trace!("BuildIdReader::copy_bytes_into @{}", offset);
|
||||
self.file.seek(SeekFrom::Start(offset as u64))?;
|
||||
self.file.read_exact(buffer)
|
||||
}
|
||||
|
||||
pub fn copy_bytes(&mut self, offset: usize, count: usize) -> Result<Vec<u8>> {
|
||||
trace!("BuildIdReader::copy_bytes @{} : {} bytes", offset, count);
|
||||
let mut buf = vec![0; count];
|
||||
self.copy_bytes_into(offset, &mut buf)
|
||||
.map_err(|source| Error::CopyBytes {
|
||||
offset,
|
||||
count,
|
||||
source,
|
||||
})?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
/// SAFETY: Caller need to ensure that `T` is safe to cast to bytes
|
||||
pub unsafe fn copy<T>(&mut self, offset: usize) -> Result<T> {
|
||||
trace!("BuildIdReader::copy @{}", offset);
|
||||
self.copy_array(offset, 1)
|
||||
.map(|v| v.into_iter().next().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
/// SAFETY: Caller need to ensure that `T` is safe to cast to bytes
|
||||
pub unsafe fn copy_array<T>(&mut self, offset: usize, num: usize) -> Result<Vec<T>> {
|
||||
trace!("BuildIdReader::copy_array @{} : {} num", offset, num);
|
||||
let mut uninit: Vec<std::mem::MaybeUninit<T>> = Vec::with_capacity(num);
|
||||
for _ in 0..num {
|
||||
uninit.push(std::mem::MaybeUninit::uninit());
|
||||
}
|
||||
let slice = std::slice::from_raw_parts_mut(
|
||||
uninit.as_mut_ptr() as *mut u8,
|
||||
uninit.len() * std::mem::size_of::<T>(),
|
||||
);
|
||||
self.copy_bytes_into(offset, slice)
|
||||
.map_err(|source| Error::CopyBytes {
|
||||
offset,
|
||||
count: slice.len(),
|
||||
source,
|
||||
})?;
|
||||
Ok(std::mem::transmute(uninit))
|
||||
}
|
||||
}
|
||||
|
||||
139
toolkit/library/buildid_reader/src/macos.rs
Normal file
139
toolkit/library/buildid_reader/src/macos.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::result::{Error, Result};
|
||||
|
||||
use goblin::mach;
|
||||
|
||||
use super::BuildIdReader;
|
||||
|
||||
use log::trace;
|
||||
|
||||
const HEADER_SIZE: usize = std::mem::size_of::<goblin::mach::header::Header64>();
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn get_build_id_bytes(&mut self, buffer: &[u8], note_name: &str) -> Result<Vec<u8>> {
|
||||
trace!("get_build_id_bytes: {}", note_name);
|
||||
let (section, note) = note_name.split_once(",").ok_or(Error::InvalidNoteName)?;
|
||||
trace!("get_build_id_bytes: {} {}", section, note);
|
||||
|
||||
let fat_header = mach::fat::FatHeader::parse(buffer).map_err(|source| Error::Goblin {
|
||||
action: "parse fat header",
|
||||
source,
|
||||
})?;
|
||||
trace!("get_build_id_bytes: fat header: {:?}", fat_header);
|
||||
|
||||
/* First we attempt to parse if there's a Fat header there
|
||||
*
|
||||
* If we have one, then we have a universal binary so we are going to
|
||||
* search the architectures to find the one we want, extract the correct
|
||||
* MachO buffer as well as the offset at which the MachO binary is.
|
||||
* Testing Universal binaries will require running gtest against a
|
||||
* Shippable build.
|
||||
*
|
||||
* If not we have a normal MachO and we directly parse the buffer we
|
||||
* have read earlier, and use a 0 offset.
|
||||
*/
|
||||
let (buf, main_offset): ([u8; HEADER_SIZE], usize) =
|
||||
if fat_header.magic == mach::fat::FAT_CIGAM || fat_header.magic == mach::fat::FAT_MAGIC
|
||||
{
|
||||
let total = std::mem::size_of::<mach::fat::FatHeader>() as usize
|
||||
+ (std::mem::size_of::<mach::fat::FatArch>() * fat_header.nfat_arch as usize);
|
||||
|
||||
let mach_buffer = self.copy_bytes(0, total)?;
|
||||
|
||||
if let mach::Mach::Fat(multi_arch) =
|
||||
mach::Mach::parse_lossy(&mach_buffer).map_err(|source| Error::Goblin {
|
||||
action: "parse mach binary",
|
||||
source,
|
||||
})?
|
||||
{
|
||||
let arches = multi_arch.arches().map_err(|source| Error::Goblin {
|
||||
action: "get multiarch arches",
|
||||
source,
|
||||
})?;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let that_arch = mach::constants::cputype::CPU_TYPE_X86_64;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let that_arch = mach::constants::cputype::CPU_TYPE_ARM64;
|
||||
|
||||
let arch_index = arches
|
||||
.iter()
|
||||
.position(|&x| x.cputype == that_arch)
|
||||
.ok_or(Error::ArchNotAvailable)?;
|
||||
trace!("get_build_id_bytes: arches[]: {:?}", arches[arch_index]);
|
||||
|
||||
let offset = arches[arch_index].offset as usize;
|
||||
|
||||
let b = self
|
||||
.copy_bytes(offset, HEADER_SIZE)?
|
||||
.try_into()
|
||||
.expect("copy_bytes didn't copy exactly as many bytes as requested");
|
||||
(b, offset)
|
||||
} else {
|
||||
return Err(Error::NotFatArchive);
|
||||
}
|
||||
} else {
|
||||
(
|
||||
buffer.try_into().map_err(|source| Error::NotEnoughData {
|
||||
expected: HEADER_SIZE,
|
||||
source,
|
||||
})?,
|
||||
0,
|
||||
)
|
||||
};
|
||||
trace!("get_build_id_bytes: {} {}", section, note);
|
||||
|
||||
let macho_head = mach::header::Header64::from_bytes(&buf);
|
||||
let mut address = main_offset + HEADER_SIZE;
|
||||
let end_of_commands = address + (macho_head.sizeofcmds as usize);
|
||||
|
||||
while address < end_of_commands {
|
||||
let command =
|
||||
unsafe { self.copy::<mach::load_command::LoadCommandHeader>(address as usize)? };
|
||||
trace!("get_build_id_bytes: command {:?}", command);
|
||||
|
||||
if command.cmd == mach::load_command::LC_SEGMENT_64 {
|
||||
let segment =
|
||||
unsafe { self.copy::<mach::load_command::SegmentCommand64>(address as usize)? };
|
||||
trace!("get_build_id_bytes: segment {:?}", segment);
|
||||
|
||||
let name = segment.name().map_err(|source| Error::Goblin {
|
||||
action: "get segment name",
|
||||
source,
|
||||
})?;
|
||||
if name == section {
|
||||
let sections_addr =
|
||||
address + std::mem::size_of::<mach::load_command::SegmentCommand64>();
|
||||
let sections = unsafe {
|
||||
self.copy_array::<mach::load_command::Section64>(
|
||||
sections_addr as usize,
|
||||
segment.nsects as usize,
|
||||
)?
|
||||
};
|
||||
trace!("get_build_id_bytes: sections {:?}", sections);
|
||||
|
||||
for section in §ions {
|
||||
trace!("get_build_id_bytes: section {:?}", section);
|
||||
if let Ok(sname) = Self::string_from_bytes(§ion.sectname) {
|
||||
trace!("get_build_id_bytes: sname {:?}", sname);
|
||||
if (sname.len() == 0) || (sname != note) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return self.copy_bytes(
|
||||
main_offset + section.addr as usize,
|
||||
section.size as usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
address += command.cmdsize as usize;
|
||||
}
|
||||
|
||||
Err(Error::NoteNotAvailable)
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Result as IOResult, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_AVAILABLE, NS_ERROR_OUT_OF_MEMORY};
|
||||
|
||||
pub struct BuildIdReader {
|
||||
file: File,
|
||||
}
|
||||
|
||||
use log::{error, trace};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::pe::header::Header>();
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::mach::header::Header64>();
|
||||
|
||||
// Target Android, Linux, *BSD, Solaris, ... Anything using ELF
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
|
||||
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::elf::header::Header>();
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn new(filename: &Path) -> Result<Self, nsresult> {
|
||||
trace!("BuildIdReader::new {:?}", filename);
|
||||
let f = File::open(filename).map_err(|e| {
|
||||
error!(
|
||||
"BuildIdReader::new failed to open {:?} with error {}",
|
||||
filename, e
|
||||
);
|
||||
NS_ERROR_INVALID_ARG
|
||||
})?;
|
||||
Ok(BuildIdReader { file: f })
|
||||
}
|
||||
|
||||
fn read_raw_build_id(&mut self, note_name: &str) -> Result<Vec<u8>, nsresult> {
|
||||
trace!("BuildIdReader::read_raw_build_id {}", note_name);
|
||||
let mut buffer = [0; MAX_BUFFER_READ];
|
||||
let _ = self.file.read_exact(&mut buffer).map_err(|e| {
|
||||
error!("BuildIdReader::read_raw_build_id failed to read exact buffer of size {} with error {}", MAX_BUFFER_READ, e);
|
||||
NS_ERROR_OUT_OF_MEMORY
|
||||
})?;
|
||||
|
||||
// This does actually depend on the platform, so it's not in this
|
||||
// impl nor source file but in the platform-dependant modules listed at
|
||||
// the end of this file
|
||||
self.get_build_id_bytes(&buffer, note_name)
|
||||
}
|
||||
|
||||
pub fn read_string_build_id(&mut self, note_name: &str) -> Result<String, nsresult> {
|
||||
trace!("BuildIdReader::read_string_build_id {}", note_name);
|
||||
let b = self.read_raw_build_id(note_name).map_err(|err| {
|
||||
error!(
|
||||
"BuildIdReader::read_string_build_id failed to read raw build id with error {}",
|
||||
err
|
||||
);
|
||||
err
|
||||
})?;
|
||||
Self::string_from_bytes(&b).ok_or(NS_ERROR_NOT_AVAILABLE)
|
||||
}
|
||||
|
||||
fn string_from_bytes(bytes: &[u8]) -> Option<String> {
|
||||
trace!("BuildIdReader::string_from_bytes {:?}", bytes);
|
||||
if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
|
||||
trace!("BuildIdReader::string_from_bytes{:?}", cstr);
|
||||
if let Ok(s) = cstr.to_str() {
|
||||
trace!("BuildIdReader::string_from_bytes{}", s);
|
||||
return Some(s.to_string());
|
||||
}
|
||||
}
|
||||
trace!("BuildIdReader::string_from_bytesNone");
|
||||
None
|
||||
}
|
||||
|
||||
fn copy_bytes_into(&mut self, offset: usize, buffer: &mut [u8]) -> IOResult<()> {
|
||||
trace!("BuildIdReader::copy_bytes_into @{}", offset);
|
||||
self.file.seek(SeekFrom::Start(offset as u64))?;
|
||||
self.file
|
||||
.by_ref()
|
||||
.take(buffer.len() as u64)
|
||||
.read_exact(buffer)
|
||||
}
|
||||
|
||||
pub fn copy_bytes(&mut self, offset: usize, count: usize) -> IOResult<Vec<u8>> {
|
||||
trace!("BuildIdReader::copy_bytes @{} : {} bytes", offset, count);
|
||||
let mut buf = vec![0; count];
|
||||
self.copy_bytes_into(offset, &mut buf).map_err(|e| {
|
||||
error!(
|
||||
"BuildIdReader::copy_bytes failed to copy bytes with error {}",
|
||||
e
|
||||
);
|
||||
e
|
||||
})?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
/// SAFETY: Caller need to ensure that `T` is safe to cast to bytes
|
||||
pub unsafe fn copy<T>(&mut self, offset: usize) -> IOResult<T> {
|
||||
trace!("BuildIdReader::copy @{}", offset);
|
||||
self.copy_array(offset, 1)
|
||||
.map(|v| v.into_iter().next().unwrap())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
/// SAFETY: Caller need to ensure that `T` is safe to cast to bytes
|
||||
pub unsafe fn copy_array<T>(&mut self, offset: usize, num: usize) -> IOResult<Vec<T>> {
|
||||
trace!("BuildIdReader::copy_array @{} : {} num", offset, num);
|
||||
let mut uninit: Vec<std::mem::MaybeUninit<T>> = Vec::with_capacity(num);
|
||||
for _ in 0..num {
|
||||
uninit.push(std::mem::MaybeUninit::uninit());
|
||||
}
|
||||
let slice = std::slice::from_raw_parts_mut(
|
||||
uninit.as_mut_ptr() as *mut u8,
|
||||
uninit.len() * std::mem::size_of::<T>(),
|
||||
);
|
||||
self.copy_bytes_into(offset, slice)?;
|
||||
std::mem::transmute(uninit)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
pub mod macos;
|
||||
|
||||
// Target Android, Linux, *BSD, Solaris, ... Anything using ELF
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
|
||||
pub mod elf;
|
||||
@@ -1,129 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use nserror::{
|
||||
nsresult, NS_ERROR_CANNOT_CONVERT_DATA, NS_ERROR_FAILURE, NS_ERROR_FILE_COPY_OR_MOVE_FAILED,
|
||||
NS_ERROR_ILLEGAL_VALUE, NS_ERROR_INVALID_ARG, NS_ERROR_INVALID_POINTER, NS_ERROR_NOT_AVAILABLE,
|
||||
NS_ERROR_NOT_INITIALIZED,
|
||||
};
|
||||
|
||||
use super::BuildIdReader;
|
||||
use goblin::{elf, elf::note::Note, elf::section_header::SectionHeader};
|
||||
use scroll::ctx::TryFromCtx;
|
||||
|
||||
use log::{error, trace};
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn get_build_id_bytes(
|
||||
&mut self,
|
||||
buffer: &[u8],
|
||||
note_name: &str,
|
||||
) -> Result<Vec<u8>, nsresult> {
|
||||
trace!("get_build_id_bytes: {}", note_name);
|
||||
let elf_head = elf::Elf::parse_header(buffer).map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to parse Elf header with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_FAILURE
|
||||
})?;
|
||||
let mut elf = elf::Elf::lazy_parse(elf_head).map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to lazy parse Elf with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_NOT_INITIALIZED
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", elf);
|
||||
let context = goblin::container::Ctx {
|
||||
container: elf.header.container().map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to get Elf container with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_INVALID_ARG
|
||||
})?,
|
||||
le: elf.header.endianness().map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to get Elf endianness with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_INVALID_ARG
|
||||
})?,
|
||||
};
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", context);
|
||||
let section_header_bytes = self
|
||||
.copy_bytes(
|
||||
elf_head.e_shoff as usize,
|
||||
(elf_head.e_shnum as usize) * (elf_head.e_shentsize as usize),
|
||||
)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to copy section header bytes with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", section_header_bytes);
|
||||
elf.section_headers =
|
||||
SectionHeader::parse_from(§ion_header_bytes, 0, elf_head.e_shnum as usize, context)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to parse sectiion headers with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_INVALID_POINTER
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", elf.section_headers);
|
||||
let shdr_strtab = &elf.section_headers[elf_head.e_shstrndx as usize];
|
||||
let shdr_strtab_bytes = self
|
||||
.copy_bytes(shdr_strtab.sh_offset as usize, shdr_strtab.sh_size as usize)
|
||||
.map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to get section header string tab bytes with error {}", e);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", shdr_strtab_bytes);
|
||||
elf.shdr_strtab =
|
||||
goblin::strtab::Strtab::parse(&shdr_strtab_bytes, 0, shdr_strtab.sh_size as usize, 0x0)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to parse section header string tab with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_ILLEGAL_VALUE
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", elf.shdr_strtab);
|
||||
let tk_note = elf
|
||||
.section_headers
|
||||
.iter()
|
||||
.find(|s| elf.shdr_strtab.get_at(s.sh_name) == Some(note_name))
|
||||
.ok_or(NS_ERROR_NOT_AVAILABLE)?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", tk_note);
|
||||
let note_bytes = self
|
||||
.copy_bytes(tk_note.sh_offset as usize, tk_note.sh_size as usize)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to copy bytes for note with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", note_bytes);
|
||||
let (note, _size) = Note::try_from_ctx(¬e_bytes, (4, context)).map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to parse note with error {}", e);
|
||||
NS_ERROR_CANNOT_CONVERT_DATA
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", note);
|
||||
Ok(note.desc.to_vec())
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use nserror::{
|
||||
nsresult, NS_ERROR_FILE_COPY_OR_MOVE_FAILED, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_INVALID_ARG,
|
||||
NS_ERROR_INVALID_POINTER, NS_ERROR_INVALID_SIGNATURE, NS_ERROR_NOT_AVAILABLE,
|
||||
NS_ERROR_NOT_INITIALIZED,
|
||||
};
|
||||
|
||||
use goblin::mach;
|
||||
|
||||
use super::BuildIdReader;
|
||||
|
||||
use log::{error, trace};
|
||||
|
||||
const HEADER_SIZE: usize = std::mem::size_of::<goblin::mach::header::Header64>();
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn get_build_id_bytes(
|
||||
&mut self,
|
||||
buffer: &[u8],
|
||||
note_name: &str,
|
||||
) -> Result<Vec<u8>, nsresult> {
|
||||
trace!("get_build_id_bytes: {}", note_name);
|
||||
let (section, note) = note_name
|
||||
.split_once(",")
|
||||
.ok_or(NS_ERROR_INVALID_SIGNATURE)?;
|
||||
trace!("get_build_id_bytes: {} {}", section, note);
|
||||
|
||||
let fat_header = mach::fat::FatHeader::parse(buffer).map_err(|e| {
|
||||
error!("Failed FatHeader::parse(): {}", e);
|
||||
NS_ERROR_NOT_INITIALIZED
|
||||
})?;
|
||||
trace!("get_build_id_bytes: fat header: {:?}", fat_header);
|
||||
|
||||
/* First we attempt to parse if there's a Fat header there
|
||||
*
|
||||
* If we have one, then we have a universal binary so we are going to
|
||||
* search the architectures to find the one we want, extract the correct
|
||||
* MachO buffer as well as the offset at which the MachO binary is.
|
||||
* Testing Universal binaries will require running gtest against a
|
||||
* Shippable build.
|
||||
*
|
||||
* If not we have a normal MachO and we directly parse the buffer we
|
||||
* have read earlier, and use a 0 offset.
|
||||
*/
|
||||
let (buf /*: [u8; HEADER_SIZE] */, main_offset /*: usize */) = if fat_header.magic
|
||||
== mach::fat::FAT_CIGAM
|
||||
|| fat_header.magic == mach::fat::FAT_MAGIC
|
||||
{
|
||||
let total = std::mem::size_of::<mach::fat::FatHeader>() as usize
|
||||
+ (std::mem::size_of::<mach::fat::FatArch>() * fat_header.nfat_arch as usize);
|
||||
|
||||
let mach_buffer = self.copy_bytes(0, total).map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to copy Mach ({}) bytes with error {}",
|
||||
total, e
|
||||
);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?;
|
||||
|
||||
if let mach::Mach::Fat(multi_arch) =
|
||||
mach::Mach::parse_lossy(&mach_buffer).map_err(|e| {
|
||||
error!("Failed Mach::parse_lossy(): {}", e);
|
||||
NS_ERROR_NOT_INITIALIZED
|
||||
})?
|
||||
{
|
||||
let arches = multi_arch.arches().map_err(|e| {
|
||||
error!("Error getting arches(): {}", e);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let that_arch = mach::constants::cputype::CPU_TYPE_X86_64;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
let that_arch = mach::constants::cputype::CPU_TYPE_ARM64;
|
||||
|
||||
let arch_index = arches
|
||||
.iter()
|
||||
.position(|&x| x.cputype == that_arch)
|
||||
.ok_or(NS_ERROR_FILE_COPY_OR_MOVE_FAILED)?;
|
||||
trace!("get_build_id_bytes: arches[]: {:?}", arches[arch_index]);
|
||||
|
||||
let offset = arches[arch_index].offset as usize;
|
||||
|
||||
if let Ok(b) = self
|
||||
.copy_bytes(offset, HEADER_SIZE)
|
||||
.map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to copy Mach-O header bytes at {} ({} bytes) with error {}", offset, HEADER_SIZE, e);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?.try_into() {
|
||||
(b, offset)
|
||||
} else {
|
||||
return Err(NS_ERROR_FILE_COPY_OR_MOVE_FAILED);
|
||||
}
|
||||
} else {
|
||||
return Err(NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
} else {
|
||||
(
|
||||
buffer.try_into().map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to get buffer of {} bytes with error {}",
|
||||
HEADER_SIZE, e
|
||||
);
|
||||
NS_ERROR_NOT_INITIALIZED
|
||||
})?,
|
||||
0,
|
||||
)
|
||||
};
|
||||
trace!("get_build_id_bytes: {} {}", section, note);
|
||||
|
||||
let macho_head = mach::header::Header64::from_bytes(&buf);
|
||||
let mut address = main_offset + HEADER_SIZE;
|
||||
let end_of_commands = address + (macho_head.sizeofcmds as usize);
|
||||
|
||||
while address < end_of_commands {
|
||||
let command = unsafe {
|
||||
self.copy::<mach::load_command::LoadCommandHeader>(address as usize)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to load MachO command at {} with error {}",
|
||||
address, e
|
||||
);
|
||||
NS_ERROR_INVALID_ARG
|
||||
})?
|
||||
};
|
||||
trace!("get_build_id_bytes: command {:?}", command);
|
||||
|
||||
if command.cmd == mach::load_command::LC_SEGMENT_64 {
|
||||
let segment = unsafe {
|
||||
self.copy::<mach::load_command::SegmentCommand64>(address as usize)
|
||||
.map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to load MachO segment at {} with error {}", address, e);
|
||||
NS_ERROR_INVALID_POINTER
|
||||
})?
|
||||
};
|
||||
trace!("get_build_id_bytes: segment {:?}", segment);
|
||||
|
||||
let name = segment.name().map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to get segment name with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_ILLEGAL_VALUE
|
||||
})?;
|
||||
if name == section {
|
||||
let sections_addr =
|
||||
address + std::mem::size_of::<mach::load_command::SegmentCommand64>();
|
||||
let sections = unsafe {
|
||||
self.copy_array::<mach::load_command::Section64>(
|
||||
sections_addr as usize,
|
||||
segment.nsects as usize,
|
||||
)
|
||||
.map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to get MachO sections at {} with error {}", sections_addr, e);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})?
|
||||
};
|
||||
trace!("get_build_id_bytes: sections {:?}", sections);
|
||||
|
||||
for section in §ions {
|
||||
trace!("get_build_id_bytes: section {:?}", section);
|
||||
if let Some(sname) = Self::string_from_bytes(§ion.sectname) {
|
||||
trace!("get_build_id_bytes: sname {:?}", sname);
|
||||
if (sname.len() == 0) || (sname != note) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return self
|
||||
.copy_bytes(main_offset + section.addr as usize, section.size as usize)
|
||||
.map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to copy section bytes at {} ({} bytes) with error {}", section.addr, section.size, e);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
address += command.cmdsize as usize;
|
||||
}
|
||||
|
||||
Err(NS_ERROR_NOT_AVAILABLE)
|
||||
}
|
||||
}
|
||||
52
toolkit/library/buildid_reader/src/result.rs
Normal file
52
toolkit/library/buildid_reader/src/result.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
// NS_ERROR_INVALID_ARG
|
||||
#[error("failed to open {}: {source}", .path.display())]
|
||||
FailedToOpenFile {
|
||||
path: PathBuf,
|
||||
source: std::io::Error,
|
||||
},
|
||||
// NS_ERROR_OUT_OF_MEMORY
|
||||
#[error("failed to read buffer of size {size}: {source}")]
|
||||
FailedToRead { size: usize, source: std::io::Error },
|
||||
// NS_ERROR_NOT_AVAILABLE
|
||||
#[error("failed to convert to string from bytes: {0}")]
|
||||
StringFromBytesNulError(#[from] std::ffi::FromBytesUntilNulError),
|
||||
// NS_ERROR_NOT_AVAILABLE
|
||||
#[error("failed to convert to string from bytes: {0}")]
|
||||
StringFromBytesUtf8Error(#[from] std::str::Utf8Error),
|
||||
#[error("failed to {action}: {source}")]
|
||||
Goblin {
|
||||
action: &'static str,
|
||||
source: goblin::error::Error,
|
||||
},
|
||||
#[error("failed to copy {count} bytes at {offset}: {source}")]
|
||||
CopyBytes {
|
||||
offset: usize,
|
||||
count: usize,
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("failed to find note")]
|
||||
NoteNotAvailable,
|
||||
// NS_ERROR_INVALID_SIGNATURE
|
||||
#[error("the note name isn't a valid form")]
|
||||
InvalidNoteName,
|
||||
#[error("failed to find architecture in fat binary")]
|
||||
ArchNotAvailable,
|
||||
// NS_ERROR_INVALID_ARG
|
||||
#[error("mach file looked like a fat archive but couldn't be parsed as one")]
|
||||
NotFatArchive,
|
||||
#[error("not enough data provided, expected at least {expected} bytes: {source}")]
|
||||
NotEnoughData {
|
||||
expected: usize,
|
||||
source: std::array::TryFromSliceError,
|
||||
},
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -2,30 +2,21 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use nserror::{
|
||||
nsresult, NS_ERROR_FAILURE, NS_ERROR_FILE_COPY_OR_MOVE_FAILED, NS_ERROR_NOT_AVAILABLE,
|
||||
};
|
||||
use crate::result::{Error, Result};
|
||||
|
||||
use goblin::pe;
|
||||
|
||||
use super::BuildIdReader;
|
||||
|
||||
use log::{error, trace};
|
||||
use log::trace;
|
||||
|
||||
impl BuildIdReader {
|
||||
pub fn get_build_id_bytes(
|
||||
&mut self,
|
||||
buffer: &[u8],
|
||||
note_name: &str,
|
||||
) -> Result<Vec<u8>, nsresult> {
|
||||
pub fn get_build_id_bytes(&mut self, buffer: &[u8], note_name: &str) -> Result<Vec<u8>> {
|
||||
trace!("get_build_id_bytes: {}", note_name);
|
||||
|
||||
let pe_head = pe::header::Header::parse(buffer).map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to parse PE buffer with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_FAILURE
|
||||
let pe_head = pe::header::Header::parse(buffer).map_err(|source| Error::Goblin {
|
||||
action: "parse PE buffer",
|
||||
source,
|
||||
})?;
|
||||
|
||||
trace!("get_build_id_bytes: {:?}", pe_head);
|
||||
@@ -39,42 +30,27 @@ impl BuildIdReader {
|
||||
optional_header_offset + pe_head.coff_header.size_of_optional_header as usize;
|
||||
let sections_size = pe_head.coff_header.number_of_sections as usize
|
||||
* goblin::pe::section_table::SIZEOF_SECTION_TABLE;
|
||||
let sections_bytes = self
|
||||
.copy_bytes(sections_offset, sections_size)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to get sections bytes at {} with error {}",
|
||||
sections_offset, e
|
||||
);
|
||||
NS_ERROR_FAILURE
|
||||
})?;
|
||||
let sections_bytes = self.copy_bytes(sections_offset, sections_size)?;
|
||||
trace!("get_build_id_bytes: {:?}", sections_bytes);
|
||||
|
||||
let pe_sections = pe_head
|
||||
.coff_header
|
||||
.sections(§ions_bytes, &mut 0)
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
"get_build_id_bytes: failed to get PE sections with error {}",
|
||||
e
|
||||
);
|
||||
NS_ERROR_FAILURE
|
||||
.map_err(|source| Error::Goblin {
|
||||
action: "get PE sections",
|
||||
source,
|
||||
})?;
|
||||
trace!("get_build_id_bytes: {:?}", pe_sections);
|
||||
|
||||
let pe_section = pe_sections
|
||||
.iter()
|
||||
.find(|s| s.name().is_ok_and(|name| name == note_name))
|
||||
.ok_or(NS_ERROR_NOT_AVAILABLE)?;
|
||||
.ok_or(Error::NoteNotAvailable)?;
|
||||
trace!("get_build_id_bytes: {:?}", pe_section);
|
||||
|
||||
self.copy_bytes(
|
||||
pe_section.pointer_to_raw_data as usize,
|
||||
pe_section.virtual_size as usize,
|
||||
)
|
||||
.map_err(|e| {
|
||||
error!("get_build_id_bytes: failed to copy PE section bytes at {} ({} bytes) with error {}", pe_section.pointer_to_raw_data, pe_section.virtual_size, e);
|
||||
NS_ERROR_FILE_COPY_OR_MOVE_FAILED
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ TEST_F(BuildIDReader, ReadFromMissingLib) {
|
||||
ASSERT_FALSE(NS_SUCCEEDED(rv))
|
||||
<< "No error reading from " << NS_ConvertUTF16toUTF8(MISSING_XUL_DLL).get()
|
||||
<< ": " << std::hex << static_cast<uint32_t>(rv);
|
||||
EXPECT_EQ(rv, NS_ERROR_INVALID_ARG);
|
||||
EXPECT_EQ(rv, NS_ERROR_FILE_UNRECOGNIZED_PATH);
|
||||
}
|
||||
|
||||
TEST_F(BuildIDReader, ReadFromRealLib) {
|
||||
|
||||
@@ -46,7 +46,7 @@ ipcclientcerts = { path = "../../../../security/manager/ssl/ipcclientcerts" }
|
||||
bitsdownload = { path = "../../../components/bitsdownload", optional = true }
|
||||
storage = { path = "../../../../storage/rust" }
|
||||
bookmark_sync = { path = "../../../components/places/bookmark_sync", optional = true }
|
||||
buildid_reader = { path = "../../buildid_reader" }
|
||||
buildid_reader_ffi = { path = "../../buildid_reader/ffi" }
|
||||
chardetng_c = "0.1.1"
|
||||
audio_thread_priority = { version = "0.32", default-features = false }
|
||||
mdns_service = { path="../../../../dom/media/webrtc/transport/mdns_service", optional = true }
|
||||
|
||||
@@ -14,7 +14,7 @@ extern crate authrs_bridge;
|
||||
extern crate bitsdownload;
|
||||
#[cfg(feature = "moz_places")]
|
||||
extern crate bookmark_sync;
|
||||
extern crate buildid_reader;
|
||||
extern crate buildid_reader_ffi;
|
||||
extern crate cascade_bloom_filter;
|
||||
extern crate cert_storage;
|
||||
extern crate chardetng_c;
|
||||
@@ -90,7 +90,8 @@ extern crate ipcclientcerts;
|
||||
#[cfg(any(
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
all(target_os = "windows", not(target_arch = "aarch64"))))]
|
||||
all(target_os = "windows", not(target_arch = "aarch64"))
|
||||
))]
|
||||
extern crate osclientcerts;
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
|
||||
Reference in New Issue
Block a user