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
|
/*!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_AR_PREFIX_H
#define XM_BINUTILS_AR_PREFIX_H
/* //////////////////////////////////////////////////////////////////////////////////////
* includes
*/
#include "../prefix.h"
#include "../coff/prefix.h"
#include "../elf/prefix.h"
#include "../macho/prefix.h"
#include "../wasm/prefix.h"
/* //////////////////////////////////////////////////////////////////////////////////////
* forward declarations
*/
extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_wasm_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
/* //////////////////////////////////////////////////////////////////////////////////////
* types
*/
#include "tbox/prefix/packed.h"
typedef struct __xm_ar_header_t {
tb_char_t name[16]; // file name (null-padded)
tb_char_t date[12]; // modification time (decimal)
tb_char_t uid[6]; // user ID (decimal)
tb_char_t gid[6]; // group ID (decimal)
tb_char_t mode[8]; // file mode (octal)
tb_char_t size[10]; // file size (decimal)
tb_char_t fmag[2]; // magic: "`\n"
} __tb_packed__ xm_ar_header_t;
#include "tbox/prefix/packed.h"
/* //////////////////////////////////////////////////////////////////////////////////////
* inline implementation
*/
/* parse decimal number from string
*
* @param str the string
* @param len the length
* @return the parsed number, or -1 on error
*/
static __tb_inline__ tb_int64_t xm_binutils_ar_parse_decimal(tb_char_t const *str, tb_size_t len) {
tb_assert_and_check_return_val(str && len > 0, -1);
tb_int64_t result = 0;
for (tb_size_t i = 0; i < len; i++) {
if (str[i] == ' ' || str[i] == '\0') {
break;
}
if (str[i] < '0' || str[i] > '9') {
return -1;
}
result = result * 10 + (str[i] - '0');
}
return result;
}
/* get member name from AR header, handling extended names (#N/L format)
*
* @param istream the input stream
* @param header the AR header
* @param name output buffer for the name
* @param name_size size of the name buffer
* @param name_len output: actual name length
* @param bytes_read output: total bytes read from stream (including newline, for extended names)
* @return tb_true on success, tb_false on failure
*/
static __tb_inline__ tb_bool_t xm_binutils_ar_get_member_name(tb_stream_ref_t istream, xm_ar_header_t const* header, tb_char_t* name, tb_size_t name_size, tb_size_t* name_len, tb_hize_t* bytes_read) {
tb_assert_and_check_return_val(istream && header && name && name_size > 0 && name_len && bytes_read, tb_false);
*bytes_read = 0;
/* check for extended name format (#N/L or #1/N)
* In BSD AR format:
* - #1/N means name is directly after header, N is total length (including name)
* - #N/L means name length is N, total length is L
* - #1/N can also mean name is in long name table at offset 1
* We'll try to read the name directly from stream first
*/
if (header->name[0] == '#') {
// find the '/' separator
tb_size_t slash_pos = 0;
for (tb_size_t i = 1; i < 16; i++) {
if (header->name[i] == '/') {
slash_pos = i;
break;
}
}
if (slash_pos > 0 && slash_pos < 16) {
// parse the number before '/' (could be name length or offset)
tb_int64_t first_num = xm_binutils_ar_parse_decimal(header->name + 1, slash_pos - 1);
// parse the number after '/' (total length)
tb_int64_t total_length = xm_binutils_ar_parse_decimal(header->name + slash_pos + 1, 16 - slash_pos - 1);
if (first_num <= 0 || total_length <= 0) {
return tb_false;
}
/* In BSD AR format, extended name is directly after header
* The name data starts immediately after the header, no newline
* Read exactly total_length bytes for the name section
*/
tb_byte_t c;
tb_size_t name_bytes = 0;
tb_hize_t bytes_read_so_far = 0;
// Read name characters until we hit null terminator or reach total_length
while (bytes_read_so_far < (tb_hize_t)total_length && name_bytes < name_size - 1) {
if (!tb_stream_bread(istream, &c, 1)) {
return tb_false;
}
bytes_read_so_far++;
if (c == '\0') {
// Stop reading name at null terminator, but continue reading to reach total_length
break;
}
// Include all characters in the name, including newlines if present
name[name_bytes++] = (tb_char_t)c;
}
name[name_bytes] = '\0';
*name_len = name_bytes;
// Skip remaining bytes to reach total_length (there may be padding or null terminators)
if (bytes_read_so_far < (tb_hize_t)total_length) {
tb_hize_t remaining_to_read = (tb_hize_t)total_length - bytes_read_so_far;
if (!tb_stream_skip(istream, remaining_to_read)) {
return tb_false;
}
}
// Total bytes read = name + padding = total_length
*bytes_read = (tb_hize_t)total_length;
return tb_true;
}
}
if (header->name[0] == '/') {
if (header->name[1] == '/') {
tb_strlcpy(name, "//", name_size);
*name_len = 2;
} else {
tb_strlcpy(name, "/", name_size);
*name_len = 1;
}
*bytes_read = 0;
return tb_true;
}
// regular name (null-terminated or space-padded)
tb_size_t i = 0;
for (i = 0; i < 16 && i < name_size - 1; i++) {
if (header->name[i] == ' ' || header->name[i] == '\0' || header->name[i] == '/') {
break;
}
name[i] = header->name[i];
}
name[i] = '\0';
*name_len = i;
*bytes_read = 0; // Regular names are in header, not read from stream
return tb_true;
}
/* check AR magic (!<arch>\n)
*
* @param istream the input stream
* @param base_offset the base offset
* @return tb_true on success, tb_false on failure
*/
static __tb_inline__ tb_bool_t xm_binutils_ar_check_magic(tb_stream_ref_t istream, tb_hize_t base_offset) {
tb_uint8_t magic[8];
if (!tb_stream_seek(istream, base_offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, magic, 8)) {
return tb_false;
}
if (magic[0] != '!' || magic[1] != '<' || magic[2] != 'a' || magic[3] != 'r' ||
magic[4] != 'c' || magic[5] != 'h' || (magic[6] != '>' && magic[6] != '\n') ||
(magic[7] != '\n' && magic[7] != '\r')) {
return tb_false;
}
return tb_true;
}
/* check if member is a symbol table (should be skipped)
*
* @param name the member name
* @return tb_true if it's a symbol table, tb_false otherwise
*/
static __tb_inline__ tb_bool_t xm_binutils_ar_is_symbol_table(tb_char_t const *name) {
tb_assert_and_check_return_val(name, tb_false);
return (tb_strcmp(name, "__.SYMDEF") == 0 || tb_strcmp(name, "__.SYMDEF SORTED") == 0 ||
tb_strcmp(name, "/") == 0 || tb_strcmp(name, "//") == 0 ||
tb_strncmp(name, "__.SYMDEF", 9) == 0);
}
/* check if member is an object file (based on extension)
*
* @param name the member name
* @return tb_true if it's likely an object file, tb_false otherwise
*/
static __tb_inline__ tb_bool_t xm_binutils_ar_is_object_file(tb_char_t const *name) {
tb_assert_and_check_return_val(name, tb_false);
tb_size_t len = tb_strlen(name);
if (len == 0) {
return tb_false;
}
// check common object file extensions
if (len >= 2 && name[len - 2] == '.' && name[len - 1] == 'o') {
return tb_true;
}
if (len >= 4 && tb_strcmp(name + len - 4, ".obj") == 0) {
return tb_true;
}
// check if it's a COFF/ELF/Mach-O file by detecting format
// For now, we'll extract all non-symbol-table members
return tb_true;
}
#endif
|