String Conversion Revamp

- String/&str to C char logic is revamped
This commit is contained in:
*Nix Fanboy
2024-10-25 15:15:29 +03:00
parent a56894585c
commit 96bfb178b0
14 changed files with 107 additions and 93 deletions

View File

@@ -37,7 +37,7 @@ Basically:
`cp header/unildd.h /my/amazing/project/`
### License
This library is licensed with [BSD-3 Clause License](https://choosealicense.com/licenses/bsd-3-clause/)
This library is licensed under [BSD-3 Clause License](https://choosealicense.com/licenses/bsd-3-clause/)
The resources used to make this library are cited as comments in the respective source files which they were used.

0
media/banner/UniLDD Banner.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

0
media/emblems/UniLDD-%100.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
media/emblems/UniLDD-%400.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

13
src/archive.rs Executable file → Normal file
View File

@@ -1,9 +1,10 @@
use crate::{
debug::{debug_objects, find_error_type, merge_members},
debug::{debug_objects, merge_members},
parse_objects,
structs::{CharVec, Debugging, ParsingError, StringPtr, ULDDObj, ULDDObjResult},
structs::{CharVec, Debugging, ParsingError, ULDDObj, ULDDObjResult},
};
use goblin::archive::Archive;
use crate::impls::{ErrorToInt, StringToCString};
pub(crate) fn parse_archive<'a>(
file_name: &'a str,
@@ -25,13 +26,13 @@ pub(crate) fn parse_archive<'a>(
error)).print(debugging);
return objects.push(ULDDObjResult {
error: ParsingError {
code: find_error_type(&error),
explanation: StringPtr::from(error.to_string()).0,
code: error.to_int(),
explanation: error.to_c_string(),
},
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
file_type: StringPtr::from("Archive").0,
file_type: "Archive".to_c_string(),
..Default::default()
},
});

14
src/coff.rs Executable file → Normal file
View File

@@ -1,6 +1,6 @@
use crate::{
debug::debug_objects,
structs::{CharVec, ParsingError, StringPtr, ULDDObj, ULDDObjResult},
structs::{CharVec, ParsingError, ULDDObj, ULDDObjResult},
types::PE_ARCH,
};
use goblin::pe::{
@@ -8,6 +8,8 @@ use goblin::pe::{
Coff,
};
use std::ptr::null_mut;
use crate::debug::option_to_c_string;
use crate::impls::StringToCString;
pub(crate) fn parse_coff(
file_name: &str,
@@ -19,17 +21,17 @@ pub(crate) fn parse_coff(
let is_64 = coff.header.characteristics & IMAGE_FILE_32BIT_MACHINE != IMAGE_FILE_32BIT_MACHINE;
let is_stripped =
coff.header.characteristics & IMAGE_FILE_DEBUG_STRIPPED == IMAGE_FILE_DEBUG_STRIPPED;
let cpu_type = StringPtr::from(PE_ARCH.get(&coff.header.machine)).0;
let cpu_type = option_to_c_string(PE_ARCH.get(&coff.header.machine));
debug_objects(file_name, member_names, "a COFF binary", debugging);
ULDDObjResult {
error: ParsingError::default(),
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
executable_format: StringPtr::from("COFF").0,
executable_format: "COFF".to_c_string(),
is_64,
os_type: StringPtr::from("Windows").0,
file_type: StringPtr::from("Windows object file").0,
os_type: "Windows".to_c_string(),
file_type: "Windows object file".to_c_string(),
is_stripped,
cpu_type,
cpu_subtype: null_mut(),

View File

@@ -1,16 +1,11 @@
use goblin::error::Error as ObjectError;
use std::ffi::c_char;
use std::fmt::Display;
use std::ptr::null_mut;
use crate::impls::StringToCString;
use crate::structs::Debugging;
pub(crate) fn find_error_type(error: &ObjectError) -> i64 {
match error {
ObjectError::Malformed(_) => -1,
ObjectError::BadMagic(_) => -2,
ObjectError::Scroll(_) => -3,
ObjectError::BufferTooShort(_, _) => -4,
ObjectError::IO(_) => -5,
_ => -6,
}
pub(crate) fn option_to_c_string<T>(option: Option<T>) -> *mut c_char where T: Display {
option.map(|v| v.to_c_string()).unwrap_or(null_mut())
}
pub(crate) fn merge_members(member_names: &mut [&str]) -> String {

18
src/elf.rs Executable file → Normal file
View File

@@ -2,10 +2,12 @@ use std::ptr::null_mut;
use crate::{
debug::debug_objects,
structs::{CharVec, ParsingError, StringPtr, ULDDObj, ULDDObjResult},
structs::{CharVec, ParsingError, ULDDObj, ULDDObjResult},
types::{ElfFileType, ElfOS, E_MACHINE, E_TYPE},
};
use goblin::elf::Elf;
use crate::debug::option_to_c_string;
use crate::impls::StringToCString;
fn find_os_from_strtab_elf(elf: &Elf<'_>, pat: &[&str]) -> bool {
[
@@ -59,7 +61,7 @@ fn find_os_elf(elf: &Elf<'_>, os_abi: u8) -> (ElfOS, *mut i8) {
}
};
(os, StringPtr::from(os.to_string()).0)
(os, os.to_c_string())
}
fn find_linux_vdso(e_machine: u16, bit_type: bool) -> Option<&'static str> {
@@ -105,21 +107,21 @@ pub(crate) fn parse_elf(
debugging: bool,
) -> ULDDObjResult {
let mut elf = elf;
let cpu_type = StringPtr::from(E_MACHINE.get(&elf.header.e_machine)).0;
let cpu_type = option_to_c_string(E_MACHINE.get(&elf.header.e_machine));
let file_type = match E_TYPE.get(&elf.header.e_type) {
_ if elf.header.e_type == 0x03 && elf.interpreter.is_some() => {
StringPtr::from(ElfFileType::Executable.to_string()).0
ElfFileType::Executable.to_c_string()
}
rest => StringPtr::from(rest).0,
rest => option_to_c_string(rest),
};
let interpreter = StringPtr::from(elf.interpreter).0;
let interpreter = option_to_c_string(elf.interpreter);
debug_objects(file_name, member_names, "an ELF binary", debugging);
ULDDObjResult {
error: ParsingError::default(),
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
executable_format: StringPtr::from("ELF").0,
executable_format: "ELF".to_c_string(),
is_64: elf.is_64,
os_type: find_os_elf(&elf, os_abi).1,
file_type,

53
src/impls.rs Executable file → Normal file
View File

@@ -1,10 +1,23 @@
use crate::{
structs::{CharVec, Debugging, ParsingError, StringPtr, ULDDObj},
structs::{CharVec, Debugging, ParsingError, ULDDObj},
ULDDObjResult, ULDDObjResultVec,
};
use anstream::{eprintln as a_eprintln, println as a_println};
use owo_colors::OwoColorize;
use std::{fmt::Display, mem::ManuallyDrop, ptr::null_mut, ffi::{c_char, CString}};
use std::{
ffi::{c_char, CString},
fmt::Display,
mem::ManuallyDrop,
ptr::null_mut,
};
pub trait StringToCString {
fn to_c_string(self) -> *mut c_char;
}
pub trait ErrorToInt {
fn to_int(&self) -> i64;
}
impl From<Vec<*mut c_char>> for CharVec {
fn from(value: Vec<*mut c_char>) -> Self {
@@ -70,9 +83,12 @@ impl From<&mut Vec<&str>> for CharVec {
}
}
impl From<String> for StringPtr {
fn from(value: String) -> Self {
let mut value = value;
impl<T> StringToCString for T
where
T: Display,
{
fn to_c_string(self) -> *mut c_char {
let mut value = self.to_string();
value.push('\0');
let c_string = match CString::from_vec_with_nul(value.into_bytes()) {
Ok(string) => string,
@@ -81,25 +97,20 @@ impl From<String> for StringPtr {
panic!("{}", error)
}
};
StringPtr(c_string.into_raw())
c_string.into_raw()
}
}
impl From<&str> for StringPtr {
fn from(value: &str) -> Self {
StringPtr::from(value.to_owned())
}
}
impl<T> From<Option<T>> for StringPtr
where
T: Display,
{
fn from(value: Option<T>) -> Self {
let Some(t) = value else {
return StringPtr(null_mut());
};
StringPtr::from(t.to_string())
impl ErrorToInt for goblin::error::Error {
fn to_int(&self) -> i64 {
match self {
goblin::error::Error::Malformed(_) => -1,
goblin::error::Error::BadMagic(_) => -2,
goblin::error::Error::Scroll(_) => -3,
goblin::error::Error::BufferTooShort(_, _) => -4,
goblin::error::Error::IO(_) => -5,
_ => -6,
}
}
}

22
src/lib.rs Executable file → Normal file
View File

@@ -30,7 +30,7 @@
//!
use archive::parse_archive;
use coff::parse_coff;
use debug::{find_error_type, merge_members};
use debug::merge_members;
use elf::parse_elf;
use goblin::Object;
use mach::parse_mach;
@@ -38,8 +38,10 @@ use owo_colors::OwoColorize;
use pe::parse_pe;
use std::ffi::{c_char, CStr, CString};
use structs::{
CharVec, Debugging, ParsingError, StringPtr, ULDDObj, ULDDObjResult, ULDDObjResultVec,
CharVec, Debugging, ParsingError, ULDDObj, ULDDObjResult, ULDDObjResultVec,
};
use crate::impls::{ErrorToInt, StringToCString};
#[doc(hidden)]
pub mod archive;
#[doc(hidden)]
@@ -101,10 +103,10 @@ fn parse_objects<'a>(
objects.push(ULDDObjResult {
error: ParsingError {
code: magic_number as i64,
explanation: StringPtr::from(msg).0,
explanation: msg.to_c_string(),
},
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
..Default::default()
},
@@ -128,10 +130,10 @@ fn parse_objects<'a>(
objects.push(ULDDObjResult {
error: ParsingError {
code: -7,
explanation: StringPtr::from(msg).0,
explanation: msg.to_c_string(),
},
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
..Default::default()
},
@@ -149,11 +151,11 @@ fn parse_objects<'a>(
objects.push(ULDDObjResult {
error: ParsingError {
code: find_error_type(&error),
explanation: StringPtr::from(error.to_string()).0,
code: error.to_int(),
explanation: error.to_c_string(),
},
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
..Default::default()
},
@@ -225,7 +227,7 @@ unsafe fn drop_c_string(ptr: *mut i8) {
///
/// # Safety
///
/// This function is designed for deallocating [`ULDDObjResultVec`] created by rust. Trying to deallocating [`ULDDObjResultVec`] created by other languages may result with errors.
/// This function is designed for deallocating [`ULDDObjResultVec`] created by rust. Trying to deallocate [`ULDDObjResultVec`] created by other languages may result with errors.
///
/// It is null pointer-safe.
///

36
src/mach.rs Executable file → Normal file
View File

@@ -1,6 +1,6 @@
use crate::{
debug::{debug_objects, find_error_type, merge_members},
structs::{CharVec, Debugging, ParsingError, StringPtr, ULDDObj, ULDDObjResult},
debug::{debug_objects, merge_members},
structs::{CharVec, Debugging, ParsingError, ULDDObj, ULDDObjResult},
types::{
MachOCpuType, MachOOs, MACH_O_ARM_CPU_SUBTYPE, MACH_O_CPUTYPE, MACH_O_FILE_TYPE,
MACH_O_X86_CPU_SUBTYPE,
@@ -8,6 +8,8 @@ use crate::{
};
use goblin::mach::{load_command::CommandVariant::BuildVersion, Mach, MachO};
use std::ptr::null_mut;
use crate::debug::option_to_c_string;
use crate::impls::{ErrorToInt, StringToCString};
fn find_os_mach(mach: &MachO<'_>) -> *mut i8 {
for lc in &mach.load_commands {
@@ -27,7 +29,7 @@ fn find_os_mach(mach: &MachO<'_>) -> *mut i8 {
0x0C => MachOOs::AppleVisionProSimulator,
_ => return null_mut(),
};
return StringPtr::from(os.to_string()).0;
return os.to_c_string();
}
}
@@ -100,13 +102,13 @@ pub(crate) fn parse_mach<'a>(
return objects.push(ULDDObjResult {
error: ParsingError {
code: find_error_type(&error),
explanation: StringPtr::from(error.to_string()).0,
code: error.to_int(),
explanation: error.to_c_string(),
},
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
executable_format: StringPtr::from("Mach-O").0,
executable_format: "Mach-O".to_c_string(),
..Default::default()
},
});
@@ -129,13 +131,13 @@ pub(crate) fn parse_mach<'a>(
error)).print(debugging);
objects.push(ULDDObjResult {
error: ParsingError {
code: find_error_type(&error),
explanation: StringPtr::from(error.to_string()).0,
code: error.to_int(),
explanation: error.to_c_string(),
},
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names.clone()),
executable_format: StringPtr::from("Mach-O").0,
executable_format: "Mach-O".to_c_string(),
..Default::default()
},
})
@@ -156,22 +158,22 @@ fn parse_mach_o(
debugging: bool,
) -> ULDDObjResult {
let mut mach_o = mach_o;
let file_type = StringPtr::from(MACH_O_FILE_TYPE.get(&mach_o.header.filetype)).0;
let file_type = option_to_c_string(MACH_O_FILE_TYPE.get(&mach_o.header.filetype));
let (cpu_type, cpu_subtype) = {
if let Some(mach_o_cpu_type) = MACH_O_CPUTYPE.get(&mach_o.header.cputype) {
let mach_o_cpu_subtype = {
match mach_o_cpu_type {
MachOCpuType::ARM | MachOCpuType::ARM64 => {
StringPtr::from(MACH_O_ARM_CPU_SUBTYPE.get(&mach_o.header.cpusubtype)).0
option_to_c_string(MACH_O_ARM_CPU_SUBTYPE.get(&mach_o.header.cpusubtype))
}
MachOCpuType::X86 | MachOCpuType::X86_64 => {
StringPtr::from(MACH_O_X86_CPU_SUBTYPE.get(&mach_o.header.cpusubtype)).0
option_to_c_string(MACH_O_X86_CPU_SUBTYPE.get(&mach_o.header.cpusubtype))
}
_ => null_mut(),
}
};
(
StringPtr::from(mach_o_cpu_type.to_string()).0,
mach_o_cpu_type.to_c_string(),
mach_o_cpu_subtype,
)
} else {
@@ -190,9 +192,9 @@ fn parse_mach_o(
ULDDObjResult {
error: ParsingError::default(),
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
executable_format: StringPtr::from("Mach-O").0,
executable_format: "Mach-O".to_c_string(),
is_64: mach_o.is_64,
os_type: find_os_mach(&mach_o),
file_type,

18
src/pe.rs Executable file → Normal file
View File

@@ -1,10 +1,12 @@
use crate::{
debug::debug_objects,
structs::{CharVec, ParsingError, StringPtr, ULDDObj, ULDDObjResult},
structs::{CharVec, ParsingError, ULDDObj, ULDDObjResult},
types::{PeOS, PeSubsystem, PE_ARCH, PE_SUBSYSTEM},
};
use goblin::pe::{characteristic::IMAGE_FILE_DEBUG_STRIPPED, PE};
use std::ptr::null_mut;
use crate::debug::option_to_c_string;
use crate::impls::StringToCString;
fn find_os_pe(pe: &PE<'_>) -> *mut i8 {
let Some(optional_header) = pe
@@ -32,7 +34,7 @@ fn find_os_pe(pe: &PE<'_>) -> *mut i8 {
PeSubsystem::Unknown => return null_mut(),
};
StringPtr::from(os.to_string()).0
os.to_c_string()
}
pub(crate) fn parse_pe(
@@ -43,7 +45,7 @@ pub(crate) fn parse_pe(
) -> ULDDObjResult {
let is_stripped = pe.header.coff_header.characteristics & IMAGE_FILE_DEBUG_STRIPPED
== IMAGE_FILE_DEBUG_STRIPPED;
let cpu_type = StringPtr::from(PE_ARCH.get(&pe.header.coff_header.machine)).0;
let cpu_type = option_to_c_string(PE_ARCH.get(&pe.header.coff_header.machine));
let file_type = pe
.header
.optional_header
@@ -57,27 +59,27 @@ pub(crate) fn parse_pe(
.windows_fields
.minor_operating_system_version;
let linker_version = format!("{}.{}", linker_major_version, linker_minor_version);
StringPtr::from(linker_version).0
linker_version.to_c_string()
} else {
null_mut()
}
};
let executable_format = if pe.is_64 {
debug_objects(file_name, member_names, "a PE32+ binary", debugging);
StringPtr::from("PE32+").0
"PE32+".to_c_string()
} else {
debug_objects(file_name, member_names, "a PE32 binary", debugging);
StringPtr::from("PE32").0
"PE32".to_c_string()
};
ULDDObjResult {
error: ParsingError::default(),
obj: ULDDObj {
file_name: StringPtr::from(file_name).0,
file_name: file_name.to_c_string(),
member_name: CharVec::from(member_names),
executable_format,
is_64: pe.is_64,
os_type: find_os_pe(&pe),
file_type: StringPtr::from(file_type).0,
file_type: option_to_c_string(file_type),
is_stripped,
cpu_type,
cpu_subtype: null_mut(),

3
src/structs.rs Executable file → Normal file
View File

@@ -99,9 +99,6 @@ pub struct ULDDObjResultVec {
pub vec: *mut ULDDObjResult,
}
#[doc(hidden)]
pub struct StringPtr(pub *mut i8);
#[doc(hidden)]
pub(crate) enum Debugging {
Info(String),

0
src/types.rs Executable file → Normal file
View File