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",
|
"goblin 0.8.2",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"scroll",
|
||||||
|
"thiserror 2.0.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "buildid_reader_ffi"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"buildid_reader",
|
||||||
|
"log",
|
||||||
"nserror",
|
"nserror",
|
||||||
"nsstring",
|
"nsstring",
|
||||||
"scroll",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2401,7 +2410,7 @@ dependencies = [
|
|||||||
"binary_http",
|
"binary_http",
|
||||||
"bitsdownload",
|
"bitsdownload",
|
||||||
"bookmark_sync",
|
"bookmark_sync",
|
||||||
"buildid_reader",
|
"buildid_reader_ffi",
|
||||||
"cascade_bloom_filter",
|
"cascade_bloom_filter",
|
||||||
"cert_storage",
|
"cert_storage",
|
||||||
"chardetng_c",
|
"chardetng_c",
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ goblin = "^0.8.1"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
scroll = "0.12"
|
scroll = "0.12"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
thiserror = "2"
|
||||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
|
||||||
|
|||||||
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
|
* 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/. */
|
* 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 std::path::Path;
|
||||||
|
|
||||||
use nserror::{nsresult, NS_OK};
|
pub mod result;
|
||||||
use nsstring::{nsAString, nsCString};
|
|
||||||
|
|
||||||
mod reader;
|
#[cfg(target_os = "windows")]
|
||||||
use reader::BuildIdReader;
|
mod windows;
|
||||||
|
|
||||||
use log::{error, trace};
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
mod macos;
|
||||||
|
|
||||||
#[no_mangle]
|
// Target Android, Linux, *BSD, Solaris, ... Anything using ELF
|
||||||
pub extern "C" fn read_toolkit_buildid_from_file(
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
|
||||||
fname: &nsAString,
|
mod elf;
|
||||||
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);
|
pub struct BuildIdReader {
|
||||||
|
file: File,
|
||||||
|
}
|
||||||
|
|
||||||
match BuildIdReader::new(&path) {
|
#[cfg(target_os = "windows")]
|
||||||
Ok(mut reader) => match reader.read_string_build_id(¬e_name) {
|
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::pe::header::Header>();
|
||||||
Ok(id) => {
|
|
||||||
trace!("read_toolkit_buildid_from_file {}", id);
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
rv_build_id.assign(&id);
|
const MAX_BUFFER_READ: usize = std::mem::size_of::<goblin::mach::header::Header64>();
|
||||||
NS_OK
|
|
||||||
}
|
// Target Android, Linux, *BSD, Solaris, ... Anything using ELF
|
||||||
Err(err) => {
|
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
|
||||||
error!("read_toolkit_buildid_from_file failed to read string buiild id from note {:?} with error {:?}", note_name, err);
|
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(err) => {
|
|
||||||
error!("read_toolkit_buildid_from_file failed to build BuildIdReader for {:?} with error {:?}", path, err);
|
|
||||||
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
|
* 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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use nserror::{
|
use crate::result::{Error, Result};
|
||||||
nsresult, NS_ERROR_FAILURE, NS_ERROR_FILE_COPY_OR_MOVE_FAILED, NS_ERROR_NOT_AVAILABLE,
|
|
||||||
};
|
|
||||||
|
|
||||||
use goblin::pe;
|
use goblin::pe;
|
||||||
|
|
||||||
use super::BuildIdReader;
|
use super::BuildIdReader;
|
||||||
|
|
||||||
use log::{error, trace};
|
use log::trace;
|
||||||
|
|
||||||
impl BuildIdReader {
|
impl BuildIdReader {
|
||||||
pub fn get_build_id_bytes(
|
pub fn get_build_id_bytes(&mut self, buffer: &[u8], note_name: &str) -> Result<Vec<u8>> {
|
||||||
&mut self,
|
|
||||||
buffer: &[u8],
|
|
||||||
note_name: &str,
|
|
||||||
) -> Result<Vec<u8>, nsresult> {
|
|
||||||
trace!("get_build_id_bytes: {}", note_name);
|
trace!("get_build_id_bytes: {}", note_name);
|
||||||
|
|
||||||
let pe_head = pe::header::Header::parse(buffer).map_err(|e| {
|
let pe_head = pe::header::Header::parse(buffer).map_err(|source| Error::Goblin {
|
||||||
error!(
|
action: "parse PE buffer",
|
||||||
"get_build_id_bytes: failed to parse PE buffer with error {}",
|
source,
|
||||||
e
|
|
||||||
);
|
|
||||||
NS_ERROR_FAILURE
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
trace!("get_build_id_bytes: {:?}", pe_head);
|
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;
|
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
|
let sections_size = pe_head.coff_header.number_of_sections as usize
|
||||||
* goblin::pe::section_table::SIZEOF_SECTION_TABLE;
|
* goblin::pe::section_table::SIZEOF_SECTION_TABLE;
|
||||||
let sections_bytes = self
|
let sections_bytes = self.copy_bytes(sections_offset, sections_size)?;
|
||||||
.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
|
|
||||||
})?;
|
|
||||||
trace!("get_build_id_bytes: {:?}", sections_bytes);
|
trace!("get_build_id_bytes: {:?}", sections_bytes);
|
||||||
|
|
||||||
let pe_sections = pe_head
|
let pe_sections = pe_head
|
||||||
.coff_header
|
.coff_header
|
||||||
.sections(§ions_bytes, &mut 0)
|
.sections(§ions_bytes, &mut 0)
|
||||||
.map_err(|e| {
|
.map_err(|source| Error::Goblin {
|
||||||
error!(
|
action: "get PE sections",
|
||||||
"get_build_id_bytes: failed to get PE sections with error {}",
|
source,
|
||||||
e
|
|
||||||
);
|
|
||||||
NS_ERROR_FAILURE
|
|
||||||
})?;
|
})?;
|
||||||
trace!("get_build_id_bytes: {:?}", pe_sections);
|
trace!("get_build_id_bytes: {:?}", pe_sections);
|
||||||
|
|
||||||
let pe_section = pe_sections
|
let pe_section = pe_sections
|
||||||
.iter()
|
.iter()
|
||||||
.find(|s| s.name().is_ok_and(|name| name == note_name))
|
.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);
|
trace!("get_build_id_bytes: {:?}", pe_section);
|
||||||
|
|
||||||
self.copy_bytes(
|
self.copy_bytes(
|
||||||
pe_section.pointer_to_raw_data as usize,
|
pe_section.pointer_to_raw_data as usize,
|
||||||
pe_section.virtual_size 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))
|
ASSERT_FALSE(NS_SUCCEEDED(rv))
|
||||||
<< "No error reading from " << NS_ConvertUTF16toUTF8(MISSING_XUL_DLL).get()
|
<< "No error reading from " << NS_ConvertUTF16toUTF8(MISSING_XUL_DLL).get()
|
||||||
<< ": " << std::hex << static_cast<uint32_t>(rv);
|
<< ": " << 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) {
|
TEST_F(BuildIDReader, ReadFromRealLib) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ ipcclientcerts = { path = "../../../../security/manager/ssl/ipcclientcerts" }
|
|||||||
bitsdownload = { path = "../../../components/bitsdownload", optional = true }
|
bitsdownload = { path = "../../../components/bitsdownload", optional = true }
|
||||||
storage = { path = "../../../../storage/rust" }
|
storage = { path = "../../../../storage/rust" }
|
||||||
bookmark_sync = { path = "../../../components/places/bookmark_sync", optional = true }
|
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"
|
chardetng_c = "0.1.1"
|
||||||
audio_thread_priority = { version = "0.32", default-features = false }
|
audio_thread_priority = { version = "0.32", default-features = false }
|
||||||
mdns_service = { path="../../../../dom/media/webrtc/transport/mdns_service", optional = true }
|
mdns_service = { path="../../../../dom/media/webrtc/transport/mdns_service", optional = true }
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ extern crate authrs_bridge;
|
|||||||
extern crate bitsdownload;
|
extern crate bitsdownload;
|
||||||
#[cfg(feature = "moz_places")]
|
#[cfg(feature = "moz_places")]
|
||||||
extern crate bookmark_sync;
|
extern crate bookmark_sync;
|
||||||
extern crate buildid_reader;
|
extern crate buildid_reader_ffi;
|
||||||
extern crate cascade_bloom_filter;
|
extern crate cascade_bloom_filter;
|
||||||
extern crate cert_storage;
|
extern crate cert_storage;
|
||||||
extern crate chardetng_c;
|
extern crate chardetng_c;
|
||||||
@@ -90,7 +90,8 @@ extern crate ipcclientcerts;
|
|||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
target_os = "macos",
|
target_os = "macos",
|
||||||
target_os = "ios",
|
target_os = "ios",
|
||||||
all(target_os = "windows", not(target_arch = "aarch64"))))]
|
all(target_os = "windows", not(target_arch = "aarch64"))
|
||||||
|
))]
|
||||||
extern crate osclientcerts;
|
extern crate osclientcerts;
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
|
|||||||
Reference in New Issue
Block a user