1#![deny(warnings)]
10
11extern crate goblin;
12extern crate pdb;
13
14extern crate serde;
15#[macro_use]
16extern crate serde_derive;
17extern crate serde_json;
18
19use std::collections::HashMap;
20use std::fs::File;
21use std::io::Write;
22use std::io::Read;
23use std::path::PathBuf;
24
25use goblin::Object;
26
27use serde::Deserialize;
28use serde_json::Deserializer;
29
30const SIMPLE_BINARY_BASE_NAME: &'static str = "simple-debug-noopt-dynamic";
31const WORKERTHREADS_BINARY_BASE_NAME: &'static str = "workerthreads-debug-noopt-dynamic";
32
33const LINUX_PLATFORM: &'static str = "linux";
34const DARWIN_PLATFORM: &'static str = "darwin";
35const WINDOWS_PLATFORM: &'static str = "windows";
36
37const N_BNSYM: u8 = 46;
38const N_FUN: u8 = 36;
39const N_ENSYM: u8 = 78;
40
41#[derive(Debug,Serialize,Deserialize)]
42struct NativeFileMetadata {
43
44 #[serde(rename = "configName")]
45 config_name: String,
46
47 #[serde(rename = "baseName")]
48 base_name: String,
49
50 #[serde(rename = "objectSha256s")]
51 objects: HashMap<String, String>,
52
53 #[serde(rename = "objectSuffix")]
54 obj_suffix: String,
55
56 #[serde(rename = "executableSha256")]
57 exec_hash: String,
58
59 #[serde(rename = "executableSuffix")]
60 exec_suffix: String,
61
62 #[serde(rename = "debugSha256")]
63 debug_hash: Option<String>,
64
65 machine: String,
66
67 platform: String,
68
69 flags: HashMap<String, String>,
70
71 compiler: String
72}
73
74pub fn setup(nft_path: &PathBuf,
75 out_path: &PathBuf,
76 rust_os: &str) {
77
78 let platform = convert_to_nft_platform(rust_os);
79
80 let mut simple_path_opt = None;
81 let mut workerthreads_path_opt = None;
82
83 for entry in nft_path.read_dir()
84 .expect("Failed to enumerate native-file-tests directory") {
85 let path = entry.expect("Failed to enumerate directory entry").path();
86
87 if path.is_file() {
88 let file_name = path.file_name().unwrap().to_string_lossy().to_string();
89 if file_name.contains(".json") {
90 let metadata = get_metadata(&path);
91 if metadata.platform == platform {
92 if metadata.base_name == SIMPLE_BINARY_BASE_NAME {
93 simple_path_opt =
94 Some(nft_path.join(get_symbol_file_name(SIMPLE_BINARY_BASE_NAME,
95 platform,
96 &metadata)));
97 } else if metadata.base_name == WORKERTHREADS_BINARY_BASE_NAME {
98 workerthreads_path_opt =
99 Some(nft_path.join(get_symbol_file_name(WORKERTHREADS_BINARY_BASE_NAME,
100 platform,
101 &metadata)));
102 }
103 }
104 }
105 }
106 }
107
108 let simple_path = simple_path_opt.expect("Path to simple binary was not set");
109 let workerthreads_path = workerthreads_path_opt.expect("Path to workerthreads binary was not set");
110
111 let mod_file_path = out_path.join("native_file_tests.rs");
112 let mut mod_file = File::create(&mod_file_path).unwrap();
113
114 let symbols = build_symbols(platform, &simple_path, &workerthreads_path);
115
116 let sym_defs = symbols.iter()
117 .map(|e| format!("pub const {}: u64 = {};\n", e.0, e.1))
118 .fold("".to_owned(), |acc, elem| acc + &elem);
119
120 let mod_file_content = format!("
121pub const SIMPLE_EXEC_PATH: &'static str = \"{}\";
122pub const WORKERTHREADS_EXEC_PATH: &'static str = \"{}\";
123
124{}
125",
126 simple_path.to_str().unwrap(),
127 workerthreads_path.to_str().unwrap(),
128 sym_defs);
129
130 mod_file.write_all(&mod_file_content.into_bytes())
131 .expect("Failed to write native file tests module");
132}
133
134fn convert_to_nft_platform(rust_os: &str) -> &str {
135 match rust_os {
136 "linux" => LINUX_PLATFORM,
137 "macos" => DARWIN_PLATFORM,
138 "windows" => WINDOWS_PLATFORM,
139 _ => panic!(format!("Unsupported platform"))
140 }
141}
142
143fn get_symbol_file_name(prefix: &str,
144 platform: &str,
145 metadata: &NativeFileMetadata) -> String {
146
147 match platform {
148 WINDOWS_PLATFORM => {
149 prefix.to_owned() + &metadata.exec_suffix + ".debug." +
150 metadata.debug_hash.as_ref().unwrap()
151 },
152 _ => {
153 prefix.to_owned() + "." + &metadata.exec_hash
154 }
155 }
156}
157
158fn get_metadata(json_path: &PathBuf) -> NativeFileMetadata {
159 let mut json_file = File::open(json_path).unwrap();
160
161 let mut de = Deserializer::from_reader(&mut json_file);
162
163 Deserialize::deserialize(&mut de).unwrap()
164}
165
166fn build_symbols(platform: &str,
167 simple_path: &PathBuf,
168 workerthreads_path: &PathBuf) -> HashMap<&'static str, String> {
169 let mut symbols = HashMap::new();
170
171 add_simple_binary_symbols(platform, simple_path, &mut symbols);
172 add_workerthreads_binary_symbols(platform, workerthreads_path, &mut symbols);
173
174 symbols
175}
176
177fn add_simple_binary_symbols(platform: &str,
178 path: &PathBuf,
179 symbols: &mut HashMap<&'static str, String>) {
180
181 for_each_symbols(platform, path, |name, value, size| {
182 if name == "function1" {
183 symbols.insert("SIMPLE_FUNCTION1", format!("0x{:x}", value));
184 } else if name == "function2" {
185 symbols.insert("SIMPLE_FUNCTION2", format!("0x{:x}", value));
186 symbols.insert("SIMPLE_FUNCTION2_LENGTH", size.to_string());
187 }
188 });
189}
190
191fn add_workerthreads_binary_symbols(platform: &str,
192 path: &PathBuf,
193 symbols: &mut HashMap<&'static str, String>) {
194
195 for_each_symbols(platform, path, |name, value, _| {
196 match name.as_str() {
197 "breakpoint_thr_func" => {
198 symbols.insert("THREAD_BREAK_FUNC", format!("0x{:x}", value));
199 },
200 "start_notification" => {
201 symbols.insert("START_NOTIFICATION_FUNC", format!("0x{:x}", value));
202 },
203 "term_notification" => {
204 symbols.insert("TERM_NOTIFICATION_FUNC", format!("0x{:x}", value));
205 },
206 _ => {}
207 };
208 });
209}
210
211fn for_each_symbols<F>(platform: &str,
212 path: &PathBuf,
213 func: F)
214 where F: FnMut(&String, u64, u64) {
215
216 match platform {
217 WINDOWS_PLATFORM => for_each_symbols_from_pdb(path, func),
218 _ => for_each_symbols_from_obj_file(path, func)
219 }
220
221}
222
223fn for_each_symbols_from_obj_file<F>(path: &PathBuf,
224 mut func: F) where F: FnMut(&String, u64, u64) {
225
226 let mut binary_file = File::open(&path).expect(&format!("Symbol file {:?} error",
227 path));
228 let mut buffer = vec![];
229 binary_file.read_to_end(&mut buffer).unwrap();
230
231 match Object::parse(&buffer).unwrap() {
232 Object::Elf(elf) => {
233 let strtab = &(elf.strtab);
234 for sym in &(elf.syms) {
235 func(&(strtab.get(sym.st_name).unwrap().unwrap().to_owned()),
236 sym.st_value,
237 sym.st_size);
238 }
239 },
240 Object::Mach(goblin::mach::Mach::Binary(mach)) => {
241 let syms = mach.symbols.unwrap();
242
243 let mut addr = 0;
244 let mut sym_name = "".to_owned();
245 for sym_result in syms.iter() {
246 let (name, nlist) = sym_result.unwrap();
247
248 match nlist.n_type {
249 N_BNSYM => {
250 addr = nlist.n_value;
251 },
252 N_FUN => {
253 if nlist.n_strx > 1 {
254 sym_name = name.split_at(1).1.to_owned();
256 }
257 },
258 N_ENSYM => {
259 func(&sym_name, addr, nlist.n_value);
260 }
261 _ => {}
262 }
263 }
264 },
265 _ => {
266 panic!(format!("Unsupported file type for file {:?}", path));
267 }
268 }
269}
270
271fn for_each_symbols_from_pdb<F>(_path: &PathBuf,
272 mut func: F) where F: FnMut(&String, u64, u64) {
273
274 func(&"".to_owned(), 0, 0);
275}