001/* 002 * BridJ - Dynamic and blazing-fast native interop for Java. 003 * http://bridj.googlecode.com/ 004 * 005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/) 006 * All rights reserved. 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions are met: 010 * 011 * * Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * * Neither the name of Olivier Chafik nor the 017 * names of its contributors may be used to endorse or promote products 018 * derived from this software without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY 021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 030 */ 031/** 032 * 033 */ 034package org.bridj; 035 036import java.io.*; 037import java.util.regex.*; 038import java.lang.annotation.Annotation; 039import java.lang.reflect.AnnotatedElement; 040import java.lang.reflect.Constructor; 041import java.lang.reflect.Member; 042import java.lang.reflect.Method; 043import java.lang.reflect.Modifier; 044import java.util.ArrayList; 045import java.util.Collections; 046import java.util.HashMap; 047import java.util.List; 048import java.util.Map; 049import java.util.Set; 050import java.util.Arrays; 051 052import org.bridj.demangling.Demangler.DemanglingException; 053import org.bridj.demangling.Demangler.MemberRef; 054import org.bridj.demangling.Demangler.Symbol; 055import org.bridj.ann.Virtual; 056import org.bridj.ann.Name; 057import org.bridj.demangling.GCC4Demangler; 058import org.bridj.demangling.VC9Demangler; 059import java.lang.reflect.Type; 060import static org.bridj.Pointer.*; 061import static org.bridj.util.AnnotationUtils.*; 062 063import java.util.Collection; 064import org.bridj.Platform.DeleteFiles; 065import org.bridj.demangling.Demangler; 066import org.bridj.util.ProcessUtils; 067import org.bridj.util.StringUtils; 068 069/** 070 * Representation of a native shared library, with symbols retrieval / matching 071 * facilities.<br> 072 * This class is not meant to be used by end users, it's used by pluggable 073 * runtimes instead. 074 * 075 * @author ochafik 076 */ 077public class NativeLibrary { 078 079 volatile long handle, symbols; 080 String path; 081 final File canonicalFile; 082 //Map<Class<?>, long[]> callbacks = new HashMap<Class<?>, long[]>(); 083 NativeEntities nativeEntities = new NativeEntities(); 084 Map<Long, Symbol> addrToName; 085 Map<String, Symbol> nameToSym; 086// Map<String, Long> nameToAddr; 087 088 protected NativeLibrary(String path, long handle, long symbols) throws IOException { 089 this.path = path; 090 this.handle = handle; 091 this.symbols = symbols; 092 this.canonicalFile = path == null ? null : new File(path).getCanonicalFile(); 093 094 Platform.addNativeLibrary(this); 095 } 096 097 long getSymbolsHandle() { 098 return symbols; 099 } 100 101 NativeEntities getNativeEntities() { 102 return nativeEntities; 103 } 104 105 static String followGNULDScript(String path) { 106 try { 107 Reader r = new FileReader(path); 108 try { 109 char c; 110 while ((c = (char) r.read()) == ' ' || c == '\t' || c == '\n') { 111 } 112 if (c == '/' && r.read() == '*') { 113 BufferedReader br = new BufferedReader(r); 114 r = br; 115 String line; 116 StringBuilder b = new StringBuilder("/*"); 117 while ((line = br.readLine()) != null) { 118 b.append(line).append('\n'); 119 } 120 String src = b.toString(); 121 Pattern ldGroupPattern = Pattern.compile("GROUP\\s*\\(\\s*([^\\s)]+)[^)]*\\)"); 122 Matcher m = ldGroupPattern.matcher(src); 123 if (m.find()) { 124 String actualPath = m.group(1); 125 if (BridJ.verbose) { 126 BridJ.info("Parsed LD script '" + path + "', found absolute reference to '" + actualPath + "'"); 127 } 128 return actualPath; 129 } else { 130 BridJ.error("Failed to parse LD script '" + path + "' !"); 131 } 132 } 133 } finally { 134 r.close(); 135 } 136 } catch (Throwable th) { 137 BridJ.error("Unexpected error: " + th, th); 138 } 139 return path; 140 } 141 142 public static NativeLibrary load(String path) throws IOException { 143 long handle = 0; 144 File file = new File(path); 145 boolean exists = file.exists(); 146 if (file.isAbsolute() && !exists) { 147 return null; 148 } 149 150 if (Platform.isUnix() && exists) { 151 path = followGNULDScript(path); 152 } 153 154 handle = JNI.loadLibrary(path); 155 if (handle == 0) { 156 return null; 157 } 158 long symbols = JNI.loadLibrarySymbols(path); 159 return new NativeLibrary(path, handle, symbols); 160 } 161 162 /*public boolean methodMatchesSymbol(Class<?> declaringClass, Method method, String symbol) { 163 return symbol.contains(method.getName()) && symbol.contains(declaringClass.getSimpleName()); 164 }*/ 165 long getHandle() { 166 if (path != null && handle == 0) { 167 throw new RuntimeException("Library was released and cannot be used anymore"); 168 } 169 return handle; 170 } 171 172 @Override 173 protected void finalize() throws Throwable { 174 release(); 175 } 176 177 public synchronized void release() { 178 if (handle == 0) { 179 return; 180 } 181 182 if (BridJ.verbose) { 183 BridJ.info("Releasing library '" + path + "'"); 184 } 185 186 nativeEntities.release(); 187 188 JNI.freeLibrarySymbols(symbols); 189 JNI.freeLibrary(handle); 190 handle = 0; 191 192 if (canonicalFile != null && Platform.temporaryExtractedLibraryCanonicalFiles.remove(canonicalFile)) { 193 if (canonicalFile.delete()) { 194 if (BridJ.verbose) { 195 BridJ.info("Deleted temporary library file '" + canonicalFile + "'"); 196 } 197 } else { 198 BridJ.error("Failed to delete temporary library file '" + canonicalFile + "'"); 199 } 200 } 201 202 } 203 204 public Pointer<?> getSymbolPointer(String name) { 205 return pointerToAddress(getSymbolAddress(name)); 206 } 207 208 public long getSymbolAddress(String name) { 209 if (nameToSym != null) { 210 Symbol addr = nameToSym.get(name); 211// long addr = nameToAddr.get(name); 212// if (addr != 0) 213 if (addr != null) { 214 return addr.getAddress(); 215 } 216 } 217 long address = JNI.findSymbolInLibrary(getHandle(), name); 218 if (address == 0) { 219 address = JNI.findSymbolInLibrary(getHandle(), "_" + name); 220 } 221 return address; 222 } 223 224 public synchronized Symbol getSymbol(AnnotatedElement member) throws FileNotFoundException { 225 org.bridj.ann.Symbol mg = getAnnotation(org.bridj.ann.Symbol.class, member); 226 String name = null; 227 228 Name nameAnn = member.getAnnotation(Name.class); 229 if (nameAnn != null) { 230 name = nameAnn.value(); 231 } else if (member instanceof Member) { 232 name = ((Member) member).getName(); 233 } 234 235 List<String> names = new ArrayList<String>(); 236 if (mg != null) { 237 names.addAll(Arrays.asList(mg.value())); 238 } 239 if (name != null) { 240 names.add(name); 241 } 242 243 for (String n : names) { 244 Symbol handle = getSymbol(n); 245 if (handle == null) { 246 handle = getSymbol("_" + n); 247 } 248 if (handle == null) { 249 handle = getSymbol(n + (Platform.useUnicodeVersionOfWindowsAPIs ? "W" : "A")); 250 } 251 if (handle != null) { 252 return handle; 253 } 254 } 255 256 if (member instanceof Method) { 257 Method method = (Method) member; 258 for (Demangler.Symbol symbol : getSymbols()) { 259 if (symbol.matches(method)) { 260 return symbol; 261 } 262 } 263 } 264 return null; 265 } 266 267 public boolean isMSVC() { 268 return Platform.isWindows(); 269 } 270 271 /** 272 * Filter for symbols 273 */ 274 public interface SymbolAccepter { 275 276 boolean accept(Symbol symbol); 277 } 278 279 public Symbol getFirstMatchingSymbol(SymbolAccepter accepter) { 280 for (Symbol symbol : getSymbols()) { 281 if (accepter.accept(symbol)) { 282 return symbol; 283 } 284 } 285 return null; 286 } 287 288 public Collection<Demangler.Symbol> getSymbols() { 289 try { 290 scanSymbols(); 291 } catch (Exception ex) { 292 assert BridJ.error("Failed to scan symbols of library '" + path + "'", ex); 293 } 294 return nameToSym == null ? Collections.EMPTY_LIST : Collections.unmodifiableCollection(nameToSym.values()); 295 } 296 297 public String getSymbolName(long address) { 298 if (addrToName == null && getSymbolsHandle() != 0)//Platform.isUnix()) 299 { 300 return JNI.findSymbolName(getHandle(), getSymbolsHandle(), address); 301 } 302 303 Demangler.Symbol symbol = getSymbol(address); 304 return symbol == null ? null : symbol.getSymbol(); 305 } 306 307 public Symbol getSymbol(long address) { 308 try { 309 scanSymbols(); 310 Symbol symbol = addrToName.get(address); 311 return symbol; 312 } catch (Exception ex) { 313 throw new RuntimeException("Failed to get name of address " + address, ex); 314 } 315 } 316 317 public Symbol getSymbol(String name) { 318 try { 319 Symbol symbol; 320 long addr; 321 322 if (nameToSym == null) {// symbols not scanned yet, try without them ! 323 addr = JNI.findSymbolInLibrary(getHandle(), name); 324 if (addr != 0) { 325 symbol = new Symbol(name, this); 326 symbol.setAddress(addr); 327 return symbol; 328 } 329 } 330 scanSymbols(); 331 if (nameToSym == null) { 332 return null; 333 } 334 335 symbol = nameToSym.get(name); 336 if (addrToName == null) { 337 if (symbol == null) { 338 addr = JNI.findSymbolInLibrary(getHandle(), name); 339 if (addr != 0) { 340 symbol = new Symbol(name, this); 341 symbol.setAddress(addr); 342 nameToSym.put(name, symbol); 343 } 344 } 345 } 346 return symbol; 347 } catch (Exception ex) { 348 ex.printStackTrace(); 349 return null; 350// throw new RuntimeException("Failed to get symbol " + name, ex); 351 } 352 } 353 354 void scanSymbols() throws Exception { 355 if (addrToName != null) { 356 return; 357 } 358 359 nameToSym = new HashMap<String, Symbol>(); 360// nameToAddr = new HashMap<String, Long>(); 361 362 String[] symbs = null; 363 if (symbs == null) { 364 //System.out.println("Calling getLibrarySymbols"); 365 symbs = JNI.getLibrarySymbols(getHandle(), getSymbolsHandle()); 366 //System.out.println("Got " + symbs + " (" + (symbs == null ? "null" : symbs.length + "") + ")"); 367 } 368 369 if (symbs == null) { 370 return; 371 } 372 373 addrToName = new HashMap<Long, Demangler.Symbol>(); 374 375 boolean is32 = !Platform.is64Bits(); 376 for (String name : symbs) { 377 if (name == null) { 378 continue; 379 } 380 381 long addr = JNI.findSymbolInLibrary(getHandle(), name); 382 if (addr == 0 && name.startsWith("_")) { 383 String n2 = name.substring(1); 384 addr = JNI.findSymbolInLibrary(getHandle(), n2); 385 if (addr == 0) { 386 n2 = "_" + name; 387 addr = JNI.findSymbolInLibrary(getHandle(), n2); 388 } 389 if (addr != 0) { 390 name = n2; 391 } 392 393 } 394 if (addr == 0) { 395 if (BridJ.verbose) { 396 BridJ.warning("Symbol '" + name + "' not found."); 397 } 398 continue; 399 } 400 //if (is32) 401 // addr = addr & 0xffffffffL; 402 //System.out.println("Symbol " + Long.toHexString(addr) + " = '" + name + "'"); 403 404 Symbol sym = new Demangler.Symbol(name, this); 405 sym.setAddress(addr); 406 addrToName.put(addr, sym); 407 nameToSym.put(name, sym); 408 //nameToAddr.put(name, addr); 409 //System.out.println("'" + name + "' = \t" + TestCPP.hex(addr) + "\n\t" + sym.getParsedRef()); 410 } 411 if (BridJ.debug) { 412 BridJ.info("Found " + nameToSym.size() + " symbols in '" + path + "' :"); 413 414 for (Symbol sym : nameToSym.values()) { 415 BridJ.info("DEBUG(BridJ): library=\"" + path + "\", symbol=\"" + sym.getSymbol() + "\", address=" + Long.toHexString(sym.getAddress()) + ", demangled=\"" + sym.getParsedRef() + "\""); 416 } 417 418 //for (Symbol sym : nameToSym.values()) 419 // System.out.println("Symbol '" + sym + "' = " + sym.getParsedRef()); 420 } 421 } 422 423 public MemberRef parseSymbol(String symbol) throws DemanglingException { 424 if ("__cxa_pure_virtual".equals(symbol)) { 425 return null; 426 } 427 428 if (Platform.isWindows()) { 429 try { 430 MemberRef result = new VC9Demangler(this, symbol).parseSymbol(); 431 if (result != null) { 432 return result; 433 } 434 } catch (Throwable th) { 435 } 436 } 437 return new GCC4Demangler(this, symbol).parseSymbol(); 438 } 439}