/*******************************************************************************
* Copyright (c) 2006, 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.utils.debug.stabs;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.ISymbolReader;
import org.eclipse.core.runtime.IProgressMonitor;
public class StabsReader implements ISymbolReader {
byte[] stabData;
byte[] stabstrData;
boolean isLe;
boolean is64;
List<String> fileList;
String[] files = null;
boolean parsed = false;
String currentFile;
public StabsReader(byte[] data, byte[] stabstr, boolean littleEndian) {
this(data,stabstr,littleEndian,false);
}
/**
* @since 5.2
*/
public StabsReader(byte[] data, byte[] stabstr, boolean littleEndian, boolean is64bit) {
stabData = data;
stabstrData = stabstr;
isLe = littleEndian;
is64 = is64bit;
fileList = new ArrayList<String>();
}
public String[] getSourceFiles() {
if (!parsed) {
parse();
parsed = true;
files = new String[fileList.size()];
for (int i = 0; i < fileList.size(); i++) {
files[i] = fileList.get(i);
}
}
return files;
}
private String makeString(long offset) {
StringBuffer buf = new StringBuffer();
for (; offset < stabstrData.length; offset++) {
byte b = stabstrData[(int) offset];
if (b == 0) {
break;
}
buf.append((char) b);
}
return buf.toString();
}
private int read_4_bytes(byte[] bytes, int offset) {
if (isLe) {
return (((bytes[offset + 3] & 0xff) << 24)
| ((bytes[offset + 2] & 0xff) << 16)
| ((bytes[offset + 1] & 0xff) << 8)
| (bytes[offset] & 0xff));
}
return (((bytes[offset] & 0xff) << 24)
| ((bytes[offset + 1] & 0xff) << 16)
| ((bytes[offset + 2] & 0xff) << 8)
| (bytes[offset + 3] & 0xff));
}
private short read_2_bytes(byte[] bytes, int offset) {
if (isLe) {
return (short) (((bytes[offset + 1] & 0xff) << 8) | (bytes[offset] & 0xff));
}
return (short) (((bytes[offset] & 0xff) << 8) | (bytes[offset + 1] & 0xff));
}
private long read_8_bytes(byte[] bytes, int offset) throws IndexOutOfBoundsException {
if (isLe) {
return (((bytes[offset + 7] & 0xff) << 56)
| ((bytes[offset +6] & 0xff) << 48)
| ((bytes[offset +5] & 0xff) << 40)
| ((bytes[offset +4] & 0xff) << 32)
| ((bytes[offset +3] & 0xff) << 24)
| ((bytes[offset +2] & 0xff) << 16)
| ((bytes[offset +1] & 0xff) << 8)
| (bytes[offset +0] & 0xff));
}
return (((bytes[offset +0] & 0xff) << 56)
| ((bytes[offset +1] & 0xff) << 48)
| ((bytes[offset +2] & 0xff) << 40)
| ((bytes[offset +3] & 0xff) << 32)
| ((bytes[offset +4] & 0xff) << 24)
| ((bytes[offset +5] & 0xff) << 16)
| ((bytes[offset +6] & 0xff) << 8)
| (bytes[offset +7] & 0xff));
}
private String fixUpPath(String path) {
// some compilers generate extra back slashes
path = path.replaceAll("\\\\\\\\", "\\\\"); //$NON-NLS-1$//$NON-NLS-2$
// translate any cygwin drive paths, e.g. //G/System/main.cpp or /cygdrive/c/system/main.c
if (path.startsWith("/cygdrive/") && ('/' == path.charAt(11))) { //$NON-NLS-1$
char driveLetter = path.charAt(10);
driveLetter = (Character.isLowerCase(driveLetter)) ? Character.toUpperCase(driveLetter) : driveLetter;
StringBuffer buf = new StringBuffer(path);
buf.delete(0, 11);
buf.insert(0, driveLetter);
buf.insert(1, ':');
path = buf.toString();
}
// translate any cygwin drive paths, e.g. //G/System/main.cpp or /cygdrive/c/system/main.c
if (path.startsWith("//") && ('/' == path.charAt(3))) { //$NON-NLS-1$
char driveLetter = path.charAt(2);
driveLetter = (Character.isLowerCase(driveLetter)) ? Character.toUpperCase(driveLetter) : driveLetter;
StringBuffer buf = new StringBuffer(path);
buf.delete(0, 3);
buf.insert(0, driveLetter);
buf.insert(1, ':');
path = buf.toString();
}
return path;
}
private void parse() {
int size = is64 ? StabConstant.SIZE_64 : StabConstant.SIZE;
long nstab = stabData.length / size;
int i, offset;
String holder = null;
long stroff = 0;
int type = 0;
int other = 0;
short desc = 0;
long value = 0;
for (i = offset = 0; i < nstab; i++, offset += size) {
// get the type; 1 byte;
type = 0xff & stabData[offset + 4];
// ignoring anything other than N_SO and N_SOL because these are source or
// object file entries
if (type == StabConstant.N_SO || type == StabConstant.N_SOL) {
// get the other
other = 0xff & stabData[offset + 5];
// get the desc
desc = read_2_bytes(stabData, offset + 6);
// get the value
value = is64 ? read_8_bytes(stabData, offset + 8) : read_4_bytes(stabData, offset + 8);
// get the offset for the string; 4 bytes
stroff = read_4_bytes(stabData, offset);
String field;
if (stroff > 0) {
field = makeString(stroff);
} else {
field = new String();
}
// Check for continuation and if any go to the next stab
// until we find a string that is not terminated with a
// continuation line '\\'
// According to the spec all the other fields are duplicated so we
// still have the data.
// From the spec continuation line on AIX is '?'
if (field.endsWith("\\") || field.endsWith("?")) { //$NON-NLS-1$ //$NON-NLS-2$
field = field.substring(0, field.length() - 1);
if (holder == null) {
holder = field;
} else {
holder += field;
}
continue;
} else if (holder != null) {
field = holder + field;
holder = null;
}
parseStabEntry(field, type, other, desc, value);
}
}
}
void parseStabEntry(String field, int type, int other, short desc, long value) {
// Parse the string
switch (type) {
case StabConstant.N_SOL :
// include file
if (field != null && field.length() > 0) {
field = fixUpPath(field);
if (!fileList.contains(field))
fileList.add(field);
}
break;
case StabConstant.N_SO :
// source file
if (field != null && field.length() > 0) {
// if it ends with "/" then the next entry will be the rest of the string.
if (field.endsWith("/")) { //$NON-NLS-1$
currentFile = field;
} else {
if (currentFile != null) {
// if this entry is an absolute path then just throw away the existing currentFile
if (new File(field).isAbsolute()) {
currentFile = field;
} else {
currentFile += field;
}
} else {
currentFile = field;
}
currentFile = fixUpPath(currentFile);
if (!fileList.contains(currentFile))
fileList.add(currentFile);
currentFile = null;
}
}
break;
}
}
/**
* @since 5.2
*/
public String[] getSourceFiles(IProgressMonitor monitor) {
return getSourceFiles();
}
}