mirror of
https://review.haiku-os.org/buildtools
synced 2024-11-23 07:18:49 +01:00
549 lines
12 KiB
C
549 lines
12 KiB
C
/* codeview.c - CodeView debug support
|
|
Copyright (C) 2022-2023 Free Software Foundation, Inc.
|
|
|
|
This file is part of GAS, the GNU Assembler.
|
|
|
|
GAS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GAS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GAS; see the file COPYING. If not, write to the Free
|
|
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
|
|
02110-1301, USA. */
|
|
|
|
#include "as.h"
|
|
#include "codeview.h"
|
|
#include "subsegs.h"
|
|
#include "filenames.h"
|
|
#include "md5.h"
|
|
|
|
#if defined (TE_PE) && defined (O_secrel)
|
|
|
|
#define NUM_MD5_BYTES 16
|
|
|
|
#define FILE_ENTRY_PADDING 2
|
|
#define FILE_ENTRY_LENGTH (sizeof (struct file_checksum) + NUM_MD5_BYTES \
|
|
+ FILE_ENTRY_PADDING)
|
|
|
|
struct line
|
|
{
|
|
struct line *next;
|
|
unsigned int lineno;
|
|
addressT frag_offset;
|
|
};
|
|
|
|
struct line_file
|
|
{
|
|
struct line_file *next;
|
|
unsigned int fileno;
|
|
struct line *lines_head, *lines_tail;
|
|
unsigned int num_lines;
|
|
};
|
|
|
|
struct line_block
|
|
{
|
|
struct line_block *next;
|
|
segT seg;
|
|
unsigned int subseg;
|
|
fragS *frag;
|
|
symbolS *sym;
|
|
struct line_file *files_head, *files_tail;
|
|
};
|
|
|
|
struct source_file
|
|
{
|
|
struct source_file *next;
|
|
unsigned int num;
|
|
char *filename;
|
|
uint32_t string_pos;
|
|
uint8_t md5[NUM_MD5_BYTES];
|
|
};
|
|
|
|
static struct line_block *blocks_head = NULL, *blocks_tail = NULL;
|
|
static struct source_file *files_head = NULL, *files_tail = NULL;
|
|
static unsigned int num_source_files = 0;
|
|
|
|
/* Return the size of the current fragment (taken from dwarf2dbg.c). */
|
|
static offsetT
|
|
get_frag_fix (fragS *frag, segT seg)
|
|
{
|
|
frchainS *fr;
|
|
|
|
if (frag->fr_next)
|
|
return frag->fr_fix;
|
|
|
|
for (fr = seg_info (seg)->frchainP; fr; fr = fr->frch_next)
|
|
if (fr->frch_last == frag)
|
|
return (char *) obstack_next_free (&fr->frch_obstack) - frag->fr_literal;
|
|
|
|
abort ();
|
|
}
|
|
|
|
/* Emit a .secrel32 relocation. */
|
|
static void
|
|
emit_secrel32_reloc (symbolS *sym)
|
|
{
|
|
expressionS exp;
|
|
|
|
memset (&exp, 0, sizeof (exp));
|
|
exp.X_op = O_secrel;
|
|
exp.X_add_symbol = sym;
|
|
exp.X_add_number = 0;
|
|
emit_expr (&exp, sizeof (uint32_t));
|
|
}
|
|
|
|
/* Emit a .secidx relocation. */
|
|
static void
|
|
emit_secidx_reloc (symbolS *sym)
|
|
{
|
|
expressionS exp;
|
|
|
|
memset (&exp, 0, sizeof (exp));
|
|
exp.X_op = O_secidx;
|
|
exp.X_add_symbol = sym;
|
|
exp.X_add_number = 0;
|
|
emit_expr (&exp, sizeof (uint16_t));
|
|
}
|
|
|
|
/* Write the DEBUG_S_STRINGTABLE subsection. */
|
|
static void
|
|
write_string_table (void)
|
|
{
|
|
uint32_t len;
|
|
unsigned int padding;
|
|
char *ptr, *start;
|
|
|
|
len = 1;
|
|
|
|
for (struct source_file *sf = files_head; sf; sf = sf->next)
|
|
{
|
|
len += strlen (sf->filename) + 1;
|
|
}
|
|
|
|
if (len % 4)
|
|
padding = 4 - (len % 4);
|
|
else
|
|
padding = 0;
|
|
|
|
ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len + padding);
|
|
|
|
bfd_putl32 (DEBUG_S_STRINGTABLE, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
bfd_putl32 (len, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
|
|
start = ptr;
|
|
|
|
*ptr = 0;
|
|
ptr++;
|
|
|
|
for (struct source_file *sf = files_head; sf; sf = sf->next)
|
|
{
|
|
size_t fn_len = strlen (sf->filename);
|
|
|
|
sf->string_pos = ptr - start;
|
|
|
|
memcpy(ptr, sf->filename, fn_len + 1);
|
|
ptr += fn_len + 1;
|
|
}
|
|
|
|
memset (ptr, 0, padding);
|
|
}
|
|
|
|
/* Write the DEBUG_S_FILECHKSMS subsection. */
|
|
static void
|
|
write_checksums (void)
|
|
{
|
|
uint32_t len;
|
|
char *ptr;
|
|
|
|
len = FILE_ENTRY_LENGTH * num_source_files;
|
|
|
|
ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len);
|
|
|
|
bfd_putl32 (DEBUG_S_FILECHKSMS, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
bfd_putl32 (len, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
|
|
for (struct source_file *sf = files_head; sf; sf = sf->next)
|
|
{
|
|
struct file_checksum fc;
|
|
|
|
fc.file_id = sf->string_pos;
|
|
fc.checksum_length = NUM_MD5_BYTES;
|
|
fc.checksum_type = CHKSUM_TYPE_MD5;
|
|
|
|
memcpy (ptr, &fc, sizeof (struct file_checksum));
|
|
ptr += sizeof (struct file_checksum);
|
|
|
|
memcpy (ptr, sf->md5, NUM_MD5_BYTES);
|
|
ptr += NUM_MD5_BYTES;
|
|
|
|
memset (ptr, 0, FILE_ENTRY_PADDING);
|
|
ptr += FILE_ENTRY_PADDING;
|
|
}
|
|
}
|
|
|
|
/* Write the DEBUG_S_LINES subsection. */
|
|
static void
|
|
write_lines_info (void)
|
|
{
|
|
while (blocks_head)
|
|
{
|
|
struct line_block *lb;
|
|
struct line_file *lf;
|
|
uint32_t len;
|
|
uint32_t off;
|
|
char *ptr;
|
|
|
|
lb = blocks_head;
|
|
|
|
bfd_putl32 (DEBUG_S_LINES, frag_more (sizeof (uint32_t)));
|
|
|
|
len = sizeof (struct cv_lines_header);
|
|
|
|
for (lf = lb->files_head; lf; lf = lf->next)
|
|
{
|
|
len += sizeof (struct cv_lines_block);
|
|
len += sizeof (struct cv_line) * lf->num_lines;
|
|
}
|
|
|
|
bfd_putl32 (len, frag_more (sizeof (uint32_t)));
|
|
|
|
/* Write the header (struct cv_lines_header). We can't use a struct
|
|
for this as we're also emitting relocations. */
|
|
|
|
emit_secrel32_reloc (lb->sym);
|
|
emit_secidx_reloc (lb->sym);
|
|
|
|
ptr = frag_more (len - sizeof (uint32_t) - sizeof (uint16_t));
|
|
|
|
/* Flags */
|
|
bfd_putl16 (0, ptr);
|
|
ptr += sizeof (uint16_t);
|
|
|
|
off = lb->files_head->lines_head->frag_offset;
|
|
|
|
/* Length of region */
|
|
bfd_putl32 (get_frag_fix (lb->frag, lb->seg) - off, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
|
|
while (lb->files_head)
|
|
{
|
|
struct cv_lines_block *block = (struct cv_lines_block *) ptr;
|
|
|
|
lf = lb->files_head;
|
|
|
|
bfd_putl32(lf->fileno * FILE_ENTRY_LENGTH, &block->file_id);
|
|
bfd_putl32(lf->num_lines, &block->num_lines);
|
|
bfd_putl32(sizeof (struct cv_lines_block)
|
|
+ (sizeof (struct cv_line) * lf->num_lines),
|
|
&block->length);
|
|
|
|
ptr += sizeof (struct cv_lines_block);
|
|
|
|
while (lf->lines_head)
|
|
{
|
|
struct line *l;
|
|
struct cv_line *l2 = (struct cv_line *) ptr;
|
|
|
|
l = lf->lines_head;
|
|
|
|
/* Only the bottom 24 bits of line_no actually encode the
|
|
line number. The top bit is a flag meaning "is
|
|
a statement". */
|
|
|
|
bfd_putl32 (l->frag_offset - off, &l2->offset);
|
|
bfd_putl32 (0x80000000 | (l->lineno & 0xffffff),
|
|
&l2->line_no);
|
|
|
|
lf->lines_head = l->next;
|
|
|
|
free(l);
|
|
|
|
ptr += sizeof (struct cv_line);
|
|
}
|
|
|
|
lb->files_head = lf->next;
|
|
free (lf);
|
|
}
|
|
|
|
blocks_head = lb->next;
|
|
|
|
free (lb);
|
|
}
|
|
}
|
|
|
|
/* Return the CodeView constant for the selected architecture. */
|
|
static uint16_t
|
|
target_processor (void)
|
|
{
|
|
switch (stdoutput->arch_info->arch)
|
|
{
|
|
case bfd_arch_i386:
|
|
if (stdoutput->arch_info->mach & bfd_mach_x86_64)
|
|
return CV_CFL_X64;
|
|
else
|
|
return CV_CFL_80386;
|
|
|
|
case bfd_arch_aarch64:
|
|
return CV_CFL_ARM64;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Write the CodeView symbols, describing the object name and
|
|
assembler version. */
|
|
static void
|
|
write_symbols_info (void)
|
|
{
|
|
static const char assembler[] = "GNU AS " VERSION;
|
|
|
|
char *path = lrealpath (out_file_name);
|
|
char *path2 = remap_debug_filename (path);
|
|
size_t path_len, padding;
|
|
uint32_t len;
|
|
struct OBJNAMESYM objname;
|
|
struct COMPILESYM3 compile3;
|
|
char *ptr;
|
|
|
|
free (path);
|
|
path = path2;
|
|
|
|
path_len = strlen (path);
|
|
|
|
len = sizeof (struct OBJNAMESYM) + path_len + 1;
|
|
len += sizeof (struct COMPILESYM3) + sizeof (assembler);
|
|
|
|
if (len % 4)
|
|
padding = 4 - (len % 4);
|
|
else
|
|
padding = 0;
|
|
|
|
len += padding;
|
|
|
|
ptr = frag_more (sizeof (uint32_t) + sizeof (uint32_t) + len);
|
|
|
|
bfd_putl32 (DEBUG_S_SYMBOLS, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
bfd_putl32 (len, ptr);
|
|
ptr += sizeof (uint32_t);
|
|
|
|
/* Write S_OBJNAME entry. */
|
|
|
|
bfd_putl16 (sizeof (struct OBJNAMESYM) - sizeof (uint16_t) + path_len + 1,
|
|
&objname.length);
|
|
bfd_putl16 (S_OBJNAME, &objname.type);
|
|
bfd_putl32 (0, &objname.signature);
|
|
|
|
memcpy (ptr, &objname, sizeof (struct OBJNAMESYM));
|
|
ptr += sizeof (struct OBJNAMESYM);
|
|
memcpy (ptr, path, path_len + 1);
|
|
ptr += path_len + 1;
|
|
|
|
free (path);
|
|
|
|
/* Write S_COMPILE3 entry. */
|
|
|
|
bfd_putl16 (sizeof (struct COMPILESYM3) - sizeof (uint16_t)
|
|
+ sizeof (assembler) + padding, &compile3.length);
|
|
bfd_putl16 (S_COMPILE3, &compile3.type);
|
|
bfd_putl32 (CV_CFL_MASM, &compile3.flags);
|
|
bfd_putl16 (target_processor (), &compile3.machine);
|
|
bfd_putl16 (0, &compile3.frontend_major);
|
|
bfd_putl16 (0, &compile3.frontend_minor);
|
|
bfd_putl16 (0, &compile3.frontend_build);
|
|
bfd_putl16 (0, &compile3.frontend_qfe);
|
|
bfd_putl16 (0, &compile3.backend_major);
|
|
bfd_putl16 (0, &compile3.backend_minor);
|
|
bfd_putl16 (0, &compile3.backend_build);
|
|
bfd_putl16 (0, &compile3.backend_qfe);
|
|
|
|
memcpy (ptr, &compile3, sizeof (struct COMPILESYM3));
|
|
ptr += sizeof (struct COMPILESYM3);
|
|
memcpy (ptr, assembler, sizeof (assembler));
|
|
ptr += sizeof (assembler);
|
|
|
|
memset (ptr, 0, padding);
|
|
}
|
|
|
|
/* Processing of the file has finished, emit the .debug$S section. */
|
|
void
|
|
codeview_finish (void)
|
|
{
|
|
segT seg;
|
|
|
|
if (!blocks_head)
|
|
return;
|
|
|
|
seg = subseg_new (".debug$S", 0);
|
|
|
|
bfd_set_section_flags (seg, SEC_READONLY | SEC_NEVER_LOAD);
|
|
|
|
bfd_putl32 (CV_SIGNATURE_C13, frag_more (sizeof (uint32_t)));
|
|
|
|
write_string_table ();
|
|
write_checksums ();
|
|
write_lines_info ();
|
|
write_symbols_info ();
|
|
}
|
|
|
|
/* Assign a new index number for the given file, or return the existing
|
|
one if already assigned. */
|
|
static unsigned int
|
|
get_fileno (const char *file)
|
|
{
|
|
struct source_file *sf;
|
|
char *path = lrealpath (file);
|
|
char *path2 = remap_debug_filename (path);
|
|
size_t path_len;
|
|
FILE *f;
|
|
|
|
free (path);
|
|
path = path2;
|
|
|
|
path_len = strlen (path);
|
|
|
|
for (sf = files_head; sf; sf = sf->next)
|
|
{
|
|
if (path_len == strlen (sf->filename)
|
|
&& !filename_ncmp (sf->filename, path, path_len))
|
|
{
|
|
free (path);
|
|
return sf->num;
|
|
}
|
|
}
|
|
|
|
sf = xmalloc (sizeof (struct source_file));
|
|
|
|
sf->next = NULL;
|
|
sf->num = num_source_files;
|
|
sf->filename = path;
|
|
|
|
f = fopen (file, "r");
|
|
if (!f)
|
|
as_fatal (_("could not open %s for reading"), file);
|
|
|
|
if (md5_stream (f, sf->md5))
|
|
{
|
|
fclose(f);
|
|
as_fatal (_("md5_stream failed"));
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
if (!files_head)
|
|
files_head = sf;
|
|
else
|
|
files_tail->next = sf;
|
|
|
|
files_tail = sf;
|
|
|
|
num_source_files++;
|
|
|
|
return num_source_files - 1;
|
|
}
|
|
|
|
/* Called for each new line in asm file. */
|
|
void
|
|
codeview_generate_asm_lineno (void)
|
|
{
|
|
const char *file;
|
|
unsigned int filenr;
|
|
unsigned int lineno;
|
|
struct line *l;
|
|
symbolS *sym = NULL;
|
|
struct line_block *lb;
|
|
struct line_file *lf;
|
|
|
|
file = as_where (&lineno);
|
|
|
|
filenr = get_fileno (file);
|
|
|
|
if (!blocks_tail || blocks_tail->frag != frag_now)
|
|
{
|
|
static int label_num = 0;
|
|
char name[32];
|
|
|
|
sprintf (name, ".Loc.%u", label_num);
|
|
label_num++;
|
|
sym = symbol_new (name, now_seg, frag_now, frag_now_fix ());
|
|
|
|
lb = xmalloc (sizeof (struct line_block));
|
|
lb->next = NULL;
|
|
lb->seg = now_seg;
|
|
lb->subseg = now_subseg;
|
|
lb->frag = frag_now;
|
|
lb->sym = sym;
|
|
lb->files_head = lb->files_tail = NULL;
|
|
|
|
if (!blocks_head)
|
|
blocks_head = lb;
|
|
else
|
|
blocks_tail->next = lb;
|
|
|
|
blocks_tail = lb;
|
|
}
|
|
else
|
|
{
|
|
lb = blocks_tail;
|
|
}
|
|
|
|
if (!lb->files_tail || lb->files_tail->fileno != filenr)
|
|
{
|
|
lf = xmalloc (sizeof (struct line_file));
|
|
lf->next = NULL;
|
|
lf->fileno = filenr;
|
|
lf->lines_head = lf->lines_tail = NULL;
|
|
lf->num_lines = 0;
|
|
|
|
if (!lb->files_head)
|
|
lb->files_head = lf;
|
|
else
|
|
lb->files_tail->next = lf;
|
|
|
|
lb->files_tail = lf;
|
|
}
|
|
else
|
|
{
|
|
lf = lb->files_tail;
|
|
}
|
|
|
|
l = xmalloc (sizeof (struct line));
|
|
l->next = NULL;
|
|
l->lineno = lineno;
|
|
l->frag_offset = frag_now_fix ();
|
|
|
|
if (!lf->lines_head)
|
|
lf->lines_head = l;
|
|
else
|
|
lf->lines_tail->next = l;
|
|
|
|
lf->lines_tail = l;
|
|
lf->num_lines++;
|
|
}
|
|
|
|
#else
|
|
|
|
void
|
|
codeview_finish (void)
|
|
{
|
|
}
|
|
|
|
void
|
|
codeview_generate_asm_lineno (void)
|
|
{
|
|
}
|
|
|
|
#endif /* TE_PE && O_secrel */
|