FileLoader_aout.cc Source File

Back to the index.

FileLoader_aout.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009-2010 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 #include <iomanip>
32 
33 using std::setw;
34 using std::setfill;
35 using std::ifstream;
36 
37 #include "AddressDataBus.h"
39 #include "FileLoader_aout.h"
40 
41 #include "thirdparty/exec_aout.h"
42 
43 struct aout_symbol {
44  uint32_t strindex;
45  uint32_t type;
46  uint32_t addr;
47 };
48 
49 
50 FileLoader_aout::FileLoader_aout(const string& filename)
51  : FileLoaderImpl(filename)
52 {
53 }
54 
55 
56 string FileLoader_aout::DetectFileType(unsigned char *buf, size_t buflen, float& matchness) const
57 {
58  matchness = 0.9;
59 
60  if (buf[0]==0x00 && buf[1]==0x8b && buf[2]==0x01 && buf[3]==0x07) {
61  /* MIPS a.out */
62  return "a.out_MIPS";
63  }
64  if (buf[0]==0x00 && buf[1]==0x87 && buf[2]==0x01 && buf[3]==0x08) {
65  /* M68K a.out */
66  // AOUT_FLAG_VADDR_ZERO_HACK for OpenBSD/mac68k
67  return "a.out_M68K_vaddr0";
68  }
69 // Luna88k a.out for OpenBSD is _almost_ like for MVME88K... just a difference
70 // of entry point (?)
71 // if (buf[0]==0x00 && buf[1]==0x99 && buf[2]==0x01 && buf[3]==0x07) {
72 // /* OpenBSD/luna88k a.out */
73 // return "a.out_M88K_fromBeginningEntryAfterHeader";
74 // }
75  if (buf[0]==0x00 && buf[1]==0x99 && buf[2]==0x01 && buf[3]==0x0b) {
76  /* OpenBSD/M88K a.out */
77  return "a.out_M88K_fromBeginning";
78  }
79  if (buf[0]==0x00 && buf[1]==0x8f && buf[2]==0x01 && buf[3]==0x0b) {
80  /* ARM a.out */
81  return "a.out_ARM_fromBeginning";
82  }
83  if (buf[0]==0x00 && buf[1]==0x86 && buf[2]==0x01 && buf[3]==0x0b) {
84  /* i386 a.out (old OpenBSD and NetBSD etc) */
85  return "a.out_i386_fromBeginning";
86  }
87  if (buf[0]==0x01 && buf[1]==0x03 && buf[2]==0x01 && buf[3]==0x07) {
88  /* SPARC a.out (old 32-bit NetBSD etc) */
89  return "a.out_SPARC_noSizes";
90  }
91  if (buf[0]==0x00 && buf[2]==0x00 && buf[8]==0x7a && buf[9]==0x75) {
92  /* DEC OSF1 on MIPS: */
93  return "a.out_MIPS_osf1";
94  }
95 
96  matchness = 0.0;
97  return "";
98 }
99 
100 
101 static uint32_t unencode32(uint8_t *p, Endianness endianness)
102 {
103  uint32_t res;
104 
105  if (endianness == BigEndian)
106  res = p[3] + (p[2] << 8) + (p[1] << 16) + (p[0] << 24);
107  else
108  res = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
109 
110  return res;
111 }
112 
113 
114 bool FileLoader_aout::LoadIntoComponent(refcount_ptr<Component> component, ostream& messages) const
115 {
116  AddressDataBus* bus = component->AsAddressDataBus();
117  if (bus == NULL) {
118  messages << "Target is not an AddressDataBus.\n";
119  return false;
120  }
121 
122  ifstream file(Filename().c_str());
123  if (!file.is_open()) {
124  messages << "Unable to read file.\n";
125  return false;
126  }
127 
128  unsigned char buf[65536];
129 
130  memset(buf, 0, sizeof(buf));
131  file.seekg(0, std::ios_base::end);
132  uint64_t totalSize = file.tellg();
133  file.seekg(0, std::ios_base::beg);
134  file.read((char *)buf, totalSize < sizeof(buf)? totalSize : sizeof(buf));
135  size_t amountRead = file.gcount();
136 
137  float matchness;
138  string format = DetectFileType(buf, amountRead, matchness);
139 
140  if (format == "") {
141  messages << "Unknown a.out format.\n";
142  return false;
143  }
144 
145  file.seekg(0, std::ios_base::beg);
146 
147  Endianness endianness = BigEndian;
148  StateVariable* var = component->GetVariable("bigendian");
149  if (var == NULL) {
150  messages << "Target does not have the 'bigendian' variable,"
151  " which is needed to\n"
152  "load a.out files.\n";
153  return false;
154  }
155 
156  endianness = var->ToInteger() == 0? LittleEndian : BigEndian;
157 
158  struct exec aout_header;
159  uint32_t entry, datasize, textsize;
160  int32_t symbsize = 0;
161  uint32_t vaddr, total_len;
162 
163  if (format.substr(format.length()-5, 5) == "_osf1") {
164  file.read((char *)buf, 32);
165  if (file.gcount() != 32) {
166  messages << "The file is too small to be an OSF1 a.out.\n";
167  return false;
168  }
169 
170  vaddr = unencode32(buf + 16, endianness);
171  entry = unencode32(buf + 20, endianness);
172 
173  symbsize = 0;
174  file.seekg(0, std::ios_base::end);
175  /* This is of course wrong, but should work anyway: */
176  textsize = (uint32_t) file.tellg() - 512;
177  datasize = 0;
178  file.seekg(512, std::ios_base::beg);
179  } else if (format.substr(format.length()-8, 8) == "_noSizes") {
180  file.seekg(0, std::ios_base::end);
181  textsize = (uint32_t) file.tellg() - 32;
182  datasize = 0;
183  vaddr = entry = 0;
184  file.seekg(32, std::ios_base::beg);
185  } else {
186  file.read((char *)&aout_header, sizeof(aout_header));
187  if (file.gcount() != sizeof(aout_header)) {
188  messages << "The file is too small to be an a.out.\n";
189  return false;
190  }
191 
192  entry = unencode32((unsigned char*)&aout_header.a_entry, endianness);
193 
194  vaddr = entry;
195 
196  if (format.substr(format.length()-7, 7) == "_vaddr0")
197  vaddr = 0;
198 
199  textsize = unencode32((unsigned char*)&aout_header.a_text, endianness);
200  datasize = unencode32((unsigned char*)&aout_header.a_data, endianness);
201  symbsize = unencode32((unsigned char*)&aout_header.a_syms, endianness);
202  }
203 
204  if (format.substr(format.length()-14, 14) == "_fromBeginning") {
205  file.seekg(0, std::ios_base::beg);
206  vaddr &= ~0xfff;
207  }
208 
209  messages.flags(std::ios::hex);
210  messages << "a.out: entry point 0x";
211  messages << setw(8) << setfill('0') << (uint32_t) entry << "\n";
212 
213  messages.flags(std::ios::dec);
214  messages << "text + data = " << textsize << " + " << datasize << " bytes\n";
215 
216  // Load text and data:
217  total_len = textsize + datasize;
218  while (total_len != 0) {
219  int len = total_len > sizeof(buf) ? sizeof(buf) : total_len;
220  file.read((char *)buf, len);
221  len = file.gcount();
222  if (len < 1)
223  break;
224 
225  // Write to the bus, one byte at a time.
226  for (int k=0; k<len; ++k) {
227  bus->AddressSelect(vaddr);
228  if (!bus->WriteData(buf[k])) {
229  messages.flags(std::ios::hex);
230  messages << "Failed to write data to virtual "
231  "address 0x" << vaddr << "\n";
232  return false;
233  }
234 
235  ++ vaddr;
236  }
237 
238  total_len -= len;
239  }
240 
241  if (total_len != 0) {
242  messages << "Failed to read the entire file.\n";
243  return false;
244  }
245 
246  SymbolRegistry* symbolRegistry = NULL;
247  CPUComponent* cpu = component->AsCPUComponent();
248  if (cpu != NULL)
249  symbolRegistry = &cpu->GetSymbolRegistry();
250 
251  // Symbols:
252  if (symbolRegistry != NULL && symbsize > 0) {
253  messages.flags(std::ios::dec);
254  messages << "symbols: " << symbsize << " bytes at 0x";
255  messages.flags(std::ios::hex);
256  messages << file.tellg() << "\n";
257 
258  vector<char> symbolData;
259  symbolData.resize(symbsize);
260  file.read(&symbolData[0], symbsize);
261  if (file.gcount() != symbsize) {
262  messages << "Failed to read all symbols.\n";
263  return false;
264  }
265 
266  off_t oldpos = file.tellg();
267  file.seekg(0, std::ios_base::end);
268  size_t strings_len = (off_t)file.tellg() - oldpos;
269  file.seekg(oldpos, std::ios_base::beg);
270 
271  messages.flags(std::ios::dec);
272  messages << "strings: " << strings_len << " bytes at 0x";
273  messages.flags(std::ios::hex);
274  messages << file.tellg() << "\n";
275 
276  vector<char> symbolStrings;
277  // Note: len + 1 for a nul terminator, for safety.
278  symbolStrings.resize(strings_len + 1);
279  file.read(&symbolStrings[0], strings_len);
280  if ((size_t)file.gcount() != strings_len) {
281  messages << "Failed to read all strings.\n";
282  return false;
283  }
284 
285  assert(sizeof(struct aout_symbol) == 12);
286 
287  int nsymbols = 0;
288 
289  struct aout_symbol* aout_symbol_ptr = (struct aout_symbol *) (void*) &symbolData[0];
290  int n_symbols = symbsize / sizeof(struct aout_symbol);
291  for (int i = 0; i < n_symbols; i++) {
292  uint32_t index = unencode32((unsigned char*)&aout_symbol_ptr[i].strindex, endianness);
293  uint32_t type = unencode32((unsigned char*)&aout_symbol_ptr[i].type, endianness);
294  uint32_t addr = unencode32((unsigned char*)&aout_symbol_ptr[i].addr, endianness);
295 
296  // TODO: These bits probably mean different things for
297  // different a.out formats. For OpenBSD/m88k at least,
298  // this bit (0x01000000) seems to mean "a normal symbol".
299  if (!(type & 0x01000000))
300  continue;
301 
302  // ... and the rectangle drawing demo says
303  // "_my_memset" at addr 1020, type 5020000
304  // "rectangles_m88k_O2.o" at addr 1020, type 1f000000
305  if ((type & 0x1f000000) == 0x1f000000)
306  continue;
307 
308  if (index >= (uint32_t)strings_len) {
309  messages << "symbol " << i << " has invalid string index\n";
310  continue;
311  }
312 
313  string symbol = ((char*) &symbolStrings[0]) + index;
314  if (symbol == "")
315  continue;
316 
317  // messages << "\"" << symbol << "\" at addr " << addr
318  // << ", type " << type << "\n";
319 
320  // Add this symbol to the symbol registry:
321  symbolRegistry->AddSymbol(symbol, addr);
322  ++ nsymbols;
323  }
324 
325  messages.flags(std::ios::dec);
326  messages << nsymbols << " symbols read\n";
327  }
328 
329  // Set the CPU's entry point.
330  stringstream ss;
331  ss << (int64_t)(int32_t)entry;
332  component->SetVariableValue("pc", ss.str());
333 
334  return true;
335 }
336 
337 
338 /*****************************************************************************/
339 
340 
341 #ifdef WITHUNITTESTS
342 
343 #include "ComponentFactory.h"
344 
345 static void Test_FileLoader_aout_Constructor()
346 {
347  FileLoader_aout aoutLoader("test/FileLoader_A.OUT_M88K");
348 }
349 
351 {
352  UNITTEST(Test_FileLoader_aout_Constructor);
353 
354  // TODO
355 }
356 
357 #endif
exec::a_data
uint32_t a_data
Definition: exec_aout.h:51
ComponentFactory.h
aout_symbol::addr
uint32_t addr
Definition: file_aout.cc:45
exec::a_syms
uint32_t a_syms
Definition: exec_aout.h:53
LittleEndian
@ LittleEndian
Definition: misc.h:159
exec::a_entry
uint32_t a_entry
Definition: exec_aout.h:54
StateVariable
StateVariables make up the persistent state of Component objects.
Definition: StateVariable.h:69
refcount_ptr< Component >
StateVariable::ToInteger
uint64_t ToInteger() const
Returns the variable as an unsignedinteger value.
Definition: StateVariable.cc:280
UNITTESTS
#define UNITTESTS(class)
Helper for unit test case execution.
Definition: UnitTest.h:184
addr
uint32_t addr
Definition: tmp_arm_multi.cc:52
FileLoader_aout::DetectFileType
string DetectFileType(unsigned char *buf, size_t buflen, float &matchness) const
Attempt to detect file type.
Definition: FileLoader_aout.cc:56
UNITTEST
#define UNITTEST(functionname)
Helper for unit test case execution.
Definition: UnitTest.h:217
SymbolRegistry::AddSymbol
void AddSymbol(const string &symbol, uint64_t vaddr)
Adds a symbol to the registry.
Definition: SymbolRegistry.cc:42
AddressDataBus::WriteData
virtual bool WriteData(const uint8_t &data, Endianness endianness=BigEndian)=0
Writes 8-bit data to the currently selected address.
SymbolRegistry
A registry for loaded symbols.
Definition: SymbolRegistry.h:41
Endianness
Endianness
Definition: misc.h:157
FileLoader_aout.h
FileLoader_aout
a.out binary loader.
Definition: FileLoader_aout.h:46
aout_symbol::strindex
uint32_t strindex
Definition: file_aout.cc:43
aout_symbol::type
uint32_t type
Definition: file_aout.cc:44
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.
aout_symbol
Definition: file_aout.cc:42
CPUComponent.h
symbol
Definition: symbol.h:37
FileLoader_aout::LoadIntoComponent
bool LoadIntoComponent(refcount_ptr< Component > component, ostream &messages) const
Loads the file into a Component.
Definition: FileLoader_aout.cc:114
FileLoader_aout::FileLoader_aout
FileLoader_aout(const string &filename)
Definition: FileLoader_aout.cc:50
FileLoaderImpl
A file loader.
Definition: FileLoaderImpl.h:42
Component::GetVariable
StateVariable * GetVariable(const string &name)
Gets a pointer to a state variable.
Definition: Component.cc:949
exec
Definition: exec_aout.h:48
exec::a_text
uint32_t a_text
Definition: exec_aout.h:50
cpu
Definition: cpu.h:326
exec_aout.h
Component::AsCPUComponent
virtual CPUComponent * AsCPUComponent()
Returns the component's CPUComponent interface.
Definition: Component.cc:360
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
FileLoaderImpl::Filename
const string & Filename() const
Definition: FileLoaderImpl.h:81
CPUComponent
A base-class for processors Component implementations.
Definition: CPUComponent.h:47

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