/*******************************************************************************
* Copyright (c) 2002, 2009 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Apple Computer - work on performance optimizations
*******************************************************************************/
package org.eclipse.cdt.utils.macho.parser;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.IAddressFactory;
import org.eclipse.cdt.core.IBinaryParser;
import org.eclipse.cdt.core.ISymbolReader;
import org.eclipse.cdt.core.IBinaryParser.IBinaryFile;
import org.eclipse.cdt.core.IBinaryParser.ISymbol;
import org.eclipse.cdt.utils.Addr32;
import org.eclipse.cdt.utils.Addr32Factory;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.cdt.utils.Addr64Factory;
import org.eclipse.cdt.utils.BinaryObjectAdapter;
import org.eclipse.cdt.utils.CPPFilt;
import org.eclipse.cdt.utils.Symbol;
import org.eclipse.cdt.utils.macho.AR;
import org.eclipse.cdt.utils.macho.MachO64;
import org.eclipse.cdt.utils.macho.MachOHelper64;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
/**
* MachOBinaryObject64
* @since 5.2
*/
public class MachOBinaryObject64 extends BinaryObjectAdapter {
protected AR.ARHeader header;
protected IAddressFactory addressFactory;
protected MachO64.Attribute attributes;
protected MachOHelper64.Sizes sizes;
protected ISymbol[] symbols;
protected String soname;
protected String[] needed;
protected long timeStamp;
protected boolean is64 = false;
private static final String[] NO_NEEDED = new String[0];
/**
* @param parser
* @param path
* @param header
*/
public MachOBinaryObject64(IBinaryParser parser, IPath path, AR.ARHeader header) {
super(parser, path, IBinaryFile.OBJECT);
this.header = header;
}
/**
* @param parser
* @param path
* @param type
*/
public MachOBinaryObject64(IBinaryParser parser, IPath path, int type) {
super(parser, path, type);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.utils.BinaryObjectAdapter#getBinaryObjectInfo()
*/
@Override
protected BinaryObjectInfo getBinaryObjectInfo() {
// we don't use this method
// overload to do nothing
return new BinaryObjectInfo();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.IBinaryParser.IBinaryFile#getContents()
*/
@Override
public InputStream getContents() throws IOException {
if (getPath() != null && header != null) {
return new ByteArrayInputStream(header.getObjectData());
}
return super.getContents();
}
protected MachOHelper64 getMachOHelper() throws IOException {
IPath path = getPath();
if (path != null) {
if (header != null) {
return new MachOHelper64(path.toOSString(), header.getObjectDataOffset());
} else {
return new MachOHelper64(path.toOSString());
}
}
return null;
}
/**
* @see org.eclipse.cdt.core.IBinaryParser.IBinaryObject#getName()
*/
@Override
public String getName() {
if (header != null) {
return header.getObjectName();
}
return super.getName();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.utils.BinaryObjectAdapter#getAddressFactory()
*/
@Override
public IAddressFactory getAddressFactory() {
if (addressFactory == null) {
if (soname == null)
loadBinaryInfo();
if (is64) {
addressFactory = new Addr64Factory();
}
else {
addressFactory = new Addr32Factory();
}
}
return addressFactory;
}
protected void clearCachedValues() {
attributes = null;
sizes = null;
symbols = null;
soname = null;
needed = null;
}
protected MachO64.Attribute internalGetAttributes() {
if (hasChanged()) {
clearCachedValues();
}
if (attributes == null) {
MachOHelper64 helper = null;
try {
helper = getMachOHelper();
if (helper != null) {
attributes = helper.getMachO().getAttributes();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (helper != null) {
helper.dispose();
}
}
}
return attributes;
}
protected MachOHelper64.Sizes internalGetSizes() {
if (hasChanged()) {
clearCachedValues();
}
if (sizes == null) {
MachOHelper64 helper = null;
try {
helper = getMachOHelper();
if (helper != null) {
sizes = helper.getSizes();
// since we're invoking the helper we might as well update
// the attributes since it's a pretty lightweight operation
if (attributes == null) {
attributes = helper.getMachO().getAttributes();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (helper != null) {
helper.dispose();
}
}
}
return sizes;
}
protected ISymbol[] internalGetSymbols() {
if (hasChanged()) {
clearCachedValues();
}
if (symbols == null) {
loadBinaryInfo();
}
return symbols;
}
protected String internalGetSoName() {
if (hasChanged()) {
clearCachedValues();
}
if (soname == null) {
loadBinaryInfo();
}
return soname;
}
protected String[] internalGetNeeded() {
if (hasChanged()) {
clearCachedValues();
}
if (needed == null) {
loadBinaryInfo();
}
return needed;
}
protected void loadBinaryInfo() {
MachOHelper64 helper = null;
try {
helper = getMachOHelper();
if (helper != null) {
//TODO we can probably optimize this further in MachOHelper
symbols = loadSymbols(helper);
//TODO is the sort necessary?
Arrays.sort(symbols);
soname = helper.getSoname();
needed = helper.getNeeded();
is64 = helper.is64();
// since we're invoking the helper we might as well update the
// sizes since it's a pretty lightweight operation by comparison
if (sizes == null) {
sizes = helper.getSizes();
}
// since we're invoking the helper we might as well update the
// attributes since it's a pretty lightweight operation by comparison
if (attributes == null) {
attributes = helper.getMachO().getAttributes();
}
}
} catch (IOException e) {
e.printStackTrace();
symbols = NO_SYMBOLS;
} finally {
if (helper != null) {
helper.dispose();
}
}
}
protected ISymbol[] loadSymbols(MachOHelper64 helper) throws IOException {
CPPFilt cppfilt = null;
try {
ArrayList<Symbol> list = new ArrayList<Symbol>();
// Hack should be remove when Elf is clean
helper.getMachO().setCppFilter(false);
cppfilt = getCPPFilt();
//TODO we can probably optimize this further in MachOHelper64
addSymbols(helper.getExternalFunctions(), ISymbol.FUNCTION, cppfilt, list);
addSymbols(helper.getLocalFunctions(), ISymbol.FUNCTION, cppfilt, list);
addSymbols(helper.getExternalObjects(), ISymbol.VARIABLE, cppfilt, list);
addSymbols(helper.getLocalObjects(), ISymbol.VARIABLE, cppfilt, list);
return list.toArray(new ISymbol[list.size()]);
} finally {
if (cppfilt != null) {
cppfilt.dispose();
}
}
}
protected CPPFilt getCPPFilt() {
MachOParser64 parser = (MachOParser64) getBinaryParser();
return parser.getCPPFilt();
}
private void addSymbols(MachO64.Symbol[] array, int type, CPPFilt cppfilt, List<Symbol> list) {
for (MachO64.Symbol element : array) {
String name = element.toString();
if (cppfilt != null) {
try {
name = cppfilt.getFunction(name);
} catch (IOException e1) {
cppfilt = null;
}
}
long addr = element.n_value;
int size = 0;
String filename = element.getFilename();
IPath filePath = (filename != null) ? new Path(filename) : null;
IAddress symbolAddr;
if (element.is64)
symbolAddr = new Addr32(element.n_value);
else
symbolAddr = new Addr64(BigInteger.valueOf(element.n_value));
list.add(new Symbol(this, name, type, symbolAddr, size, filePath, element.getLineNumber(addr), element.getLineNumber(addr + size - 1)));
}
}
@Override
public String getCPU() {
MachO64.Attribute attribute = internalGetAttributes();
if (attribute != null) {
return attribute.getCPU();
}
return ""; //$NON-NLS-1$
}
@Override
public boolean hasDebug() {
MachO64.Attribute attribute = internalGetAttributes();
if (attribute != null) {
return attribute.hasDebug();
}
return false;
}
@Override
public boolean isLittleEndian() {
MachO64.Attribute attribute = internalGetAttributes();
if (attribute != null) {
return attribute.isLittleEndian();
}
return false;
}
@Override
public long getBSS() {
MachOHelper64.Sizes size = internalGetSizes();
if (size != null) {
return size.bss;
}
return 0;
}
@Override
public long getData() {
MachOHelper64.Sizes size = internalGetSizes();
if (size != null) {
return size.data;
}
return 0;
}
@Override
public long getText() {
MachOHelper64.Sizes size = internalGetSizes();
if (size != null) {
return size.text;
}
return 0;
}
@Override
public ISymbol[] getSymbols() {
ISymbol[] syms = internalGetSymbols();
if (syms != null) {
return syms;
}
return NO_SYMBOLS;
}
@Override
public ISymbol getSymbol(IAddress addr) {
//TODO should this be cached?
// fall back to super implementation for now
return super.getSymbol(addr);
}
@Override
public String[] getNeededSharedLibs() {
String[] libs = internalGetNeeded();
if (libs != null) {
return libs;
}
return NO_NEEDED;
}
@Override
public String getSoName() {
String name = internalGetSoName();
if (name != null) {
return name;
}
return ""; //$NON-NLS-1$
}
@Override
protected boolean hasChanged() {
IPath path = getPath();
if (path != null) {
File file = path.toFile();
if (file != null) {
long modification = file.lastModified();
if (modification != timeStamp) {
timeStamp = modification;
return true;
}
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
if (adapter.equals(MachO64.class)) {
try {
return new MachO64(getPath().toOSString());
} catch (IOException e) {
}
}
if (adapter.equals(ISymbolReader.class)) {
MachO64 macho = (MachO64)getAdapter(MachO64.class);
if (macho != null) {
return macho.getSymbolReader();
}
}
return super.getAdapter(adapter);
}
}