summaryrefslogtreecommitdiff
path: root/src/coff_image.cpp
blob: c9717c1ab42568f663440f45ac1cb2213306c9a3 (plain)
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);
    }
}