001 /**
002 *
003 */
004 package org.bridj;
005
006 import java.io.*;
007 import java.util.regex.*;
008 import java.lang.annotation.Annotation;
009 import java.lang.reflect.AnnotatedElement;
010 import java.lang.reflect.Constructor;
011 import java.lang.reflect.Member;
012 import java.lang.reflect.Method;
013 import java.lang.reflect.Modifier;
014 import java.util.ArrayList;
015 import java.util.Collections;
016 import java.util.HashMap;
017 import java.util.List;
018 import java.util.Map;
019 import java.util.Set;
020 import java.util.Arrays;
021
022 import org.bridj.demangling.Demangler.DemanglingException;
023 import org.bridj.demangling.Demangler.MemberRef;
024 import org.bridj.demangling.Demangler.Symbol;
025 import org.bridj.ann.Virtual;
026 import org.bridj.demangling.GCC4Demangler;
027 import org.bridj.demangling.VC9Demangler;
028 import java.lang.reflect.Type;
029 import static org.bridj.Pointer.*;
030 import static org.bridj.util.AnnotationUtils.*;
031
032 import java.util.Collection;
033 import org.bridj.Platform.DeleteFiles;
034 import org.bridj.demangling.Demangler;
035 import org.bridj.util.ProcessUtils;
036 import org.bridj.util.StringUtils;
037
038 /**
039 * Representation of a native shared library, with symbols retrieval / matching facilities.<br>
040 * This class is not meant to be used by end users, it's used by pluggable runtimes instead.
041 * @author ochafik
042 */
043 public class NativeLibrary {
044 volatile long handle, symbols;
045 String path;
046 final File canonicalFile;
047 //Map<Class<?>, long[]> callbacks = new HashMap<Class<?>, long[]>();
048 NativeEntities nativeEntities = new NativeEntities();
049
050 Map<Long, Symbol> addrToName;
051 Map<String, Symbol> nameToSym;
052 // Map<String, Long> nameToAddr;
053
054
055 protected NativeLibrary(String path, long handle, long symbols) throws IOException {
056 this.path = path;
057 this.handle = handle;
058 this.symbols = symbols;
059 this.canonicalFile = path == null ? null : new File(path).getCanonicalFile();
060
061 Platform.addNativeLibrary(this);
062 }
063
064 long getSymbolsHandle() {
065 return symbols;
066 }
067 NativeEntities getNativeEntities() {
068 return nativeEntities;
069 }
070 static String followGNULDScript(String path) {
071 try {
072 Reader r = new FileReader(path);
073 try {
074 char c;
075 while ((c = (char)r.read()) == ' ' || c == '\t' || c == '\n') {}
076 if (c == '/' && r.read() == '*') {
077 BufferedReader br = new BufferedReader(r);
078 r = br;
079 String line;
080 StringBuilder b = new StringBuilder("/*");
081 while ((line = br.readLine()) != null)
082 b.append(line).append('\n');
083 String src = b.toString();
084 Pattern ldGroupPattern = Pattern.compile("GROUP\\s*\\(\\s*([^\\s)]+)[^)]*\\)");
085 Matcher m = ldGroupPattern.matcher(src);
086 if (m.find()) {
087 String actualPath = m.group(1);
088 if (BridJ.verbose)
089 BridJ.info("Parsed LD script '" + path + "', found absolute reference to '" + actualPath + "'");
090 return actualPath;
091 } else {
092 BridJ.error("Failed to parse LD script '" + path + "' !");
093 }
094 }
095 } finally {
096 r.close();
097 }
098 } catch (Throwable th) {
099 BridJ.error("Unexpected error: " + th, th);
100 }
101 return path;
102 }
103 public static NativeLibrary load(String path) throws IOException {
104 long handle = 0;
105 File file = new File(path);
106 boolean exists = file.exists();
107 if (file.isAbsolute() && !exists)
108 return null;
109
110 if (Platform.isUnix() && exists)
111 path = followGNULDScript(path);
112
113 handle = JNI.loadLibrary(path);
114 if (handle == 0)
115 return null;
116 long symbols = JNI.loadLibrarySymbols(path);
117 return new NativeLibrary(path, handle, symbols);
118 }
119
120 /*public boolean methodMatchesSymbol(Class<?> declaringClass, Method method, String symbol) {
121 return symbol.contains(method.getName()) && symbol.contains(declaringClass.getSimpleName());
122 }*/
123
124 long getHandle() {
125 if (path != null && handle == 0)
126 throw new RuntimeException("Library was released and cannot be used anymore");
127 return handle;
128 }
129 @Override
130 protected void finalize() throws Throwable {
131 release();
132 }
133 public synchronized void release() {
134 if (handle == 0)
135 return;
136
137 if (BridJ.verbose)
138 BridJ.info("Releasing library '" + path + "'");
139
140 nativeEntities.release();
141
142 JNI.freeLibrarySymbols(symbols);
143 JNI.freeLibrary(handle);
144 handle = 0;
145
146 if (canonicalFile != null && Platform.temporaryExtractedLibraryCanonicalFiles.remove(canonicalFile)) {
147 if (canonicalFile.delete()) {
148 if (BridJ.verbose)
149 BridJ.info("Deleted temporary library file '" + canonicalFile + "'");
150 } else
151 BridJ.error("Failed to delete temporary library file '" + canonicalFile + "'");
152 }
153
154 }
155 public Pointer<?> getSymbolPointer(String name) {
156 return pointerToAddress(getSymbolAddress(name));
157 }
158 public long getSymbolAddress(String name) {
159 if (nameToSym != null) {
160 Symbol addr = nameToSym.get(name);
161 // long addr = nameToAddr.get(name);
162 // if (addr != 0)
163 if (addr != null)
164 return addr.getAddress();
165 }
166 long address = JNI.findSymbolInLibrary(getHandle(), name);
167 if (address == 0)
168 address = JNI.findSymbolInLibrary(getHandle(), "_" + name);
169 return address;
170 }
171
172 public synchronized Symbol getSymbol(AnnotatedElement member) throws FileNotFoundException {
173 org.bridj.ann.Symbol mg = getAnnotation(org.bridj.ann.Symbol.class, member);
174 String name = null;
175 if (member instanceof Member)
176 name = ((Member)member).getName();
177
178 List<String> names = new ArrayList<String>();
179 if (mg != null)
180 names.addAll(Arrays.asList(mg.value()));
181 if (name != null)
182 names.add(name);
183
184 for (String n : names)
185 {
186 Symbol handle = getSymbol(n);
187 if (handle == null)
188 handle = getSymbol("_" + n);
189 if (handle == null)
190 handle = getSymbol(n + (Platform.useUnicodeVersionOfWindowsAPIs ? "W" : "A"));
191 if (handle != null)
192 return handle;
193 }
194
195 if (member instanceof Method) {
196 Method method = (Method)member;
197 for (Demangler.Symbol symbol : getSymbols()) {
198 if (symbol.matches(method))
199 return symbol;
200 }
201 }
202 return null;
203 }
204
205 public boolean isMSVC() {
206 return Platform.isWindows();
207 }
208 /** Filter for symbols */
209 public interface SymbolAccepter {
210 boolean accept(Symbol symbol);
211 }
212 public Symbol getFirstMatchingSymbol(SymbolAccepter accepter) {
213 for (Symbol symbol : getSymbols())
214 if (accepter.accept(symbol))
215 return symbol;
216 return null;
217 }
218 public Collection<Demangler.Symbol> getSymbols() {
219 try {
220 scanSymbols();
221 } catch (Exception ex) {
222 assert BridJ.error("Failed to scan symbols of library '" + path + "'", ex);
223 }
224 return nameToSym == null ? Collections.EMPTY_LIST : Collections.unmodifiableCollection(nameToSym.values());
225 }
226 public String getSymbolName(long address) {
227 if (addrToName == null && getSymbolsHandle() != 0)//Platform.isUnix())
228 return JNI.findSymbolName(getHandle(), getSymbolsHandle(), address);
229
230 Demangler.Symbol symbol = getSymbol(address);
231 return symbol == null ? null : symbol.getSymbol();
232 }
233
234 public Symbol getSymbol(long address) {
235 try {
236 scanSymbols();
237 Symbol symbol = addrToName.get(address);
238 return symbol;
239 } catch (Exception ex) {
240 throw new RuntimeException("Failed to get name of address " + address, ex);
241 }
242 }
243 public Symbol getSymbol(String name) {
244 try {
245 Symbol symbol;
246 long addr;
247
248 if (nameToSym == null) {// symbols not scanned yet, try without them !
249 addr = JNI.findSymbolInLibrary(getHandle(), name);
250 if (addr != 0) {
251 symbol = new Symbol(name, this);
252 symbol.setAddress(addr);
253 return symbol;
254 }
255 }
256 scanSymbols();
257 if (nameToSym == null)
258 return null;
259
260 symbol = nameToSym.get(name);
261 if (addrToName == null) {
262 if (symbol == null) {
263 addr = JNI.findSymbolInLibrary(getHandle(), name);
264 if (addr != 0) {
265 symbol = new Symbol(name, this);
266 symbol.setAddress(addr);
267 nameToSym.put(name, symbol);
268 }
269 }
270 }
271 return symbol;
272 } catch (Exception ex) {
273 ex.printStackTrace();
274 return null;
275 // throw new RuntimeException("Failed to get symbol " + name, ex);
276 }
277 }
278 void scanSymbols() throws Exception {
279 if (addrToName != null)
280 return;
281
282 nameToSym = new HashMap<String, Symbol>();
283 // nameToAddr = new HashMap<String, Long>();
284
285 String[] symbs = null;
286 if (false) // TODO turn to false !!!
287 try {
288 if (Platform.isMacOSX()) {
289 Process process = java.lang.Runtime.getRuntime().exec(new String[] {"nm", "-gj", path});
290 BufferedReader rin = new BufferedReader(new InputStreamReader(process.getInputStream()));
291 String line;
292 List<String> symbsList = new ArrayList<String>();
293 while ((line = rin.readLine()) != null) {
294 symbsList.add(line);
295 }
296 symbs = symbsList.toArray(new String[symbsList.size()]);
297 }
298 } catch (Exception ex) {
299 ex.printStackTrace();
300 }
301 if (symbs == null) {
302 //System.out.println("Calling getLibrarySymbols");
303 symbs = JNI.getLibrarySymbols(getHandle(), getSymbolsHandle());
304 //System.out.println("Got " + symbs + " (" + (symbs == null ? "null" : symbs.length + "") + ")");
305 }
306
307 if (symbs == null)
308 return;
309
310 addrToName = new HashMap<Long, Demangler.Symbol>();
311
312 boolean is32 = !Platform.is64Bits();
313 for (String name : symbs) {
314 if (name == null)
315 continue;
316
317 long addr = JNI.findSymbolInLibrary(getHandle(), name);
318 if (addr == 0 && name.startsWith("_")) {
319 String n2 = name.substring(1);
320 addr = JNI.findSymbolInLibrary(getHandle(), n2);
321 if (addr == 0) {
322 n2 = "_" + name;
323 addr = JNI.findSymbolInLibrary(getHandle(), n2);
324 }
325 if (addr != 0)
326 name = n2;
327
328 }
329 if (addr == 0) {
330 if (BridJ.verbose)
331 BridJ.warning("Symbol '" + name + "' not found.");
332 continue;
333 }
334 //if (is32)
335 // addr = addr & 0xffffffffL;
336 //System.out.println("Symbol " + Long.toHexString(addr) + " = '" + name + "'");
337
338 Symbol sym = new Demangler.Symbol(name, this);
339 sym.setAddress(addr);
340 addrToName.put(addr, sym);
341 nameToSym.put(name, sym);
342 //nameToAddr.put(name, addr);
343 //System.out.println("'" + name + "' = \t" + TestCPP.hex(addr) + "\n\t" + sym.getParsedRef());
344 }
345 if (BridJ.debug) {
346 BridJ.info("Found " + nameToSym.size() + " symbols in '" + path + "' :");
347
348 for (Symbol sym : nameToSym.values())
349 BridJ.info("DEBUG(BridJ): library=\"" + path + "\", symbol=\"" + sym.getSymbol() + "\", address=" + Long.toHexString(sym.getAddress()) + ", demangled=\"" + sym.getParsedRef() + "\"");
350
351 //for (Symbol sym : nameToSym.values())
352 // System.out.println("Symbol '" + sym + "' = " + sym.getParsedRef());
353 }
354 }
355
356 public MemberRef parseSymbol(String symbol) throws DemanglingException {
357 if ("__cxa_pure_virtual".equals(symbol))
358 return null;
359
360 Demangler demangler;
361 if (Platform.isWindows())
362 demangler = new VC9Demangler(this, symbol);
363 else
364 demangler = new GCC4Demangler(this, symbol);
365 return demangler.parseSymbol();
366 }
367 }