/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.tele.method;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import com.oracle.max.elf.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.memory.*;
import com.sun.max.tele.MaxPlatform.OS;
import com.sun.max.tele.debug.darwin.*;
import com.sun.max.tele.debug.darwin.DarwinMachO.*;
import com.sun.max.tele.object.*;
import com.sun.max.unsafe.*;
public abstract class TeleNativeLibrary extends AbstractVmHolder implements MaxNativeLibrary, MaxCodeHoldingRegion<MaxNativeLibrary> {
private static final class NativeLibraryMemoryRegion extends TeleFixedMemoryRegion implements MaxEntityMemoryRegion<MaxNativeLibrary> {
private final List<MaxEntityMemoryRegion< ? extends MaxEntity>> children = new ArrayList<MaxEntityMemoryRegion< ? extends MaxEntity>>();
private MaxNativeLibrary library;
private NativeLibraryMemoryRegion(MaxVM vm, MaxNativeLibrary library) {
super(vm, "Native library " + library.path(), library.base(), library.length());
this.library = library;
}
public MaxEntityMemoryRegion< ? extends MaxEntity> parent() {
// Native libraries are roots in the forest
return null;
}
public List<MaxEntityMemoryRegion< ? extends MaxEntity>> children() {
return children;
}
public void addChild(TeleNativeFunction teleNativeFunction) {
children.add(teleNativeFunction.memoryRegion());
}
public MaxNativeLibrary owner() {
return library;
}
}
protected String path;
protected Address base;
protected long length;
protected String sentinel;
private String entityName;
private Address sentinelAddress;
private TeleNativeFunction[] functions;
private NativeLibraryMemoryRegion nativeLibraryMemoryRegion;
private final RemoteCodePointerManager codePointerManager;
private boolean sortByName;
public static TeleNativeLibrary create(MaxVM vm, OS os, String path) {
try {
String className = TeleNativeLibrary.class.getPackage().getName() + "." + "TeleNativeLibrary$" + os.toString();
Class<?> klass = Class.forName(className);
Constructor<?> cons = klass.getDeclaredConstructor(TeleVM.class, String.class, Address.class);
TeleNativeLibrary result = (TeleNativeLibrary) cons.newInstance(vm, path, Address.zero());
return result;
} catch (Exception ex) {
ProgramError.unexpected("cannot create OS-specific subclass of TeleNativeLibrary", ex);
}
return null;
}
private TeleNativeLibrary(TeleVM vm, String path, Address base) {
super(vm);
this.path = path;
this.base = base;
this.codePointerManager = new NativeRemoteCodePointerManager(vm, this);
}
public RemoteCodePointerManager codePointerManager() {
return codePointerManager;
}
public void setSentinel(String sentinel, Address sentinelAddress) {
this.sentinel = sentinel;
this.sentinelAddress = sentinelAddress;
}
/**
* If the sentinel symbol has been resolved, gather the functions.
*/
public void gatherFunctions() throws Exception {
if (sentinel == null) {
return;
}
ArrayList<TeleNativeFunction> functionList = new ArrayList<TeleNativeFunction>();
long sentinelOffset = readSymbols(functionList);
assert sentinelOffset != 0;
base = base.plus(sentinelAddress.minus(sentinelOffset));
nativeLibraryMemoryRegion = new NativeLibraryMemoryRegion(vm(), this);
functions = new TeleNativeFunction[functionList.size()];
functionList.toArray(functions);
Arrays.sort(functions);
// relocate the native function address by base
for (int i = 0; i < functions.length; i++) {
functions[i].updateAddress();
if (i > 0) {
functions[i - 1].updateLength(functions[i].getCodeStart().minus(functions[i - 1].getCodeStart()).toInt());
}
}
functions[functions.length - 1].updateLength(base.plus(length).minus(functions[functions.length - 2].getCodeStart()).toInt());
for (int i = 0; i < functions.length; i++) {
nativeLibraryMemoryRegion.addChild(functions[i]);
}
//ok, now sort by name
sortByName = true;
Arrays.sort(functions);
}
boolean sortByName() {
return sortByName;
}
public String entityName() {
if (entityName == null) {
String name = new File(path).getName();
int index = name.lastIndexOf('.');
if (index > 0) {
name = name.substring(0, index);
}
entityName = "Native-" + name;
}
return entityName;
}
@Override
public String toString() {
return path;
}
public String entityDescription() {
return "Native library " + path();
}
public MaxEntityMemoryRegion<MaxNativeLibrary> memoryRegion() {
return nativeLibraryMemoryRegion;
}
public boolean contains(Address address) {
return nativeLibraryMemoryRegion.contains(address);
}
public TeleObject representation() {
return null;
}
public String path() {
return path;
}
public MaxNativeFunction[] functions() {
return functions;
}
public TeleNativeFunction findNativeFunction(Address address) {
for (TeleNativeFunction teleNativeFunction : functions) {
if (teleNativeFunction.contains(address)) {
return teleNativeFunction;
}
}
return null;
}
public Address base() {
return base;
}
public int length() {
return (int) length;
}
/**
* Process the library symbol table.
* if {@code functionList != null} populate with symbols else just check for sentinel.
* @param functionList
* @return the offset of the sentinel symbol or zero if not found.
* @throws Exception
*/
protected abstract long readSymbols(ArrayList<TeleNativeFunction> functionList) throws Exception;
static class Darwin extends TeleNativeLibrary {
private static final String IGNORE = "_mh_dylib_header";
Darwin(TeleVM vm, String path, Address base) {
super(vm, path, base);
}
@Override
protected long readSymbols(ArrayList<TeleNativeFunction> functionList) throws Exception {
long sentinelOffset = 0;
DarwinMachO machO = new DarwinMachO(path);
LoadCommand[] loadCommands = machO.getLoadCommands();
for (int i = 0; i < loadCommands.length; i++) {
if (loadCommands[i].cmd == LoadCommand.LC_SYMTAB) {
SymTabLoadCommand slc = (SymTabLoadCommand) loadCommands[i];
NList64[] symbolTable = slc.getSymbolTable();
for (NList64 nlist64 : symbolTable) {
if ((nlist64.type & NList64.STAB) != 0) {
continue;
} else if ((nlist64.type & NList64.TYPE) == NList64.SECT) {
Section64 s64 = DarwinMachO.getSection(loadCommands, nlist64.sect);
// There are many TEXT sections but we are only interested in the __text one.
if (s64.sectname.equals("__text")) {
String name = slc.getSymbolName(nlist64);
if (!name.equals(IGNORE)) {
// value is the offset from the start of the __TEXT segment, not the __text section
functionList.add(new TeleNativeFunction(vm(), name, Address.fromLong(nlist64.value), this));
if (sentinelOffset == 0 && sentinel != null && name.equals(sentinel)) {
sentinelOffset = nlist64.value;
// set the base/length from the Section
// N.B. base is also the offset from the start of the __TEXT segment
base = Address.fromLong(s64.addr);
length = s64.size;
}
}
}
}
}
}
}
// relocate the offsets to be from the start of the __text section
for (TeleNativeFunction f : functionList) {
f.base = f.base.minus(base);
}
return sentinelOffset;
}
}
static class Unix extends TeleNativeLibrary {
Unix(TeleVM vm, String path, Address base) {
super(vm, path, base);
}
@Override
protected long readSymbols(ArrayList<TeleNativeFunction> functionList) throws Exception {
long sentinelOffset = 0;
RandomAccessFile raf = new RandomAccessFile(path, "r");
ELFHeader header = ELFLoader.readELFHeader(raf);
ELFSectionHeaderTable elfSHT = ELFLoader.readSHT(raf, header);
ELFSymbolLookup elfSym = new ELFSymbolLookup(raf, header, elfSHT);
for (Map.Entry<String, List<ELFSymbolTable.Entry>> mapEntry : elfSym.symbolMap.entrySet()) {
List<ELFSymbolTable.Entry> entryList = mapEntry.getValue();
for (ELFSymbolTable.Entry entry : entryList) {
ELFSymbolTable.Entry64 entry64 = (ELFSymbolTable.Entry64) entry;
if (entry64.isFunction() && entry64.st_value != 0) {
ELFSectionHeaderTable.Entry64 section = getSection(elfSHT, entry64.st_shndx);
if (section.getName().equals(".text")) {
functionList.add(new TeleNativeFunction(vm(), entry64.getName(), Address.fromLong(entry64.st_value), this));
if (sentinel != null && entry64.getName().equals(sentinel)) {
sentinelOffset = entry64.st_value;
base = Address.fromLong(section.sh_addr);
length = section.sh_size;
}
}
}
}
}
raf.close();
// relocate the offsets to be from the start of the .text section
for (TeleNativeFunction f : functionList) {
f.base = f.base.minus(base);
}
return sentinelOffset;
}
private ELFSectionHeaderTable.Entry64 getSection(ELFSectionHeaderTable elfSHT, short shIndex) {
return (ELFSectionHeaderTable.Entry64) elfSHT.entries[shIndex];
}
}
static class Linux extends Unix {
Linux(TeleVM vm, String path, Address base) {
super(vm, path, base);
}
}
static class Solaris extends Unix {
Solaris(TeleVM vm, String path, Address base) {
super(vm, path, base);
}
}
}