/******************************************************************************* * Copyright (c) 2000, 2016 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 * Markus Schorn (Wind River Systems) * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePreferenceConstants; import org.eclipse.cdt.core.IBinaryParser.IBinaryExecutable; import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; import org.eclipse.cdt.core.IBinaryParser.IBinaryShared; import org.eclipse.cdt.core.IBinaryParser.ISymbol; import org.eclipse.cdt.core.ISourceFinder; import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.core.model.BinaryFilePresentation; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.IBinary; import org.eclipse.cdt.core.model.IBuffer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.core.util.MementoTokenizer; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; public class Binary extends Openable implements IBinary { private int fBinType; private String hasDebug; private String cpu; private String[] needed; private long longData; private long longText; private long longBSS; private String endian; private String soname; private long fLastModification; private IBinaryObject binaryObject; private boolean showInBinaryContainer; public Binary(ICElement parent, IFile file, IBinaryObject bin) { super(parent, file, ICElement.C_BINARY); binaryObject = bin; showInBinaryContainer= determineShowInBinaryContainer(bin); } private boolean determineShowInBinaryContainer(IBinaryObject bin) { BinaryFilePresentation presentation= bin.getAdapter(BinaryFilePresentation.class); if (presentation != null) { return presentation.showInBinaryContainer(); } return BinaryFilePresentation.showInBinaryContainer(bin); } public Binary(ICElement parent, IPath path, IBinaryObject bin) { super (parent, path, ICElement.C_BINARY); binaryObject = bin; showInBinaryContainer= determineShowInBinaryContainer(bin); } @Override public boolean isSharedLib() { return getType() == IBinaryFile.SHARED; } @Override public boolean isExecutable() { return getType() == IBinaryFile.EXECUTABLE; } @Override public boolean isObject() { return getType() == IBinaryFile.OBJECT; } @Override public boolean isCore() { return getType() == IBinaryFile.CORE; } @Override public boolean hasDebug() { if (isObject() || isExecutable() || isSharedLib()) { if (hasDebug == null || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj != null) { hasDebug = String.valueOf(obj.hasDebug()); } } } return Boolean.valueOf(hasDebug).booleanValue(); } @Override public String getCPU() { if (isObject() || isExecutable() || isSharedLib() || isCore()) { if (cpu == null || hasChanged()) { IBinaryObject obj = getBinaryObject(); cpu = obj.getCPU(); } } return (cpu == null ? "" : cpu); //$NON-NLS-1$ } @Override public String[] getNeededSharedLibs() { if (isExecutable() || isSharedLib()) { if (needed == null || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj instanceof IBinaryExecutable) { needed = ((IBinaryExecutable)obj).getNeededSharedLibs(); } } } return (needed == null ? new String[0] : needed); } @Override public long getText() { if (isObject() || isExecutable() || isSharedLib()) { if (longText == -1 || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj != null) { longText = obj.getText(); } } } return longText; } @Override public long getData() { if (isObject() || isExecutable() || isSharedLib()) { if (longData == -1 || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj != null) { longData = obj.getData(); } } } return longData; } @Override public long getBSS() { if (isObject() || isExecutable() || isSharedLib()) { if (longBSS == -1 || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj != null) { longBSS = obj.getBSS(); } } } return longBSS; } @Override public String getSoname() { if (isSharedLib()) { if (soname == null || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj instanceof IBinaryShared) { soname = ((IBinaryShared)obj).getSoName(); } } } return (soname == null ? "" : soname); //$NON-NLS-1$ } @Override public boolean isLittleEndian() { if (isObject() || isExecutable() || isSharedLib() || isCore()) { if (endian == null || hasChanged()) { IBinaryObject obj = getBinaryObject(); if (obj != null) { endian = String.valueOf(obj.isLittleEndian()); } } } return Boolean.valueOf(endian).booleanValue(); } protected IBinaryObject getBinaryObject() { return binaryObject; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @SuppressWarnings("rawtypes") @Override public Object getAdapter(Class adapter) { if (IBinaryObject.class.equals(adapter)) { return getBinaryObject(); } return super.getAdapter(adapter); } protected int getType() { IBinaryObject obj = getBinaryObject(); if (obj != null && (fBinType == 0 || hasChanged())) { fBinType = obj.getType(); } return fBinType; } @Override protected boolean hasChanged() { long modification = getModificationStamp(); boolean changed = modification != fLastModification; fLastModification = modification; if (changed) { hasDebug = null; needed = null; cpu = null; endian = null; longBSS = -1; longData = -1; longText = -1; soname = null; } return changed; } protected long getModificationStamp() { IResource res = getResource(); if (res != null) { return res.getModificationStamp(); } return 0; } /* (non-Javadoc) * @see org.eclipse.cdt.core.model.ICElement#isReadOnly() */ @Override public boolean isReadOnly() { return true; } @Override public CElementInfo createElementInfo() { return new BinaryInfo(this); } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.model.Openable#buildStructure(org.eclipse.cdt.internal.core.model.OpenableInfo, org.eclipse.core.runtime.IProgressMonitor, java.util.Map, org.eclipse.core.resources.IResource) */ @Override protected boolean buildStructure(OpenableInfo info, IProgressMonitor pm, Map<ICElement, CElementInfo> newElements, IResource underlyingResource) throws CModelException { return computeChildren(info, underlyingResource); } boolean computeChildren(OpenableInfo info, IResource res) throws CModelException { boolean ok = false; if (isObject() || isExecutable() || isSharedLib()) { Map<IPath, BinaryModule> hash = new HashMap<IPath, BinaryModule>(); IBinaryObject obj = getBinaryObject(); if (obj != null) { // First check if we can get the list of source // files used to build the binary from the symbol // information. If not, fall back on information from the binary parser. boolean showSourceFiles = Platform.getPreferencesService().getBoolean(CCorePlugin.PLUGIN_ID, CCorePreferenceConstants.SHOW_SOURCE_FILES_IN_BINARIES, false, null); if (!showSourceFiles || !addSourceFiles(info, obj, hash)) { ISymbol[] symbols = obj.getSymbols(); for (ISymbol symbol : symbols) { switch (symbol.getType()) { case ISymbol.FUNCTION: addFunction(info, symbol, hash); break; case ISymbol.VARIABLE: addVariable(info, symbol, hash); break; } } } ok = true; } } return ok; } private boolean addSourceFiles(OpenableInfo info, IBinaryObject obj, Map<IPath, BinaryModule> hash) throws CModelException { // Try to get the list of source files used to build the binary from the // symbol information. ISymbolReader symbolreader = obj.getAdapter(ISymbolReader.class); if (symbolreader == null) return false; String[] sourceFiles = symbolreader.getSourceFiles(); if (sourceFiles != null && sourceFiles.length > 0) { ISourceFinder srcFinder = (ISourceFinder) getAdapter(ISourceFinder.class); try { for (String filename : sourceFiles) { // Find the file locally if (srcFinder != null) { String localPath = srcFinder.toLocalPath(filename); if (localPath != null) { filename = localPath; } } // Be careful how you use this File object. If filename is a relative path, the resulting File // object will apply the relative path to the working directory, which is not what we want. // Stay away from methods that return or use the absolute path of the object. Note that // File.isAbsolute() returns false when the object was constructed with a relative path. File file = new File(filename); // Create a translation unit for this file and add it as a child of the binary String id = CoreModel.getRegistedContentTypeId(getCProject().getProject(), file.getName()); if (id == null) { // Don't add files we can't get an ID for. continue; } // See if this source file is already in the project. // We check this to determine if we should create a TranslationUnit or ExternalTranslationUnit IFile wkspFile = null; if (file.isAbsolute()) { IFile[] filesInWP = ResourceLookup.findFilesForLocation(new Path(filename)); for (IFile element : filesInWP) { if (element.isAccessible()) { wkspFile = element; break; } } } TranslationUnit tu; if (wkspFile != null) tu = new TranslationUnit(this, wkspFile, id); else { // If we have an absolute path (for the host file system), then use an IPath to create the // ExternalTranslationUnit, as that is the more accurate way to specify the file. If it's // not, then use the path specification we got from the debug information. We want to // avoid, e.g., converting a UNIX path to a Windows one when debugging a UNIX-built binary // on Windows. The opportunity to remap source paths was taken above, when we called // ISourceFinder. If a mapping didn't occur, we want to preserve whatever the debug // information told us. See bugzilla 297781 if (file.isAbsolute()) { tu = new ExternalTranslationUnit(this, Path.fromOSString(filename), id); } else { tu = new ExternalTranslationUnit(this, URIUtil.toURI(filename, true), id); } } if (! info.includesChild(tu)) info.addChild(tu); } return true; } finally { if (srcFinder != null) { srcFinder.dispose(); } } } return false; } private void addFunction(OpenableInfo info, ISymbol symbol, Map<IPath, BinaryModule> hash) throws CModelException { IPath filename= symbol.getFilename(); BinaryFunction function = null; if (filename != null && !filename.isEmpty()) { BinaryModule module = null; if (hash.containsKey(filename)) { module = hash.get(filename); } else { // A special container we do not want the file to be parse. module = new BinaryModule(this, filename); hash.put(filename, module); info.addChild(module); } function = new BinaryFunction(module, symbol.getName(), symbol.getAddress()); function.setLines(symbol.getStartLine(), symbol.getEndLine()); module.addChild(function); } else { //function = new Function(parent, symbol.getName()); function = new BinaryFunction(this, symbol.getName(), symbol.getAddress()); function.setLines(symbol.getStartLine(), symbol.getEndLine()); info.addChild(function); } // if (function != null) { // if (!external) { // function.getFunctionInfo().setAccessControl(IConstants.AccStatic); // } // } } private void addVariable(OpenableInfo info, ISymbol symbol, Map<IPath, BinaryModule> hash) throws CModelException { IPath filename= symbol.getFilename(); BinaryVariable variable = null; if (filename != null && !filename.isEmpty()) { BinaryModule module = null; if (hash.containsKey(filename)) { module = hash.get(filename); } else { module = new BinaryModule(this, filename); hash.put(filename, module); info.addChild(module); } variable = new BinaryVariable(module, symbol.getName(), symbol.getAddress()); variable.setLines(symbol.getStartLine(), symbol.getEndLine()); module.addChild(variable); } else { variable = new BinaryVariable(this, symbol.getName(), symbol.getAddress()); variable.setLines(symbol.getStartLine(), symbol.getEndLine()); info.addChild(variable); } //if (variable != null) { // if (!external) { // variable.getVariableInfo().setAccessControl(IConstants.AccStatic); // } //} } /** * @see org.eclipse.cdt.core.model.IOpenable#getBuffer() * * overridden from default as we do not need to create our children to provider a buffer since the buffer just contains * IBinaryOject contents which is not model specific. */ @Override public IBuffer getBuffer() throws CModelException { if (hasBuffer()) { IBuffer buffer = getBufferManager().getBuffer(this); if (buffer == null) { // try to (re)open a buffer buffer = openBuffer(null); } return buffer; } return null; } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.model.Openable#openBuffer(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IBuffer openBuffer(IProgressMonitor pm) throws CModelException { // create buffer - translation units only use default buffer factory BufferManager bufManager = getBufferManager(); IBuffer buffer = getBufferFactory().createBuffer(this); if (buffer == null) return null; // set the buffer source if (buffer.getCharacters() == null){ IBinaryObject bin = getBinaryObject(); if (bin != null) { StringBuilder sb = new StringBuilder(); try { BufferedReader stream = new BufferedReader(new InputStreamReader(bin.getContents())); char[] buf = new char[512]; int len; while ((len = stream.read(buf, 0, buf.length)) != -1) { sb.append(buf, 0, len); } } catch (IOException e) { // nothint. } buffer.setContents(sb.toString()); } else { IResource file = this.getResource(); if (file != null && file.getType() == IResource.FILE) { buffer.setContents(Util.getResourceContentsAsCharArray((IFile)file)); } } } // add buffer to buffer cache bufManager.addBuffer(buffer); // listen to buffer changes buffer.addBufferChangedListener(this); return buffer; } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.model.Openable#hasBuffer() */ @Override protected boolean hasBuffer() { return true; } /* (non-Javadoc) * @see org.eclipse.cdt.core.model.ICElement#exists() */ @Override public boolean exists() { IResource res = getResource(); if (res != null) return res.exists(); return super.exists(); } /* (non-Javadoc) * @see org.eclipse.cdt.internal.core.model.CElement#closing(java.lang.Object) */ @Override protected void closing(Object info) throws CModelException { ICProject cproject = getCProject(); CProjectInfo pinfo = (CProjectInfo)CModelManager.getDefault().peekAtInfo(cproject); if (pinfo != null && pinfo.vBin != null) { pinfo.vBin.removeChild(this); } super.closing(info); } @Override public boolean showInBinaryContainer() { return showInBinaryContainer; } @Override public ICElement getHandleFromMemento(String token, MementoTokenizer memento) { return null; } @Override public String getHandleMemento() { return null; } @Override protected char getHandleMementoDelimiter() { Assert.isTrue(false, "Should not be called"); //$NON-NLS-1$ return 0; } }