Skip to content

Commit

Permalink
Add test for SONAME and fix the implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
afranchuk committed Apr 22, 2024
1 parent e56f764 commit f0b4480
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 63 deletions.
2 changes: 2 additions & 0 deletions src/linux/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ pub enum ModuleReaderError {
section: Box<Self>,
generated: Box<Self>,
},
#[error("no dynamic string table section")]
NoDynStrSection,
#[error("a string in the strtab did not have a terminating nul byte")]
StrTabNoNulByte,
#[error("no SONAME found in dynamic linking information")]
Expand Down
174 changes: 111 additions & 63 deletions src/linux/module_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,36 @@ fn build_id_from_bytes(data: &[u8]) -> Vec<u8> {
)
}

// `name` should be null-terminated
fn section_header_with_name<'a>(
section_headers: &'a elf::SectionHeaders,
strtab_index: usize,
name: &[u8],
module_memory: &impl ModuleMemory,
) -> Result<Option<&'a elf::SectionHeader>, Error> {
let strtab_section_header = section_headers.get(strtab_index).ok_or(Error::NoStrTab)?;
for header in section_headers {
let sh_name = header.sh_name as u64;
if sh_name >= strtab_section_header.sh_size {
log::warn!("invalid sh_name offset");
continue;
}
if sh_name + name.len() as u64 >= strtab_section_header.sh_size {
// This can't be a match.
continue;
}
let n = read(
module_memory,
strtab_section_header.sh_offset + sh_name,
name.len() as u64,
)?;
if name == &*n {
return Ok(Some(header));
}
}
Ok(None)
}

/// Types which can be read from an `impl ModuleMemory`.
pub trait ReadFromModule: Sized {
fn read_from_module(module_memory: impl ModuleMemory) -> Result<Self, Error>;
Expand Down Expand Up @@ -131,15 +161,23 @@ impl<T: ModuleMemory> ModuleReader<T> {
pub fn soname(&self) -> Result<String, Error> {
let section_headers = self.read_section_headers()?;

let strtab_section_header = section_headers
.get(self.header.e_shstrndx as usize)
.ok_or(Error::NoStrTab)?;

let dynamic_section_header = section_headers
.iter()
.find(|h| h.sh_type == elf::section_header::SHT_DYNAMIC)
.ok_or(Error::NoDynamicSection)?;

let dynstr_section_header =
match section_headers.get(dynamic_section_header.sh_link as usize) {
Some(header) if header.sh_type == elf::section_header::SHT_STRTAB => header,
_ => section_header_with_name(
&section_headers,
self.header.e_shstrndx as usize,
b".dynstr\0",
&self.module_memory,
)?
.ok_or(Error::NoDynStrSection)?,
};

let dynamic_section: &[u8] = &read(
&self.module_memory,
dynamic_section_header.sh_offset,
Expand All @@ -152,11 +190,11 @@ impl<T: ModuleMemory> ModuleReader<T> {
let dyn_: elf::dynamic::Dyn = dynamic_section.gread_with(&mut offset, self.context)?;
if dyn_.d_tag == elf::dynamic::DT_SONAME {
let strtab_offset = dyn_.d_val;
if strtab_offset < strtab_section_header.sh_size {
if strtab_offset < dynstr_section_header.sh_size {
let name = read(
&self.module_memory,
strtab_section_header.sh_offset + strtab_offset,
strtab_section_header.sh_size - strtab_offset,
dynstr_section_header.sh_offset + strtab_offset,
dynstr_section_header.sh_size - strtab_offset,
)?;
return CStr::from_bytes_until_nul(&name)
.map(|s| s.to_string_lossy().into_owned())
Expand Down Expand Up @@ -203,39 +241,19 @@ impl<T: ModuleMemory> ModuleReader<T> {
pub fn build_id_from_section(&self) -> Result<Vec<u8>, Error> {
let section_headers = self.read_section_headers()?;

let strtab_section_header = section_headers
.get(self.header.e_shstrndx as usize)
.ok_or(Error::NoStrTab)?;
let header = section_header_with_name(
&section_headers,
self.header.e_shstrndx as usize,
NOTE_SECTION_NAME,
&self.module_memory,
)?
.ok_or(Error::NoSectionNote)?;

for header in &section_headers {
let sh_name = header.sh_name as u64;
if sh_name >= strtab_section_header.sh_size {
log::warn!("invalid sh_name offset");
continue;
}
if sh_name + NOTE_SECTION_NAME.len() as u64 >= strtab_section_header.sh_size {
// This can't be a match.
continue;
}
let name = read(
&self.module_memory,
strtab_section_header.sh_offset + sh_name,
NOTE_SECTION_NAME.len() as u64,
)?;
if NOTE_SECTION_NAME == &*name {
return match self.find_build_id_note(
header.sh_offset,
header.sh_size,
header.sh_addralign,
) {
Ok(Some(v)) => Ok(v),
Ok(None) => Err(Error::NoSectionNote),
Err(e) => Err(e),
};
}
match self.find_build_id_note(header.sh_offset, header.sh_size, header.sh_addralign) {
Ok(Some(v)) => Ok(v),
Ok(None) => Err(Error::NoSectionNote),
Err(e) => Err(e),
}

Err(Error::NoSectionNote)
}

/// Generate a build id by hashing the first page of the text section.
Expand Down Expand Up @@ -309,48 +327,71 @@ mod test {
/// * ELF header
/// * program header: text segment
/// * program header: note
/// * program header: dynamic
/// * section header: null
/// * section header: .text
/// * section header: .note.gnu.build-id
/// * section header: .shstrtab
/// * section header: .dynamic
/// * section header: .dynstr
/// * note header (build id note)
/// * shstrtab
/// * dynamic (SONAME)
/// * dynstr (SONAME string = libfoo.so.1)
/// * program (calls exit(0))
const TINY_ELF: &[u8] = &[
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x02, 0x00, 0x40, 0x00,
0x04, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0xea, 0x02, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x03, 0x00, 0x40, 0x00,
0x06, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xea, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xb0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x68, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x02, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf4, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x68, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x01, 0x02,
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x00,
0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65, 0x2e, 0x67, 0x6e, 0x75,
0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x69, 0x64, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74,
0x72, 0x74, 0x61, 0x62, 0x00, 0x6a, 0x3c, 0x58, 0x31, 0xff, 0x0f, 0x05,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x02, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0xbd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x02,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e,
0x55, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f, 0x10, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65,
0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x69, 0x64, 0x00, 0x2e,
0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d,
0x69, 0x63, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c,
0x69, 0x62, 0x66, 0x6f, 0x6f, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x6a, 0x3c, 0x58, 0x31,
0xff, 0x0f, 0x05, 0x66, 0x6f, 0x6f, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x6a, 0x3c, 0x58,
0x31, 0xff, 0x0f, 0x05, 0x05,
];

#[test]
Expand Down Expand Up @@ -382,4 +423,11 @@ mod test {
vec![0x6a, 0x3c, 0x58, 0x31, 0xff, 0x0f, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}

#[test]
fn soname() {
let reader = ModuleReader::new(TINY_ELF).unwrap();
let soname = reader.soname().unwrap();
assert_eq!(soname, "libfoo.so.1");
}
}

0 comments on commit f0b4480

Please sign in to comment.