|
1 | 1 | #[cfg(feature = "parse")] |
2 | 2 | use { |
3 | | - flate2::bufread::ZlibDecoder, goblin::elf::Elf, serde_json::Value, std::fmt, std::io::Read, |
4 | | - std::str::FromStr, |
| 3 | + flate2::bufread::ZlibDecoder, |
| 4 | + goblin::elf::Elf, |
| 5 | + serde_json::Value, |
| 6 | + std::io::Read, |
| 7 | + std::str::{from_utf8, FromStr}, |
5 | 8 | }; |
6 | 9 |
|
| 10 | +/// Name of the section containing the IDL type value. |
| 11 | +pub const IDL_TYPE_SECTION: &str = ".idl.type"; |
| 12 | + |
| 13 | +/// Name of the section containing the IDL data. |
| 14 | +pub const IDL_DATA_SECTION: &str = ".idl.data"; |
| 15 | + |
| 16 | +const ANCHOR_IDL_TYPE: &str = "anchor"; |
| 17 | +const CODAMA_IDL_TYPE: &str = "codama"; |
| 18 | + |
| 19 | +/// Defines the IDL type. |
7 | 20 | #[derive(Clone, Debug)] |
8 | 21 | pub enum IdlType { |
9 | 22 | Anchor, |
10 | | - Kinobi, |
| 23 | + Codama, |
| 24 | +} |
| 25 | + |
| 26 | +impl IdlType { |
| 27 | + pub const fn as_str(&self) -> &'static str { |
| 28 | + match self { |
| 29 | + IdlType::Anchor => ANCHOR_IDL_TYPE, |
| 30 | + IdlType::Codama => CODAMA_IDL_TYPE, |
| 31 | + } |
| 32 | + } |
11 | 33 | } |
12 | 34 |
|
13 | | -impl fmt::Display for IdlType { |
14 | | - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 35 | +impl std::fmt::Display for IdlType { |
| 36 | + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
15 | 37 | match self { |
16 | | - IdlType::Anchor => write!(f, "Anchor"), |
17 | | - IdlType::Kinobi => write!(f, "Kinobi"), |
| 38 | + IdlType::Anchor => write!(f, "{ANCHOR_IDL_TYPE}"), |
| 39 | + IdlType::Codama => write!(f, "{CODAMA_IDL_TYPE}"), |
18 | 40 | } |
19 | 41 | } |
20 | 42 | } |
21 | 43 |
|
22 | | -impl FromStr for IdlType { |
| 44 | +impl std::str::FromStr for IdlType { |
23 | 45 | type Err = &'static str; |
24 | 46 |
|
25 | 47 | fn from_str(s: &str) -> Result<Self, &'static str> { |
26 | 48 | match s.to_lowercase().as_str() { |
27 | | - "anchor" => Ok(IdlType::Anchor), |
28 | | - "kinobi" => Ok(IdlType::Kinobi), |
| 49 | + ANCHOR_IDL_TYPE => Ok(IdlType::Anchor), |
| 50 | + CODAMA_IDL_TYPE => Ok(IdlType::Codama), |
29 | 51 | _ => Err("Invalid IDL type"), |
30 | 52 | } |
31 | 53 | } |
32 | 54 | } |
33 | 55 |
|
34 | | -fn get_section_name(idl_type: IdlType) -> String { |
35 | | - match idl_type { |
36 | | - IdlType::Anchor => ".solana.idl".to_string(), |
37 | | - IdlType::Kinobi => ".kinobi.idl".to_string(), |
38 | | - } |
39 | | -} |
40 | | - |
41 | | -pub fn parse_idl_from_program_binary( |
42 | | - buffer: &[u8], |
43 | | - idl_type: IdlType, |
44 | | -) -> goblin::error::Result<Value> { |
| 56 | +#[cfg(feature = "parse")] |
| 57 | +pub fn parse_idl_from_program_binary(buffer: &[u8]) -> goblin::error::Result<(IdlType, Value)> { |
45 | 58 | let elf = Elf::parse(buffer)?; |
46 | 59 |
|
47 | | - let section_name = get_section_name(idl_type); |
| 60 | + let mut idl_type: Option<IdlType> = None; |
| 61 | + let mut idl_json: Option<Value> = None; |
48 | 62 |
|
49 | | - // Iterate over section headers and print information |
| 63 | + // Iterate over section headers and retrieve the IDL data. |
50 | 64 | for sh in &elf.section_headers { |
51 | 65 | let name = elf.shdr_strtab.get_at(sh.sh_name).unwrap_or("<invalid>"); |
52 | | - if name == section_name { |
53 | | - // Get offset of .solana.idl section data |
54 | | - let offset = sh.sh_offset as usize; |
55 | | - |
56 | | - // Get offset & size of the compressed IDL bytes |
57 | | - let _data_loc = &buffer[offset + 4..offset + 8]; |
58 | | - let data_loc = u32::from_le_bytes(_data_loc.try_into().unwrap()); |
59 | | - let _data_size = &buffer[offset + 8..offset + 16]; |
60 | | - let data_size = u64::from_le_bytes(_data_size.try_into().unwrap()); |
61 | | - |
62 | | - let compressed_data = |
63 | | - &buffer[data_loc as usize..(data_loc as u64 + data_size) as usize]; |
64 | | - let mut d = ZlibDecoder::new(compressed_data); |
65 | | - let mut decompressed_data = Vec::new(); |
66 | | - d.read_to_end(&mut decompressed_data).unwrap(); |
67 | | - |
68 | | - let json: Value = serde_json::from_slice(&decompressed_data).unwrap(); |
69 | | - return Ok(json); |
| 66 | + |
| 67 | + match name { |
| 68 | + IDL_DATA_SECTION => { |
| 69 | + let (location, size) = get_section_data_offset(buffer, sh.sh_offset as usize); |
| 70 | + |
| 71 | + let slice = &buffer[location..location + size]; |
| 72 | + let mut compressed_data = ZlibDecoder::new(slice); |
| 73 | + let mut data = Vec::new(); |
| 74 | + compressed_data.read_to_end(&mut data).unwrap(); |
| 75 | + |
| 76 | + idl_json = Some(serde_json::from_slice(&data).unwrap()); |
| 77 | + } |
| 78 | + IDL_TYPE_SECTION => { |
| 79 | + let (location, size) = get_section_data_offset(buffer, sh.sh_offset as usize); |
| 80 | + let slice = &buffer[location..location + size]; |
| 81 | + |
| 82 | + idl_type = Some(IdlType::from_str(from_utf8(slice).unwrap()).unwrap()); |
| 83 | + } |
| 84 | + // Ignore other sections. |
| 85 | + _ => (), |
70 | 86 | } |
71 | 87 | } |
72 | | - Err(goblin::error::Error::Malformed( |
73 | | - "Could not find .solana.idl section".to_string(), |
74 | | - )) |
| 88 | + |
| 89 | + if idl_type.is_some() && idl_json.is_some() { |
| 90 | + #[allow(clippy::unnecessary_unwrap)] |
| 91 | + Ok((idl_type.unwrap(), idl_json.unwrap())) |
| 92 | + } else { |
| 93 | + // Returns an error if we could not find the IDL information. |
| 94 | + Err(goblin::error::Error::Malformed( |
| 95 | + "Could not find .idl.* sections".to_string(), |
| 96 | + )) |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +#[cfg(feature = "parse")] |
| 101 | +#[inline(always)] |
| 102 | +fn get_section_data_offset(buffer: &[u8], offset: usize) -> (usize, usize) { |
| 103 | + let slice = &buffer[offset + 4..offset + 8]; |
| 104 | + let location = u32::from_le_bytes(slice.try_into().unwrap()); |
| 105 | + |
| 106 | + let slice = &buffer[offset + 8..offset + 16]; |
| 107 | + let size = u64::from_le_bytes(slice.try_into().unwrap()); |
| 108 | + |
| 109 | + (location as usize, size as usize) |
75 | 110 | } |
0 commit comments