native_file_tests/
lib.rs

1//
2// Copyright (c) 2011-2017, UDI Contributors
3// All rights reserved.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at http://mozilla.org/MPL/2.0/.
8//
9#![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                            // remove leading _ from symbol name
255                            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}