1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use bincode::deserialize;
#[cfg(unix)]
use libloading::os::unix::*;
#[cfg(windows)]
use libloading::os::windows::*;
use log::*;
use solana_sdk::account::KeyedAccount;
use solana_sdk::loader_instruction::LoaderInstruction;
pub use solana_sdk::native_loader::*;
use solana_sdk::native_program;
use solana_sdk::native_program::ProgramError;
use solana_sdk::pubkey::Pubkey;
use std::env;
use std::path::PathBuf;
use std::str;
#[cfg(unix)]
const PLATFORM_FILE_PREFIX_NATIVE: &str = "lib";
#[cfg(windows)]
const PLATFORM_FILE_PREFIX_NATIVE: &str = "";
#[cfg(any(target_os = "macos", target_os = "ios"))]
const PLATFORM_FILE_EXTENSION_NATIVE: &str = "dylib";
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
const PLATFORM_FILE_EXTENSION_NATIVE: &str = "so";
#[cfg(windows)]
const PLATFORM_FILE_EXTENSION_NATIVE: &str = "dll";
fn create_path(name: &str) -> PathBuf {
let current_exe = env::current_exe().unwrap();
let current_exe_directory = PathBuf::from(current_exe.parent().unwrap());
let library_file_name = PathBuf::from(PLATFORM_FILE_PREFIX_NATIVE.to_string() + name)
.with_extension(PLATFORM_FILE_EXTENSION_NATIVE);
let file_path = current_exe_directory.join(&library_file_name);
if file_path.exists() {
file_path
} else {
current_exe_directory.join("deps").join(library_file_name)
}
}
pub fn check_id(program_id: &Pubkey) -> bool {
program_id.as_ref() == NATIVE_LOADER_PROGRAM_ID
}
pub fn entrypoint(
program_id: &Pubkey,
keyed_accounts: &mut [KeyedAccount],
ix_userdata: &[u8],
tick_height: u64,
) -> Result<(), ProgramError> {
if keyed_accounts[0].account.executable {
let name = keyed_accounts[0].account.userdata.clone();
let name = match str::from_utf8(&name) {
Ok(v) => v,
Err(e) => {
warn!("Invalid UTF-8 sequence: {}", e);
return Err(ProgramError::GenericError);
}
};
trace!("Call native {:?}", name);
let path = create_path(&name);
match Library::open(Some(&path), libc::RTLD_NODELETE | libc::RTLD_NOW) {
Ok(library) => unsafe {
let entrypoint: Symbol<native_program::Entrypoint> =
match library.get(native_program::ENTRYPOINT.as_bytes()) {
Ok(s) => s,
Err(e) => {
warn!(
"{:?}: Unable to find {:?} in program",
e,
native_program::ENTRYPOINT
);
return Err(ProgramError::GenericError);
}
};
return entrypoint(
program_id,
&mut keyed_accounts[1..],
ix_userdata,
tick_height,
);
},
Err(e) => {
warn!("Unable to load: {:?}", e);
return Err(ProgramError::GenericError);
}
}
} else if let Ok(instruction) = deserialize(ix_userdata) {
if keyed_accounts[0].signer_key().is_none() {
warn!("key[0] did not sign the transaction");
return Err(ProgramError::GenericError);
}
match instruction {
LoaderInstruction::Write { offset, bytes } => {
trace!("NativeLoader::Write offset {} bytes {:?}", offset, bytes);
let offset = offset as usize;
if keyed_accounts[0].account.userdata.len() < offset + bytes.len() {
warn!(
"Error: Overflow, {} < {}",
keyed_accounts[0].account.userdata.len(),
offset + bytes.len()
);
return Err(ProgramError::GenericError);
}
keyed_accounts[0].account.userdata = bytes;
}
LoaderInstruction::Finalize => {
keyed_accounts[0].account.executable = true;
trace!(
"NativeLoader::Finalize prog: {:?}",
keyed_accounts[0].signer_key().unwrap()
);
}
}
} else {
warn!("Invalid userdata in instruction: {:?}", ix_userdata);
return Err(ProgramError::GenericError);
}
Ok(())
}