FileLoader.cc Source File

Back to the index.

FileLoader.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008-2019 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <assert.h>
29 #include <string.h>
30 #include <fstream>
31 
32 using std::ifstream;
33 
34 #include "AddressDataBus.h"
35 #include "Component.h"
36 #include "FileLoader.h"
37 #include "FileLoader_aout.h"
38 #include "FileLoader_bout.h"
39 #include "FileLoader_ELF.h"
40 #include "FileLoader_raw.h"
41 
42 
43 FileLoader::FileLoader(const string& filename)
44  : m_filename(filename)
45 {
46  m_fileLoaders.push_back(new FileLoader_aout(filename));
47  m_fileLoaders.push_back(new FileLoader_bout(filename));
48  m_fileLoaders.push_back(new FileLoader_ELF(filename));
49  m_fileLoaders.push_back(new FileLoader_raw(filename));
50 }
51 
52 
53 const string& FileLoader::GetFilename() const
54 {
55  return m_filename;
56 }
57 
58 
60 {
61  loader = NULL;
62 
63  // TODO: Disk images?
64 
65  ifstream file(m_filename.c_str());
66 
67  unsigned char buf[512];
68  memset(buf, 0, sizeof(buf));
69  size_t amountRead = 0;
70  if (file.is_open()) {
71  file.read((char *)buf, sizeof(buf));
72  amountRead = file.gcount();
73  }
74 
75  // Ask all file loaders about how well they handle the format. Return
76  // the format string from the loader that had the highest score.
77  float bestMatch = 0.0;
78  string bestFormat = "Unknown";
79  FileLoaderImplVector::const_iterator it = m_fileLoaders.begin();
80  for (; it != m_fileLoaders.end(); ++it) {
81  float match;
82  string format = (*it)->DetectFileType(buf, amountRead, match);
83  if (match > bestMatch) {
84  bestMatch = match;
85  bestFormat = format;
86  loader = *it;
87  }
88  }
89 
90  if (bestMatch == 0.0 && !file.is_open())
91  return "Not accessible";
92 
93  return bestFormat;
94 }
95 
96 
97 bool FileLoader::Load(refcount_ptr<Component> component, ostream& messages) const
98 {
99  AddressDataBus * bus = component->AsAddressDataBus();
100  if (bus == NULL) {
101  // We cannot load into something that isn't an AddressDataBus.
102  messages << "Error: " << component->GenerateShortestPossiblePath()
103  << " is not an AddressDataBus.\n";
104  return false;
105  }
106 
108  string fileFormat = DetectFileFormat(loaderImpl);
109 
110  if (!loaderImpl.IsNULL())
111  return loaderImpl->LoadIntoComponent(component, messages);
112 
113  messages << "Error: File format '" <<
114  fileFormat << "' not yet implemented. TODO\n";
115 
116  return false;
117 }
118 
119 
120 /*****************************************************************************/
121 
122 
123 #ifdef WITHUNITTESTS
124 
125 #include "ComponentFactory.h"
126 
127 static void Test_FileLoader_Constructor()
128 {
129  FileLoader fileLoader("test/FileLoader_ELF_MIPS");
130  UnitTest::Assert("filename mismatch?",
131  fileLoader.GetFilename(), "test/FileLoader_ELF_MIPS");
132 }
133 
134 static void Test_FileLoader_Constructor_NonExistingFile()
135 {
136  FileLoader fileLoader("test/Nonexisting");
137  UnitTest::Assert("filename mismatch?",
138  fileLoader.GetFilename(), "test/Nonexisting");
139 }
140 
141 static void Test_FileLoader_DetectFileFormat_NonexistingFile()
142 {
143  FileLoader fileLoader("test/Nonexisting");
145  UnitTest::Assert("file format detection failure?",
146  fileLoader.DetectFileFormat(loaderImpl), "Not accessible");
147 }
148 
149 static void Test_FileLoader_DetectFileFormat_NonsenseFile()
150 {
151  FileLoader fileLoader("test/FileLoader_NonsenseFile");
153  UnitTest::Assert("file format detection failure?",
154  fileLoader.DetectFileFormat(loaderImpl), "Unknown");
155 }
156 
157 static void Test_FileLoader_DetectFileFormat_ELF32()
158 {
159  FileLoader fileLoader("test/FileLoader_ELF_MIPS");
161  UnitTest::Assert("file format detection failure?",
162  fileLoader.DetectFileFormat(loaderImpl), "ELF32");
163 }
164 
165 static void Test_FileLoader_DetectFileFormat_ELF64()
166 {
167  FileLoader fileLoader("test/FileLoader_ELF_SH5");
169  UnitTest::Assert("file format detection failure?",
170  fileLoader.DetectFileFormat(loaderImpl), "ELF64");
171 }
172 
173 static void Test_FileLoader_DetectFileFormat_aout_88K()
174 {
175  FileLoader fileLoader("test/FileLoader_A.OUT_M88K");
177  UnitTest::Assert("file format detection failure?",
178  fileLoader.DetectFileFormat(loaderImpl), "a.out_M88K_fromBeginning");
179 }
180 
181 static void Test_FileLoader_DetectFileFormat_bout_i960()
182 {
183  FileLoader fileLoader("test/FileLoader_B.OUT_i960");
185  UnitTest::Assert("file format detection failure?",
186  fileLoader.DetectFileFormat(loaderImpl), "b.out_i960_little");
187 }
188 
189 static refcount_ptr<Component> SetupTestMachineAndLoad(
190  string machineName, string fileName, bool ramAtZero)
191 {
192  FileLoader fileLoader(fileName);
195  UnitTest::Assert("could not add machine type?", !machine.IsNULL());
196 
197  machine->SetVariableValue("name", "\"machine\"");
198  refcount_ptr<Component> component =
199  machine->LookupPath("machine.mainbus0.cpu0");
200  UnitTest::Assert("could not look up CPU to load into?",
201  !component.IsNULL());
202 
203  refcount_ptr<Component> ram = machine->LookupPath("machine.mainbus0.ram0");
204  UnitTest::Assert("could not look up RAM?", !ram.IsNULL());
205  if (ramAtZero)
206  ram->SetVariableValue("memoryMappedBase", "0");
207 
208  stringstream messages;
209  UnitTest::Assert("could not load the file " + fileName + " for"
210  " machine " + machineName, fileLoader.Load(component, messages));
211 
212  return machine;
213 }
214 
215 #if 0
216 static void Test_FileLoader_Load_ELF32()
217 {
218  // Yes, it is odd to load a MIPS binary into a RISC-V machine, but still...
220  SetupTestMachineAndLoad("riscv-virt", "test/FileLoader_ELF_MIPS", false);
221 
222  // Read from CPU, to make sure the file was loaded:
224  machine->LookupPath("machine.mainbus0.cpu0");
225  AddressDataBus * bus = cpu->AsAddressDataBus();
226  bus->AddressSelect((int32_t)0x00010000);
227  uint32_t word = 0x12345678;
228  bus->ReadData(word, BigEndian);
229  UnitTest::Assert("memory (CPU) wasn't filled with data from the file?",
230  word, 0x8f8b8008);
231 
232  // Read directly from RAM too, to make sure the file was loaded:
234  machine->LookupPath("machine.mainbus0.ram0");
235  AddressDataBus * ramBus = ram->AsAddressDataBus();
236  ramBus->AddressSelect(0x1000c);
237  uint32_t word2 = 0x12345678;
238  ramBus->ReadData(word2, BigEndian);
239  UnitTest::Assert("memory (RAM) wasn't filled with data from the file?",
240  word2, 0x006b7021);
241 }
242 #endif
243 
244 static void Test_FileLoader_Load_ELF64()
245 {
247  SetupTestMachineAndLoad("riscv-virt", "test/FileLoader_ELF_RISCV64", true);
248 
249  // Read from CPU, to make sure the file was loaded:
251  machine->LookupPath("machine.mainbus0.cpu0");
252  AddressDataBus * bus = cpu->AsAddressDataBus();
253  bus->AddressSelect((int32_t)0x00010078);
254  uint32_t word = 0x12345678;
255  bus->ReadData(word, BigEndian);
256  UnitTest::Assert("memory (CPU) wasn't filled with data from the file?",
257  word, 0x011122ec);
258 
259  // Read directly from RAM too, to make sure the file was loaded:
261  machine->LookupPath("machine.mainbus0.ram0");
262  AddressDataBus * ramBus = ram->AsAddressDataBus();
263  ramBus->AddressSelect(0x1007c);
264  uint32_t word2 = 0x12345678;
265  ramBus->ReadData(word2, BigEndian);
266  UnitTest::Assert("memory (RAM) wasn't filled with data from the file?",
267  word2, 0x0010aa87);
268 }
269 
270 static void Test_FileLoader_Load_aout()
271 {
273  SetupTestMachineAndLoad("testm88k", "test/FileLoader_A.OUT_M88K", true);
274 
275  // Read from CPU, to make sure the file was loaded:
277  machine->LookupPath("machine.mainbus0.cpu0");
278  AddressDataBus * bus = cpu->AsAddressDataBus();
279  bus->AddressSelect((int32_t)0x12b8);
280  uint32_t word = 0x12345678;
281  bus->ReadData(word, BigEndian);
282  UnitTest::Assert("memory (CPU) wasn't filled with data from the file?",
283  word, 0x67ff0020);
284 
285  // Read directly from RAM too, to make sure the file was loaded:
287  machine->LookupPath("machine.mainbus0.ram0");
288  AddressDataBus * ramBus = ram->AsAddressDataBus();
289  ramBus->AddressSelect(0x12e0);
290  uint32_t word2 = 0xfdecba98;
291  ramBus->ReadData(word2, BigEndian);
292  UnitTest::Assert("memory (RAM) wasn't filled with data from the file?",
293  word2, 0xf6c05802);
294 }
295 
297 {
298  UNITTEST(Test_FileLoader_Constructor);
299  UNITTEST(Test_FileLoader_Constructor_NonExistingFile);
300 
301  UNITTEST(Test_FileLoader_DetectFileFormat_NonexistingFile);
302  UNITTEST(Test_FileLoader_DetectFileFormat_NonsenseFile);
303  UNITTEST(Test_FileLoader_DetectFileFormat_ELF32);
304  UNITTEST(Test_FileLoader_DetectFileFormat_ELF64);
305  UNITTEST(Test_FileLoader_DetectFileFormat_aout_88K);
306  UNITTEST(Test_FileLoader_DetectFileFormat_bout_i960);
307 
308  // UNITTEST(Test_FileLoader_Load_ELF32);
309  UNITTEST(Test_FileLoader_Load_ELF64);
310  UNITTEST(Test_FileLoader_Load_aout);
311 }
312 
313 #endif
AddressDataBus::ReadData
virtual bool ReadData(uint8_t &data, Endianness endianness=BigEndian)=0
Reads 8-bit data from the currently selected address.
refcount_ptr::IsNULL
bool IsNULL() const
Checks whether or not an object is referenced by the reference counted pointer.
Definition: refcount_ptr.h:218
FileLoaderImpl::LoadIntoComponent
virtual bool LoadIntoComponent(refcount_ptr< Component > component, ostream &messages) const =0
Loads the file into a Component.
ComponentFactory.h
FileLoader_bout.h
refcount_ptr< const FileLoaderImpl >
UNITTESTS
#define UNITTESTS(class)
Helper for unit test case execution.
Definition: UnitTest.h:184
FileLoader_ELF
ELF binary loader.
Definition: FileLoader_ELF.h:46
FileLoader_raw
Raw binary loader (no specific format).
Definition: FileLoader_raw.h:46
FileLoader::Load
bool Load(refcount_ptr< Component > component, ostream &messages) const
Loads the file into a CPU or an AddressDataBus.
Definition: FileLoader.cc:97
FileLoader_ELF.h
FileLoader::DetectFileFormat
string DetectFileFormat(refcount_ptr< const FileLoaderImpl > &loader) const
Attempt to detect the file format of the file.
Definition: FileLoader.cc:59
UNITTEST
#define UNITTEST(functionname)
Helper for unit test case execution.
Definition: UnitTest.h:217
UnitTest::Assert
static void Assert(const string &strFailMessage, bool condition)
Asserts that a boolean condition is correct.
Definition: UnitTest.cc:40
FileLoader::FileLoader
FileLoader(const string &filename)
Constructs a FileLoader object.
Definition: FileLoader.cc:43
FileLoader_raw.h
machine
Definition: machine.h:97
FileLoader_aout.h
FileLoader_aout
a.out binary loader.
Definition: FileLoader_aout.h:46
ComponentFactory::CreateComponent
static refcount_ptr< Component > CreateComponent(const string &componentNameAndOptionalArgs, GXemul *gxemul=NULL)
Creates a component given a short component name.
Definition: ComponentFactory.cc:87
Component.h
FileLoader
A class used to load binary files into emulated memory.
Definition: FileLoader.h:55
Component::SetVariableValue
bool SetVariableValue(const string &name, const string &expression)
Sets a variable to a new value.
Definition: Component.cc:1030
AddressDataBus::AddressSelect
virtual void AddressSelect(uint64_t address)=0
Place an address on the bus.
Component::GenerateShortestPossiblePath
string GenerateShortestPossiblePath() const
Generates a short string representation of the path to the Component.
Definition: Component.cc:721
FileLoader::GetFilename
const string & GetFilename() const
Retrieves the filename of this FileLoader.
Definition: FileLoader.cc:53
FileLoader.h
FileLoader_bout
b.out binary loader.
Definition: FileLoader_bout.h:46
cpu
Definition: cpu.h:326
AddressDataBus
An interface for implementing components that read/write data via an address bus.
Definition: AddressDataBus.h:45
AddressDataBus.h
BigEndian
@ BigEndian
Definition: misc.h:158
Component::AsAddressDataBus
virtual AddressDataBus * AsAddressDataBus()
Returns the component's AddressDataBus interface, if any.
Definition: Component.cc:367

Generated on Tue Aug 25 2020 19:25:06 for GXemul by doxygen 1.8.18