summaryrefslogtreecommitdiff
path: root/tools/audio/elf32.h
blob: 109be5a2ed08667f0702187cea16541c5b457c22 (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/* SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET */
/* SPDX-License-Identifier: CC0-1.0 */
#ifndef ELF32_H_
#define ELF32_H_

#include <stdint.h>

#include "util.h"

#define elf32_read16(x)  be16toh(x)
#define elf32_write16(x) htobe16(x)
#define elf32_read32(x)  be32toh(x)
#define elf32_write32(x) htobe32(x)

#ifndef ELF32_QUALIFIERS
#define ELF32_QUALIFIERS static UNUSED ALWAYS_INLINE
#endif

#define GET_PTR(data, offset) ((void *)&((uint8_t *)(data))[(offset)])

#define EI_NIDENT     16
#define EI_MAG0       0x00
#define EI_MAG1       0x01
#define EI_MAG2       0x02
#define EI_MAG3       0x03
#define EI_CLASS      0x04
#define EI_DATA       0x05
#define EI_VERSION    0x06
#define EI_OSABI      0x07
#define EI_ABIVERSION 0x08
#define EI_PAD        0x09

typedef struct {
    uint8_t e_ident[EI_NIDENT];
    uint16_t e_type;
    uint16_t e_machine;
    uint32_t e_version;
    uint32_t e_entry;
    uint32_t e_phoff;
    uint32_t e_shoff;
    uint32_t e_flags;
    uint16_t e_ehsize;
    uint16_t e_phentsize;
    uint16_t e_phnum;
    uint16_t e_shentsize;
    uint16_t e_shnum;
    uint16_t e_shstrndx;
} Elf32_Ehdr;

#define ELF32_HAS_MAGIC(ehdr)                                                                                    \
    ((ehdr)->e_ident[EI_MAG0] == '\x7F' && (ehdr)->e_ident[EI_MAG1] == 'E' && (ehdr)->e_ident[EI_MAG2] == 'L' && \
     (ehdr)->e_ident[EI_MAG3] == 'F')

#define ELF32_IS_32(ehdr) ((ehdr)->e_ident[EI_CLASS] == 1 /*EI_CLASS_32*/)

#define ELF32_IS_BE(ehdr) ((ehdr)->e_ident[EI_DATA] == 2 /*EI_DATA_BE*/)

typedef struct {
    uint32_t sh_name;
    uint32_t sh_type;
    uint32_t sh_flags;
    uint32_t sh_addr;
    uint32_t sh_offset;
    uint32_t sh_size;
    uint32_t sh_link;
    uint32_t sh_info;
    uint32_t sh_addralign;
    uint32_t sh_entsize;
} Elf32_Shdr;

typedef struct {
    uint32_t st_name;
    uint32_t st_value;
    uint32_t st_size;
    uint8_t st_info;
    uint8_t st_other;
    uint16_t st_shndx;
} Elf32_Sym;

// sh_type

#define SHT_NULL          0x00000000
#define SHT_PROGBITS      0x00000001
#define SHT_SYMTAB        0x00000002
#define SHT_STRTAB        0x00000003
#define SHT_RELA          0x00000004
#define SHT_HASH          0x00000005
#define SHT_DYNAMIC       0x00000006
#define SHT_NOTE          0x00000007
#define SHT_NOBITS        0x00000008
#define SHT_REL           0x00000009
#define SHT_SHLIB         0x0000000A
#define SHT_DYNSYM        0x0000000B
#define SHT_INIT_ARRAY    0x0000000E
#define SHT_FINI_ARRAY    0x0000000F
#define SHT_PREINIT_ARRAY 0x00000010
#define SHT_GROUP         0x00000011
#define SHT_SYMTAB_SHNDX  0x00000012
#define SHT_NUM           0x00000013
#define SHT_LOOS          0x60000000
// MIPS specific
#define SHT_MIPS_DEBUG   0x70000005
#define SHT_MIPS_REGINFO 0x70000006
#define SHT_MIPS_OPTIONS 0x7000000D

// st_shndx

#define SHN_UND       0x0000
#define SHN_ABS       0xFFF1
#define SHN_COMMON    0xFFF2
#define SHN_LORESERVE 0xFF00
#define SHN_XINDEX    0xFFFF

// st_info [3:0]

#define ST_NOTYPE  0
#define ST_OBJECT  1
#define ST_FUNC    2
#define ST_SECTION 3
#define ST_FILE    4

// st_info [7:4]

#define SB_LOCAL  0
#define SB_GLOBAL 1
#define SB_WEAK   2

#define ELF32_ERR_PREFIX "[ELF32] "

ELF32_QUALIFIERS void
validate_read(size_t offset, size_t size, size_t data_size)
{
    if (offset + size > data_size)
        error(ELF32_ERR_PREFIX "Could not read %ld bytes at %08lX", size, offset);
}

ELF32_QUALIFIERS void *
elf32_read(const char *path, size_t *data_size_out)
{
    size_t data_size;
    void *data = util_read_whole_file(path, &data_size);
    if (data == NULL)
        error(ELF32_ERR_PREFIX "File is empty?");

    validate_read(0, sizeof(Elf32_Ehdr), data_size);

    Elf32_Ehdr *ehdr = GET_PTR(data, 0);

    if (!ELF32_HAS_MAGIC(ehdr))
        error(ELF32_ERR_PREFIX "Not an ELF file?");
    if (!ELF32_IS_32(ehdr))
        error(ELF32_ERR_PREFIX "Not ELF32?");
    if (!ELF32_IS_BE(ehdr))
        error(ELF32_ERR_PREFIX "Not big-endian?");

    *data_size_out = data_size;
    return data;
}

ELF32_QUALIFIERS Elf32_Shdr *
elf32_get_symtab(void *data, size_t data_size)
{
    Elf32_Ehdr *ehdr = GET_PTR(data, 0);
    uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
    uint16_t e_shnum = elf32_read16(ehdr->e_shnum);

    Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
    for (size_t i = 0; i < e_shnum; i++, shdr++) {
        validate_read(e_shoff + i * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);

        if (elf32_read32(shdr->sh_type) == SHT_SYMTAB) {
            // there should be only one section of this type
            return shdr;
        }
    }
    return NULL;
}

ELF32_QUALIFIERS Elf32_Shdr *
ef32_section_foridx(size_t idx, void *data, size_t data_size)
{
    Elf32_Ehdr *ehdr = GET_PTR(data, 0);
    uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
    uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
    Elf32_Shdr *shdr = GET_PTR(data, e_shoff);

    if (idx >= e_shnum)
        return NULL;

    validate_read(e_shoff + idx * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
    return &shdr[idx];
}

ELF32_QUALIFIERS Elf32_Shdr *
elf32_get_shstrtab(void *data, size_t data_size)
{
    Elf32_Ehdr *ehdr = GET_PTR(data, 0);
    return ef32_section_foridx(elf32_read16(ehdr->e_shstrndx), data, data_size);
}

ELF32_QUALIFIERS const char *
elf32_get_string(size_t offset, Elf32_Shdr *strtab, void *data, size_t data_size)
{
    uint32_t sh_offset = elf32_read32(strtab->sh_offset);

    validate_read(sh_offset + offset, 1, data_size);
    return (const char *)GET_PTR(data, sh_offset + offset);
}

ELF32_QUALIFIERS Elf32_Shdr *
elf32_section_forname(const char *name, Elf32_Shdr *shstrtab, void *data, size_t data_size)
{
    Elf32_Ehdr *ehdr = GET_PTR(data, 0);
    uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
    uint16_t e_shnum = elf32_read16(ehdr->e_shnum);

    Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
    for (size_t i = 0; i < e_shnum; i++, shdr++) {
        validate_read(e_shoff + i * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);

        const char *s_name = elf32_get_string(elf32_read32(shdr->sh_name), shstrtab, data, data_size);
        if (strequ(s_name, name)) {
            return shdr;
        }
    }
    return NULL;
}

ELF32_QUALIFIERS Elf32_Shdr *
elf32_get_strtab(void *data, size_t data_size)
{
    return elf32_section_forname(".strtab", elf32_get_shstrtab(data, data_size), data, data_size);
}

#endif