/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Copyright (c) 2005, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the University of California, Los Angeles nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Created Sep 5, 2005
*/
package com.oracle.max.elf;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* The <code>ELFSectionHeaderTable</code> class represents a cleaned-up view
* of the ELF format's section header table that contains a summary of each
* section in the ELF file. Each section might contain code, data, symbol
* table information, etc. This class reads this header table from a
* random access file.
*/
public class ELFSectionHeaderTable {
public static final int SHT_NULL = 0;
public static final int SHT_PROGBITS = 1;
public static final int SHT_SYMTAB = 2;
public static final int SHT_STRTAB = 3;
public static final int SHT_RELA = 4;
public static final int SHT_HASH = 5;
public static final int SHT_DYNAMIC = 6;
public static final int SHT_NOTE = 7;
public static final int SHT_NOBITS = 8;
public static final int SHT_REL = 9;
public static final int SHT_SHLIB = 10;
public static final int SHT_DYNSYM = 11;
public static final int SHT_LOPROC = 0x70000000;
public static final int SHT_HIPROC = 0x7fffffff;
public static final int SHT_LOUSER = 0x80000000;
public static final int SHT_HIUSER = 0x8fffffff;
public static final int SHF_WRITE = 0x1;
public static final int SHF_ALLOC = 0x2;
public static final int SHF_EXECINSTR = 0x4;
public static final int SHF_MASKPROC = 0xf0000000;
private static final int ELF32_SHTENT_SIZE = 40;
private static final int ELF64_SHTENT_SIZE = 64;
/************************************************************************/
private static final int SHN_UNDEF = 0;
private static final short NO_OF_SECTIONS = 5;
/************************************************************************/
public abstract class Entry {
public abstract int getType();
public String getTypeString() {
switch (getType()) {
case SHT_NULL:
return "null";
case SHT_PROGBITS:
return "program";
case SHT_SYMTAB:
return "symtab";
case SHT_STRTAB:
return "strtab";
case SHT_RELA:
return "rela";
case SHT_HASH:
return "hash";
case SHT_DYNAMIC:
return "dynamic";
case SHT_NOTE:
return "note";
case SHT_NOBITS:
return "nobits";
case SHT_REL:
return "rel";
case SHT_SHLIB:
return "shlib";
case SHT_DYNSYM:
return "dynsym";
default:
return "unknown";
}
}
public String getFlagString() {
final StringBuffer buffer = new StringBuffer();
final int flags = getFlags();
if ((flags & SHF_WRITE) != 0) {
buffer.append("WRITE ");
}
if ((flags & SHF_ALLOC) != 0) {
buffer.append("ALLOC ");
}
if ((flags & SHF_EXECINSTR) != 0) {
buffer.append("EXEC ");
}
return buffer.toString();
}
public boolean isStringTable() {
return getType() == SHT_STRTAB;
}
public boolean isSymbolTable() {
return getType() == SHT_SYMTAB;
}
public boolean is32Bit() {
return this instanceof Entry32;
}
public boolean is64Bit() {
return this instanceof Entry64;
}
protected abstract int getFlags();
public abstract String getName();
public abstract long getOffset();
public abstract long getSize();
public abstract int getLink();
public abstract int getEntrySize();
public abstract void setEntrySize(long size);
}
public class Entry32 extends Entry {
public int sh_name;
public int sh_type;
public int sh_flags;
public int sh_addr;
public int sh_offset;
public int sh_size;
public int sh_link;
public int sh_info;
public int sh_addralign;
public int sh_entsize;
@Override
public int getType() {
return sh_type;
}
@Override
protected int getFlags() {
return sh_flags;
}
@Override
public String getName() {
if (strtab != null) {
return strtab.getString(sh_name);
}
return "";
}
@Override
public long getOffset() {
return sh_offset;
}
@Override
public long getSize() {
return sh_size;
}
@Override
public int getLink() {
return sh_link;
}
@Override
public int getEntrySize() {
return sh_entsize;
}
@Override
public void setEntrySize(long size) {
sh_size = (int) size;
}
}
public class Entry64 extends Entry {
public int sh_name;
public int sh_type;
public long sh_flags;
public long sh_addr;
public long sh_offset;
public long sh_size;
public int sh_link;
public int sh_info;
public long sh_addralign;
public long sh_entsize;
@Override
public int getType() {
return sh_type;
}
@Override
protected int getFlags() {
return (int) sh_flags;
}
@Override
public String getName() {
if (strtab != null) {
return strtab.getString(sh_name);
}
return "";
}
@Override
public long getOffset() {
return sh_offset;
}
@Override
public long getSize() {
assert sh_size < Long.MAX_VALUE;
return sh_size;
}
@Override
public int getLink() {
return sh_link;
}
@Override
public int getEntrySize() {
assert sh_entsize < Integer.MAX_VALUE;
return (int) sh_entsize;
}
@Override
public void setEntrySize(long size) {
sh_size = size;
}
}
public final ELFHeader header;
public final Entry[] entries;
protected ELFStringTable strtab;
private long offsetCount;
/**
* The constructor for the <code>ELFSectionHeaderTable</code> class creates a new instance
* corresponding to the specified ELF header. The ELF header contains an entry that
* stores the offset of the beginning of the section header table relative to the start
* of the file.
* @param elfHeader the ELF header containing information about this ELF file
*/
public ELFSectionHeaderTable(ELFHeader elfHeader) {
this.header = elfHeader;
entries = new Entry[elfHeader.e_shnum];
offsetCount = 0;
}
/**
* The <code>read()</code> method reads the section header table from the specified
* file. The file must support random access, since the beginning offset of the table
* is specified in the header table informationi.
* @param fis the random access file that contains the section header table
* @throws IOException if there is a problem reading the data from the file
*/
public void read(RandomAccessFile fis) throws IOException {
if (entries.length == 0) {
return;
}
// seek to the beginning of the section header table
fis.seek(header.e_shoff);
final ELFDataInputStream is = new ELFDataInputStream(header, fis);
// load each of the section header entries
for (int cntr = 0; cntr < entries.length; cntr++) {
entries[cntr] = readEntry(fis, is);
}
}
private Entry readEntry(RandomAccessFile fis, ELFDataInputStream is) throws IOException {
if (header.is32Bit()) {
return readEntry32(fis, is);
} else if (header.is64Bit()) {
return readEntry64(fis, is);
}
throw new Error("unknown bit size");
}
private Entry32 readEntry32(RandomAccessFile fis, ELFDataInputStream is) throws IOException {
final Entry32 e = new Entry32();
e.sh_name = is.read_Elf32_Word();
e.sh_type = is.read_Elf32_Word();
e.sh_flags = is.read_Elf32_Word();
e.sh_addr = is.read_Elf32_Addr();
e.sh_offset = is.read_Elf32_Off();
e.sh_size = is.read_Elf32_Word();
e.sh_link = is.read_Elf32_Word();
e.sh_info = is.read_Elf32_Word();
e.sh_addralign = is.read_Elf32_Word();
e.sh_entsize = is.read_Elf32_Word();
for (int pad = ELF32_SHTENT_SIZE; pad < header.e_shentsize; pad++) {
fis.read();
}
return e;
}
private Entry64 readEntry64(RandomAccessFile fis, ELFDataInputStream is) throws IOException {
final Entry64 e = new Entry64();
e.sh_name = is.read_Elf64_Word(); // 4
e.sh_type = is.read_Elf64_Word(); // 4
e.sh_flags = is.read_Elf64_XWord(); // 8
e.sh_addr = is.read_Elf64_Addr(); // 8
e.sh_offset = is.read_Elf64_Off(); // 8
e.sh_size = is.read_Elf64_XWord(); // 8
e.sh_link = is.read_Elf64_Word(); // 4
e.sh_info = is.read_Elf64_Word(); // 4
e.sh_addralign = is.read_Elf64_XWord(); // 8
e.sh_entsize = is.read_Elf64_XWord(); // 8
for (int pad = ELF64_SHTENT_SIZE; pad < header.e_shentsize; pad++) {
fis.read();
}
return e;
}
public void setStringTable(ELFStringTable str) {
strtab = str;
}
public ELFStringTable getStringTable() {
return strtab;
}
public String getSectionName(int ind) {
if (ind < 0 || ind >= entries.length) {
return "";
}
return entries[ind].getName();
}
public String setSectionName(int cntr) throws Exception {
switch (cntr) {
case 0:
return "";
case 1:
return "maxvm_image";
case 2:
return ".shstrtab";
case 3:
return ".symtab";
case 4:
return ".strtab";
default:
break;
}
throw new Error("unknown section number");
}
Entry64 setSectionHeaderForNull(Entry64 e) throws IOException {
e.sh_name = strtab.getIndex("\0");
if (e.sh_name == -1) {
throw new Error("Unknown Section Name");
}
e.sh_type = SHT_NULL;
e.sh_addr = 0; // If the section will appear in the memory image of a process then it should contain the address else it should be 0.
e.sh_addralign = 0; // 0 or 1 means that this sections has no alignment constraints.
e.sh_entsize = 0; // If the section does not hold fixed sized entries then this should be 0.
e.sh_flags = 0;
e.sh_info = 0;
e.sh_link = 0;
e.sh_size = 0;
e.sh_offset = 0;
return e;
}
Entry64 setSectionHeaderForMaxvm(Entry64 e, long size) throws IOException {
e.sh_name = strtab.getIndex("maxvm_image");
if (e.sh_name == -1) {
throw new Error("Unknown Section Name");
}
e.sh_type = SHT_PROGBITS;
e.sh_addr = 0; // If the section will appear in the memory image of a process then it should contain the address else it should be 0.
e.sh_addralign = 0x1; // 0 or 1 means that this sections has no alignment constraints.
e.sh_entsize = 0; // If the section does not hold fixed sized entries then this should be 0.
e.sh_flags = SHF_ALLOC;
e.sh_info = 0;
e.sh_link = SHN_UNDEF;
e.sh_size = size;
e.sh_offset = offsetCount + header.e_ehsize; // This is the first section after the header section.
offsetCount = e.sh_offset + size;
return e;
}
Entry64 setSectionHeaderForShStrTab(Entry64 e, long size) throws IOException {
e.sh_name = strtab.getIndex(".shstrtab");
if (e.sh_name == -1) {
throw new Error("Unknown Section Name");
}
e.sh_type = SHT_STRTAB;
e.sh_addr = 0; // If the section will appear in the memory image of a process then it should contain the address else it should be 0.
e.sh_addralign = 0x1; // 0 or 1 means that this sections has no alignment constraints.
e.sh_entsize = 0; // If the section does not hold fixed sized entries then this should be 0.
e.sh_flags = SHF_ALLOC;
e.sh_info = 0;
e.sh_link = SHN_UNDEF;
e.sh_size = strtab.getStringLength();
e.sh_offset = offsetCount;
offsetCount += e.sh_size;
return e;
}
Entry64 setSectionHeaderForSymTab(Entry64 e) throws IOException {
e.sh_name = strtab.getIndex(".symtab");
if (e.sh_name == -1) {
throw new Error("Unknown Section Name");
}
e.sh_type = SHT_SYMTAB;
e.sh_link = 4; // this is the value of the position of the symbol table header in the section header table.
e.sh_addr = 0; // If the section will appear in the memory image of a process then it should contain the address else it should be 0.
// the addr align is 8 since this section contains long variables which are 8 bytes long and hence the address alignment should be 8.
e.sh_addralign = 0x8;
e.sh_entsize = 24; // If the section does not hold fixed sized entries then this should be 0.
e.sh_flags = 0;
e.sh_info = 2; //One greater than the symbol table index of the last local symbol (binding STB_LOCAL)
e.sh_size = e.sh_entsize * 4; // Totally 4 symbols.
if (offsetCount % 8 != 0) {
offsetCount += 8 - (offsetCount % 8);
}
e.sh_offset = offsetCount;
offsetCount += e.sh_size;
return e;
}
Entry64 setSectionHeaderForStrTab(Entry64 e) {
e.sh_type = SHT_STRTAB;
e.sh_addr = 0; // If the section will appear in the memory image of a process then it should contain the address else it should be 0.
e.sh_addralign = 0x1; // 0 or 1 means that this sections has no alignment constraints.
e.sh_entsize = 0; // If the section does not hold fixed sized entries then this should be 0.
e.sh_flags = 0;
e.sh_info = 0;
e.sh_link = SHN_UNDEF;
e.sh_name = strtab.getIndex(".strtab");
if (e.sh_name == -1) {
System.out.println("ERROR");
}
e.sh_size = 0x00; // The size of the this section is to be filled up later.
e.sh_offset = offsetCount; // The offset count to the end of the section will be filled up later
return e;
}
public long getOffsetCount() {
return offsetCount;
}
public void setOffsetCount(long offset) {
offsetCount = offset;
}
public void write(long size) throws IOException {
if (entries.length == 0) {
return;
}
String sectionName = "";
// The Symbol table and the string table associated with the symbol table are
// created later. We first create the section headers and use the values in those
// to create the symbol table and its string table.
// load each of the section header entries
for (int cntr = 0; cntr < NO_OF_SECTIONS; cntr++) {
try {
sectionName = setSectionName(cntr);
entries[cntr] = writeEntry(sectionName, size);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
private Entry writeEntry(String sectionName, long size) throws IOException {
if (header.is32Bit()) {
return writeEntry32();
} else if (header.is64Bit()) {
return writeEntry64(sectionName, size);
}
throw new Error("unknown bit size");
}
private Entry writeEntry32() {
Entry32 e = new Entry32();
return e;
}
private Entry writeEntry64(String sectionName, long size) throws IOException {
Entry64 e = new Entry64();
if (sectionName.equalsIgnoreCase("")) {
e = setSectionHeaderForNull(e);
}
if (sectionName.equalsIgnoreCase("maxvm_image")) {
e = setSectionHeaderForMaxvm(e, size);
} else if (sectionName.equalsIgnoreCase(".shstrtab")) {
e = setSectionHeaderForShStrTab(e, size);
} else if (sectionName.equalsIgnoreCase(".symtab")) {
e = setSectionHeaderForSymTab(e);
} else if (sectionName.equalsIgnoreCase(".strtab")) {
e = setSectionHeaderForStrTab(e);
}
return e;
}
public void writeSectionHeadersToFile64(ELFDataOutputStream os, RandomAccessFile fis) throws IOException {
for (int cntr = 0; cntr < NO_OF_SECTIONS; cntr++) {
Entry ent = entries[cntr];
Entry64 e64 = (Entry64) ent;
os.write_Elf64_Word(e64.sh_name);
os.write_Elf64_Word(e64.sh_type);
os.write_Elf64_XWord(e64.sh_flags);
os.write_Elf64_Addr(e64.sh_addr);
os.write_Elf64_Off(e64.sh_offset);
os.write_Elf64_XWord(e64.sh_size);
os.write_Elf64_Word(e64.sh_link);
os.write_Elf64_Word(e64.sh_info);
os.write_Elf64_XWord(e64.sh_addralign);
os.write_Elf64_XWord(e64.sh_entsize);
}
}
}