/src/binutils-gdb/gas/codeview.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* codeview.c - CodeView debug support |
2 | | Copyright (C) 2022-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | #include "as.h" |
22 | | #include "codeview.h" |
23 | | #include "subsegs.h" |
24 | | #include "filenames.h" |
25 | | #include "md5.h" |
26 | | |
27 | | #if defined (TE_PE) && defined (O_secrel) |
28 | | |
29 | | #define NUM_MD5_BYTES 16 |
30 | | |
31 | | #define FILE_ENTRY_PADDING 2 |
32 | | #define FILE_ENTRY_LENGTH (sizeof (struct file_checksum) + NUM_MD5_BYTES \ |
33 | | + FILE_ENTRY_PADDING) |
34 | | |
35 | | struct line |
36 | | { |
37 | | struct line *next; |
38 | | unsigned int lineno; |
39 | | addressT frag_offset; |
40 | | }; |
41 | | |
42 | | struct line_file |
43 | | { |
44 | | struct line_file *next; |
45 | | unsigned int fileno; |
46 | | struct line *lines_head, *lines_tail; |
47 | | unsigned int num_lines; |
48 | | }; |
49 | | |
50 | | struct line_block |
51 | | { |
52 | | struct line_block *next; |
53 | | segT seg; |
54 | | unsigned int subseg; |
55 | | fragS *frag; |
56 | | symbolS *sym; |
57 | | struct line_file *files_head, *files_tail; |
58 | | }; |
59 | | |
60 | | struct source_file |
61 | | { |
62 | | struct source_file *next; |
63 | | unsigned int num; |
64 | | char *filename; |
65 | | uint32_t string_pos; |
66 | | uint8_t md5[NUM_MD5_BYTES]; |
67 | | }; |
68 | | |
69 | | static struct line_block *blocks_head = NULL, *blocks_tail = NULL; |
70 | | static struct source_file *files_head = NULL, *files_tail = NULL; |
71 | | static unsigned int num_source_files = 0; |
72 | | |
73 | | /* Return the size of the current fragment (taken from dwarf2dbg.c). */ |
74 | | static offsetT |
75 | | get_frag_fix (fragS *frag, segT seg) |
76 | | { |
77 | | frchainS *fr; |
78 | | |
79 | | if (frag->fr_next) |
80 | | return frag->fr_fix; |
81 | | |
82 | | for (fr = seg_info (seg)->frchainP; fr; fr = fr->frch_next) |
83 | | if (fr->frch_last == frag) |
84 | | return (char *) obstack_next_free (&fr->frch_obstack) - frag->fr_literal; |
85 | | |
86 | | abort (); |
87 | | } |
88 | | |
89 | | /* Emit a .secrel32 relocation. */ |
90 | | static void |
91 | | emit_secrel32_reloc (symbolS *sym) |
92 | | { |
93 | | expressionS exp; |
94 | | |
95 | | memset (&exp, 0, sizeof (exp)); |
96 | | exp.X_op = O_secrel; |
97 | | exp.X_add_symbol = sym; |
98 | | exp.X_add_number = 0; |
99 | | emit_expr (&exp, sizeof (uint32_t)); |
100 | | } |
101 | | |
102 | | /* Emit a .secidx relocation. */ |
103 | | static void |
104 | | emit_secidx_reloc (symbolS *sym) |
105 | | { |
106 | | expressionS exp; |
107 | | |
108 | | memset (&exp, 0, sizeof (exp)); |
109 | | exp.X_op = O_secidx; |
110 | | exp.X_add_symbol = sym; |
111 | | exp.X_add_number = 0; |
112 | | emit_expr (&exp, sizeof (uint16_t)); |
113 | | } |
114 | | |
115 | | /* Write the DEBUG_S_STRINGTABLE subsection. */ |
116 | | static void |
117 | | write_string_table (void) |
118 | | { |
119 | | uint32_t len; |
120 | | unsigned int padding; |
121 | | char *ptr, *start; |
122 | | |
123 | | len = 1; |
124 | | |
125 | | for (struct source_file *sf = files_head; sf; sf = sf->next) |
126 | | { |
127 | | len += strlen (sf->filename) + 1; |
128 | | } |
129 | | |
130 | | if (len % 4) |
131 | | padding = 4 - (len % 4); |
132 | | else |
133 | | padding = 0; |
134 | | |
135 | | ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len + padding); |
136 | | |
137 | | bfd_putl32 (DEBUG_S_STRINGTABLE, ptr); |
138 | | ptr += sizeof (uint32_t); |
139 | | bfd_putl32 (len, ptr); |
140 | | ptr += sizeof (uint32_t); |
141 | | |
142 | | start = ptr; |
143 | | |
144 | | *ptr = 0; |
145 | | ptr++; |
146 | | |
147 | | for (struct source_file *sf = files_head; sf; sf = sf->next) |
148 | | { |
149 | | size_t fn_len = strlen (sf->filename); |
150 | | |
151 | | sf->string_pos = ptr - start; |
152 | | |
153 | | memcpy(ptr, sf->filename, fn_len + 1); |
154 | | ptr += fn_len + 1; |
155 | | } |
156 | | |
157 | | memset (ptr, 0, padding); |
158 | | } |
159 | | |
160 | | /* Write the DEBUG_S_FILECHKSMS subsection. */ |
161 | | static void |
162 | | write_checksums (void) |
163 | | { |
164 | | uint32_t len; |
165 | | char *ptr; |
166 | | |
167 | | len = FILE_ENTRY_LENGTH * num_source_files; |
168 | | |
169 | | ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len); |
170 | | |
171 | | bfd_putl32 (DEBUG_S_FILECHKSMS, ptr); |
172 | | ptr += sizeof (uint32_t); |
173 | | bfd_putl32 (len, ptr); |
174 | | ptr += sizeof (uint32_t); |
175 | | |
176 | | for (struct source_file *sf = files_head; sf; sf = sf->next) |
177 | | { |
178 | | struct file_checksum fc; |
179 | | |
180 | | fc.file_id = sf->string_pos; |
181 | | fc.checksum_length = NUM_MD5_BYTES; |
182 | | fc.checksum_type = CHKSUM_TYPE_MD5; |
183 | | |
184 | | memcpy (ptr, &fc, sizeof (struct file_checksum)); |
185 | | ptr += sizeof (struct file_checksum); |
186 | | |
187 | | memcpy (ptr, sf->md5, NUM_MD5_BYTES); |
188 | | ptr += NUM_MD5_BYTES; |
189 | | |
190 | | memset (ptr, 0, FILE_ENTRY_PADDING); |
191 | | ptr += FILE_ENTRY_PADDING; |
192 | | } |
193 | | } |
194 | | |
195 | | /* Write the DEBUG_S_LINES subsection. */ |
196 | | static void |
197 | | write_lines_info (void) |
198 | | { |
199 | | while (blocks_head) |
200 | | { |
201 | | struct line_block *lb; |
202 | | struct line_file *lf; |
203 | | uint32_t len; |
204 | | uint32_t off; |
205 | | char *ptr; |
206 | | |
207 | | lb = blocks_head; |
208 | | |
209 | | bfd_putl32 (DEBUG_S_LINES, frag_more (sizeof (uint32_t))); |
210 | | |
211 | | len = sizeof (struct cv_lines_header); |
212 | | |
213 | | for (lf = lb->files_head; lf; lf = lf->next) |
214 | | { |
215 | | len += sizeof (struct cv_lines_block); |
216 | | len += sizeof (struct cv_line) * lf->num_lines; |
217 | | } |
218 | | |
219 | | bfd_putl32 (len, frag_more (sizeof (uint32_t))); |
220 | | |
221 | | /* Write the header (struct cv_lines_header). We can't use a struct |
222 | | for this as we're also emitting relocations. */ |
223 | | |
224 | | emit_secrel32_reloc (lb->sym); |
225 | | emit_secidx_reloc (lb->sym); |
226 | | |
227 | | ptr = frag_more (len - sizeof (uint32_t) - sizeof (uint16_t)); |
228 | | |
229 | | /* Flags */ |
230 | | bfd_putl16 (0, ptr); |
231 | | ptr += sizeof (uint16_t); |
232 | | |
233 | | off = lb->files_head->lines_head->frag_offset; |
234 | | |
235 | | /* Length of region */ |
236 | | bfd_putl32 (get_frag_fix (lb->frag, lb->seg) - off, ptr); |
237 | | ptr += sizeof (uint32_t); |
238 | | |
239 | | while (lb->files_head) |
240 | | { |
241 | | struct cv_lines_block *block = (struct cv_lines_block *) ptr; |
242 | | |
243 | | lf = lb->files_head; |
244 | | |
245 | | bfd_putl32(lf->fileno * FILE_ENTRY_LENGTH, &block->file_id); |
246 | | bfd_putl32(lf->num_lines, &block->num_lines); |
247 | | bfd_putl32(sizeof (struct cv_lines_block) |
248 | | + (sizeof (struct cv_line) * lf->num_lines), |
249 | | &block->length); |
250 | | |
251 | | ptr += sizeof (struct cv_lines_block); |
252 | | |
253 | | while (lf->lines_head) |
254 | | { |
255 | | struct line *l; |
256 | | struct cv_line *l2 = (struct cv_line *) ptr; |
257 | | |
258 | | l = lf->lines_head; |
259 | | |
260 | | /* Only the bottom 24 bits of line_no actually encode the |
261 | | line number. The top bit is a flag meaning "is |
262 | | a statement". */ |
263 | | |
264 | | bfd_putl32 (l->frag_offset - off, &l2->offset); |
265 | | bfd_putl32 (0x80000000 | (l->lineno & 0xffffff), |
266 | | &l2->line_no); |
267 | | |
268 | | lf->lines_head = l->next; |
269 | | |
270 | | free(l); |
271 | | |
272 | | ptr += sizeof (struct cv_line); |
273 | | } |
274 | | |
275 | | lb->files_head = lf->next; |
276 | | free (lf); |
277 | | } |
278 | | |
279 | | blocks_head = lb->next; |
280 | | |
281 | | free (lb); |
282 | | } |
283 | | } |
284 | | |
285 | | /* Return the CodeView constant for the selected architecture. */ |
286 | | static uint16_t |
287 | | target_processor (void) |
288 | | { |
289 | | switch (stdoutput->arch_info->arch) |
290 | | { |
291 | | case bfd_arch_i386: |
292 | | if (stdoutput->arch_info->mach & bfd_mach_x86_64) |
293 | | return CV_CFL_X64; |
294 | | else |
295 | | return CV_CFL_80386; |
296 | | |
297 | | case bfd_arch_aarch64: |
298 | | return CV_CFL_ARM64; |
299 | | |
300 | | default: |
301 | | return 0; |
302 | | } |
303 | | } |
304 | | |
305 | | /* Write the CodeView symbols, describing the object name and |
306 | | assembler version. */ |
307 | | static void |
308 | | write_symbols_info (void) |
309 | | { |
310 | | static const char assembler[] = "GNU AS " VERSION; |
311 | | |
312 | | char *path = lrealpath (out_file_name); |
313 | | char *path2 = remap_debug_filename (path); |
314 | | size_t path_len, padding; |
315 | | uint32_t len; |
316 | | struct OBJNAMESYM objname; |
317 | | struct COMPILESYM3 compile3; |
318 | | char *ptr; |
319 | | |
320 | | free (path); |
321 | | path = path2; |
322 | | |
323 | | path_len = strlen (path); |
324 | | |
325 | | len = sizeof (struct OBJNAMESYM) + path_len + 1; |
326 | | len += sizeof (struct COMPILESYM3) + sizeof (assembler); |
327 | | |
328 | | if (len % 4) |
329 | | padding = 4 - (len % 4); |
330 | | else |
331 | | padding = 0; |
332 | | |
333 | | len += padding; |
334 | | |
335 | | ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len); |
336 | | |
337 | | bfd_putl32 (DEBUG_S_SYMBOLS, ptr); |
338 | | ptr += sizeof (uint32_t); |
339 | | bfd_putl32 (len, ptr); |
340 | | ptr += sizeof (uint32_t); |
341 | | |
342 | | /* Write S_OBJNAME entry. */ |
343 | | |
344 | | bfd_putl16 (sizeof (struct OBJNAMESYM) - sizeof (uint16_t) + path_len + 1, |
345 | | &objname.length); |
346 | | bfd_putl16 (S_OBJNAME, &objname.type); |
347 | | bfd_putl32 (0, &objname.signature); |
348 | | |
349 | | memcpy (ptr, &objname, sizeof (struct OBJNAMESYM)); |
350 | | ptr += sizeof (struct OBJNAMESYM); |
351 | | memcpy (ptr, path, path_len + 1); |
352 | | ptr += path_len + 1; |
353 | | |
354 | | free (path); |
355 | | |
356 | | /* Write S_COMPILE3 entry. */ |
357 | | |
358 | | bfd_putl16 (sizeof (struct COMPILESYM3) - sizeof (uint16_t) |
359 | | + sizeof (assembler) + padding, &compile3.length); |
360 | | bfd_putl16 (S_COMPILE3, &compile3.type); |
361 | | bfd_putl32 (CV_CFL_MASM, &compile3.flags); |
362 | | bfd_putl16 (target_processor (), &compile3.machine); |
363 | | bfd_putl16 (0, &compile3.frontend_major); |
364 | | bfd_putl16 (0, &compile3.frontend_minor); |
365 | | bfd_putl16 (0, &compile3.frontend_build); |
366 | | bfd_putl16 (0, &compile3.frontend_qfe); |
367 | | bfd_putl16 (0, &compile3.backend_major); |
368 | | bfd_putl16 (0, &compile3.backend_minor); |
369 | | bfd_putl16 (0, &compile3.backend_build); |
370 | | bfd_putl16 (0, &compile3.backend_qfe); |
371 | | |
372 | | memcpy (ptr, &compile3, sizeof (struct COMPILESYM3)); |
373 | | ptr += sizeof (struct COMPILESYM3); |
374 | | memcpy (ptr, assembler, sizeof (assembler)); |
375 | | ptr += sizeof (assembler); |
376 | | |
377 | | memset (ptr, 0, padding); |
378 | | } |
379 | | |
380 | | /* Processing of the file has finished, emit the .debug$S section. */ |
381 | | void |
382 | | codeview_finish (void) |
383 | | { |
384 | | segT seg; |
385 | | |
386 | | if (!blocks_head) |
387 | | return; |
388 | | |
389 | | seg = subseg_new (".debug$S", 0); |
390 | | |
391 | | bfd_set_section_flags (seg, SEC_READONLY | SEC_NEVER_LOAD); |
392 | | |
393 | | bfd_putl32 (CV_SIGNATURE_C13, frag_more (sizeof (uint32_t))); |
394 | | |
395 | | write_string_table (); |
396 | | write_checksums (); |
397 | | write_lines_info (); |
398 | | write_symbols_info (); |
399 | | } |
400 | | |
401 | | /* Assign a new index number for the given file, or return the existing |
402 | | one if already assigned. */ |
403 | | static unsigned int |
404 | | get_fileno (const char *file) |
405 | | { |
406 | | struct source_file *sf; |
407 | | char *path = lrealpath (file); |
408 | | char *path2 = remap_debug_filename (path); |
409 | | size_t path_len; |
410 | | FILE *f; |
411 | | |
412 | | free (path); |
413 | | path = path2; |
414 | | |
415 | | path_len = strlen (path); |
416 | | |
417 | | for (sf = files_head; sf; sf = sf->next) |
418 | | { |
419 | | if (path_len == strlen (sf->filename) |
420 | | && !filename_ncmp (sf->filename, path, path_len)) |
421 | | { |
422 | | free (path); |
423 | | return sf->num; |
424 | | } |
425 | | } |
426 | | |
427 | | sf = xmalloc (sizeof (struct source_file)); |
428 | | |
429 | | sf->next = NULL; |
430 | | sf->num = num_source_files; |
431 | | sf->filename = path; |
432 | | |
433 | | f = fopen (file, "r"); |
434 | | if (!f) |
435 | | as_fatal (_("could not open %s for reading"), file); |
436 | | |
437 | | if (md5_stream (f, sf->md5)) |
438 | | { |
439 | | fclose(f); |
440 | | as_fatal (_("md5_stream failed")); |
441 | | } |
442 | | |
443 | | fclose(f); |
444 | | |
445 | | if (!files_head) |
446 | | files_head = sf; |
447 | | else |
448 | | files_tail->next = sf; |
449 | | |
450 | | files_tail = sf; |
451 | | |
452 | | num_source_files++; |
453 | | |
454 | | return num_source_files - 1; |
455 | | } |
456 | | |
457 | | /* Called for each new line in asm file. */ |
458 | | void |
459 | | codeview_generate_asm_lineno (void) |
460 | | { |
461 | | const char *file; |
462 | | unsigned int filenr; |
463 | | unsigned int lineno; |
464 | | struct line *l; |
465 | | symbolS *sym = NULL; |
466 | | struct line_block *lb; |
467 | | struct line_file *lf; |
468 | | |
469 | | file = as_where (&lineno); |
470 | | |
471 | | filenr = get_fileno (file); |
472 | | |
473 | | if (!blocks_tail || blocks_tail->frag != frag_now) |
474 | | { |
475 | | static int label_num = 0; |
476 | | char name[32]; |
477 | | |
478 | | sprintf (name, ".Loc.%u", label_num); |
479 | | label_num++; |
480 | | sym = symbol_new (name, now_seg, frag_now, frag_now_fix ()); |
481 | | |
482 | | lb = xmalloc (sizeof (struct line_block)); |
483 | | lb->next = NULL; |
484 | | lb->seg = now_seg; |
485 | | lb->subseg = now_subseg; |
486 | | lb->frag = frag_now; |
487 | | lb->sym = sym; |
488 | | lb->files_head = lb->files_tail = NULL; |
489 | | |
490 | | if (!blocks_head) |
491 | | blocks_head = lb; |
492 | | else |
493 | | blocks_tail->next = lb; |
494 | | |
495 | | blocks_tail = lb; |
496 | | } |
497 | | else |
498 | | { |
499 | | lb = blocks_tail; |
500 | | } |
501 | | |
502 | | if (!lb->files_tail || lb->files_tail->fileno != filenr) |
503 | | { |
504 | | lf = xmalloc (sizeof (struct line_file)); |
505 | | lf->next = NULL; |
506 | | lf->fileno = filenr; |
507 | | lf->lines_head = lf->lines_tail = NULL; |
508 | | lf->num_lines = 0; |
509 | | |
510 | | if (!lb->files_head) |
511 | | lb->files_head = lf; |
512 | | else |
513 | | lb->files_tail->next = lf; |
514 | | |
515 | | lb->files_tail = lf; |
516 | | } |
517 | | else |
518 | | { |
519 | | lf = lb->files_tail; |
520 | | } |
521 | | |
522 | | l = xmalloc (sizeof (struct line)); |
523 | | l->next = NULL; |
524 | | l->lineno = lineno; |
525 | | l->frag_offset = frag_now_fix (); |
526 | | |
527 | | if (!lf->lines_head) |
528 | | lf->lines_head = l; |
529 | | else |
530 | | lf->lines_tail->next = l; |
531 | | |
532 | | lf->lines_tail = l; |
533 | | lf->num_lines++; |
534 | | } |
535 | | |
536 | | /* Output a compressed CodeView integer. The return value is the number of |
537 | | bytes used. */ |
538 | | |
539 | | unsigned int |
540 | | output_cv_comp (char *p, offsetT value, int sign) |
541 | | { |
542 | | char *orig = p; |
543 | | |
544 | | if (sign) |
545 | | { |
546 | | if (value < -0xfffffff || value > 0xfffffff) |
547 | | { |
548 | | as_bad (_("value cannot be expressed as a .cv_scomp")); |
549 | | return 0; |
550 | | } |
551 | | } |
552 | | else |
553 | | { |
554 | | if (value < 0 || value > 0x1fffffff) |
555 | | { |
556 | | as_bad (_("value cannot be expressed as a .cv_ucomp")); |
557 | | return 0; |
558 | | } |
559 | | } |
560 | | |
561 | | if (sign) |
562 | | { |
563 | | if (value >= 0) |
564 | | value <<= 1; |
565 | | else |
566 | | value = (-value << 1) | 1; |
567 | | } |
568 | | |
569 | | if (value <= 0x7f) |
570 | | { |
571 | | *p++ = value; |
572 | | } |
573 | | else if (value <= 0x3fff) |
574 | | { |
575 | | *p++ = 0x80 | (value >> 8); |
576 | | *p++ = value & 0xff; |
577 | | } |
578 | | else |
579 | | { |
580 | | *p++ = 0xc0 | (value >> 24); |
581 | | *p++ = (value >> 16) & 0xff; |
582 | | *p++ = (value >> 8) & 0xff; |
583 | | *p++ = value & 0xff; |
584 | | } |
585 | | |
586 | | return p - orig; |
587 | | } |
588 | | |
589 | | /* Return the size needed to output a compressed CodeView integer. */ |
590 | | |
591 | | unsigned int |
592 | | sizeof_cv_comp (offsetT value, int sign) |
593 | | { |
594 | | if (sign) |
595 | | { |
596 | | if (value < -0xfffffff || value > 0xfffffff) |
597 | | return 0; |
598 | | |
599 | | if (value >= 0) |
600 | | value <<= 1; |
601 | | else |
602 | | value = (-value << 1) | 1; |
603 | | } |
604 | | else |
605 | | { |
606 | | if (value > 0x1fffffff) |
607 | | return 0; |
608 | | } |
609 | | |
610 | | if (value <= 0x7f) |
611 | | return 1; |
612 | | else if (value <= 0x3fff) |
613 | | return 2; |
614 | | else if (value <= 0x1fffffff) |
615 | | return 4; |
616 | | else |
617 | | return 0; |
618 | | } |
619 | | |
620 | | #else |
621 | | |
622 | | void |
623 | | codeview_finish (void) |
624 | 28 | { |
625 | 28 | } |
626 | | |
627 | | void |
628 | | codeview_generate_asm_lineno (void) |
629 | 0 | { |
630 | 0 | } |
631 | | |
632 | | #endif /* TE_PE && O_secrel */ |