RetroLinker
Linker for several 8-bit, 16-bit and 32-bit formats
Loading...
Searching...
No Matches
mzexe.cc
1
2#include <cppunit/extensions/HelperMacros.h>
3#include <cppunit/TestFixture.h>
4
5#include "../../src/format/mzexe.h"
6
7using namespace Linker;
8using namespace Microsoft;
9
10namespace UnitTests
11{
12
13class TestMZFormat : public CppUnit::TestFixture
14{
15 CPPUNIT_TEST_SUITE(TestMZFormat);
16 CPPUNIT_TEST(testCreateEXE);
17 CPPUNIT_TEST(testEXEFileSize);
18 CPPUNIT_TEST(testEXEHeaderValues);
19 CPPUNIT_TEST(testEXERelocations);
20 CPPUNIT_TEST(testEXEHeaderSize);
21 CPPUNIT_TEST_SUITE_END();
22private:
23 MZFormat exe;
24 std::shared_ptr<Section> code;
25
27 void testCreateEXE();
28 void testEXEFileSize();
29 void testEXEHeaderValues();
30 void testEXERelocations();
31 void testEXEHeaderSize();
32
33 std::string store();
34 void load(std::string data);
35
36 std::string generate_image(size_t size);
37 void set_image(std::string data);
38 void test_image(std::string data);
39
40 std::vector<MZFormat::Relocation> generate_relocations(size_t count);
41 void test_relocations(std::vector<MZFormat::Relocation>& relocations);
42public:
43 void setUp();
44 void tearDown();
45};
46
47void TestMZFormat::testCreateEXE()
48{
49 exe.CalculateValues();
50 std::string image = store();
51 load(image);
52 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
53 CPPUNIT_ASSERT(exe.GetHeaderSize() >= 0x20);
54 test_image("");
55}
56
57void TestMZFormat::testEXEFileSize()
58{
59 std::string data;
60
61 data = generate_image(1);
62 exe.Clear();
63 set_image(data);
64 exe.CalculateValues();
65 load(store());
66 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
67 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
68 test_image(data);
69
70 /* check roll over */
71
72 data = generate_image(511 - 0x20);
73 exe.Clear();
74 set_image(data);
75 exe.CalculateValues();
76 load(store());
77 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
78 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
79 test_image(data);
80
81 data = generate_image(512 - 0x20);
82 exe.Clear();
83 set_image(data);
84 exe.CalculateValues();
85 load(store());
86 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
87 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
88 test_image(data);
89
90 data = generate_image(513 - 0x20);
91 exe.Clear();
92 set_image(data);
93 exe.CalculateValues();
94 load(store());
95 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
96 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
97 test_image(data);
98
99 data = generate_image(1023 - 0x20);
100 exe.Clear();
101 set_image(data);
102 exe.CalculateValues();
103 load(store());
104 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
105 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
106 test_image(data);
107
108 data = generate_image(1024 - 0x20);
109 exe.Clear();
110 set_image(data);
111 exe.CalculateValues();
112 load(store());
113 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
114 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
115 test_image(data);
116
117 data = generate_image(1025 - 0x20);
118 exe.Clear();
119 set_image(data);
120 exe.CalculateValues();
121 load(store());
122 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
123 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
124 test_image(data);
125
126 /* check largest file size */
127
128 if(true)
129 return; /* ignore this test, takes a while to run */
130
131 data = generate_image((0xFFFF << 9) - 0x20);
132 exe.Clear();
133 set_image(data);
134 exe.CalculateValues();
135 load(store());
136 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
137 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
138 test_image(data);
139}
140
141void TestMZFormat::testEXEHeaderValues()
142{
143 std::string data;
144
145 static const uint16_t min_extra_paras = 0x1234;
146 static const uint16_t max_extra_paras = 0x5678;
147 static const uint16_t cs = 0x0102;
148 static const uint16_t ip = 0x0304;
149 static const uint16_t ss = 0x0506;
150 static const uint16_t sp = 0x0506;
151 static const uint16_t overlay_number = 0xABCD;
152
153 /* TODO: also test checksum */
154
155 data = generate_image((cs << 4) + ip + 1 - 0x20);
156 exe.Clear();
157 set_image(data);
158 exe.SetSignature(MZFormat::MAGIC_ZM);
159 exe.min_extra_paras = min_extra_paras;
160 exe.extra_paras = max_extra_paras - min_extra_paras;
161 exe.cs = cs;
162 exe.ip = ip;
163 exe.ss = ss;
164 exe.sp = sp;
165 exe.overlay_number = overlay_number;
166 exe.CalculateValues();
167 load(store());
168 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_ZM, exe.GetSignature());
169 CPPUNIT_ASSERT(exe.GetHeaderSize() == 0x20);
170 test_image(data);
171 CPPUNIT_ASSERT_EQUAL(min_extra_paras, exe.min_extra_paras);
172 CPPUNIT_ASSERT_EQUAL(max_extra_paras, exe.max_extra_paras);
173 CPPUNIT_ASSERT_EQUAL(overlay_number, exe.overlay_number);
174 CPPUNIT_ASSERT_EQUAL(cs, exe.cs);
175 CPPUNIT_ASSERT_EQUAL(ip, exe.ip);
176 CPPUNIT_ASSERT_EQUAL(ss, exe.ss);
177 CPPUNIT_ASSERT_EQUAL(sp, exe.sp);
178}
179
180void TestMZFormat::testEXERelocations()
181{
182 std::vector<MZFormat::Relocation> relocations;
183 std::string data;
184
185 relocations = generate_relocations(1);
186 data = generate_image(2);
187 exe.Clear();
188 set_image(data);
189 exe.relocations = relocations;
190 exe.CalculateValues();
191 load(store());
192 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
193 CPPUNIT_ASSERT(exe.relocation_offset >= 0x1C);
194 CPPUNIT_ASSERT(exe.GetHeaderSize() >= exe.relocation_offset + 4 * exe.relocations.size());
195 test_relocations(relocations);
196 test_image(data);
197
198 /* test repositioned 0 relocations */
199
200 static const uint16_t relocation_offset1 = 0x56;
201 exe.Clear();
202 set_image(data);
203 exe.relocation_offset = relocation_offset1;
204 exe.CalculateValues();
205 load(store());
206 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
207 CPPUNIT_ASSERT_EQUAL(relocation_offset1, exe.relocation_offset);
208 CPPUNIT_ASSERT_EQUAL(0ul, exe.relocations.size());
209 CPPUNIT_ASSERT(exe.GetHeaderSize() >= exe.relocation_offset);
210 test_image(data);
211
212 /* test ill position relocation being overridden */
213
214 static const uint16_t relocation_offset2 = 0x03;
215 relocations = generate_relocations(1);
216 exe.Clear();
217 set_image(data);
218 exe.relocation_offset = relocation_offset2;
219 exe.relocations = relocations;
220 exe.CalculateValues();
221 load(store());
222 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
223// std::cout << exe.relocation_offset << std::endl;
224 CPPUNIT_ASSERT(exe.relocation_offset >= 0x1C);
225 CPPUNIT_ASSERT(exe.GetHeaderSize() >= exe.relocation_offset + 4 * exe.relocations.size());
226 test_relocations(relocations);
227 test_image(data);
228
229 /* test too many relocations */
230
231 relocations = generate_relocations(0x3FF8);
232 exe.Clear();
233 set_image(data);
234 exe.relocations = relocations;
235 CPPUNIT_ASSERT_THROW(exe.CalculateValues(), Exception);
236
237 /* test invalid relocation offset */
238
239 static const uint16_t relocation_offset3 = 0xFFFF;
240 relocations = generate_relocations(1);
241 exe.Clear();
242 exe.relocation_offset = relocation_offset3;
243 exe.relocations = relocations;
244 CPPUNIT_ASSERT_THROW(exe.CalculateValues(), Exception);
245}
246
247void TestMZFormat::testEXEHeaderSize()
248{
249 std::vector<MZFormat::Relocation> relocations;
250 std::string data;
251
252 /* normal */
253
254 offset_t normal_header_size;
255
256 data = generate_image(4);
257 relocations = generate_relocations(4);
258 exe.Clear();
259 set_image(data);
260 exe.relocations = relocations;
261 exe.CalculateValues();
262 load(store());
263 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
264 CPPUNIT_ASSERT(exe.relocation_offset >= 0x1C);
265 CPPUNIT_ASSERT(exe.GetHeaderSize() >= exe.relocation_offset + 4 * exe.relocations.size());
266 test_relocations(relocations);
267 test_image(data);
268
269 normal_header_size = exe.GetHeaderSize();
270
271 offset_t header_align = 0x10;
272 while(header_align <= normal_header_size)
273 header_align <<= 1;
274
275 /* higher alignment */
276
277 data = generate_image(4);
278 relocations = generate_relocations(4);
279 exe.Clear();
280 set_image(data);
281 exe.relocations = relocations;
282 exe.option_header_align = header_align;
283 exe.CalculateValues();
284 load(store());
285 CPPUNIT_ASSERT_EQUAL(MZFormat::MAGIC_MZ, exe.GetSignature());
286 CPPUNIT_ASSERT(exe.relocation_offset >= 0x1C);
287 CPPUNIT_ASSERT(exe.GetHeaderSize() >= exe.relocation_offset + 4 * exe.relocations.size());
288 CPPUNIT_ASSERT((exe.GetHeaderSize() & (header_align - 1)) == 0);
289 test_relocations(relocations);
290 test_image(data);
291}
292
293void TestMZFormat::setUp()
294{
295 exe.Clear();
296 code = std::make_shared<Linker::Section>(".code");
297}
298
299void TestMZFormat::tearDown()
300{
301 exe.Clear();
302 code = nullptr;
303}
304
305std::string TestMZFormat::store()
306{
307 std::string image;
308 std::ostringstream out;
309 Writer wr(::Undefined, &out);
310 exe.WriteFile(wr);
311 return out.str();
312}
313
314void TestMZFormat::load(std::string data)
315{
316 std::istringstream in(data);
317 Reader rd(::Undefined, &in);
318 CPPUNIT_ASSERT_NO_THROW(exe.ReadFile(rd));
319}
320
321std::string TestMZFormat::generate_image(size_t size)
322{
323 /* first it generates a sequence of 256 sequential bytes, but then the next 256 bytes are incremented by 3 each, then 5, then 7 etc. */
324 std::ostringstream out;
325 for(size_t i = 0; i < size; i++)
326 {
327 int off = i & 0xFF;
328 size_t inc = (i >> 7) | 1;
329 out.put(char(inc * off));
330 }
331 return out.str();
332}
333
334void TestMZFormat::set_image(std::string data)
335{
336 code->Reset();
337 code->Append(data.c_str(), data.size());
338 std::shared_ptr<Linker::Segment> segment = std::make_shared<Linker::Segment>(".code");
339 segment->Append(code);
340 exe.image = segment;
341/* if(exe.image_segment)
342 delete exe.image_segment;
343 exe.image_segment = segment;*/
344}
345
346void TestMZFormat::test_image(std::string data)
347{
348 uint32_t file_size = exe.GetHeaderSize() + data.size();
349 CPPUNIT_ASSERT_EQUAL(file_size & 0x1FF, (uint32_t)exe.last_block_size);
350 CPPUNIT_ASSERT_EQUAL((file_size + 0x1FF) >> 9, (uint32_t)exe.file_size_blocks);
351 CPPUNIT_ASSERT_EQUAL(file_size, exe.GetFileSize());
352 std::shared_ptr<Linker::Buffer> buffer = std::dynamic_pointer_cast<Linker::Buffer>(exe.image);
353 assert(buffer != nullptr); /* internal check */
354 CPPUNIT_ASSERT_EQUAL(data.size(), buffer->ActualDataSize());
355 for(uint32_t i = 0; i < data.size(); i++)
356 {
357 CPPUNIT_ASSERT_EQUAL(data[i] & 0xFF, buffer->GetByte(i) & 0xFF);
358 }
359}
360
361std::vector<MZFormat::Relocation> TestMZFormat::generate_relocations(size_t count)
362{
363 std::vector<MZFormat::Relocation> relocations;
364 for(size_t i = 0; i < count; i++)
365 {
366 int off = i & 0xFF;
367 size_t inc = (i >> 7) | 1;
368 off = (off * inc) & 0xFF;
369 relocations.push_back(MZFormat::Relocation(off * 0x0102, off * 0x0304));
370 }
371 return relocations;
372}
373
374void TestMZFormat::test_relocations(std::vector<MZFormat::Relocation>& relocations)
375{
376 /* TODO: relocations do not have to be exactly the same, in the exact same order */
377 CPPUNIT_ASSERT_EQUAL(relocations.size(), exe.relocations.size());
378 for(size_t i = 0; i < relocations.size(); i++)
379 {
380 CPPUNIT_ASSERT(relocations[i] == exe.relocations[i]);
381 }
382}
383
384}
385
Definition common.h:121
A helper class, encapsulating functionality needed to import binary data.
Definition reader.h:16
A helper class, encapsulating functionality needed to export binary data.
Definition writer.h:15
MZ .EXE format for MS-DOS.
Definition mzexe.h:35
void WriteFile(Linker::Writer &wr) override
Stores data in memory to file.
Definition mzexe.cc:234
uint16_t relocation_offset
Offset to first relocation. Updated by CalculateValues.
Definition mzexe.h:79
uint16_t cs
Initial value for the code segment (CS)
Definition mzexe.h:77
void ReadFile(Linker::Reader &rd) override
Loads file into memory.
Definition mzexe.cc:174
uint16_t min_extra_paras
Minimum required extra memory, in paragraphs.
Definition mzexe.h:65
@ MAGIC_ZM
According to some sources such as Ralf Brown's interrupt list, some early excutables started with the...
Definition mzexe.h:45
@ MAGIC_MZ
The most common magic number "MZ".
Definition mzexe.h:43
uint16_t last_block_size
Size of last 512 byte block, 0 if full. Set by CalculateValues.
Definition mzexe.h:54
uint16_t extra_paras
Required maximum extra paragraphs after bss.
Definition mzexe.h:193
void CalculateValues() override
Intermediate step between processing module and generating output file to set up headers and manageme...
Definition mzexe.cc:318
uint16_t file_size_blocks
Size of MZ image in 512 blocks, rounded up. Set by CalculateValues.
Definition mzexe.h:56
std::shared_ptr< Linker::Writable > image
The program image, placed after the MZ header.
Definition mzexe.h:145
void Clear() override
Resets all fields to their default values, deallocate memory.
Definition mzexe.cc:148
uint16_t ip
Entry point initial value for IP.
Definition mzexe.h:75
uint16_t sp
Initial value for the stack (SP)
Definition mzexe.h:71
std::vector< Relocation > relocations
Address relocation offsets to paragraph fixups.
Definition mzexe.h:113
uint32_t option_header_align
User provided alignment value for header size.
Definition mzexe.h:199
uint16_t ss
Initial value for the stack segment (SS)
Definition mzexe.h:69
uint16_t overlay_number
Overlay number, should be 0 for main programs, not used for .exm files.
Definition mzexe.h:82
uint16_t max_extra_paras
Maximum required extra memory, in paragraphs. Set by CalculateValues using extra_paras.
Definition mzexe.h:67
Definition mzexe.cc:14
Represents a relocation entry in the header, as a pair of 16-bit words.
Definition mzexe.h:92