summaryrefslogtreecommitdiff
path: root/core/src/xmake/binutils/prefix.h
blob: e7811652aadf44e7dc45ae4f0f5a0c6b0d60a2cf (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
/*!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_PREFIX_H
#define XM_BINUTILS_PREFIX_H

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

/* //////////////////////////////////////////////////////////////////////////////////////
 * macros
 */
#define XM_BINUTILS_FORMAT_COFF    1
#define XM_BINUTILS_FORMAT_ELF     2
#define XM_BINUTILS_FORMAT_MACHO   3
#define XM_BINUTILS_FORMAT_AR      4
#define XM_BINUTILS_FORMAT_PE      5
#define XM_BINUTILS_FORMAT_UNKNOWN 0

// COFF machine types (for format detection)
#define XM_BINUTILS_COFF_MACHINE_I386    0x014c
#define XM_BINUTILS_COFF_MACHINE_AMD64   0x8664
#define XM_BINUTILS_COFF_MACHINE_ARM     0x01c0
#define XM_BINUTILS_COFF_MACHINE_ARM64   0xaa64

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

/* read magic bytes from stream (preserves stream position)
 *
 * @param istream    the input stream
 * @param magic      the buffer to store magic bytes (must be at least 4 bytes)
 * @param size       the number of bytes to read (typically 4)
 *
 * @return           tb_true on success, tb_false on failure
 */
static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, tb_uint8_t *magic, tb_size_t size) {
    tb_assert_and_check_return_val(istream && magic && size > 0, tb_false);

    tb_hize_t saved_pos = tb_stream_offset(istream);
    if (!tb_stream_seek(istream, 0)) {
        return tb_false;
    }

    tb_bool_t ok = tb_false;
    if (tb_stream_bread(istream, magic, size)) {
        ok = tb_true;
    }

    tb_stream_seek(istream, saved_pos);
    return ok;
}

/* detect object file format from stream
 *
 * @param istream the input stream
 * @return        XM_BINUTILS_FORMAT_COFF, XM_BINUTILS_FORMAT_ELF, XM_BINUTILS_FORMAT_MACHO,
 *                 XM_BINUTILS_FORMAT_AR, XM_BINUTILS_FORMAT_UNKNOWN, or -1 on error
 */
static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) {
    tb_assert_and_check_return_val(istream, -1);

    // peek first 8 bytes
    tb_byte_t* p = tb_null;
    if (!tb_stream_peek(istream, &p, 8)) {
        return -1;
    }

    // check AR archive format first (!<arch>\n)
    if (p[0] == '!' && p[1] == '<' && p[2] == 'a' &&
        p[3] == 'r' && p[4] == 'c' && p[5] == 'h' &&
        (p[6] == '>' || p[6] == '\n') &&
        (p[7] == '\n' || p[7] == '\r')) {
        return XM_BINUTILS_FORMAT_AR;
    }

    // check PE/DOS magic (0x5A4D 'M' 'Z')
    if (p[0] == 'M' && p[1] == 'Z') {
        return XM_BINUTILS_FORMAT_PE;
    }

    // check ELF magic (0x7f 'E' 'L' 'F')
    if (p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[3] == 'F') {
        return XM_BINUTILS_FORMAT_ELF;
    }

    // check Mach-O magic
    if (p[0] == 0xfe && p[1] == 0xed && p[2] == 0xfa &&
        (p[3] == 0xce || p[3] == 0xcf)) {
        return XM_BINUTILS_FORMAT_MACHO; // Mach-O 32/64 (big endian)
    }
    if (p[0] == 0xce && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) {
        return XM_BINUTILS_FORMAT_MACHO; // Mach-O 32 (little endian)
    }
    if (p[0] == 0xcf && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) {
        return XM_BINUTILS_FORMAT_MACHO; // Mach-O 64 (little endian)
    }

    // check COFF (object files start with machine type, not a magic number)
    // COFF header: machine (2 bytes) + nsects (2 bytes) + time (4 bytes) + ...
    // Read machine type to verify if it's a valid COFF file
    tb_uint16_t machine = (p[1] << 8) | p[0];

    // check if it's a valid COFF machine type
    // Import header: 0x0000 0xffff
    if (machine == 0x0000) {
        // read second word to check if it is import header
        tb_uint16_t machine2 = (p[3] << 8) | p[2];
        if (machine2 == 0xffff) {
             return XM_BINUTILS_FORMAT_COFF;
        }
    }

    if (machine == XM_BINUTILS_COFF_MACHINE_I386 ||
        machine == XM_BINUTILS_COFF_MACHINE_AMD64 ||
        machine == XM_BINUTILS_COFF_MACHINE_ARM ||
        machine == XM_BINUTILS_COFF_MACHINE_ARM64) {
        return XM_BINUTILS_FORMAT_COFF;
    }

    // unknown format
    return XM_BINUTILS_FORMAT_UNKNOWN;
}

/* copy data from input stream to output stream
 *
 * @param istream    the input stream
 * @param ostream    the output stream
 * @param size       the size to copy
 *
 * @return           tb_true on success, tb_false on failure
 */
static __tb_inline__ tb_bool_t xm_binutils_stream_copy(tb_stream_ref_t istream, tb_stream_ref_t ostream, tb_hize_t size) {
    tb_assert_and_check_return_val(istream && ostream, tb_false);
    if (size == 0) {
        return tb_true;
    }

    tb_byte_t data[TB_STREAM_BLOCK_MAXN];
    tb_hize_t writ = 0;
    do {
        tb_size_t need = (tb_size_t)tb_min(size - writ, (tb_hize_t)TB_STREAM_BLOCK_MAXN);
        tb_check_break(need);

        if (!tb_stream_bread(istream, data, need)) {
            return tb_false;
        }
        if (!tb_stream_bwrit(ostream, data, need)) {
            return tb_false;
        }
        writ += need;

        tb_check_break(writ < size);
    } while (1);

    return tb_true;
}

/* sanitize symbol name (replace non-alphanumeric characters with underscores)
 *
 * @param name       the symbol name
 */
static __tb_inline__ void xm_binutils_sanitize_symbol_name(tb_char_t* name) {
    tb_assert_and_check_return(name);
    for (tb_size_t i = 0; name[i]; i++) {
        if (!tb_isalpha(name[i]) && !tb_isdigit(name[i]) && name[i] != '_') {
            name[i] = '_';
        }
    }
}

/* read string from stream at specified offset
 *
 * @param istream    the input stream
 * @param offset     the offset to read from
 * @param name       the buffer to store the string
 * @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_read_string(tb_stream_ref_t istream, tb_hize_t offset, tb_char_t *name, tb_size_t name_size) {
    tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false);

    tb_hize_t saved_pos = tb_stream_offset(istream);
    if (!tb_stream_seek(istream, offset)) {
        return tb_false;
    }

    tb_size_t pos = 0;
    tb_byte_t c;
    while (pos < name_size - 1) {
        if (!tb_stream_bread(istream, &c, 1)) {
            tb_stream_seek(istream, saved_pos);
            return tb_false;
        }
        if (c == 0) {
            break;
        }
        name[pos++] = (tb_char_t)c;
    }
    name[pos] = '\0';

    tb_stream_seek(istream, saved_pos);
    return tb_true;
}

/* check if architecture is 64-bit
 *
 * @param arch    the architecture string
 * @return        tb_true if 64-bit, tb_false otherwise
 */
static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch) {
    if (!arch) {
        return tb_true;
    }
    // x86_64
    if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) {
        return tb_true;
    }
    // ARM64
    else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0 ||
             tb_strcmp(arch, "arm64-v8a") == 0) {
        return tb_true;
    }
    // MIPS64
    else if (tb_strncmp(arch, "mips64", 6) == 0) {
        return tb_true;
    }
    // PowerPC64
    else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) {
        return tb_true;
    }
    // RISC-V 64
    else if (tb_strncmp(arch, "riscv64", 7) == 0 ||
             (tb_strncmp(arch, "riscv", 5) == 0 && tb_strstr(arch, "64"))) {
        return tb_true;
    }
    // SPARC64
    else if (tb_strncmp(arch, "sparc64", 7) == 0) {
        return tb_true;
    }
    // s390x
    else if (tb_strcmp(arch, "s390x") == 0) {
        return tb_true;
    }
    // LoongArch64
    else if (tb_strncmp(arch, "loongarch64", 11) == 0) {
        return tb_true;
    }
    // WebAssembly 64
    else if (tb_strcmp(arch, "wasm64") == 0) {
        return tb_true;
    }
    // IA-64
    else if (tb_strcmp(arch, "ia64") == 0 || tb_strcmp(arch, "itanium") == 0) {
        return tb_true;
    }
    return tb_false;
}


#endif