/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the Archimulator multicore architectural simulator.
* <p>
* Archimulator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* Archimulator 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 for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.os.elf;
import archimulator.util.buffer.BigEndianBufferAccessor;
import archimulator.util.buffer.BufferAccessor;
import archimulator.util.buffer.LittleEndianBufferAccessor;
import archimulator.util.buffer.RandomAccessFileBuffer;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* ELF file.
*
* @author Min Cai
*/
public class ElfFile {
private RandomAccessFile file;
private RandomAccessFileBuffer buffer;
private ElfIdentification identification;
private ElfHeader header;
private List<ElfSectionHeader> sectionHeaders;
private List<ElfProgramHeader> programHeaders;
private ElfStringTable stringTable;
private boolean littleEndian;
private Map<Integer, Symbol> symbols;
private Map<Integer, Symbol> localFunctionSymbols;
private Map<Integer, Symbol> localObjectSymbols;
private Map<Integer, Symbol> commonObjectSymbols;
private long position;
/**
* Create an ELF file.
*
* @param filename the file name
*/
public ElfFile(String filename) {
try {
this.file = new RandomAccessFile(filename, "r");
this.buffer = new RandomAccessFileBuffer(this.file);
this.sectionHeaders = new ArrayList<>();
this.programHeaders = new ArrayList<>();
this.identification = new ElfIdentification();
this.identification.read(this);
if (this.identification.getClz() != ElfIdentification.ElfClass32) {
throw new IllegalArgumentException();
}
this.littleEndian = (this.identification.getData() == ElfIdentification.ElfData2Lsb);
this.header = new ElfHeader(this);
if (this.header.getMachine() != ElfHeader.EM_MIPS) {
throw new IllegalArgumentException();
}
this.setPosition(this.header.getSectionHeaderTableOffset());
for (int i = 0; i < this.header.getSectionHeaderTableEntryCount(); i++) {
this.setPosition(this.header.getSectionHeaderTableOffset() + (i * this.header.getSectionHeaderTableEntrySize()));
this.sectionHeaders.add(new ElfSectionHeader(this));
}
this.stringTable = new ElfStringTable(this, this.sectionHeaders.get(this.header.getSectionHeaderStringTableIndex()));
this.setPosition(this.header.getProgramHeaderTableOffset());
for (int i = 0; i < this.header.getProgramHeaderTableEntryCount(); i++) {
this.programHeaders.add(new ElfProgramHeader(this));
}
this.symbols = new HashMap<>();
this.localFunctionSymbols = new HashMap<>();
this.localObjectSymbols = new HashMap<>();
this.commonObjectSymbols = new HashMap<>();
this.loadSymbols();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Load symbols.
*/
private void loadSymbols() {
List<ElfSectionHeader> sections = this.getSectionHeaders(ElfSectionHeader.SHT_SYMTAB);
if (!sections.isEmpty()) {
this.loadSymbolsBySection(sections.get(0));
}
this.loadLocalFunctions();
this.loadLocalObjects();
this.loadCommonObjects();
}
/**
* Load symbols by the specified section.
*
* @param sectionHeader the section header
*/
private void loadSymbolsBySection(ElfSectionHeader sectionHeader) {
try {
int numSymbols = 1;
if (sectionHeader.getEntrySize() != 0) {
numSymbols = (int) sectionHeader.getSize() / (int) sectionHeader.getEntrySize();
}
long offset = sectionHeader.getOffset();
for (int c = 0; c < numSymbols; offset += sectionHeader.getEntrySize(), c++) {
this.setPosition(offset);
Symbol symbol = new Symbol(this, sectionHeader);
symbol.setNameIndex(this.readUnsignedWord());
symbol.setValue(this.readUnsignedWord());
symbol.setSize(this.readUnsignedWord());
symbol.setInfo(this.read());
symbol.setOther(this.read());
symbol.setSectionHeaderTableIndex(this.readUnsignedHalfWord());
this.symbols.put((int) symbol.getValue(), symbol);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Get the symbol at the specified address.
*
* @param address the address
* @return the symbol at the specified address
*/
public Symbol getSymbol(long address) {
for (Symbol symbol : this.symbols.values()) {
if (symbol.getValue() == address) {
return symbol;
}
}
return null;
}
/**
* Load local functions.
*/
public void loadLocalFunctions() {
this.symbols.values().stream().filter(symbol -> symbol.getType() == Symbol.STT_FUNC).forEach(symbol -> {
int idx = symbol.getSectionHeaderTableIndex();
if (idx > Symbol.SHN_LOPROC && idx < Symbol.SHN_HIPROC) {
if (symbol.getName().length() > 0) {
this.localFunctionSymbols.put((int) symbol.getValue(), symbol);
}
} else if (idx >= 0 && this.getSectionHeaders().get(idx).getType() != ElfSectionHeader.SHT_NULL) {
this.localFunctionSymbols.put((int) symbol.getValue(), symbol);
}
});
}
/**
* Load local objects.
*/
public void loadLocalObjects() {
this.symbols.values().stream().filter(symbol -> symbol.getType() == Symbol.STT_OBJECT).forEach(symbol -> {
int idx = symbol.getSectionHeaderTableIndex();
if (idx > Symbol.SHN_LOPROC && idx < Symbol.SHN_HIPROC) {
if (symbol.getName().length() > 0) {
this.localObjectSymbols.put((int) symbol.getValue(), symbol);
}
} else if (idx >= 0 && this.getSectionHeaders().get(idx).getType() != ElfSectionHeader.SHT_NULL) {
this.localObjectSymbols.put((int) symbol.getValue(), symbol);
}
});
}
/**
* Load common objects.
*/
public void loadCommonObjects() {
this.symbols.values().stream()
.filter(symbol -> symbol.getBind() == Symbol.STB_GLOBAL && symbol.getType() == Symbol.STT_OBJECT)
.filter(symbol -> symbol.getSectionHeaderTableIndex() == Symbol.SHN_COMMON)
.forEach(symbol -> this.commonObjectSymbols.put((int) symbol.getValue(), symbol));
}
/**
* Get the list of section headers matching the specified type.
*
* @param type the section header type to be matched
* @return the list of section headers matching the specified type
*/
public List<ElfSectionHeader> getSectionHeaders(int type) {
return this.getSectionHeaders().stream().filter(sectionHeader -> sectionHeader.getType() == type).collect(Collectors.toList());
}
/**
* Close the ELF file.
*
* @throws IOException
*/
public void close()
throws IOException {
this.file.close();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
this.close();
}
/**
* Get the current cursor position.
*
* @return the current cursor position
*/
public long getPosition() {
return this.position;
}
/**
* Set the current cursor position.
*
* @param position the current cursor position
*/
public void setPosition(long position) {
this.position = position;
}
/**
* Get the buffer accessor.
*
* @return the buffer accessor
*/
public BufferAccessor getBufferAccessor() {
if (this.littleEndian) {
return new LittleEndianBufferAccessor();
} else {
return new BigEndianBufferAccessor();
}
}
/**
* Read the content from the current position in the ELF file.
*
* @param content the content buffer
* @throws IOException
*/
public void read(byte[] content)
throws IOException {
this.file.seek(this.position);
this.file.readFully(content);
this.position += content.length;
}
/**
* Read the content from the specified offset in the ELF file.
*
* @param offset the offset
* @param content the content buffer
* @throws IOException
*/
public void read(long offset, byte[] content)
throws IOException {
this.file.seek(offset);
this.file.readFully(content);
}
/**
* Read a byte of data from the current position in the ELF file.
*
* @return a byte of data read from the current position in the ELF file
* @throws IOException
*/
public int read()
throws IOException {
this.file.seek(this.position);
int data = this.file.read();
this.position++;
return data;
}
/**
* Read a byte of data from the specified offset in the ELF file.
*
* @param offset the offset
* @return a byte of data read from the specified offset in the ELF file
* @throws IOException
*/
public int read(long offset)
throws IOException {
this.file.seek(offset);
return this.file.read();
}
/**
* Read an unsigned word of data from the current position in the ELF file.
*
* @return an unsigned word of data from the current position in the ELF file
* @throws IOException
*/
public long readUnsignedWord() throws IOException {
long data = this.readUnsignedWord(this.position);
this.position += 4;
return data;
}
/**
* Read an unsigned word of data from the specified offset in the ELF file.
*
* @param offset the offset
* @return an unsigned word of data from the specified offset in the ELF file
* @throws IOException
*/
public long readUnsignedWord(long offset) throws IOException {
return this.getBufferAccessor().getU4(this.buffer, offset);
}
/**
* Read an unsigned half word of data from the current position in the ELF file.
*
* @return an unsigned half word of data from the current position in the ELF file
* @throws IOException
*/
public int readUnsignedHalfWord() throws IOException {
int data = this.readUnsignedHalfWord(this.position);
this.position += 2;
return data;
}
/**
* Read an unsigned half word of data from the specified offset in the ELF file.
*
* @param offset the offset
* @return an unsigned half word of data from the specified offset in the ELF file
* @throws IOException
*/
public int readUnsignedHalfWord(long offset) throws IOException {
return this.getBufferAccessor().getU2(this.buffer, offset);
}
/**
* Get the identification.
*
* @return the identification
*/
public ElfIdentification getIdentification() {
return this.identification;
}
/**
* Get the header.
*
* @return the header
*/
public ElfHeader getHeader() {
return this.header;
}
/**
* Get the list of section headers.
*
* @return the list of section headers
*/
public List<ElfSectionHeader> getSectionHeaders() {
return this.sectionHeaders;
}
/**
* Get the list of program headers.
*
* @return the list of program headers
*/
public List<ElfProgramHeader> getProgramHeaders() {
return this.programHeaders;
}
/**
* Get the string table.
*
* @return the string table
*/
public ElfStringTable getStringTable() {
return this.stringTable;
}
/**
* Get a value indicating whether the ELF file is little endian or not.
*
* @return a value indicating whether the ELF file is little endian or not
*/
public boolean isLittleEndian() {
return this.littleEndian;
}
/**
* Get the map of symbols.
*
* @return the map of symbols
*/
public Map<Integer, Symbol> getSymbols() {
return symbols;
}
/**
* Get the map of local function symbols.
*
* @return the map of local function symbols
*/
public Map<Integer, Symbol> getLocalFunctionSymbols() {
return localFunctionSymbols;
}
/**
* Get the map of local object symbols.
*
* @return the map of local object symbols
*/
public Map<Integer, Symbol> getLocalObjectSymbols() {
return localObjectSymbols;
}
/**
* Get the map of common object symbols.
*
* @return the map of common object symbols
*/
public Map<Integer, Symbol> getCommonObjectSymbols() {
return commonObjectSymbols;
}
}