1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 #![allow(bad_style)]
12 
13 // This is a hack for compatibility with rustc 1.25.0. The no_std mode of this
14 // crate is not supported pre-1.30.0, but in std mode the `char` module here
15 // moved in rustc 1.26.0 (ish). As a result, in std mode we use `std::char` to
16 // retain compatibility with rustc 1.25.0, but in `no_std` mode (which is
17 // 1.30.0+ already) we use `core::char`.
18 #[cfg(feature = "std")]
19 use std::char;
20 #[cfg(not(feature = "std"))]
21 use core::char;
22 
23 use core::mem;
24 use core::slice;
25 
26 use winapi::ctypes::*;
27 use winapi::shared::basetsd::*;
28 use winapi::shared::minwindef::*;
29 use winapi::um::processthreadsapi;
30 use winapi::um::dbghelp;
31 use winapi::um::dbghelp::*;
32 
33 use SymbolName;
34 use types::BytesOrWideString;
35 
36 // Store an OsString on std so we can provide the symbol name and filename.
37 pub struct Symbol {
38     name: *const [u8],
39     addr: *mut c_void,
40     line: Option<u32>,
41     filename: Option<*const [u16]>,
42     #[cfg(feature = "std")]
43     _filename_cache: Option<::std::ffi::OsString>,
44     #[cfg(not(feature = "std"))]
45     _filename_cache: (),
46 }
47 
48 impl Symbol {
name(&self) -> Option<SymbolName>49     pub fn name(&self) -> Option<SymbolName> {
50         Some(SymbolName::new(unsafe { &*self.name }))
51     }
52 
addr(&self) -> Option<*mut c_void>53     pub fn addr(&self) -> Option<*mut c_void> {
54         Some(self.addr as *mut _)
55     }
56 
filename_raw(&self) -> Option<BytesOrWideString>57     pub fn filename_raw(&self) -> Option<BytesOrWideString> {
58         self.filename.map(|slice| {
59             unsafe {
60                 BytesOrWideString::Wide(&*slice)
61             }
62         })
63     }
64 
lineno(&self) -> Option<u32>65     pub fn lineno(&self) -> Option<u32> {
66         self.line
67     }
68 
69     #[cfg(feature = "std")]
filename(&self) -> Option<&::std::ffi::OsString>70     pub fn filename(&self) -> Option<&::std::ffi::OsString> {
71         self._filename_cache.as_ref()
72     }
73 }
74 
75 #[repr(C, align(8))]
76 struct Aligned8<T>(T);
77 
resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol))78 pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) {
79     const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>();
80     let mut data = Aligned8([0u8; SIZE]);
81     let data = &mut data.0;
82     let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW);
83     info.MaxNameLen = MAX_SYM_NAME as ULONG;
84     // the struct size in C.  the value is different to
85     // `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81)
86     // due to struct alignment.
87     info.SizeOfStruct = 88;
88 
89     let _c = ::dbghelp_init();
90 
91     let mut displacement = 0u64;
92     let ret = dbghelp::SymFromAddrW(processthreadsapi::GetCurrentProcess(),
93                                     addr as DWORD64,
94                                     &mut displacement,
95                                     info);
96     if ret != TRUE {
97         return
98     }
99 
100     // If the symbol name is greater than MaxNameLen, SymFromAddrW will
101     // give a buffer of (MaxNameLen - 1) characters and set NameLen to
102     // the real value.
103     let name_len = ::core::cmp::min(info.NameLen as usize,
104                                     info.MaxNameLen as usize - 1);
105     let name_ptr = info.Name.as_ptr() as *const u16;
106     let name = slice::from_raw_parts(name_ptr, name_len);
107 
108     // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like
109     // all other platforms
110     let mut name_len = 0;
111     let mut name_buffer = [0; 256];
112     {
113         let mut remaining = &mut name_buffer[..];
114         for c in char::decode_utf16(name.iter().cloned()) {
115             let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
116             let len = c.len_utf8();
117             if len < remaining.len() {
118                 c.encode_utf8(remaining);
119                 let tmp = remaining;
120                 remaining = &mut tmp[len..];
121                 name_len += len;
122             } else {
123                 break
124             }
125         }
126     }
127     let name = &name_buffer[..name_len] as *const [u8];
128 
129     let mut line = mem::zeroed::<IMAGEHLP_LINEW64>();
130     line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD;
131     let mut displacement = 0;
132     let ret = dbghelp::SymGetLineFromAddrW64(processthreadsapi::GetCurrentProcess(),
133                                              addr as DWORD64,
134                                              &mut displacement,
135                                              &mut line);
136 
137     let mut filename = None;
138     let mut lineno = None;
139     if ret == TRUE {
140         lineno = Some(line.LineNumber as u32);
141 
142         let base = line.FileName;
143         let mut len = 0;
144         while *base.offset(len) != 0 {
145             len += 1;
146         }
147 
148         let len = len as usize;
149 
150         filename = Some(slice::from_raw_parts(base, len) as *const [u16]);
151     }
152 
153 
154     cb(&super::Symbol {
155         inner: Symbol {
156             name,
157             addr: info.Address as *mut _,
158             line: lineno,
159             filename,
160             _filename_cache: cache(filename),
161         },
162     })
163 }
164 
165 #[cfg(feature = "std")]
cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString>166 unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
167     use std::os::windows::ffi::OsStringExt;
168     filename.map(|f| {
169         ::std::ffi::OsString::from_wide(&*f)
170     })
171 }
172 
173 #[cfg(not(feature = "std"))]
cache(_filename: Option<*const [u16]>)174 unsafe fn cache(_filename: Option<*const [u16]>) {
175 }
176