1/* codeview.c - CodeView debug support 2 Copyright (C) 2022-2024 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 35struct line 36{ 37 struct line *next; 38 unsigned int lineno; 39 addressT frag_offset; 40}; 41 42struct 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 50struct 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 60struct 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 69static struct line_block *blocks_head = NULL, *blocks_tail = NULL; 70static struct source_file *files_head = NULL, *files_tail = NULL; 71static unsigned int num_source_files = 0; 72 73/* Return the size of the current fragment (taken from dwarf2dbg.c). */ 74static offsetT 75get_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. */ 90static void 91emit_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. */ 103static void 104emit_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. */ 116static void 117write_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. */ 161static void 162write_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. */ 196static void 197write_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. */ 286static uint16_t 287target_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. */ 307static void 308write_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. */ 381void 382codeview_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. */ 403static unsigned int 404get_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. */ 458void 459codeview_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#else 537 538void 539codeview_finish (void) 540{ 541} 542 543void 544codeview_generate_asm_lineno (void) 545{ 546} 547 548#endif /* TE_PE && O_secrel */ 549