/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.linker;
//
//
// typedef struct elf32_hdr{
// EI_NIDENT = 16;
// char e_ident[EI_NIDENT]; : e_ident[0...3] = '\x7f' "ELF" $B$G$"$k$3$H(B
// Elf32_Half e_type; : ET_EXEC $B$G$"$k$3$H(B
// Elf32_Half e_machine; : EM_386 $B$G$"$k$3$H(B ($B>-Mh(B
// EM_486$B%5%]!<%H$9$k(B)
// Elf32_Word e_version;
// Elf32_Addr e_entry; /* Entry point */
// Elf32_Off e_phoff;
// Elf32_Off e_shoff;
// Elf32_Word e_flags;
// Elf32_Half e_ehsize;
// Elf32_Half e_phentsize;
// Elf32_Half e_phnum;
// Elf32_Half e_shentsize;
// Elf32_Half e_shnum;
// Elf32_Half e_shstrndx;
// } Elf32_Ehdr;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collection;
import java.util.Vector;
import org.jnode.assembler.UnresolvedObjectRefException;
import org.jnode.assembler.x86.X86BinaryAssembler;
import org.jnode.assembler.x86.X86BinaryAssembler.X86ObjectRef;
public class Elf {
static final short ET_NONE = 0; /* No file type */
static final short ET_REL = 1; /* Relocatable file */
static final short ET_EXEC = 2; /* Executable file */
static final short ET_DYN = 3; /* Shared object file */
static final short ET_CORE = 4; /* Core file */
static final short ET_NUM = 5; /* Number of defined types. */
/* These constants define the various ELF target machines */
static final short EM_386 = 3;
static final short EM_486 = 6; /* Perhaps disused */
static final short EM_X86_64 = 62;
/* Class constants */
static final int ELFCLASS32 = 1; // 32-bit objects
static final int ELFCLASS64 = 2; // 64-bit objects
/* Version constants */
static final int EV_NONE = 0;
static final int EV_CURRENT = 1;
// These constants define the sizes of the various structures
static final short EHSIZE = 52; // Elf header size
static final short EHSIZE_EXT = 64; // Elf header size + alignment
static final short SHENTSIZE = 40; // Section header entry size
// Align on 4 bytes
static final short ALIGN = 4;
static final short EI_MAG0 = 0; // File identification
static final short EI_MAG1 = 1; // File identification
static final short EI_MAG2 = 2; // File identification
static final short EI_MAG3 = 3; // File identification
static final short EI_CLASS = 4; // File class
static final short EI_DATA = 5; // Data encoding
static final short EI_VERSION = 6; // File version
static final short EI_PAD = 7; // Start of padding bytes
static final short EI_NIDENT = 16; // Size of e_ident[]
byte e_ident[] = new byte[EI_NIDENT]; // : e_ident[0...3] = '\x7f' "ELF"
short e_type;
short e_machine;
int e_version;
long e_entry; // Entry point
long e_phoff;
long e_shoff;
int e_flags;
short e_shentsize;
short e_shstrndx;
Vector<Section> sections;
Section symSection;
Section strSection;
Section shstrSection;
Section textSection;
Section reltextSection;
Section dataSection;
Section reldataSection;
Section bssSection;
private Elf(short type) {
e_ident[EI_MAG0] = 0x7f;
e_ident[EI_MAG1] = (byte) 'E';
e_ident[EI_MAG2] = (byte) 'L';
e_ident[EI_MAG3] = (byte) 'F';
e_ident[EI_CLASS] = 1; // 0=none, 1=32-bit, 2=64-bit
e_ident[EI_DATA] = 1; // 0=none, 1=lsb, 2=msb
e_ident[EI_VERSION] = (byte) EV_CURRENT;
e_type = type;
e_machine = EM_386;
e_version = EV_CURRENT;
e_entry = 0;
e_phoff = 0;
e_shoff = 0;
e_flags = 0;
e_shstrndx = 0;
sections = new Vector<Section>();
if (e_type != ET_NONE) {
// Section 0 should be NULL
sections.addElement(Section.newNullInstance(this));
// Add this sections first! Otherwise we cannot create names for the
// sections
sections.addElement(shstrSection = Section.newStrTabSection(this));
e_shstrndx = (short) (sections.size() - 1);
// Now add a String table
sections.addElement(strSection = Section.newStrTabSection(this));
// Now add a Symbol table
sections.addElement(symSection = Section.newSymTabSection(this));
// Add a .text section
sections.addElement(textSection = Section.newTextSection(this));
// Add a .data section
sections.addElement(dataSection = Section.newDataSection(this));
// Add a .bss section
sections.addElement(bssSection = Section.newBssSection(this));
// Add a .rel.text section
sections.addElement(reltextSection = Section.newRelTabSection(this, symSection, textSection));
// Add a .rel.data section
sections.addElement(reldataSection = Section.newRelTabSection(this, symSection, dataSection));
}
}
private Elf() {
this(ET_NONE);
}
public static Elf newRelInstance() {
return new Elf(ET_REL);
}
public static Elf newExeInstance() {
return new Elf(ET_EXEC);
}
public static Elf newFromFile(String filename) throws IOException {
Elf elf = new Elf();
elf.load(filename);
return elf;
}
/**
* Wrap my contents into an elf object file.
*
* @return Elf
*/
public static Elf toElf(X86BinaryAssembler stream) {
final Elf elf = Elf.newRelInstance();
// Store the emitted objects in the text section
final Section textSection = elf.getSectionByName(".text");
final Section relTextSection = elf.getRelTextSection();
textSection.setBody(stream.getBytes(), 0, stream.getLength());
// Add all symbols & relocs
final Collection<X86ObjectRef> objectRefs = stream.getObjectRefs();
System.out.println("Creating " + objectRefs.size() + " symbols");
int cnt = 0;
for (X86ObjectRef ref : objectRefs) {
/*
* if ((cnt % 1000) == 0) { long end = System.currentTimeMillis();
* System.out.println("At " + cnt + ", it took " + (end-start) +
* "ms"); start = end; }
*/
final Object obj = ref.getObject();
final Symbol sym;
if (ref.isResolved()) {
try {
sym = new Symbol(elf, obj.toString(), ref.getOffset(), textSection);
} catch (UnresolvedObjectRefException ex) {
throw new RuntimeException(ex);
}
} else {
final int[] offsets = ref.getUnresolvedOffsets();
sym = new Symbol(elf, obj.toString(), 0, null);
for (int j = 0; j < offsets.length; j++) {
if (ref.isRelJump()) {
relTextSection.addPcRelReloc(sym, offsets[j]);
} else {
relTextSection.addAbsReloc(sym, offsets[j]);
}
}
}
if (ref.isPublic()) {
sym.setBind(Symbol.STB_GLOBAL);
} else {
sym.setBind(Symbol.STB_LOCAL);
}
elf.addSymbol(sym);
cnt++;
}
return elf;
}
// --------------------------------------------
// Header
// --------------------------------------------
public boolean isRel() {
return (e_type == ET_REL);
}
public boolean isExe() {
return (e_type == ET_EXEC);
}
/**
* Does this elf file contain 32-bit objects.
*
* @return true is the file contains 32-bit objects
*/
public boolean isClass32() {
return (e_ident[EI_CLASS] == ELFCLASS32);
}
/**
* Does this elf file contain 64-bit objects.
*
* @return true is the file contains 64-bit objects
*/
public boolean isClass64() {
return (e_ident[EI_CLASS] == ELFCLASS64);
}
/**
* Gets the entry point address.
*
* @return the address
*/
public long getEntry() {
return e_entry;
}
/**
* Sets the entry point address.
*
* @param e
*/
public void setEntry(long e) {
e_entry = e;
}
// --------------------------------------------
// Strings
// --------------------------------------------
public String getString(int addr) {
return strSection.getString(addr);
}
public int addString(String name) {
return strSection.addString(name);
}
public String getSHString(int addr) {
return shstrSection.getString(addr);
}
protected Section getSHStrSection() {
return shstrSection;
}
// --------------------------------------------
// Symbols
// --------------------------------------------
public int getNoSymbols() {
return symSection.getNoSymbols();
}
public Symbol getSymbol(int index) {
return symSection.getSymbol(index);
}
public Symbol getSymbolByAddress(int address) {
return symSection.getSymbolByAddress(address);
}
public void addSymbol(Symbol s) {
symSection.addSymbol(s);
}
public int getIndexOfSymbol(Symbol symbol) {
return symSection.getIndexOfSymbol(symbol);
}
// --------------------------------------------
// Relocs
// --------------------------------------------
public Section getRelTextSection() {
return reltextSection;
}
public Section getRelDataSection() {
return reldataSection;
}
// --------------------------------------------
// Sections
// --------------------------------------------
public int getNoSections() {
return sections.size();
}
public Section getSection(int index) {
return ((index >= 0) && (index < getNoSections())) ? sections.elementAt(index) : null;
}
public Section getSectionByName(String name) {
for (int i = 1; i < getNoSections(); i++) {
final Section s = getSection(i);
final String sname = s.getName();
if ((sname != null) && sname.equals(name)) {
return s;
}
}
return null;
}
protected int getSectionIndex(Section s) {
for (int i = 0; i < getNoSections(); i++) {
if (s == getSection(i))
return i;
}
return -1;
}
// --------------------------------------------
// Loading & Storing
// --------------------------------------------
// Load the ELF data to file
private void load(String filename) throws IOException {
RandomAccessFile in;
int i;
int e_shnum;
in = new RandomAccessFile(filename, "r");
try {
// Load the elf header
LoadUtil.bytes(in, e_ident);
e_type = LoadUtil.little16(in);
e_machine = LoadUtil.little16(in);
e_version = LoadUtil.little32(in);
e_entry = LoadUtil.loadAddr(in, e_ident);
e_phoff = LoadUtil.loadOff(in, e_ident);
e_shoff = LoadUtil.loadOff(in, e_ident);
e_flags = LoadUtil.little32(in);
/* e_ehsize = */
LoadUtil.little16(in);
/* e_phentsize = */
LoadUtil.little16(in);
/* e_phnum = */
LoadUtil.little16(in);
e_shentsize = LoadUtil.little16(in);
e_shnum = LoadUtil.little16(in);
e_shstrndx = LoadUtil.little16(in);
if (!((e_ident[0] == 0x7F) && (e_ident[1] == 'E') && (e_ident[2] == 'L') && (e_ident[3] == 'F'))) {
throw new IOException("Not Elf Format :" + filename);
}
final int expectedMachine = (isClass32()) ? EM_386 : EM_X86_64;
if (e_machine != expectedMachine) {
throw new IOException("Not Match CPU Type (" + e_machine + "):" + filename);
}
// Load the program header (ignored for now)
in.seek(e_phoff);
// Load the section headers
in.seek(e_shoff);
for (i = 0; i < e_shnum; i++) {
Section s = Section.newInstance(this, in);
sections.addElement(s);
// System.out.println("Read section " + i);
}
// Load the sections
for (i = 0; i < e_shnum; i++) {
// System.out.println("Read section body " + i);
Section s = sections.elementAt(i);
s.loadBody(in);
if (i == e_shstrndx)
shstrSection = s;
else if (s.isSymTab())
symSection = s;
else if (s.isStrTab())
strSection = s;
}
} finally {
in.close();
}
}
// Store the ELF data from file
public boolean store(String filename) throws IOException {
FileOutputStream out;
int i;
int ofs = 0;
out = new FileOutputStream(filename);
// Prepare to store the sections and initialize their offsets
ofs = EHSIZE_EXT;
// Skip section 0, since it is always 0
for (i = 1; i < getNoSections(); i++) {
Section s = getSection(i);
s.setOffset(ofs);
ofs += s.prepareStoreBody();
// align
while ((ofs % ALIGN) != 0)
ofs++;
}
e_shoff = ofs;
// Now start storing
ofs = 0;
// Store the header
ofs += StoreUtil.bytes(out, e_ident);
ofs += StoreUtil.little16(out, e_type);
ofs += StoreUtil.little16(out, e_machine);
ofs += StoreUtil.little32(out, e_version);
ofs += StoreUtil.storeAddr(out, e_ident, e_entry);
ofs += StoreUtil.storeOff(out, e_ident, 0);
ofs += StoreUtil.storeOff(out, e_ident, e_shoff);
ofs += StoreUtil.little32(out, e_flags);
ofs += StoreUtil.little16(out, EHSIZE);
ofs += StoreUtil.little16(out, 0);
ofs += StoreUtil.little16(out, 0);
ofs += StoreUtil.little16(out, SHENTSIZE);
ofs += StoreUtil.little16(out, getNoSections());
ofs += StoreUtil.little16(out, e_shstrndx);
// Align till 0x40
ofs += StoreUtil.little32(out, 0);
ofs += StoreUtil.little32(out, 0);
ofs += StoreUtil.little32(out, 0);
// Just a check
if (ofs != EHSIZE_EXT) {
throw new RuntimeException("Strange, invalid header size");
}
// Write the section bodies
// Here again, skip section 0, since it is always empty
for (i = 1; i < getNoSections(); i++) {
Section s = getSection(i);
if (s.getOffset() != ofs) {
throw new RuntimeException("Strange, offset mismatch in section-body (section=" + i + ", ofs=" + ofs
+ ", expected=" + s.getOffset());
}
ofs += s.storeBody(out);
// align
while ((ofs % ALIGN) != 0) {
ofs += StoreUtil.little8(out, 0);
}
System.out.println("store section " + i + ", ofs=" + ofs + ", name=" + s.getName());
}
// Write the section headers
for (i = 0; i < getNoSections(); i++) {
ofs += getSection(i).store(out);
}
out.close();
return (true);
}
public void print() {
System.out.println("----- Elf Header -----");
System.out.println("e_type : " + Integer.toString(e_type, 16));
System.out.println("e_machine : " + Integer.toString(e_machine, 16));
System.out.println("e_version : " + Integer.toString(e_version, 16));
System.out.println("e_entry : " + Long.toString(e_entry, 16));
// System.out.println( "e_phentsize : " + Integer.toString( e_phentsize,
// 16));
// System.out.println( "e_phnum : " + Integer.toString( e_phnum, 16));
System.out.println("e_phoff : " + Long.toString(e_phoff, 16));
System.out.println("e_shnum : " + Integer.toString(getNoSections(), 16));
System.out.println("e_shoff : " + Long.toString(e_shoff, 16));
System.out.println("e_shstrndx : " + Integer.toString(e_shstrndx, 16));
System.out.println(" ----- BRK ----- ");
// System.out.println(" strtab = [" + strtab + "]");
for (int i = 1; i < getNoSections(); i++) {
final Section s = getSection(i);
s.print();
}
}
public static void main(String args[]) throws Exception {
Elf elf = newFromFile("test.o");
elf.print();
System.out.println("\nNow do the store");
elf = newRelInstance();
elf.store("test-elf.o");
elf.print();
// System.out.println("\nNow try to read it back again");
// elf = newFromFile("test-elf.o");
// elf.print();
}
}