/src/binutils-gdb/bfd/wasm-module.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* BFD back-end for WebAssembly modules. |
2 | | Copyright (C) 2017-2025 Free Software Foundation, Inc. |
3 | | |
4 | | Based on srec.c, mmo.c, and binary.c |
5 | | |
6 | | This file is part of BFD, the Binary File Descriptor library. |
7 | | |
8 | | This program is free software; you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation; either version 3 of the License, or |
11 | | (at your option) any later version. |
12 | | |
13 | | This program is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | GNU General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program; if not, write to the Free Software |
20 | | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
21 | | MA 02110-1301, USA. */ |
22 | | |
23 | | /* The WebAssembly module format is a simple object file format |
24 | | including up to 11 numbered sections, plus any number of named |
25 | | "custom" sections. It is described at: |
26 | | https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md. */ |
27 | | |
28 | | #include "sysdep.h" |
29 | | #include "bfd.h" |
30 | | #include "libiberty.h" |
31 | | #include "libbfd.h" |
32 | | #include "wasm-module.h" |
33 | | |
34 | | #include <limits.h> |
35 | | #ifndef CHAR_BIT |
36 | | #define CHAR_BIT 8 |
37 | | #endif |
38 | | |
39 | | typedef struct |
40 | | { |
41 | | asymbol * symbols; |
42 | | bfd_size_type symcount; |
43 | | } tdata_type; |
44 | | |
45 | | static const char * const wasm_numbered_sections[] = |
46 | | { |
47 | | NULL, /* Custom section, different layout. */ |
48 | | WASM_SECTION ( 1, "type"), |
49 | | WASM_SECTION ( 2, "import"), |
50 | | WASM_SECTION ( 3, "function"), |
51 | | WASM_SECTION ( 4, "table"), |
52 | | WASM_SECTION ( 5, "memory"), |
53 | | WASM_SECTION ( 6, "global"), |
54 | | WASM_SECTION ( 7, "export"), |
55 | | WASM_SECTION ( 8, "start"), |
56 | | WASM_SECTION ( 9, "element"), |
57 | | WASM_SECTION (10, "code"), |
58 | | WASM_SECTION (11, "data"), |
59 | | }; |
60 | | |
61 | 40.5k | #define WASM_NUMBERED_SECTIONS ARRAY_SIZE (wasm_numbered_sections) |
62 | | |
63 | | /* Resolve SECTION_CODE to a section name if there is one, NULL |
64 | | otherwise. */ |
65 | | |
66 | | static const char * |
67 | | wasm_section_code_to_name (bfd_byte section_code) |
68 | 40.5k | { |
69 | 40.5k | if (section_code < WASM_NUMBERED_SECTIONS) |
70 | 40.0k | return wasm_numbered_sections[section_code]; |
71 | | |
72 | 571 | return NULL; |
73 | 40.5k | } |
74 | | |
75 | | /* Translate section name NAME to a section code, or 0 if it's a |
76 | | custom name. */ |
77 | | |
78 | | static unsigned int |
79 | | wasm_section_name_to_code (const char *name) |
80 | 0 | { |
81 | 0 | unsigned i; |
82 | |
|
83 | 0 | for (i = 1; i < WASM_NUMBERED_SECTIONS; i++) |
84 | 0 | if (strcmp (name, wasm_numbered_sections[i]) == 0) |
85 | 0 | return i; |
86 | | |
87 | 0 | return 0; |
88 | 0 | } |
89 | | |
90 | | /* WebAssembly LEB128 integers are sufficiently like DWARF LEB128 |
91 | | integers that we use _bfd_safe_read_leb128, but there are two |
92 | | points of difference: |
93 | | |
94 | | - WebAssembly requires a 32-bit value to be encoded in at most 5 |
95 | | bytes, etc. |
96 | | - _bfd_safe_read_leb128 accepts incomplete LEB128 encodings at the |
97 | | end of the buffer, while these are invalid in WebAssembly. |
98 | | |
99 | | Those differences mean that we will accept some files that are |
100 | | invalid WebAssembly. */ |
101 | | |
102 | | /* Read an LEB128-encoded integer from ABFD's I/O stream, reading one |
103 | | byte at a time. Set ERROR_RETURN if no complete integer could be |
104 | | read, LENGTH_RETURN to the number of bytes read (including bytes in |
105 | | incomplete numbers). SIGN means interpret the number as SLEB128. */ |
106 | | |
107 | | static bfd_vma |
108 | | wasm_read_leb128 (bfd *abfd, |
109 | | bool *error_return, |
110 | | unsigned int *length_return, |
111 | | bool sign) |
112 | 72.9k | { |
113 | 72.9k | bfd_vma result = 0; |
114 | 72.9k | unsigned int num_read = 0; |
115 | 72.9k | unsigned int shift = 0; |
116 | 72.9k | unsigned char byte = 0; |
117 | 72.9k | unsigned char lost, mask; |
118 | 72.9k | int status = 1; |
119 | | |
120 | 127k | while (bfd_read (&byte, 1, abfd) == 1) |
121 | 126k | { |
122 | 126k | num_read++; |
123 | | |
124 | 126k | if (shift < CHAR_BIT * sizeof (result)) |
125 | 116k | { |
126 | 116k | result |= ((bfd_vma) (byte & 0x7f)) << shift; |
127 | | /* These bits overflowed. */ |
128 | 116k | lost = byte ^ (result >> shift); |
129 | | /* And this is the mask of possible overflow bits. */ |
130 | 116k | mask = 0x7f ^ ((bfd_vma) 0x7f << shift >> shift); |
131 | 116k | shift += 7; |
132 | 116k | } |
133 | 9.60k | else |
134 | 9.60k | { |
135 | 9.60k | lost = byte; |
136 | 9.60k | mask = 0x7f; |
137 | 9.60k | } |
138 | 126k | if ((lost & mask) != (sign && (bfd_signed_vma) result < 0 ? mask : 0)) |
139 | 9.59k | status |= 2; |
140 | | |
141 | 126k | if ((byte & 0x80) == 0) |
142 | 71.4k | { |
143 | 71.4k | status &= ~1; |
144 | 71.4k | if (sign && shift < CHAR_BIT * sizeof (result) && (byte & 0x40)) |
145 | 0 | result |= -((bfd_vma) 1 << shift); |
146 | 71.4k | break; |
147 | 71.4k | } |
148 | 126k | } |
149 | | |
150 | 72.9k | if (length_return != NULL) |
151 | 72.9k | *length_return = num_read; |
152 | 72.9k | if (error_return != NULL) |
153 | 72.9k | *error_return = status != 0; |
154 | | |
155 | 72.9k | return result; |
156 | 72.9k | } |
157 | | |
158 | | /* Encode an integer V as LEB128 and write it to ABFD, return TRUE on |
159 | | success. */ |
160 | | |
161 | | static bool |
162 | | wasm_write_uleb128 (bfd *abfd, bfd_vma v) |
163 | 0 | { |
164 | 0 | do |
165 | 0 | { |
166 | 0 | bfd_byte c = v & 0x7f; |
167 | 0 | v >>= 7; |
168 | |
|
169 | 0 | if (v) |
170 | 0 | c |= 0x80; |
171 | |
|
172 | 0 | if (bfd_write (&c, 1, abfd) != 1) |
173 | 0 | return false; |
174 | 0 | } |
175 | 0 | while (v); |
176 | | |
177 | 0 | return true; |
178 | 0 | } |
179 | | |
180 | | /* Read the LEB128 integer at P, saving it to X; at end of buffer, |
181 | | jump to error_return. */ |
182 | | #define READ_LEB128(x, p, end) \ |
183 | 26.9k | do \ |
184 | 26.9k | { \ |
185 | 26.9k | if ((p) >= (end)) \ |
186 | 26.9k | goto error_return; \ |
187 | 26.9k | (x) = _bfd_safe_read_leb128 (abfd, &(p), false, (end)); \ |
188 | 25.9k | } \ |
189 | 26.9k | while (0) |
190 | | |
191 | | /* Verify the magic number at the beginning of a WebAssembly module |
192 | | ABFD, setting ERRORPTR if there's a mismatch. */ |
193 | | |
194 | | static bool |
195 | | wasm_read_magic (bfd *abfd, bool *errorptr) |
196 | 3.42M | { |
197 | 3.42M | bfd_byte magic_const[SIZEOF_WASM_MAGIC] = WASM_MAGIC; |
198 | 3.42M | bfd_byte magic[SIZEOF_WASM_MAGIC]; |
199 | | |
200 | 3.42M | if (bfd_read (magic, sizeof (magic), abfd) == sizeof (magic) |
201 | 3.42M | && memcmp (magic, magic_const, sizeof (magic)) == 0) |
202 | 33.8k | return true; |
203 | | |
204 | 3.39M | *errorptr = true; |
205 | 3.39M | return false; |
206 | 3.42M | } |
207 | | |
208 | | /* Read the version number from ABFD, returning TRUE if it's a supported |
209 | | version. Set ERRORPTR otherwise. */ |
210 | | |
211 | | static bool |
212 | | wasm_read_version (bfd *abfd, bool *errorptr) |
213 | 33.8k | { |
214 | 33.8k | bfd_byte vers_const[SIZEOF_WASM_VERSION] = WASM_VERSION; |
215 | 33.8k | bfd_byte vers[SIZEOF_WASM_VERSION]; |
216 | | |
217 | 33.8k | if (bfd_read (vers, sizeof (vers), abfd) == sizeof (vers) |
218 | | /* Don't attempt to parse newer versions, which are likely to |
219 | | require code changes. */ |
220 | 33.8k | && memcmp (vers, vers_const, sizeof (vers)) == 0) |
221 | 32.7k | return true; |
222 | | |
223 | 1.09k | *errorptr = true; |
224 | 1.09k | return false; |
225 | 33.8k | } |
226 | | |
227 | | /* Read the WebAssembly header (magic number plus version number) from |
228 | | ABFD, setting ERRORPTR to TRUE if there is a mismatch. */ |
229 | | |
230 | | static bool |
231 | | wasm_read_header (bfd *abfd, bool *errorptr) |
232 | 3.42M | { |
233 | 3.42M | if (! wasm_read_magic (abfd, errorptr)) |
234 | 3.39M | return false; |
235 | | |
236 | 33.8k | if (! wasm_read_version (abfd, errorptr)) |
237 | 1.09k | return false; |
238 | | |
239 | 32.7k | return true; |
240 | 33.8k | } |
241 | | |
242 | | /* Scan the "function" subsection of the "name" section ASECT in the |
243 | | wasm module ABFD. Create symbols. Return TRUE on success. */ |
244 | | |
245 | | static bool |
246 | | wasm_scan_name_function_section (bfd *abfd, sec_ptr asect) |
247 | 3.79k | { |
248 | 3.79k | bfd_byte *p; |
249 | 3.79k | bfd_byte *end; |
250 | 3.79k | bfd_vma payload_size; |
251 | 3.79k | bfd_vma symcount = 0; |
252 | 3.79k | tdata_type *tdata = abfd->tdata.any; |
253 | 3.79k | asymbol *symbols = NULL; |
254 | 3.79k | sec_ptr space_function_index; |
255 | 3.79k | size_t amt; |
256 | | |
257 | 3.79k | p = asect->contents; |
258 | 3.79k | end = asect->contents + asect->size; |
259 | | |
260 | 3.79k | if (!p) |
261 | 2 | return false; |
262 | | |
263 | 22.5k | while (p < end) |
264 | 22.2k | { |
265 | 22.2k | bfd_byte subsection_code = *p++; |
266 | 22.2k | if (subsection_code == WASM_FUNCTION_SUBSECTION) |
267 | 2.41k | break; |
268 | | |
269 | | /* subsection_code is documented to be a varuint7, meaning that |
270 | | it has to be a single byte in the 0 - 127 range. If it isn't, |
271 | | the spec must have changed underneath us, so give up. */ |
272 | 19.8k | if (subsection_code & 0x80) |
273 | 341 | return false; |
274 | | |
275 | 19.4k | READ_LEB128 (payload_size, p, end); |
276 | | |
277 | 19.1k | if (payload_size > (size_t) (end - p)) |
278 | 365 | return false; |
279 | | |
280 | 18.7k | p += payload_size; |
281 | 18.7k | } |
282 | | |
283 | 2.75k | if (p >= end) |
284 | 337 | return false; |
285 | | |
286 | 2.41k | READ_LEB128 (payload_size, p, end); |
287 | | |
288 | 2.41k | if (payload_size > (size_t) (end - p)) |
289 | 361 | return false; |
290 | | |
291 | 2.05k | end = p + payload_size; |
292 | | |
293 | 2.05k | READ_LEB128 (symcount, p, end); |
294 | | |
295 | | /* Sanity check: each symbol has at least two bytes. */ |
296 | 1.71k | if (symcount > payload_size / 2) |
297 | 342 | return false; |
298 | | |
299 | 1.37k | tdata->symcount = symcount; |
300 | | |
301 | 1.37k | space_function_index |
302 | 1.37k | = bfd_make_section_with_flags (abfd, WASM_SECTION_FUNCTION_INDEX, |
303 | 1.37k | SEC_READONLY | SEC_CODE); |
304 | | |
305 | 1.37k | if (!space_function_index) |
306 | 0 | space_function_index |
307 | 0 | = bfd_get_section_by_name (abfd, WASM_SECTION_FUNCTION_INDEX); |
308 | | |
309 | 1.37k | if (!space_function_index) |
310 | 0 | return false; |
311 | | |
312 | 1.37k | if (_bfd_mul_overflow (tdata->symcount, sizeof (asymbol), &amt)) |
313 | 0 | { |
314 | 0 | bfd_set_error (bfd_error_file_too_big); |
315 | 0 | return false; |
316 | 0 | } |
317 | 1.37k | symbols = bfd_alloc (abfd, amt); |
318 | 1.37k | if (!symbols) |
319 | 0 | return false; |
320 | | |
321 | 2.18k | for (symcount = 0; p < end && symcount < tdata->symcount; symcount++) |
322 | 1.49k | { |
323 | 1.49k | bfd_vma idx; |
324 | 1.49k | bfd_vma len; |
325 | 1.49k | char *name; |
326 | 1.49k | asymbol *sym; |
327 | | |
328 | 1.49k | READ_LEB128 (idx, p, end); |
329 | 1.49k | READ_LEB128 (len, p, end); |
330 | | |
331 | 1.16k | if (len > (size_t) (end - p)) |
332 | 350 | goto error_return; |
333 | | |
334 | 811 | name = bfd_alloc (abfd, len + 1); |
335 | 811 | if (!name) |
336 | 0 | goto error_return; |
337 | | |
338 | 811 | memcpy (name, p, len); |
339 | 811 | name[len] = 0; |
340 | 811 | p += len; |
341 | | |
342 | 811 | sym = &symbols[symcount]; |
343 | 811 | sym->the_bfd = abfd; |
344 | 811 | sym->name = name; |
345 | 811 | sym->value = idx; |
346 | 811 | sym->flags = BSF_GLOBAL | BSF_FUNCTION; |
347 | 811 | sym->section = space_function_index; |
348 | 811 | sym->udata.p = NULL; |
349 | 811 | } |
350 | | |
351 | 689 | if (symcount < tdata->symcount) |
352 | 337 | goto error_return; |
353 | | |
354 | 352 | tdata->symbols = symbols; |
355 | 352 | abfd->symcount = symcount; |
356 | | |
357 | 352 | return true; |
358 | | |
359 | 1.69k | error_return: |
360 | 1.69k | if (symbols) |
361 | 1.02k | bfd_release (abfd, symbols); |
362 | 1.69k | tdata->symcount = 0; |
363 | 1.69k | return false; |
364 | 689 | } |
365 | | |
366 | | /* Read a byte from ABFD and return it, or EOF for EOF or error. |
367 | | Set ERRORPTR on non-EOF error. */ |
368 | | |
369 | | static int |
370 | | wasm_read_byte (bfd *abfd, bool *errorptr) |
371 | 65.1k | { |
372 | 65.1k | bfd_byte byte; |
373 | | |
374 | 65.1k | if (bfd_read (&byte, 1, abfd) != 1) |
375 | 7.59k | { |
376 | 7.59k | if (bfd_get_error () != bfd_error_file_truncated) |
377 | 315 | *errorptr = true; |
378 | 7.59k | return EOF; |
379 | 7.59k | } |
380 | | |
381 | 57.5k | return byte; |
382 | 65.1k | } |
383 | | |
384 | | /* Scan the wasm module ABFD, creating sections and symbols. |
385 | | Return TRUE on success. */ |
386 | | |
387 | | static bool |
388 | | wasm_scan (bfd *abfd) |
389 | 16.3k | { |
390 | 16.3k | bool error = false; |
391 | | /* Fake VMAs for now. Choose 0x80000000 as base to avoid clashes |
392 | | with actual data addresses. */ |
393 | 16.3k | bfd_vma vma = 0x80000000; |
394 | 16.3k | int section_code; |
395 | 16.3k | unsigned int bytes_read; |
396 | 16.3k | asection *bfdsec; |
397 | | |
398 | 16.3k | if (bfd_seek (abfd, 0, SEEK_SET) != 0) |
399 | 0 | goto error_return; |
400 | | |
401 | 16.3k | if (!wasm_read_header (abfd, &error)) |
402 | 0 | goto error_return; |
403 | | |
404 | 65.1k | while ((section_code = wasm_read_byte (abfd, &error)) != EOF) |
405 | 57.5k | { |
406 | 57.5k | if (section_code != 0) |
407 | 40.5k | { |
408 | 40.5k | const char *sname = wasm_section_code_to_name (section_code); |
409 | | |
410 | 40.5k | if (!sname) |
411 | 571 | goto error_return; |
412 | | |
413 | 40.0k | bfdsec = bfd_make_section_anyway_with_flags (abfd, sname, |
414 | 40.0k | SEC_HAS_CONTENTS); |
415 | 40.0k | if (bfdsec == NULL) |
416 | 0 | goto error_return; |
417 | | |
418 | 40.0k | bfdsec->size = wasm_read_leb128 (abfd, &error, &bytes_read, false); |
419 | 40.0k | if (error) |
420 | 919 | goto error_return; |
421 | 40.0k | } |
422 | 16.9k | else |
423 | 16.9k | { |
424 | 16.9k | bfd_vma payload_len; |
425 | 16.9k | bfd_vma namelen; |
426 | 16.9k | char *name; |
427 | 16.9k | char *prefix = WASM_SECTION_PREFIX; |
428 | 16.9k | size_t prefixlen = strlen (prefix); |
429 | 16.9k | ufile_ptr filesize; |
430 | | |
431 | 16.9k | payload_len = wasm_read_leb128 (abfd, &error, &bytes_read, false); |
432 | 16.9k | if (error) |
433 | 906 | goto error_return; |
434 | 16.0k | namelen = wasm_read_leb128 (abfd, &error, &bytes_read, false); |
435 | 16.0k | if (error || bytes_read > payload_len |
436 | 16.0k | || namelen > payload_len - bytes_read) |
437 | 2.44k | goto error_return; |
438 | 13.5k | payload_len -= namelen + bytes_read; |
439 | 13.5k | filesize = bfd_get_file_size (abfd); |
440 | 13.5k | if (filesize != 0 && namelen > filesize) |
441 | 1.53k | { |
442 | 1.53k | bfd_set_error (bfd_error_file_truncated); |
443 | 1.53k | return false; |
444 | 1.53k | } |
445 | 12.0k | name = bfd_alloc (abfd, namelen + prefixlen + 1); |
446 | 12.0k | if (!name) |
447 | 0 | goto error_return; |
448 | 12.0k | memcpy (name, prefix, prefixlen); |
449 | 12.0k | if (bfd_read (name + prefixlen, namelen, abfd) != namelen) |
450 | 460 | goto error_return; |
451 | 11.5k | name[prefixlen + namelen] = 0; |
452 | | |
453 | 11.5k | bfdsec = bfd_make_section_anyway_with_flags (abfd, name, |
454 | 11.5k | SEC_HAS_CONTENTS); |
455 | 11.5k | if (bfdsec == NULL) |
456 | 0 | goto error_return; |
457 | | |
458 | 11.5k | bfdsec->size = payload_len; |
459 | 11.5k | } |
460 | | |
461 | 50.6k | bfdsec->vma = vma; |
462 | 50.6k | bfdsec->lma = vma; |
463 | 50.6k | bfdsec->alignment_power = 0; |
464 | 50.6k | bfdsec->filepos = bfd_tell (abfd); |
465 | 50.6k | if (bfdsec->size != 0) |
466 | 42.2k | { |
467 | 42.2k | bfdsec->contents = _bfd_alloc_and_read (abfd, bfdsec->size, |
468 | 42.2k | bfdsec->size); |
469 | 42.2k | if (!bfdsec->contents) |
470 | 1.92k | goto error_return; |
471 | 40.3k | bfdsec->alloced = 1; |
472 | 40.3k | } |
473 | | |
474 | 48.7k | vma += bfdsec->size; |
475 | 48.7k | } |
476 | | |
477 | | /* Make sure we're at actual EOF. There's no indication in the |
478 | | WebAssembly format of how long the file is supposed to be. */ |
479 | 7.59k | if (error) |
480 | 315 | goto error_return; |
481 | | |
482 | 7.27k | return true; |
483 | | |
484 | 7.54k | error_return: |
485 | 7.54k | return false; |
486 | 7.59k | } |
487 | | |
488 | | /* Put a numbered section ASECT of ABFD into the table of numbered |
489 | | sections pointed to by FSARG. */ |
490 | | |
491 | | static void |
492 | | wasm_register_section (bfd *abfd ATTRIBUTE_UNUSED, |
493 | | asection *asect, |
494 | | void *fsarg) |
495 | 0 | { |
496 | 0 | sec_ptr *numbered_sections = fsarg; |
497 | 0 | int idx = wasm_section_name_to_code (asect->name); |
498 | |
|
499 | 0 | if (idx == 0) |
500 | 0 | return; |
501 | | |
502 | 0 | numbered_sections[idx] = asect; |
503 | 0 | } |
504 | | |
505 | | struct compute_section_arg |
506 | | { |
507 | | bfd_vma pos; |
508 | | bool failed; |
509 | | }; |
510 | | |
511 | | /* Compute the file position of ABFD's section ASECT. FSARG is a |
512 | | pointer to the current file position. |
513 | | |
514 | | We allow section names of the form .wasm.id to encode the numbered |
515 | | section with ID id, if it exists; otherwise, a custom section with |
516 | | ID "id" is produced. Arbitrary section names are for sections that |
517 | | are assumed already to contain a section header; those are appended |
518 | | to the WebAssembly module verbatim. */ |
519 | | |
520 | | static void |
521 | | wasm_compute_custom_section_file_position (bfd *abfd, |
522 | | sec_ptr asect, |
523 | | void *fsarg) |
524 | 0 | { |
525 | 0 | struct compute_section_arg *fs = fsarg; |
526 | 0 | int idx; |
527 | |
|
528 | 0 | if (fs->failed) |
529 | 0 | return; |
530 | | |
531 | 0 | idx = wasm_section_name_to_code (asect->name); |
532 | |
|
533 | 0 | if (idx != 0) |
534 | 0 | return; |
535 | | |
536 | 0 | if (startswith (asect->name, WASM_SECTION_PREFIX)) |
537 | 0 | { |
538 | 0 | const char *name = asect->name + strlen (WASM_SECTION_PREFIX); |
539 | 0 | bfd_size_type payload_len = asect->size; |
540 | 0 | bfd_size_type name_len = strlen (name); |
541 | 0 | bfd_size_type nl = name_len; |
542 | |
|
543 | 0 | payload_len += name_len; |
544 | |
|
545 | 0 | do |
546 | 0 | { |
547 | 0 | payload_len++; |
548 | 0 | nl >>= 7; |
549 | 0 | } |
550 | 0 | while (nl); |
551 | |
|
552 | 0 | if (bfd_seek (abfd, fs->pos, SEEK_SET) != 0 |
553 | 0 | || ! wasm_write_uleb128 (abfd, 0) |
554 | 0 | || ! wasm_write_uleb128 (abfd, payload_len) |
555 | 0 | || ! wasm_write_uleb128 (abfd, name_len) |
556 | 0 | || bfd_write (name, name_len, abfd) != name_len) |
557 | 0 | goto error_return; |
558 | 0 | fs->pos = asect->filepos = bfd_tell (abfd); |
559 | 0 | } |
560 | 0 | else |
561 | 0 | { |
562 | 0 | asect->filepos = fs->pos; |
563 | 0 | } |
564 | | |
565 | | |
566 | 0 | fs->pos += asect->size; |
567 | 0 | return; |
568 | | |
569 | 0 | error_return: |
570 | 0 | fs->failed = true; |
571 | 0 | } |
572 | | |
573 | | /* Compute the file positions for the sections of ABFD. Currently, |
574 | | this writes all numbered sections first, in order, then all custom |
575 | | sections, in section order. |
576 | | |
577 | | The spec says that the numbered sections must appear in order of |
578 | | their ids, but custom sections can appear in any position and any |
579 | | order, and more than once. FIXME: support that. */ |
580 | | |
581 | | static bool |
582 | | wasm_compute_section_file_positions (bfd *abfd) |
583 | 0 | { |
584 | 0 | bfd_byte magic[SIZEOF_WASM_MAGIC] = WASM_MAGIC; |
585 | 0 | bfd_byte vers[SIZEOF_WASM_VERSION] = WASM_VERSION; |
586 | 0 | sec_ptr numbered_sections[WASM_NUMBERED_SECTIONS]; |
587 | 0 | struct compute_section_arg fs; |
588 | 0 | unsigned int i; |
589 | |
|
590 | 0 | if (bfd_seek (abfd, (bfd_vma) 0, SEEK_SET) != 0 |
591 | 0 | || bfd_write (magic, sizeof (magic), abfd) != (sizeof magic) |
592 | 0 | || bfd_write (vers, sizeof (vers), abfd) != sizeof (vers)) |
593 | 0 | return false; |
594 | | |
595 | 0 | for (i = 0; i < WASM_NUMBERED_SECTIONS; i++) |
596 | 0 | numbered_sections[i] = NULL; |
597 | |
|
598 | 0 | bfd_map_over_sections (abfd, wasm_register_section, numbered_sections); |
599 | |
|
600 | 0 | fs.pos = bfd_tell (abfd); |
601 | 0 | for (i = 0; i < WASM_NUMBERED_SECTIONS; i++) |
602 | 0 | { |
603 | 0 | sec_ptr sec = numbered_sections[i]; |
604 | 0 | bfd_size_type size; |
605 | |
|
606 | 0 | if (! sec) |
607 | 0 | continue; |
608 | 0 | size = sec->size; |
609 | 0 | if (bfd_seek (abfd, fs.pos, SEEK_SET) != 0) |
610 | 0 | return false; |
611 | 0 | if (! wasm_write_uleb128 (abfd, i) |
612 | 0 | || ! wasm_write_uleb128 (abfd, size)) |
613 | 0 | return false; |
614 | 0 | fs.pos = sec->filepos = bfd_tell (abfd); |
615 | 0 | fs.pos += size; |
616 | 0 | } |
617 | | |
618 | 0 | fs.failed = false; |
619 | |
|
620 | 0 | bfd_map_over_sections (abfd, wasm_compute_custom_section_file_position, &fs); |
621 | |
|
622 | 0 | if (fs.failed) |
623 | 0 | return false; |
624 | | |
625 | 0 | abfd->output_has_begun = true; |
626 | |
|
627 | 0 | return true; |
628 | 0 | } |
629 | | |
630 | | static bool |
631 | | wasm_set_section_contents (bfd *abfd, |
632 | | sec_ptr section, |
633 | | const void *location, |
634 | | file_ptr offset, |
635 | | bfd_size_type count) |
636 | 0 | { |
637 | 0 | if (count == 0) |
638 | 0 | return true; |
639 | | |
640 | 0 | if (! abfd->output_has_begun |
641 | 0 | && ! wasm_compute_section_file_positions (abfd)) |
642 | 0 | return false; |
643 | | |
644 | 0 | if (bfd_seek (abfd, section->filepos + offset, SEEK_SET) != 0 |
645 | 0 | || bfd_write (location, count, abfd) != count) |
646 | 0 | return false; |
647 | | |
648 | 0 | return true; |
649 | 0 | } |
650 | | |
651 | | static bool |
652 | | wasm_write_object_contents (bfd* abfd) |
653 | 0 | { |
654 | 0 | bfd_byte magic[] = WASM_MAGIC; |
655 | 0 | bfd_byte vers[] = WASM_VERSION; |
656 | |
|
657 | 0 | if (bfd_seek (abfd, 0, SEEK_SET) != 0) |
658 | 0 | return false; |
659 | | |
660 | 0 | if (bfd_write (magic, sizeof (magic), abfd) != sizeof (magic) |
661 | 0 | || bfd_write (vers, sizeof (vers), abfd) != sizeof (vers)) |
662 | 0 | return false; |
663 | | |
664 | 0 | return true; |
665 | 0 | } |
666 | | |
667 | | static bool |
668 | | wasm_mkobject (bfd *abfd) |
669 | 16.3k | { |
670 | 16.3k | tdata_type *tdata = (tdata_type *) bfd_alloc (abfd, sizeof (tdata_type)); |
671 | | |
672 | 16.3k | if (! tdata) |
673 | 0 | return false; |
674 | | |
675 | 16.3k | tdata->symbols = NULL; |
676 | 16.3k | tdata->symcount = 0; |
677 | | |
678 | 16.3k | abfd->tdata.any = tdata; |
679 | | |
680 | 16.3k | return true; |
681 | 16.3k | } |
682 | | |
683 | | static long |
684 | | wasm_get_symtab_upper_bound (bfd *abfd) |
685 | 19 | { |
686 | 19 | tdata_type *tdata = abfd->tdata.any; |
687 | | |
688 | 19 | return (tdata->symcount + 1) * (sizeof (asymbol *)); |
689 | 19 | } |
690 | | |
691 | | static long |
692 | | wasm_canonicalize_symtab (bfd *abfd, asymbol **alocation) |
693 | 19 | { |
694 | 19 | tdata_type *tdata = abfd->tdata.any; |
695 | 19 | size_t i; |
696 | | |
697 | 105 | for (i = 0; i < tdata->symcount; i++) |
698 | 86 | alocation[i] = &tdata->symbols[i]; |
699 | 19 | alocation[i] = NULL; |
700 | | |
701 | 19 | return tdata->symcount; |
702 | 19 | } |
703 | | |
704 | | static asymbol * |
705 | | wasm_make_empty_symbol (bfd *abfd) |
706 | 53.0k | { |
707 | 53.0k | size_t amt = sizeof (asymbol); |
708 | 53.0k | asymbol *new_symbol = (asymbol *) bfd_zalloc (abfd, amt); |
709 | | |
710 | 53.0k | if (! new_symbol) |
711 | 0 | return NULL; |
712 | 53.0k | new_symbol->the_bfd = abfd; |
713 | 53.0k | return new_symbol; |
714 | 53.0k | } |
715 | | |
716 | | static void |
717 | | wasm_print_symbol (bfd *abfd, |
718 | | void * filep, |
719 | | asymbol *symbol, |
720 | | bfd_print_symbol_type how) |
721 | 0 | { |
722 | 0 | FILE *file = (FILE *) filep; |
723 | |
|
724 | 0 | switch (how) |
725 | 0 | { |
726 | 0 | case bfd_print_symbol_name: |
727 | 0 | fprintf (file, "%s", symbol->name); |
728 | 0 | break; |
729 | | |
730 | 0 | default: |
731 | 0 | bfd_print_symbol_vandf (abfd, filep, symbol); |
732 | 0 | fprintf (file, " %-5s %s", symbol->section->name, symbol->name); |
733 | 0 | } |
734 | 0 | } |
735 | | |
736 | | static void |
737 | | wasm_get_symbol_info (bfd *abfd ATTRIBUTE_UNUSED, |
738 | | asymbol *symbol, |
739 | | symbol_info *ret) |
740 | 28 | { |
741 | 28 | bfd_symbol_info (symbol, ret); |
742 | 28 | } |
743 | | |
744 | | /* Check whether ABFD is a WebAssembly module; if so, scan it. */ |
745 | | |
746 | | static bfd_cleanup |
747 | | wasm_object_p (bfd *abfd) |
748 | 3.41M | { |
749 | 3.41M | bool error; |
750 | 3.41M | asection *s; |
751 | | |
752 | 3.41M | if (bfd_seek (abfd, 0, SEEK_SET) != 0) |
753 | 0 | return NULL; |
754 | | |
755 | 3.41M | if (!wasm_read_header (abfd, &error)) |
756 | 3.39M | { |
757 | 3.39M | bfd_set_error (bfd_error_wrong_format); |
758 | 3.39M | return NULL; |
759 | 3.39M | } |
760 | | |
761 | 16.3k | if (!wasm_mkobject (abfd)) |
762 | 0 | return NULL; |
763 | | |
764 | 16.3k | if (!wasm_scan (abfd) |
765 | 16.3k | || !bfd_default_set_arch_mach (abfd, bfd_arch_wasm32, 0)) |
766 | 9.08k | { |
767 | 9.08k | bfd_release (abfd, abfd->tdata.any); |
768 | 9.08k | abfd->tdata.any = NULL; |
769 | 9.08k | return NULL; |
770 | 9.08k | } |
771 | | |
772 | 7.27k | s = bfd_get_section_by_name (abfd, WASM_NAME_SECTION); |
773 | 7.27k | if (s != NULL && wasm_scan_name_function_section (abfd, s)) |
774 | 352 | abfd->flags |= HAS_SYMS; |
775 | | |
776 | 7.27k | return _bfd_no_cleanup; |
777 | 16.3k | } |
778 | | |
779 | | /* BFD_JUMP_TABLE_WRITE */ |
780 | | #define wasm_set_arch_mach _bfd_generic_set_arch_mach |
781 | | |
782 | | /* BFD_JUMP_TABLE_SYMBOLS */ |
783 | | #define wasm_get_symbol_version_string _bfd_nosymbols_get_symbol_version_string |
784 | | #define wasm_bfd_is_local_label_name bfd_generic_is_local_label_name |
785 | | #define wasm_bfd_is_target_special_symbol _bfd_bool_bfd_asymbol_false |
786 | | #define wasm_get_lineno _bfd_nosymbols_get_lineno |
787 | | #define wasm_find_nearest_line _bfd_nosymbols_find_nearest_line |
788 | | #define wasm_find_nearest_line_with_alt _bfd_nosymbols_find_nearest_line_with_alt |
789 | | #define wasm_find_line _bfd_nosymbols_find_line |
790 | | #define wasm_find_inliner_info _bfd_nosymbols_find_inliner_info |
791 | | #define wasm_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol |
792 | | #define wasm_read_minisymbols _bfd_generic_read_minisymbols |
793 | | #define wasm_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol |
794 | | |
795 | | const bfd_target wasm_vec = |
796 | | { |
797 | | "wasm", /* Name. */ |
798 | | bfd_target_unknown_flavour, |
799 | | BFD_ENDIAN_LITTLE, |
800 | | BFD_ENDIAN_LITTLE, |
801 | | (HAS_SYMS | WP_TEXT), /* Object flags. */ |
802 | | (SEC_CODE | SEC_DATA | SEC_HAS_CONTENTS), /* Section flags. */ |
803 | | 0, /* Leading underscore. */ |
804 | | ' ', /* AR_pad_char. */ |
805 | | 255, /* AR_max_namelen. */ |
806 | | 0, /* Match priority. */ |
807 | | TARGET_KEEP_UNUSED_SECTION_SYMBOLS, /* keep unused section symbols. */ |
808 | | /* Routines to byte-swap various sized integers from the data sections. */ |
809 | | bfd_getl64, bfd_getl_signed_64, bfd_putl64, |
810 | | bfd_getl32, bfd_getl_signed_32, bfd_putl32, |
811 | | bfd_getl16, bfd_getl_signed_16, bfd_putl16, |
812 | | |
813 | | /* Routines to byte-swap various sized integers from the file headers. */ |
814 | | bfd_getl64, bfd_getl_signed_64, bfd_putl64, |
815 | | bfd_getl32, bfd_getl_signed_32, bfd_putl32, |
816 | | bfd_getl16, bfd_getl_signed_16, bfd_putl16, |
817 | | |
818 | | { |
819 | | _bfd_dummy_target, |
820 | | wasm_object_p, /* bfd_check_format. */ |
821 | | _bfd_dummy_target, |
822 | | _bfd_dummy_target, |
823 | | }, |
824 | | { |
825 | | _bfd_bool_bfd_false_error, |
826 | | wasm_mkobject, |
827 | | _bfd_bool_bfd_false_error, |
828 | | _bfd_bool_bfd_false_error, |
829 | | }, |
830 | | { /* bfd_write_contents. */ |
831 | | _bfd_bool_bfd_false_error, |
832 | | wasm_write_object_contents, |
833 | | _bfd_bool_bfd_false_error, |
834 | | _bfd_bool_bfd_false_error, |
835 | | }, |
836 | | |
837 | | BFD_JUMP_TABLE_GENERIC (_bfd_generic), |
838 | | BFD_JUMP_TABLE_COPY (_bfd_generic), |
839 | | BFD_JUMP_TABLE_CORE (_bfd_nocore), |
840 | | BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive), |
841 | | BFD_JUMP_TABLE_SYMBOLS (wasm), |
842 | | BFD_JUMP_TABLE_RELOCS (_bfd_norelocs), |
843 | | BFD_JUMP_TABLE_WRITE (wasm), |
844 | | BFD_JUMP_TABLE_LINK (_bfd_nolink), |
845 | | BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic), |
846 | | |
847 | | NULL, |
848 | | |
849 | | NULL, |
850 | | }; |