/**
* ****************************************************************************
* 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;
import archimulator.analysis.BasicBlock;
import archimulator.analysis.ElfAnalyzer;
import archimulator.analysis.Function;
import archimulator.analysis.Instruction;
import archimulator.common.ContextMapping;
import archimulator.isa.Memory;
import archimulator.isa.StaticInstruction;
import archimulator.isa.dissembler.MipsDisassembler;
import archimulator.os.elf.ElfFile;
import archimulator.os.elf.ElfSectionHeader;
import java.util.*;
/**
* Basic process.
*
* @author Min Cai
*/
public class BasicProcess extends Process {
private Map<Integer, Integer> pcsToMachineInstructions;
private Map<Integer, StaticInstruction> machineInstructionsToStaticInstructions;
private Map<String, SortedMap<Integer, Instruction>> instructions;
private ElfAnalyzer elfAnalyzer;
private Map<Integer, String> pcToFunctionNameMappingCache;
/**
* Create a basic process.
*
* @param kernel the kernel
* @param contextMapping the context mapping
*/
public BasicProcess(Kernel kernel, ContextMapping contextMapping) {
super(kernel, contextMapping);
}
/**
* Load the program.
* @param kernel the kernel
* @param contextMapping the context mapping
*/
@Override
protected void loadProgram(Kernel kernel, ContextMapping contextMapping) {
this.pcsToMachineInstructions = new TreeMap<>();
this.machineInstructionsToStaticInstructions = new TreeMap<>();
this.instructions = new HashMap<>();
List<String> commandLineArgumentList = Arrays.asList((contextMapping.getExecutable() + " " + contextMapping.getArguments()).split(" "));
String elfFileName = commandLineArgumentList.get(0);
ElfFile elfFile = new ElfFile(elfFileName);
for (ElfSectionHeader sectionHeader : elfFile.getSectionHeaders()) {
if (sectionHeader.getName().equals(".dynamic")) {
throw new IllegalArgumentException("dynamic linking is not supported");
}
if (sectionHeader.getType() == ElfSectionHeader.SHT_PROGBITS || sectionHeader.getType() == ElfSectionHeader.SHT_NOBITS) {
if (sectionHeader.getSize() > 0 && (sectionHeader.getFlags() & ElfSectionHeader.SHF_ALLOC) != 0) {
// this.memory.map((int) sectionHeader.getAddress(), (int) sectionHeader.getSize()));
if (sectionHeader.getType() == ElfSectionHeader.SHT_NOBITS) {
this.getMemory().zero((int) sectionHeader.getAddress(), (int) sectionHeader.getSize());
} else {
this.getMemory().writeBlock((int) sectionHeader.getAddress(), (int) sectionHeader.getSize(), sectionHeader.readContent(elfFile));
if ((sectionHeader.getFlags() & ElfSectionHeader.SHF_EXECINSTR) != 0) {
this.instructions.put(sectionHeader.getName(), new TreeMap<>());
for (int i = 0; i < (int) sectionHeader.getSize(); i += 4) {
int pc = (int) sectionHeader.getAddress() + i;
this.predecode(sectionHeader.getName(), this.getMemory(), pc);
}
}
}
if (sectionHeader.getAddress() >= DATA_BASE) {
this.setDataTop((int) Math.max(this.getDataTop(), sectionHeader.getAddress() + sectionHeader.getSize() - 1));
}
}
}
if (sectionHeader.getName().equals(".text")) {
this.setTextSize((int) (sectionHeader.getAddress() + sectionHeader.getSize() - TEXT_BASE));
}
}
this.setProgramEntry((int) elfFile.getHeader().getEntry());
this.setHeapTop(roundUp(this.getDataTop(), Memory.getPageSize()));
this.setStackBase(STACK_BASE);
// this.stackSize = STACK_SIZE; //TODO
this.setStackSize(MAX_ENVIRON);
this.setEnvironmentBase(STACK_BASE - MAX_ENVIRON);
// this.memory.map(this.stackBase - this.stackSize, this.stackSize);
this.getMemory().zero(this.getStackBase() - this.getStackSize(), this.getStackSize());
int stackPointer = this.getEnvironmentBase();
this.getMemory().writeWord(stackPointer, commandLineArgumentList.size());
stackPointer += 4;
int argAddress = stackPointer;
stackPointer += (commandLineArgumentList.size() + 1) * 4;
int environmentAddress = stackPointer;
stackPointer += (this.getEnvironments().size() + 1) * 4;
for (int i = 0; i < commandLineArgumentList.size(); i++) {
this.getMemory().writeWord(argAddress + i * 4, stackPointer);
stackPointer += this.getMemory().writeString(stackPointer, commandLineArgumentList.get(i));
}
this.getMemory().writeWord(argAddress + commandLineArgumentList.size() * 4, 0);
for (int i = 0; i < this.getEnvironments().size(); i++) {
this.getMemory().writeWord(environmentAddress + i * 4, stackPointer);
stackPointer += this.getMemory().writeString(stackPointer, this.getEnvironments().get(i));
}
this.getMemory().writeWord(environmentAddress + this.getEnvironments().size() * 4, 0);
if (stackPointer > this.getStackBase()) {
throw new IllegalArgumentException("'environ' overflow, increment MAX_ENVIRON");
}
this.elfAnalyzer = new ElfAnalyzer(elfFileName, elfFile, this.instructions, this.getProgramEntry());
this.elfAnalyzer.buildControlFlowGraphs();
this.pcToFunctionNameMappingCache = new TreeMap<>();
}
/**
* Predecode the instruction at the specified program counter (PC).
*
* @param sectionName the section name
* @param memory the memory
* @param pc the program counter (PC)
*/
private void predecode(String sectionName, Memory memory, int pc) {
int machineInstruction = memory.readWord(pc);
this.pcsToMachineInstructions.put(pc, machineInstruction);
if (!this.machineInstructionsToStaticInstructions.containsKey(machineInstruction)) {
StaticInstruction staticInstruction = this.decode(machineInstruction);
this.machineInstructionsToStaticInstructions.put(machineInstruction, staticInstruction);
}
this.instructions.get(sectionName).put(pc, new Instruction(this, pc, this.getStaticInstruction(pc)));
}
/**
* Get the disassembly instruction at the specified program counter (PC) for the specified process.
*
* @param process the process
* @param pc the program counter (PC)
* @return the disassembly instruction at the specified program counter (PC) for the specified progress
*/
public static String getDisassemblyInstruction(Process process, int pc) {
return MipsDisassembler.disassemble(pc, process.getStaticInstruction(pc));
}
@Override
public StaticInstruction getStaticInstruction(int pc) {
return this.machineInstructionsToStaticInstructions.get(this.pcsToMachineInstructions.get(pc));
}
@Override
public String getFunctionNameFromPc(int pc) {
if (this.pcToFunctionNameMappingCache.containsKey(pc)) {
return this.pcToFunctionNameMappingCache.get(pc);
}
for (Function function : this.elfAnalyzer.getProgram().getFunctions()) {
for (BasicBlock basicBlock : function.getBasicBlocks()) {
for (Instruction instruction : basicBlock.getInstructions()) {
if (instruction.getPc() == pc) {
String functionName = function.getSymbol().getName();
this.pcToFunctionNameMappingCache.put(pc, functionName);
return functionName;
}
}
}
}
return null;
}
@Override
public ElfAnalyzer getElfAnalyzer() {
return elfAnalyzer;
}
}