1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
/* SPDX-License-Identifier: Unlicense
*/
#include "coff_image.h"
#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#ifdef __GNUC__
#define _PRINTF(strindex, first) __attribute__((format(printf, strindex, first)))
#else
#define _PRINTF(strindex, first)
#endif
static _PRINTF(1, 2) char *Error(const char *fmt, ...)
{
if (0 == strlen(fmt)) {
return nullptr;
}
char *error{};
size_t size{};
FILE *error_stream = open_memstream(&error, &size);
assert(error_stream);
va_list ap;
va_start(ap, fmt);
vfprintf(error_stream, fmt, ap);
va_end(ap);
fclose(error_stream);
assert(error != nullptr);
assert(*error != '\0');
return error;
}
static char *ValidateCOFF(const DataView& d)
{
using namespace COFF;
size_t expected_size = kFileHeaderSize;
if (d.size < expected_size) {
return Error(
"data size (%zu) is too low, expected at least %zu: "
"COFF header could not fit",
d.size,
expected_size);
}
const auto header = FileHeader::FromBytes(d.buffer);
if (header.magic != FileHeader::kMagicSierraM68k) {
return Error(
"COFF Magic is invalid: expected 0x%04x, got 0x%04x",
FileHeader::kMagicSierraM68k,
header.magic);
}
const size_t oh_nbytes = header.optional_header_nbytes;
if (oh_nbytes && oh_nbytes < kOptionalHeaderSize) {
return Error(
"COFF optional header size is invalid: expected 0 or at least %zu, got %zu",
kOptionalHeaderSize,
oh_nbytes);
}
expected_size += oh_nbytes;
if (d.size < expected_size) {
return Error(
"data size (%zu) is too low, expected at least %zu: "
"COFF optional header could not fit",
d.size,
expected_size);
}
expected_size += header.nsections * kSectionHeaderSize;
if (d.size < expected_size) {
return Error(
"data size (%zu) is too low, expected at least %zu: "
"%u sections headers could not fit",
d.size,
expected_size,
header.nsections);
}
if (header.symtable_offset > d.size) {
return Error(
"COFF symbol table offset is too big to fit into the file: "
"expected (<=%zu), got (%zu)",
size_t(d.size),
size_t(header.symtable_offset));
}
for (size_t i = 0; i < header.nsections; i++) {
const auto section = SectionHeader::FromBytes(d.buffer + COFF::kFileHeaderSize +
header.optional_header_nbytes + COFF::kSectionHeaderSize * i);
const size_t section_end = section.section_offset + section.size;
if (section_end > d.size) {
return Error(
"data size (%zu) is too low, expected at least %zu: "
"section %zu (%.8s) data could not fit",
d.size,
section_end,
i,
section.name);
}
}
return nullptr;
}
COFF::Image::Image(DataBuffer&& data)
: _data(static_cast<DataBuffer&&>(data))
, _error(ValidateCOFF(_data.View()))
, _file_header(_error ? COFF::FileHeader{} : COFF::FileHeader::FromBytes(_data.View().buffer))
, _has_optional_header(_error ? false : _file_header.optional_header_nbytes >= kOptionalHeaderSize)
, _optional_header(_has_optional_header
? COFF::OptionalHeader::FromBytes(_data.View().buffer)
: COFF::OptionalHeader{})
{}
COFF::Image::~Image()
{
if (_error) {
free(_error);
}
}
|