blob: 55a2a8b8bd87a6ceb0fe9f4a8b5a93495a60417c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Nokia 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:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.symbols.files;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
import org.eclipse.cdt.debug.edc.internal.symbols.Section;
import org.eclipse.cdt.debug.edc.internal.symbols.Symbol;
import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
import org.eclipse.cdt.debug.edc.symbols.ISymbol;
import org.eclipse.cdt.utils.Addr32;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.cdt.utils.coff.Coff.SectionHeader;
import org.eclipse.cdt.utils.coff.PE;
import org.eclipse.cdt.utils.coff.PE.NTOptionalHeader;
import org.eclipse.core.runtime.IPath;
/**
* This class handles PE-COFF files for the purpose of supporting symbolics.
*/
public class PEFileExecutableSymbolicsReader extends BaseExecutableSymbolicsReader {
static public final String CODEVIEW_SECTION_NAME = "CodeView_Data";
/** .CRT is another initialized data section utilized by the Microsoft C/C++ run-time libraries
* @since 5.2
*/
public final static String _CRT = ".CRT"; //$NON-NLS-1$
protected boolean isLE;
protected Map<Integer, ISection> sectionsByPEID = new HashMap<Integer, ISection>();
public PEFileExecutableSymbolicsReader(IPath binaryFile, PE peFile) throws IOException {
super(binaryFile);
isLE = true;
exeBaseAddress = new Addr32(peFile.getNTOptionalHeader().ImageBase);
modificationDate = binaryFile.toFile().lastModified();
sectionMapper = new SectionMapper(binaryFile, isLE);
recordSections(peFile);
// TODO: better selection.
boolean isWin32 = false, isEABI = false;
for (ISymbol symbol : symbols) {
String symname = symbol.getName();
if (symname.startsWith("__Z") && symname.endsWith("v")) {
isWin32 = true;
isEABI = true;
break;
} else if (symname.startsWith("_Z") && symname.endsWith("v")) {
isEABI = true;
break;
} else if (symname.contains("@") && symname.contains("?")) {
isWin32 = true;
break;
}
}
if (isWin32 && isEABI)
unmangler = new UnmanglerWin32EABI();
else if (isEABI)
unmangler = new UnmanglerEABI();
else
unmangler = new UnmanglerWin32();
}
/**
* Determine the executable format and record the sections.
* @param peFile
*
* @throws IOException if file reading fails
*/
private void recordSections(PE peFile) throws IOException {
// start from zero so that we can use it as index to the array list
// for quick access.
int id = 0;
Map<String, Object> props;
SectionHeader[] secHeaders = peFile.getSectionHeaders();
long imageBase = peFile.getNTOptionalHeader().ImageBase & 0xffffffffL;
SectionHeader rDataHeader = null;
int peSectionID = 0;
for (SectionHeader s : secHeaders) {
peSectionID++;
String name = new String(s.s_name).trim();
if (name.startsWith("/")) //$NON-NLS-1$
{
int stringTableOffset = Integer.parseInt(name.substring(1));
name = peFile.getStringTableEntry(stringTableOffset);
}
// Remember how to map this section
if (executableSections.containsKey(name))
throw new IllegalStateException("duplicate section " + name);
IExecutableSection exeSection = new ExecutableSection(sectionMapper, name,
new SectionInfo(s.s_scnptr, s.s_paddr));
executableSections.put(name, exeSection);
String sectionName = name;
// Convert the name to our unified name.
if (sectionName.equals(SectionHeader._TEXT))
name = ISection.NAME_TEXT;
else if (sectionName.equals(SectionHeader._DATA) || sectionName.equals(_CRT))
name = ISection.NAME_DATA;
else if (sectionName.equals(".rdata")) // add this name in SectionHeader ?
{
name = ISection.NAME_RODATA;
rDataHeader = s;
}
else if (sectionName.equals(SectionHeader._BSS))
name = ISection.NAME_BSS;
else { // ignore other section.
continue;
}
// Well, PE is a _modified_ version of COFF, where
// section.s_paddr of COFF becomes "VirtualSize"
// (memory size of the section) in PE, while s_size
// is raw data size (file size of the section).
long size = s.s_paddr; // not s_size !
props = new HashMap<String, Object>();
props.put(ISection.PROPERTY_NAME, name);
// Note the s_vaddr is relative to image base.
// For Section we need absolute address.
Section newSection = new Section(id++, size, new Addr64(Long.toString(imageBase + s.s_vaddr)), props);
sections.add(newSection);
sectionsByPEID.put(peSectionID, newSection);
}
// load the symbol table
//
/*
* Note this "rawSymbols" array contains both standard and auxiliary
* symbol records. It's assumed symbols in the array are in the same
* order they appear in the symbol table section, no sorting of any kind
* is done.
*
* Actually auxiliary symbols should not be treated the same as standard
* symbols by Coff and PE in CDT core. But fixing that would break API,
* which is not allowed for CDT 7.0 at this time......... 04/07/10
*/
org.eclipse.cdt.utils.coff.Coff.Symbol[] rawSymbols = peFile.getSymbols();
for (int i=0; i < rawSymbols.length; i++) {
org.eclipse.cdt.utils.coff.Coff.Symbol symbol = rawSymbols[i];
if (!(symbol.n_type == 0)) // Change to Coff.isNoSymbol for CDT 8.0.
{
String symName = symbol.getName(peFile.getStringTable());
symbols.add(new Symbol(symName, new Addr32(symbol.n_value), 1));
}
// skip auxiliary symbol record(s) if any as otherwise they may
// give us bogus match in any symbol table lookup.
if (symbol.n_numaux > 0) {
i += symbol.n_numaux;
}
}
if (rDataHeader != null)
checkForCodeView(peFile, rDataHeader, imageBase, id);
// now sort it by address for faster lookups
Collections.sort(symbols);
}
private void checkForCodeView(PE peFile, SectionHeader rDataHeader, long imageBase, int id) throws IOException { //$NON-NLS-1$
// figure out the file offset of the debug directory
// entries
final int IMAGE_DIRECTORY_ENTRY_DEBUG = 6;
final int DEBUGDIRSZ = 28;
NTOptionalHeader ntHeader = peFile.getNTOptionalHeader();
if (ntHeader == null
|| ntHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_DEBUG)
return;
int debugDir = ntHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
if (debugDir == 0)
return;
int debugFormats = ntHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / 28;
if (debugFormats == 0)
return;
int offsetInto_rdata = debugDir
- rDataHeader.s_vaddr;
int fileOffset = rDataHeader.s_scnptr
+ offsetInto_rdata;
RandomAccessFile accessFile = new RandomAccessFile(
binaryFile.toOSString(), "r");
// loop through the debug directories looking for
// CodeView (type 2)
for (int j = 0; j < debugFormats; j++) {
PE.IMAGE_DEBUG_DIRECTORY dir = new PE.IMAGE_DEBUG_DIRECTORY(
accessFile, fileOffset);
if ((dir.Type == 2) && (dir.SizeOfData > 0)) {
// CodeView found, seek to actual data
int debugBase = dir.PointerToRawData;
accessFile.seek(debugBase);
// sanity check. the first four bytes of the
// CodeView
// data should be "NB11"
String s2 = accessFile.readLine();
if (s2.startsWith("NB11")) { //$NON-NLS-1$
// Attribute att = peFile.getAttribute();
long start = debugBase;
long size = accessFile.length() - start;
String name = CODEVIEW_SECTION_NAME;
IExecutableSection exeSection = new ExecutableSection(sectionMapper, name,
new SectionInfo(start, size));
executableSections.put(name, exeSection);
}
}
fileOffset += DEBUGDIRSZ;
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReader#getByteOrder()
*/
public ByteOrder getByteOrder() {
return isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSymbolicsReader#getSymbolAtAddress()
*/
@Override
public ISymbol getSymbolAtAddress(IAddress linkAddress) {
int insertion = Collections.binarySearch(symbols, linkAddress);
if (insertion >= 0) {
return symbols.get(insertion++);
}
if (insertion == -1) {
return null;
}
insertion = -insertion - 1;
if (insertion == symbols.size()) {
return null;
}
return symbols.get(insertion - 1);
}
public ISection getSectionByPEID(int peID)
{
return sectionsByPEID.get(peID);
}
}