0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
407N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * See LICENSE.txt included in this distribution for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at LICENSE.txt.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A
0N/A/*
1380N/A * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A */
0N/Apackage org.opensolaris.opengrok.analysis.executables;
0N/A
392N/Aimport java.io.FileInputStream;
392N/Aimport java.io.IOException;
392N/Aimport java.io.InputStream;
392N/Aimport java.io.Reader;
392N/Aimport java.io.StringReader;
392N/Aimport java.nio.ByteOrder;
392N/Aimport java.nio.MappedByteBuffer;
392N/Aimport java.nio.channels.FileChannel;
1231N/Aimport java.util.ArrayList;
392N/Aimport java.util.HashMap;
1231N/Aimport java.util.List;
459N/Aimport java.util.logging.Level;
1417N/Aimport java.util.logging.Logger;
1384N/A
392N/Aimport org.apache.lucene.analysis.TokenStream;
392N/Aimport org.apache.lucene.document.Document;
392N/Aimport org.apache.lucene.document.Field;
392N/Aimport org.opensolaris.opengrok.analysis.FileAnalyzer;
392N/Aimport org.opensolaris.opengrok.analysis.FileAnalyzerFactory;
1384N/Aimport org.opensolaris.opengrok.analysis.XrefWriter;
392N/Aimport org.opensolaris.opengrok.analysis.plain.PlainFullTokenizer;
1195N/Aimport org.opensolaris.opengrok.util.IOUtils;
804N/Aimport org.opensolaris.opengrok.web.Util;
0N/A
0N/A/**
0N/A * Analyzes ELF (Executable and Linking Format) files.
0N/A * Created on September 23, 2005
0N/A *
0N/A * @author Chandan
804N/A * @author Trond Norbye
0N/A */
0N/Apublic class ELFAnalyzer extends FileAnalyzer {
1417N/A private static final Logger logger = Logger.getLogger(ELFAnalyzer.class.getName());
1185N/A private StringBuilder content;
1231N/A private PlainFullTokenizer plainfull;
1231N/A
1232N/A private static final List<String> READABLE_SECTIONS;
1231N/A static {
1232N/A READABLE_SECTIONS = new ArrayList<String>();
1232N/A READABLE_SECTIONS.add(".debug_str");
1232N/A READABLE_SECTIONS.add(".comment");
1232N/A READABLE_SECTIONS.add(".data");
1232N/A READABLE_SECTIONS.add(".data1");
1232N/A READABLE_SECTIONS.add(".rodata");
1232N/A READABLE_SECTIONS.add(".rodata1");
1231N/A }
804N/A
554N/A /**
554N/A * Creates a new instance of ELFAnalyzer
1469N/A * @param factory the factory to use to obtain default settings
554N/A */
202N/A protected ELFAnalyzer(FileAnalyzerFactory factory) {
804N/A super(factory);
1185N/A content = new StringBuilder();
1234N/A plainfull = new PlainFullTokenizer(new StringReader(""));
0N/A }
804N/A
1469N/A /**
1469N/A * {@inheritDoc}
1469N/A */
554N/A @Override
889N/A public void analyze(Document doc, InputStream in) throws IOException {
889N/A if (in instanceof FileInputStream) {
889N/A parseELF((FileInputStream) in);
1185N/A if (content.length() > 0) {
889N/A doc.add(new Field("full", " ", Field.Store.YES, Field.Index.ANALYZED));
889N/A }
889N/A } else {
889N/A String fullpath = doc.get("fullpath");
1469N/A @SuppressWarnings("resource")
889N/A final FileInputStream fin = new FileInputStream(fullpath);
889N/A try {
889N/A parseELF(fin);
1185N/A if (content.length() > 0) {
816N/A doc.add(new Field("full", " ", Field.Store.YES, Field.Index.ANALYZED));
459N/A }
889N/A } finally {
1195N/A IOUtils.close(fin);
459N/A }
459N/A }
0N/A }
0N/A
1469N/A /**
1469N/A * Parse the given ELF input stream.
1469N/A * @param f stream to parse
1469N/A * @throws IOException
1469N/A */
1469N/A @SuppressWarnings({ "resource", "boxing" })
459N/A public void parseELF(FileInputStream f) throws IOException {
804N/A FileChannel fch = f.getChannel();
1469N/A try {
1469N/A MappedByteBuffer fmap =
1469N/A fch.map(FileChannel.MapMode.READ_ONLY, 0, fch.size());
1469N/A ELFHeader eh = new ELFHeader(fmap);
804N/A
1469N/A if (eh.e_shnum <= 0) {
1469N/A logger.log(Level.FINE, "Skipping file, no section headers");
1469N/A return;
1469N/A }
804N/A
1469N/A fmap.position(eh.e_shoff + (eh.e_shstrndx * eh.e_shentsize));
1469N/A ELFSection stringSection = new ELFSection(fmap);
804N/A
1469N/A if (stringSection.sh_size == 0) {
1469N/A logger.log(Level.FINE, "Skipping file, no section name string table");
1469N/A return ;
804N/A }
804N/A
1469N/A HashMap<String, Integer> sectionMap = new HashMap<String, Integer>();
1469N/A ELFSection[] sections = new ELFSection[eh.e_shnum];
1469N/A int[] readables = new int[eh.e_shnum];
1469N/A
1469N/A int ri = 0;
1469N/A for (int i = 0; i < eh.e_shnum; i++) {
1469N/A fmap.position(eh.e_shoff + (i * eh.e_shentsize));
804N/A
1469N/A sections[i] = new ELFSection(fmap);
1469N/A String sectionName =
1469N/A getName(stringSection.sh_offset, sections[i].sh_name, fmap);
1469N/A if (sectionName != null) {
1469N/A sectionMap.put(sectionName, sections[i].sh_offset);
1469N/A }
1469N/A
1469N/A if (sections[i].sh_type == ELFSection.SHT_STRTAB) {
1469N/A readables[ri++] = i;
1469N/A } else if (READABLE_SECTIONS.contains(sectionName)) {
1469N/A readables[ri++] = i;
804N/A }
804N/A }
1469N/A
1469N/A boolean lastPrintable = false;
1469N/A StringBuilder sb = new StringBuilder();
1469N/A for (int i = 0; i < ri; i++) {
1469N/A fmap.position(sections[readables[i]].sh_offset);
1469N/A int size = sections[readables[i]].sh_size;
1469N/A byte c;
1469N/A while (size-- > 0) {
1469N/A c = fmap.get();
1469N/A if (isReadable(c)) {
1469N/A lastPrintable = true;
1469N/A sb.append((char) c);
1469N/A } else if (lastPrintable) {
1469N/A lastPrintable = false;
1469N/A sb.append(' ');
1469N/A }
1469N/A }
1469N/A sb.append('\n');
1469N/A }
1469N/A sb.trimToSize();
1469N/A content = sb;
1469N/A } finally {
1469N/A IOUtils.close(fch);
804N/A }
0N/A }
1190N/A
1469N/A private static boolean isReadable(int c) {
1469N/A return c > ' ' && c <= 127;
0N/A }
804N/A
1469N/A private static String getName(int tab, int stroff, MappedByteBuffer fmap) {
804N/A if (tab == 0) {
392N/A return null;
392N/A }
804N/A StringBuilder sb = new StringBuilder(20);
804N/A byte c;
804N/A int start = tab + stroff;
804N/A while ((c = fmap.get(start++)) != 0x00) {
804N/A sb.append((char) c);
804N/A }
804N/A return sb.toString();
0N/A }
804N/A
1469N/A /**
1469N/A * {@inheritDoc}
1469N/A */
554N/A @Override
1380N/A public TokenStream overridableTokenStream(String fieldName, Reader reader) {
804N/A if ("full".equals(fieldName)) {
1185N/A char[] cs = new char[content.length()];
1185N/A content.getChars(0, cs.length, cs, 0);
1185N/A plainfull.reInit(cs, cs.length);
804N/A return plainfull;
804N/A }
1380N/A return super.overridableTokenStream(fieldName, reader);
0N/A }
804N/A
0N/A /**
0N/A * Write a cross referenced HTML file.
0N/A * @param out Writer to write
0N/A */
554N/A @Override
1384N/A public void writeXref(XrefWriter out) throws IOException {
804N/A out.write("</pre>");
1469N/A out.write(Util.htmlize(content.toString(), "<br/>"));
804N/A out.write("<pre>");
0N/A }
804N/A
811N/A private static class ELFHeader {
804N/A // Elf32 Addr = readInt
804N/A // elf32 half = readUnsignedShort
804N/A // Off = int
804N/A // Sword = int
804N/A // Word = int
804N/A // un = unsignedBtye
804N/A
804N/A public EI_Class ei_class;
804N/A public EI_Data ei_data;
1185N/A @SuppressWarnings("unused")
804N/A public int ei_version;
804N/A public E_Type e_type;
804N/A public E_Machine e_machine;
804N/A public E_Version e_version;
804N/A public int e_entry;
804N/A public int e_phoff;
804N/A public int e_shoff;
804N/A public int e_flags;
804N/A public int e_ehsize;
804N/A public int e_phentsize;
804N/A public int e_phnum;
804N/A public int e_shentsize;
804N/A public int e_shnum;
804N/A public int e_shstrndx;
804N/A
804N/A public ELFHeader(MappedByteBuffer fmap) throws IllegalArgumentException {
804N/A if (fmap.get(ELFIdentification.EI_MAG0.value()) != 0x7f ||
804N/A fmap.get(ELFIdentification.EI_MAG1.value()) != 'E' ||
804N/A fmap.get(ELFIdentification.EI_MAG2.value()) != 'L' ||
804N/A fmap.get(ELFIdentification.EI_MAG3.value()) != 'F') {
804N/A throw new IllegalArgumentException("Not an ELF file");
804N/A }
804N/A
804N/A ei_class = EI_Class.valueOf(fmap.get(ELFIdentification.EI_CLASS.value()));
804N/A ei_data = EI_Data.valueOf(fmap.get(ELFIdentification.EI_DATA.value()));
804N/A ei_version = fmap.get(ELFIdentification.EI_VERSION.value());
804N/A
804N/A if (ei_data == EI_Data.ELFDATA2LSB) {
804N/A fmap.order(ByteOrder.LITTLE_ENDIAN);
804N/A } else {
804N/A fmap.order(ByteOrder.BIG_ENDIAN);
804N/A }
804N/A
804N/A fmap.position(ELFIdentification.EI_NIDENT.value());
804N/A e_type = E_Type.valueOf(fmap.getShort());
804N/A e_machine = E_Machine.valueOf(fmap.getShort());
804N/A e_version = E_Version.valueOf(fmap.getInt());
804N/A e_entry = fmap.getInt();
804N/A e_phoff = fmap.getInt();
804N/A e_shoff = fmap.getInt();
804N/A e_flags = fmap.getInt();
804N/A e_ehsize = fmap.getShort();
804N/A e_phentsize = fmap.getShort();
804N/A e_phnum = fmap.getShort();
804N/A e_shentsize = fmap.getShort();
804N/A e_shnum = fmap.getShort();
804N/A e_shstrndx = fmap.getShort();
804N/A }
804N/A
554N/A @Override
804N/A public String toString() {
1469N/A return (e_machine.toString() + " " + ei_class.toString() + " "
1469N/A + "\ne_type: " + e_type.toString() + "\ne_machine: "
1469N/A + e_machine.value() + "\ne_version: " + e_version + "\ne_entry: "
1469N/A + e_entry + "\ne_phoff: " + e_phoff + "\ne_shoff: " + e_shoff
1469N/A + "\ne_flags: " + e_flags + "\ne_ehsize: " + e_ehsize
1469N/A + "\ne_phentsize:" + e_phentsize + "\ne_phnum: " + e_phnum
1469N/A + "\ne_shentsize" + e_shentsize + "\ne_shnum: " + e_shnum
1469N/A + "\ne_shstrndx: " + e_shstrndx);
804N/A }
0N/A }
804N/A
804N/A private static class ELFSection {
1469N/A /** Section header table entry unused */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_NULL = 0;
1469N/A /** Program data */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_PROGBITS = 1;
1469N/A /** Symbol table */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_SYMTAB = 2;
1469N/A /** String table */
1469N/A public static final int SHT_STRTAB = 3;
1469N/A /** Relocation entries with addends */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_RELA = 4;
1469N/A /** Symbol hash table */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_HASH = 5;
1469N/A /** Dynamic linking information */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_DYNAMIC = 6;
1469N/A /** Notes */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_NOTE = 7;
1469N/A /** Program space with no data (bss) */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_NOBITS = 8;
1469N/A /** Relocation entries, no addends */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_REL = 9;
1469N/A /** Reserved */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_SHLIB = 10;
1469N/A /** Dynamic linker symbol table */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_DYNSYM = 11;
1469N/A /** Array of constructors */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_INIT_ARRAY = 14;
1469N/A /** Array of destructors */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_FINI_ARRAY = 15;
1469N/A /** Array of pre-constructors */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_PREINIT_ARRAY = 16;
1469N/A /** Section group */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_GROUP = 17;
1469N/A /** Extended section indeces */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_SYMTAB_SHNDX = 18;
1469N/A /** Number of defined types. */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_NUM = 19;
1469N/A /** Start OS-specific */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_LOOS = 0x60000000;
1469N/A /** Prelink library list */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_GNU_LIBLIST = 0x6ffffff7;
1469N/A /** Checksum for DSO content */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_CHECKSUM = 0x6ffffff8;
1469N/A /** Sun-specific low bound */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_LOSUNW = 0x6ffffffa;
1469N/A @SuppressWarnings("unused")
972N/A public static final int SHT_SUNW_COMDAT = 0x6ffffffb;
1469N/A /** Sun-specific high bound */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_HISUNW = 0x6fffffff;
1469N/A /** End OS-specific type */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_HIOS = 0x6fffffff;
1469N/A /** Start of processor-specific */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_LOPROC = 0x70000000;
1469N/A /** End of processor-specific */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_HIPROC = 0x7fffffff;
1469N/A /** Start of application-specific */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_LOUSER = 0x80000000;
1469N/A /** End of application-specific */
1469N/A @SuppressWarnings("unused")
1469N/A public static final int SHT_HIUSER = 0x8fffffff;
804N/A
804N/A public int sh_name;
804N/A public int sh_type;
804N/A public int sh_flags;
804N/A public int sh_addr;
804N/A public int sh_offset;
804N/A public int sh_size;
804N/A public int sh_link;
804N/A public int sh_info;
804N/A public int sh_addralign;
804N/A public int sh_entsize;
804N/A
804N/A public ELFSection(MappedByteBuffer fmap) {
804N/A sh_name = fmap.getInt();
804N/A sh_type = fmap.getInt();
804N/A sh_flags = fmap.getInt();
804N/A sh_addr = fmap.getInt();
804N/A sh_offset = fmap.getInt();
804N/A sh_size = fmap.getInt();
804N/A sh_link = fmap.getInt();
804N/A sh_info = fmap.getInt();
804N/A sh_addralign = fmap.getInt();
804N/A sh_entsize = fmap.getInt();
804N/A }
804N/A
554N/A @Override
804N/A public String toString() {
1469N/A return ("\nsh_name : " + sh_name + "\nsh_type : " + sh_type
1469N/A + "\nsh_flags: " + sh_flags + "\nsh_addr: " + sh_addr
1469N/A + "\nsh_offset: " + sh_offset + "\nsh_size: " + sh_size
1469N/A + "\nsh_link: " + sh_link + "\nsh_info: " + sh_info
1469N/A + "\nsh_addralign: " + sh_addralign + "\nsh_entsize: "
1469N/A + sh_entsize);
804N/A }
0N/A }
554N/A
804N/A private static enum ELFIdentification {
804N/A
804N/A EI_MAG0(0),
804N/A EI_MAG1(1),
804N/A EI_MAG2(2),
804N/A EI_MAG3(3),
804N/A EI_CLASS(4),
804N/A EI_DATA(5),
804N/A EI_VERSION(6),
804N/A EI_PAD(7),
804N/A EI_NIDENT(16);
804N/A private final int value;
804N/A
804N/A private ELFIdentification(int value) {
804N/A this.value = value;
804N/A }
804N/A
804N/A public int value() {
804N/A return this.value;
804N/A }
1181N/A }
804N/A
804N/A private static enum EI_Class {
804N/A ELFCLASSNONE(0),
804N/A ELFCLASS32(1),
804N/A ELFCLASS64(2);
804N/A
804N/A final String[] textual = {
804N/A "None", "32", "64"
804N/A };
804N/A
804N/A private final int value;
804N/A
804N/A private EI_Class(int value) {
804N/A this.value = value;
804N/A }
804N/A
804N/A static EI_Class valueOf(byte value) throws IllegalArgumentException {
804N/A switch (value) {
804N/A case 0: return ELFCLASSNONE;
804N/A case 1: return ELFCLASS32;
804N/A case 2: return ELFCLASS64;
804N/A default:
1469N/A throw new IllegalArgumentException("Invalid EI_CLASS value:"
1469N/A + value);
804N/A }
804N/A }
804N/A
804N/A @Override
804N/A public String toString() {
804N/A return textual[value];
804N/A }
1181N/A }
804N/A
804N/A private static enum EI_Data {
1469N/A ELFDATANONE,
1469N/A ELFDATA2LSB,
1469N/A ELFDATA2MSB;
804N/A
804N/A static EI_Data valueOf(byte value) throws IllegalArgumentException {
804N/A switch (value) {
804N/A case 0: return ELFDATANONE;
804N/A case 1: return ELFDATA2LSB;
804N/A case 2: return ELFDATA2MSB;
804N/A default:
1469N/A throw new IllegalArgumentException("Invalid EI_DATA value:"
1469N/A + value);
804N/A }
804N/A }
1181N/A }
804N/A
804N/A private static enum E_Type {
804N/A ET_NONE(0),
804N/A ET_REL(1),
804N/A ET_EXEC(2),
804N/A ET_DYN(3),
804N/A ET_CORE(4),
804N/A ET_UNKNOWN(0xFFFF);
804N/A
804N/A final String[] textual = {
804N/A "None", "Relocable", "Executable", "Shared object", "Core"
804N/A };
804N/A
804N/A private final int value;
804N/A
804N/A private E_Type(int value) {
804N/A this.value = value;
804N/A }
804N/A
804N/A static E_Type valueOf(short value) {
804N/A switch (value) {
804N/A case 0: return ET_NONE;
804N/A case 1: return ET_REL;
804N/A case 2: return ET_EXEC;
804N/A case 3: return ET_DYN;
804N/A case 4: return ET_CORE;
804N/A default:
804N/A return ET_UNKNOWN;
804N/A }
804N/A }
804N/A
804N/A public int value() {
804N/A return this.value;
804N/A }
804N/A
804N/A @Override
804N/A public String toString() {
804N/A if (value == ET_UNKNOWN.value()) {
804N/A return "Unknown";
804N/A }
804N/A return textual[value];
804N/A }
1181N/A }
804N/A
804N/A private static enum E_Machine {
804N/A EM_NONE(0),
804N/A EM_M32(1),
804N/A EM_SPARC(2),
804N/A EM_386(3),
804N/A EM_68K(4),
804N/A EM_88K(5),
804N/A EM_860(7),
804N/A EM_MIPS(8),
804N/A EM_UNKNOWN(0xFFFF);
804N/A
804N/A final String[] textual = {
804N/A "No machine", "AT&T WE 32100", "SPARC", "Intel 80386",
804N/A "Motorola 68000", "Motorola 88000", null,
804N/A "Intel 80860", "MIPS RS3000"
804N/A };
804N/A
804N/A private final int value;
804N/A
804N/A private E_Machine(int value) {
804N/A this.value = value;
804N/A }
804N/A
804N/A static E_Machine valueOf(short value) {
804N/A switch (value) {
804N/A case 0: return EM_NONE;
804N/A case 1: return EM_M32;
804N/A case 2: return EM_SPARC;
804N/A case 3: return EM_386;
804N/A case 4: return EM_68K;
804N/A case 5: return EM_88K;
804N/A case 7: return EM_860;
804N/A case 8: return EM_MIPS;
804N/A default:
804N/A return EM_UNKNOWN;
804N/A }
804N/A }
804N/A
804N/A public int value() {
804N/A return this.value;
804N/A }
804N/A
804N/A @Override
804N/A public String toString() {
804N/A if (value == EM_UNKNOWN.value()) {
804N/A return "Unknown";
804N/A }
804N/A return textual[value];
804N/A }
1181N/A }
804N/A
804N/A private static enum E_Version {
804N/A EV_NONE(0),
804N/A EV_CURRENT(1);
804N/A
804N/A final String[] textual = {
804N/A "Invalid", "Current"
804N/A };
804N/A
804N/A private final int value;
804N/A
804N/A private E_Version(int value) {
804N/A this.value = value;
804N/A }
804N/A
804N/A static E_Version valueOf(int value) throws IllegalArgumentException {
804N/A switch (value) {
804N/A case 0: return EV_NONE;
804N/A case 1: return EV_CURRENT;
804N/A default:
1469N/A throw new IllegalArgumentException("Illegal (or unknown) "
1469N/A + "version number: " + value);
804N/A }
804N/A }
804N/A
804N/A @Override
804N/A public String toString() {
804N/A return textual[value];
804N/A }
1181N/A }
202N/A}