summaryrefslogtreecommitdiff
path: root/core/src/xmake/binutils/coff/prefix.h
blob: 8b882f0f603a5a4ed953ba56bd53efe1b8d2f96c (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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/*!A cross-platform build utility based on Lua
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (C) 2015-present, Xmake Open Source Community.
 *
 * @author      ruki
 * @file        prefix.h
 *
 */
#ifndef XM_BINUTILS_COFF_PREFIX_H
#define XM_BINUTILS_COFF_PREFIX_H

/* //////////////////////////////////////////////////////////////////////////////////////
 * includes
 */
#include "../prefix.h"

/* //////////////////////////////////////////////////////////////////////////////////////
 * macros
 */
#define XM_COFF_MACHINE_I386    0x014c
#define XM_COFF_MACHINE_AMD64   0x8664
#define XM_COFF_MACHINE_ARM     0x01c0
#define XM_COFF_MACHINE_ARM64   0xaa64

// PE Optional Header Magic
#define XM_PE32_MAGIC           0x10b
#define XM_PE32P_MAGIC          0x20b

// COFF section flags
#define XM_COFF_SCN_CNT_CODE                0x20  // IMAGE_SCN_CNT_CODE
#define XM_COFF_SCN_CNT_INITIALIZED_DATA     0x40  // IMAGE_SCN_CNT_INITIALIZED_DATA
#define XM_COFF_SCN_CNT_UNINITIALIZED_DATA   0x80  // IMAGE_SCN_CNT_UNINITIALIZED_DATA

#define XM_COFF_SECTION_RDATA   0x40000040  // IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ

/* //////////////////////////////////////////////////////////////////////////////////////
 * types
 */
#include "tbox/prefix/packed.h"
typedef struct __xm_coff_header_t {
    tb_uint16_t machine;
    tb_uint16_t nsects;
    tb_uint32_t time;
    tb_uint32_t symtabofs;
    tb_uint32_t nsyms;
    tb_uint16_t opthdr;
    tb_uint16_t flags;
} __tb_packed__ xm_coff_header_t;

typedef struct __xm_pe32_data_directory_t {
    tb_uint32_t vaddr;
    tb_uint32_t size;
} __tb_packed__ xm_pe32_data_directory_t;

typedef struct __xm_pe32_opt_header_t {
    tb_uint16_t magic;
    tb_uint8_t  major_linker_version;
    tb_uint8_t  minor_linker_version;
    tb_uint32_t size_of_code;
    tb_uint32_t size_of_initialized_data;
    tb_uint32_t size_of_uninitialized_data;
    tb_uint32_t address_of_entry_point;
    tb_uint32_t base_of_code;
    tb_uint32_t base_of_data;
    tb_uint32_t image_base;
    tb_uint32_t section_alignment;
    tb_uint32_t file_alignment;
    tb_uint16_t major_operating_system_version;
    tb_uint16_t minor_operating_system_version;
    tb_uint16_t major_image_version;
    tb_uint16_t minor_image_version;
    tb_uint16_t major_subsystem_version;
    tb_uint16_t minor_subsystem_version;
    tb_uint32_t win32_version_value;
    tb_uint32_t size_of_image;
    tb_uint32_t size_of_headers;
    tb_uint32_t checksum;
    tb_uint16_t subsystem;
    tb_uint16_t dll_characteristics;
    tb_uint32_t size_of_stack_reserve;
    tb_uint32_t size_of_stack_commit;
    tb_uint32_t size_of_heap_reserve;
    tb_uint32_t size_of_heap_commit;
    tb_uint32_t loader_flags;
    tb_uint32_t number_of_rva_and_sizes;
    xm_pe32_data_directory_t data_directory[16];
} __tb_packed__ xm_pe32_opt_header_t;

typedef struct __xm_pe32p_opt_header_t {
    tb_uint16_t magic;
    tb_uint8_t  major_linker_version;
    tb_uint8_t  minor_linker_version;
    tb_uint32_t size_of_code;
    tb_uint32_t size_of_initialized_data;
    tb_uint32_t size_of_uninitialized_data;
    tb_uint32_t address_of_entry_point;
    tb_uint32_t base_of_code;
    tb_uint64_t image_base;
    tb_uint32_t section_alignment;
    tb_uint32_t file_alignment;
    tb_uint16_t major_operating_system_version;
    tb_uint16_t minor_operating_system_version;
    tb_uint16_t major_image_version;
    tb_uint16_t minor_image_version;
    tb_uint16_t major_subsystem_version;
    tb_uint16_t minor_subsystem_version;
    tb_uint32_t win32_version_value;
    tb_uint32_t size_of_image;
    tb_uint32_t size_of_headers;
    tb_uint32_t checksum;
    tb_uint16_t subsystem;
    tb_uint16_t dll_characteristics;
    tb_uint64_t size_of_stack_reserve;
    tb_uint64_t size_of_stack_commit;
    tb_uint64_t size_of_heap_reserve;
    tb_uint64_t size_of_heap_commit;
    tb_uint32_t loader_flags;
    tb_uint32_t number_of_rva_and_sizes;
    xm_pe32_data_directory_t data_directory[16];
} __tb_packed__ xm_pe32p_opt_header_t;

typedef struct __xm_coff_import_directory_table_t {
    tb_uint32_t original_first_thunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    tb_uint32_t time_date_stamp;      // 0 if not bound, -1 if bound, and real date\time stamp in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) O.W. date/time stamp of DLL bound to (Old BIND)
    tb_uint32_t forwarder_chain;      // -1 if no forwarders
    tb_uint32_t name_rva;             // RVA to name
    tb_uint32_t first_thunk;          // RVA to IAT (if bound this IAT has actual addresses)
} __tb_packed__ xm_coff_import_directory_table_t;

typedef struct __xm_coff_import_header_t {
    tb_uint16_t sig1;     // 0
    tb_uint16_t sig2;     // 0xffff
    tb_uint16_t version;
    tb_uint16_t machine;
    tb_uint32_t time;
    tb_uint32_t size;     // size of data
    tb_uint16_t ordinal;  // ordinal or hint
    tb_uint16_t type;     // type
} __tb_packed__ xm_coff_import_header_t;

typedef struct __xm_coff_anon_header_t {
    tb_uint16_t sig1;     // 0
    tb_uint16_t sig2;     // 0xffff
    tb_uint16_t version;
    tb_uint16_t machine;
    tb_uint32_t time;
    tb_uint8_t  clsid[16];
    tb_uint32_t size;     // size of data
} __tb_packed__ xm_coff_anon_header_t;

typedef struct __xm_coff_section_t {
    tb_char_t name[8];
    tb_uint32_t vsize;
    tb_uint32_t vaddr;
    tb_uint32_t size;
    tb_uint32_t ofs;
    tb_uint32_t relocofs;
    tb_uint32_t linenoofs;
    tb_uint16_t nreloc;
    tb_uint16_t nlineno;
    tb_uint32_t flags;
} __tb_packed__ xm_coff_section_t;

typedef struct __xm_coff_symbol_t {
    union {
        struct {
            tb_char_t name[8];
        } shortname;
        struct {
            tb_uint32_t zeros;
            tb_uint32_t offset;
        } longname;
    } n;
    tb_uint32_t value;
    tb_int16_t sect;
    tb_uint16_t type;
    tb_uint8_t scl;
    tb_uint8_t naux;
} __tb_packed__ xm_coff_symbol_t;

typedef struct __xm_coff_aux_section_t {
    tb_uint32_t length;
    tb_uint16_t nreloc;
    tb_uint16_t nlineno;
    tb_uint8_t reserved[10];
} __tb_packed__ xm_coff_aux_section_t;

typedef struct __xm_coff_symbol_tail_t {
    tb_uint32_t value;
    tb_int16_t  sect;
    tb_uint16_t type;
    tb_uint8_t  scl;
    tb_uint8_t  naux;
} __tb_packed__ xm_coff_symbol_tail_t;
#include "tbox/prefix/packed.h"

/* //////////////////////////////////////////////////////////////////////////////////////
 * inline implementation
 */

/* get machine type from architecture string
 *
 * @param arch    the architecture string (e.g., "x86_64", "i386", "arm64")
 * @return        the machine type
 */
static __tb_inline__ tb_uint16_t xm_binutils_coff_get_machine(tb_char_t const *arch) {
    if (!arch) {
        return XM_COFF_MACHINE_I386;
    }
    if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) {
        return XM_COFF_MACHINE_AMD64;
    } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) {
        return XM_COFF_MACHINE_ARM64;
    } else if (tb_strcmp(arch, "arm") == 0) {
        return XM_COFF_MACHINE_ARM;
    } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) {
        return XM_COFF_MACHINE_I386;
    }
    return XM_COFF_MACHINE_I386;
}

/* write string to stream
 *
 * @param ostream the output stream
 * @param str     the string to write
 * @param len     the length (0 for auto-detect)
 */
static __tb_inline__ tb_void_t xm_binutils_coff_write_string(tb_stream_ref_t ostream, tb_char_t const *str, tb_size_t len) {
    tb_assert_and_check_return(ostream && str);
    if (len == 0) {
        len = tb_strlen(str);
    }
    tb_stream_bwrit(ostream, (tb_byte_t const *)str, len);
}

/* write padding bytes to stream
 *
 * @param ostream the output stream
 * @param count   the number of padding bytes
 */
static __tb_inline__ tb_void_t xm_binutils_coff_write_padding(tb_stream_ref_t ostream, tb_size_t count) {
    tb_assert_and_check_return(ostream);
    tb_byte_t zero = 0;
    while (count-- > 0) {
        tb_stream_bwrit(ostream, &zero, 1);
    }
}

/* write symbol name to stream (handles short and long names)
 *
 * @param ostream       the output stream
 * @param name          the symbol name
 * @param strtab_offset the string table offset (updated if long name)
 */
static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_t ostream, tb_char_t const *name, tb_uint32_t *strtab_offset) {
    tb_assert_and_check_return(ostream && name && strtab_offset);
    tb_size_t len = tb_strlen(name);
    if (len <= 8) {
        // short name: store directly in symbol name field
        xm_binutils_coff_write_string(ostream, name, len);
        if (len < 8) {
            xm_binutils_coff_write_padding(ostream, 8 - len);
        }
    } else {
        // long name: store offset in string table
        tb_uint32_t zeros = 0;
        tb_stream_bwrit(ostream, (tb_byte_t const *)&zeros, 4);
        tb_stream_bwrit(ostream, (tb_byte_t const *)strtab_offset, 4);
        *strtab_offset += (tb_uint32_t)(len + 1); // +1 for null terminator
    }
}

/* //////////////////////////////////////////////////////////////////////////////////////
 * readsyms inline implementation
 */

/* read string from COFF string table
 *
 * @param istream       the input stream
 * @param strtab_offset the string table offset (including 4-byte size field)
 * @param offset        the string offset (from start of string table content, after size field)
 * @return              the string (static buffer, valid until next call)
 */
static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istream, tb_hize_t strtab_offset, tb_uint32_t offset, tb_char_t *name, tb_size_t name_size) {
    tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false);

    // In COFF format, the offset in symbol table is from the start of string table
    // (including the 4-byte size field). So offset=4 points to the first string after
    // the size field, offset=74 points to a string at position 74 from the start.

    // read string table size first to validate offset
    tb_uint32_t strtab_size = 0;
    tb_hize_t saved_pos = tb_stream_offset(istream);
    if (!tb_stream_seek(istream, strtab_offset)) {
        return tb_false;
    }
    if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_size, 4)) {
        tb_stream_seek(istream, saved_pos);
        return tb_false;
    }

    // check offset (must be >= 4 to skip the size field, and < strtab_size)
    if (offset < 4 || offset >= strtab_size) {
        tb_stream_seek(istream, saved_pos);
        return tb_false;
    }

    // restore position and use common implementation
    tb_stream_seek(istream, saved_pos);
    return xm_binutils_read_string(istream, strtab_offset + offset, name, name_size);
}

/* get symbol name from COFF symbol entry
 *
 * @param istream       the input stream
 * @param sym           the symbol entry
 * @param strtab_offset the string table offset
 * @param name          the buffer to store the symbol name
 * @param name_size     the size of the buffer
 * @return              tb_true on success, tb_false on failure
 */
static __tb_inline__ tb_bool_t xm_binutils_coff_get_symbol_name(tb_stream_ref_t istream, xm_coff_symbol_t const *sym, tb_hize_t strtab_offset, tb_char_t *name, tb_size_t name_size) {
    tb_assert_and_check_return_val(istream && sym && name && name_size > 0, tb_false);

    // check if it's a long name (first 4 bytes are zeros)
    if (sym->n.longname.zeros == 0) {
        // long name: read from string table
        return xm_binutils_coff_read_string(istream, strtab_offset, sym->n.longname.offset, name, name_size);
    } else {
        // short name: use directly
        tb_size_t len = tb_min(8, name_size - 1);
        tb_strncpy(name, sym->n.shortname.name, len);
        name[len] = '\0';
        // trim trailing nulls
        while (len > 0 && name[len - 1] == '\0') {
            len--;
        }
        name[len] = '\0';
        return tb_true;
    }
}

/* get symbol type character (nm-style) from COFF symbol
 *
 * @param scl      the storage class
 * @param sect     the section number (0 = undefined, 1-based)
 * @param sections the section headers array
 * @param nsects   the number of sections
 * @return         the type character (T/t/D/d/B/b/U)
 */
static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t scl, tb_int16_t sect, xm_coff_section_t const *sections, tb_uint16_t nsects) {
    // undefined symbol
    if (sect == 0) {
        return 'U';
    }

    // check if external
    tb_bool_t is_external = (scl == 2); // IMAGE_SYM_CLASS_EXTERNAL

    // check section flags to determine type
    if (sections && sect > 0 && sect <= nsects) {
        tb_uint32_t flags = sections[sect - 1].flags; // section numbers are 1-based
        // IMAGE_SCN_CNT_CODE (0x20) - code section
        if (flags & XM_COFF_SCN_CNT_CODE) {
            return is_external ? 'T' : 't';  // text section
        }
        // IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x80) - bss section
        if (flags & XM_COFF_SCN_CNT_UNINITIALIZED_DATA) {
            return is_external ? 'B' : 'b';  // bss section
        }
        // IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) - data section
        if (flags & XM_COFF_SCN_CNT_INITIALIZED_DATA) {
            return is_external ? 'D' : 'd';  // data section
        }
    }

    // fallback: use section number heuristic
    if (sect == 1) {
        return is_external ? 'T' : 't';  // text section
    } else if (sect == 2) {
        return is_external ? 'D' : 'd';  // data section
    } else if (sect == 3) {
        return is_external ? 'B' : 'b';  // bss section
    }

    return is_external ? 'S' : 's';  // other section
}

#endif