/* * TCC - Tiny C Compiler * * Copyright (c) 2001-2004 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "tcc.h" /* stab debug support */ static const struct { int type; int size; int encoding; const char *name; } default_debug[] = { { VT_INT, 4, DW_ATE_signed, "int:t1=r1;-2147483648;2147483647;" }, { VT_BYTE, 1, DW_ATE_signed_char, "char:t2=r2;0;127;" }, #if LONG_SIZE == 4 { VT_LONG | VT_INT, 4, DW_ATE_signed, "long int:t3=r3;-2147483648;2147483647;" }, #else { VT_LLONG | VT_LONG, 8, DW_ATE_signed, "long int:t3=r3;-9223372036854775808;9223372036854775807;" }, #endif { VT_INT | VT_UNSIGNED, 4, DW_ATE_unsigned, "unsigned int:t4=r4;0;037777777777;" }, #if LONG_SIZE == 4 { VT_LONG | VT_INT | VT_UNSIGNED, 4, DW_ATE_unsigned, "long unsigned int:t5=r5;0;037777777777;" }, #else /* use octal instead of -1 so size_t works (-gstabs+ in gcc) */ { VT_LLONG | VT_LONG | VT_UNSIGNED, 8, DW_ATE_unsigned, "long unsigned int:t5=r5;0;01777777777777777777777;" }, #endif { VT_QLONG, 16, DW_ATE_signed, "__int128:t6=r6;0;-1;" }, { VT_QLONG | VT_UNSIGNED, 16, DW_ATE_unsigned, "__int128 unsigned:t7=r7;0;-1;" }, { VT_LLONG, 8, DW_ATE_signed, "long long int:t8=r8;-9223372036854775808;9223372036854775807;" }, { VT_LLONG | VT_UNSIGNED, 8, DW_ATE_unsigned, "long long unsigned int:t9=r9;0;01777777777777777777777;" }, { VT_SHORT, 2, DW_ATE_signed, "short int:t10=r10;-32768;32767;" }, { VT_SHORT | VT_UNSIGNED, 2, DW_ATE_unsigned, "short unsigned int:t11=r11;0;65535;" }, { VT_BYTE | VT_DEFSIGN, 1, DW_ATE_signed_char, "signed char:t12=r12;-128;127;" }, { VT_BYTE | VT_DEFSIGN | VT_UNSIGNED, 1, DW_ATE_unsigned_char, "unsigned char:t13=r13;0;255;" }, { VT_FLOAT, 4, DW_ATE_float, "float:t14=r1;4;0;" }, { VT_DOUBLE, 8, DW_ATE_float, "double:t15=r1;8;0;" }, #ifdef TCC_USING_DOUBLE_FOR_LDOUBLE { VT_DOUBLE | VT_LONG, 8, DW_ATE_float, "long double:t16=r1;8;0;" }, #else { VT_LDOUBLE, 16, DW_ATE_float, "long double:t16=r1;16;0;" }, #endif { -1, -1, -1, "_Float32:t17=r1;4;0;" }, { -1, -1, -1, "_Float64:t18=r1;8;0;" }, { -1, -1, -1, "_Float128:t19=r1;16;0;" }, { -1, -1, -1, "_Float32x:t20=r1;8;0;" }, { -1, -1, -1, "_Float64x:t21=r1;16;0;" }, { -1, -1, -1, "_Decimal32:t22=r1;4;0;" }, { -1, -1, -1, "_Decimal64:t23=r1;8;0;" }, { -1, -1, -1, "_Decimal128:t24=r1;16;0;" }, /* if default char is unsigned */ { VT_BYTE | VT_UNSIGNED, 1, DW_ATE_unsigned_char, "unsigned char:t25=r25;0;255;" }, /* boolean type */ { VT_BOOL, 1, DW_ATE_boolean, "bool:t26=r26;0;255;" }, #if LONG_SIZE == 4 { VT_VOID, 1, DW_ATE_unsigned_char, "void:t27=27" }, #else /* bitfields use these */ { VT_LONG | VT_INT, 8, DW_ATE_signed, "long int:t27=r27;-9223372036854775808;9223372036854775807;" }, { VT_LONG | VT_INT | VT_UNSIGNED, 8, DW_ATE_unsigned, "long unsigned int:t28=r28;0;01777777777777777777777;" }, { VT_VOID, 1, DW_ATE_unsigned_char, "void:t29=29" }, #endif }; #define N_DEFAULT_DEBUG (sizeof (default_debug) / sizeof (default_debug[0])) /* dwarf debug */ #define DWARF_LINE_BASE -5 #define DWARF_LINE_RANGE 14 #define DWARF_OPCODE_BASE 13 #if defined TCC_TARGET_ARM64 #define DWARF_MIN_INSTR_LEN 4 #elif defined TCC_TARGET_ARM #define DWARF_MIN_INSTR_LEN 2 #else #define DWARF_MIN_INSTR_LEN 1 #endif #define DWARF_ABBREV_COMPILE_UNIT 1 #define DWARF_ABBREV_BASE_TYPE 2 #define DWARF_ABBREV_VARIABLE_EXTERNAL 3 #define DWARF_ABBREV_VARIABLE_STATIC 4 #define DWARF_ABBREV_VARIABLE_LOCAL 5 #define DWARF_ABBREV_FORMAL_PARAMETER 6 #define DWARF_ABBREV_POINTER 7 #define DWARF_ABBREV_ARRAY_TYPE 8 #define DWARF_ABBREV_SUBRANGE_TYPE 9 #define DWARF_ABBREV_TYPEDEF 10 #define DWARF_ABBREV_ENUMERATOR_SIGNED 11 #define DWARF_ABBREV_ENUMERATOR_UNSIGNED 12 #define DWARF_ABBREV_ENUMERATION_TYPE 13 #define DWARF_ABBREV_MEMBER 14 #define DWARF_ABBREV_MEMBER_BF 15 #define DWARF_ABBREV_STRUCTURE_TYPE 16 #define DWARF_ABBREV_STRUCTURE_EMPTY_TYPE 17 #define DWARF_ABBREV_UNION_TYPE 18 #define DWARF_ABBREV_UNION_EMPTY_TYPE 19 #define DWARF_ABBREV_SUBPROGRAM_EXTERNAL 20 #define DWARF_ABBREV_SUBPROGRAM_STATIC 21 #define DWARF_ABBREV_LEXICAL_BLOCK 22 #define DWARF_ABBREV_LEXICAL_EMPTY_BLOCK 23 #define DWARF_ABBREV_SUBROUTINE_TYPE 24 #define DWARF_ABBREV_SUBROUTINE_EMPTY_TYPE 25 #define DWARF_ABBREV_FORMAL_PARAMETER2 26 /* all entries should have been generated with dwarf_uleb128 except has_children. All values are currently below 128 so this currently works. */ static const unsigned char dwarf_abbrev_init[] = { DWARF_ABBREV_COMPILE_UNIT, DW_TAG_compile_unit, 1, DW_AT_producer, DW_FORM_strp, DW_AT_language, DW_FORM_data1, DW_AT_name, DW_FORM_line_strp, DW_AT_comp_dir, DW_FORM_line_strp, DW_AT_low_pc, DW_FORM_addr, #if PTR_SIZE == 4 DW_AT_high_pc, DW_FORM_data4, #else DW_AT_high_pc, DW_FORM_data8, #endif DW_AT_stmt_list, DW_FORM_sec_offset, 0, 0, DWARF_ABBREV_BASE_TYPE, DW_TAG_base_type, 0, DW_AT_byte_size, DW_FORM_udata, DW_AT_encoding, DW_FORM_data1, DW_AT_name, DW_FORM_strp, 0, 0, DWARF_ABBREV_VARIABLE_EXTERNAL, DW_TAG_variable, 0, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, DW_AT_external, DW_FORM_flag, DW_AT_location, DW_FORM_exprloc, 0, 0, DWARF_ABBREV_VARIABLE_STATIC, DW_TAG_variable, 0, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, DW_AT_location, DW_FORM_exprloc, 0, 0, DWARF_ABBREV_VARIABLE_LOCAL, DW_TAG_variable, 0, DW_AT_name, DW_FORM_strp, DW_AT_type, DW_FORM_ref4, DW_AT_location, DW_FORM_exprloc, 0, 0, DWARF_ABBREV_FORMAL_PARAMETER, DW_TAG_formal_parameter, 0, DW_AT_name, DW_FORM_strp, DW_AT_type, DW_FORM_ref4, DW_AT_location, DW_FORM_exprloc, 0, 0, DWARF_ABBREV_POINTER, DW_TAG_pointer_type, 0, DW_AT_byte_size, DW_FORM_data1, DW_AT_type, DW_FORM_ref4, 0, 0, DWARF_ABBREV_ARRAY_TYPE, DW_TAG_array_type, 1, DW_AT_type, DW_FORM_ref4, DW_AT_sibling, DW_FORM_ref4, 0, 0, DWARF_ABBREV_SUBRANGE_TYPE, DW_TAG_subrange_type, 0, DW_AT_type, DW_FORM_ref4, DW_AT_upper_bound, DW_FORM_udata, 0, 0, DWARF_ABBREV_TYPEDEF, DW_TAG_typedef, 0, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, 0, 0, DWARF_ABBREV_ENUMERATOR_SIGNED, DW_TAG_enumerator, 0, DW_AT_name, DW_FORM_strp, DW_AT_const_value, DW_FORM_sdata, 0, 0, DWARF_ABBREV_ENUMERATOR_UNSIGNED, DW_TAG_enumerator, 0, DW_AT_name, DW_FORM_strp, DW_AT_const_value, DW_FORM_udata, 0, 0, DWARF_ABBREV_ENUMERATION_TYPE, DW_TAG_enumeration_type, 1, DW_AT_name, DW_FORM_strp, DW_AT_encoding, DW_FORM_data1, DW_AT_byte_size, DW_FORM_data1, DW_AT_type, DW_FORM_ref4, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_sibling, DW_FORM_ref4, 0, 0, DWARF_ABBREV_MEMBER, DW_TAG_member, 0, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, DW_AT_data_member_location, DW_FORM_udata, 0, 0, DWARF_ABBREV_MEMBER_BF, DW_TAG_member, 0, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, DW_AT_bit_size, DW_FORM_udata, DW_AT_data_bit_offset, DW_FORM_udata, 0, 0, DWARF_ABBREV_STRUCTURE_TYPE, DW_TAG_structure_type, 1, DW_AT_name, DW_FORM_strp, DW_AT_byte_size, DW_FORM_udata, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_sibling, DW_FORM_ref4, 0, 0, DWARF_ABBREV_STRUCTURE_EMPTY_TYPE, DW_TAG_structure_type, 0, DW_AT_name, DW_FORM_strp, DW_AT_byte_size, DW_FORM_udata, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, 0, 0, DWARF_ABBREV_UNION_TYPE, DW_TAG_union_type, 1, DW_AT_name, DW_FORM_strp, DW_AT_byte_size, DW_FORM_udata, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_sibling, DW_FORM_ref4, 0, 0, DWARF_ABBREV_UNION_EMPTY_TYPE, DW_TAG_union_type, 0, DW_AT_name, DW_FORM_strp, DW_AT_byte_size, DW_FORM_udata, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, 0, 0, DWARF_ABBREV_SUBPROGRAM_EXTERNAL, DW_TAG_subprogram, 1, DW_AT_external, DW_FORM_flag, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, DW_AT_low_pc, DW_FORM_addr, #if PTR_SIZE == 4 DW_AT_high_pc, DW_FORM_data4, #else DW_AT_high_pc, DW_FORM_data8, #endif DW_AT_sibling, DW_FORM_ref4, DW_AT_frame_base, DW_FORM_exprloc, 0, 0, DWARF_ABBREV_SUBPROGRAM_STATIC, DW_TAG_subprogram, 1, DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_udata, DW_AT_decl_line, DW_FORM_udata, DW_AT_type, DW_FORM_ref4, DW_AT_low_pc, DW_FORM_addr, #if PTR_SIZE == 4 DW_AT_high_pc, DW_FORM_data4, #else DW_AT_high_pc, DW_FORM_data8, #endif DW_AT_sibling, DW_FORM_ref4, DW_AT_frame_base, DW_FORM_exprloc, 0, 0, DWARF_ABBREV_LEXICAL_BLOCK, DW_TAG_lexical_block, 1, DW_AT_low_pc, DW_FORM_addr, #if PTR_SIZE == 4 DW_AT_high_pc, DW_FORM_data4, #else DW_AT_high_pc, DW_FORM_data8, #endif 0, 0, DWARF_ABBREV_LEXICAL_EMPTY_BLOCK, DW_TAG_lexical_block, 0, DW_AT_low_pc, DW_FORM_addr, #if PTR_SIZE == 4 DW_AT_high_pc, DW_FORM_data4, #else DW_AT_high_pc, DW_FORM_data8, #endif 0, 0, DWARF_ABBREV_SUBROUTINE_TYPE, DW_TAG_subroutine_type, 1, DW_AT_type, DW_FORM_ref4, DW_AT_sibling, DW_FORM_ref4, 0, 0, DWARF_ABBREV_SUBROUTINE_EMPTY_TYPE, DW_TAG_subroutine_type, 0, DW_AT_type, DW_FORM_ref4, 0, 0, DWARF_ABBREV_FORMAL_PARAMETER2, DW_TAG_formal_parameter, 0, DW_AT_type, DW_FORM_ref4, 0, 0, 0 }; static const unsigned char dwarf_line_opcodes[] = { 0 ,1 ,1 ,1 ,1 ,0 ,0 ,0 ,1 ,0 ,0 ,1 }; /* ------------------------------------------------------------------------- */ /* debug state */ struct _tccdbg { int last_line_num, new_file; int section_sym; int debug_next_type; struct _debug_hash { int debug_type; Sym *type; } *debug_hash; struct _debug_anon_hash { Sym *type; int n_debug_type; int *debug_type; } *debug_anon_hash; int n_debug_hash; int n_debug_anon_hash; struct _debug_info { int start; int end; int n_sym; struct debug_sym { int type; unsigned long value; char *str; Section *sec; int sym_index; int info; int file; int line; } *sym; struct _debug_info *child, *next, *last, *parent; } *debug_info, *debug_info_root; struct { int info; int abbrev; int line; int str; int line_str; } dwarf_sym; struct { int start; int dir_size; char **dir_table; int filename_size; struct dwarf_filename_struct { int dir_entry; char *name; } *filename_table; int line_size; int line_max_size; unsigned char *line_data; int cur_file; int last_file; int last_pc; int last_line; } dwarf_line; struct { int start; Sym *func; int line; int base_type_used[N_DEFAULT_DEBUG]; } dwarf_info; /* test coverage */ struct { unsigned long offset; unsigned long last_file_name; unsigned long last_func_name; int ind; int line; } tcov_data; }; #define last_line_num s1->dState->last_line_num #define new_file s1->dState->new_file #define section_sym s1->dState->section_sym #define debug_next_type s1->dState->debug_next_type #define debug_hash s1->dState->debug_hash #define debug_anon_hash s1->dState->debug_anon_hash #define n_debug_hash s1->dState->n_debug_hash #define n_debug_anon_hash s1->dState->n_debug_anon_hash #define debug_info s1->dState->debug_info #define debug_info_root s1->dState->debug_info_root #define dwarf_sym s1->dState->dwarf_sym #define dwarf_line s1->dState->dwarf_line #define dwarf_info s1->dState->dwarf_info #define tcov_data s1->dState->tcov_data /* ------------------------------------------------------------------------- */ static void put_stabs(TCCState *s1, const char *str, int type, int other, int desc, unsigned long value); ST_FUNC void tcc_debug_new(TCCState *s1) { int shf = 0; if (!s1->dState) s1->dState = tcc_mallocz(sizeof *s1->dState); #ifdef CONFIG_TCC_BACKTRACE /* include stab info with standalone backtrace support */ if (s1->do_debug && s1->output_type == TCC_OUTPUT_MEMORY) s1->do_backtrace = 1; if (s1->do_backtrace) shf = SHF_ALLOC | SHF_WRITE; // SHF_WRITE needed for musl/SELINUX #endif if (s1->dwarf) { s1->dwlo = s1->nb_sections; dwarf_info_section = new_section(s1, ".debug_info", SHT_PROGBITS, shf); dwarf_abbrev_section = new_section(s1, ".debug_abbrev", SHT_PROGBITS, shf); dwarf_line_section = new_section(s1, ".debug_line", SHT_PROGBITS, shf); dwarf_aranges_section = new_section(s1, ".debug_aranges", SHT_PROGBITS, shf); shf |= SHF_MERGE | SHF_STRINGS; dwarf_str_section = new_section(s1, ".debug_str", SHT_PROGBITS, shf); dwarf_str_section->sh_entsize = 1; dwarf_info_section->sh_addralign = dwarf_abbrev_section->sh_addralign = dwarf_line_section->sh_addralign = dwarf_aranges_section->sh_addralign = dwarf_str_section->sh_addralign = 1; if (s1->dwarf >= 5) { dwarf_line_str_section = new_section(s1, ".debug_line_str", SHT_PROGBITS, shf); dwarf_line_str_section->sh_entsize = 1; dwarf_line_str_section->sh_addralign = 1; } s1->dwhi = s1->nb_sections; } else { stab_section = new_section(s1, ".stab", SHT_PROGBITS, shf); stab_section->sh_entsize = sizeof(Stab_Sym); stab_section->sh_addralign = sizeof ((Stab_Sym*)0)->n_value; stab_section->link = new_section(s1, ".stabstr", SHT_STRTAB, shf); /* put first entry */ put_stabs(s1, "", 0, 0, 0, 0); } } /* put stab debug information */ static void put_stabs(TCCState *s1, const char *str, int type, int other, int desc, unsigned long value) { Stab_Sym *sym; unsigned offset; if (type == N_SLINE && (offset = stab_section->data_offset) && (sym = (Stab_Sym*)(stab_section->data + offset) - 1) && sym->n_type == type && sym->n_value == value) { /* just update line_number in previous entry */ sym->n_desc = desc; return; } sym = section_ptr_add(stab_section, sizeof(Stab_Sym)); if (str) { sym->n_strx = put_elf_str(stab_section->link, str); } else { sym->n_strx = 0; } sym->n_type = type; sym->n_other = other; sym->n_desc = desc; sym->n_value = value; } static void put_stabs_r(TCCState *s1, const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index) { put_elf_reloc(symtab_section, stab_section, stab_section->data_offset + 8, sizeof ((Stab_Sym*)0)->n_value == PTR_SIZE ? R_DATA_PTR : R_DATA_32, sym_index); put_stabs(s1, str, type, other, desc, value); } static void put_stabn(TCCState *s1, int type, int other, int desc, int value) { put_stabs(s1, NULL, type, other, desc, value); } /* ------------------------------------------------------------------------- */ #define dwarf_data1(s,data) \ { unsigned char *p = section_ptr_add((s), 1); *p = (data); } #define dwarf_data2(s,data) \ write16le(section_ptr_add((s), 2), (data)) #define dwarf_data4(s,data) \ write32le(section_ptr_add((s), 4), (data)) #define dwarf_data8(s,data) \ write64le(section_ptr_add((s), 8), (data)) static int dwarf_get_section_sym(Section *s) { TCCState *s1 = s->s1; return put_elf_sym(symtab_section, 0, 0, ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, s->sh_num, NULL); } static void dwarf_reloc(Section *s, int sym, int rel) { TCCState *s1 = s->s1; put_elf_reloca(symtab_section, s, s->data_offset, rel, sym, 0); } static void dwarf_string(Section *s, Section *dw, int sym, const char *str) { TCCState *s1 = s->s1; int offset, len; char *ptr; len = strlen(str) + 1; offset = dw->data_offset; ptr = section_ptr_add(dw, len); memmove(ptr, str, len); put_elf_reloca(symtab_section, s, s->data_offset, R_DATA_32DW, sym, PTR_SIZE == 4 ? 0 : offset); dwarf_data4(s, PTR_SIZE == 4 ? offset : 0); } static void dwarf_strp(Section *s, const char *str) { TCCState *s1 = s->s1; dwarf_string(s, dwarf_str_section, dwarf_sym.str, str); } static void dwarf_line_strp(Section *s, const char *str) { TCCState *s1 = s->s1; dwarf_string(s, dwarf_line_str_section, dwarf_sym.line_str, str); } static void dwarf_line_op(TCCState *s1, unsigned char op) { if (dwarf_line.line_size >= dwarf_line.line_max_size) { dwarf_line.line_max_size += 1024; dwarf_line.line_data = (unsigned char *)tcc_realloc(dwarf_line.line_data, dwarf_line.line_max_size); } dwarf_line.line_data[dwarf_line.line_size++] = op; } static void dwarf_file(TCCState *s1) { int i, j; char *filename; int index_offset = s1->dwarf < 5; if (!strcmp(file->filename, "")) { dwarf_line.cur_file = 1; return; } filename = strrchr(file->filename, '/'); if (filename == NULL) { for (i = 1; i < dwarf_line.filename_size; i++) if (dwarf_line.filename_table[i].dir_entry == 0 && strcmp(dwarf_line.filename_table[i].name, file->filename) == 0) { dwarf_line.cur_file = i + index_offset; return; } i = -index_offset; filename = file->filename; } else { char *undo = filename; char *dir = file->filename; *filename++ = '\0'; for (i = 0; i < dwarf_line.dir_size; i++) if (strcmp(dwarf_line.dir_table[i], dir) == 0) { for (j = 1; j < dwarf_line.filename_size; j++) if (dwarf_line.filename_table[j].dir_entry - index_offset == i && strcmp(dwarf_line.filename_table[j].name, filename) == 0) { *undo = '/'; dwarf_line.cur_file = j + index_offset; return; } break; } if (i == dwarf_line.dir_size) { dwarf_line.dir_size++; dwarf_line.dir_table = (char **) tcc_realloc(dwarf_line.dir_table, dwarf_line.dir_size * sizeof (char *)); dwarf_line.dir_table[i] = tcc_strdup(dir); } *undo = '/'; } dwarf_line.filename_table = (struct dwarf_filename_struct *) tcc_realloc(dwarf_line.filename_table, (dwarf_line.filename_size + 1) * sizeof (struct dwarf_filename_struct)); dwarf_line.filename_table[dwarf_line.filename_size].dir_entry = i + index_offset; dwarf_line.filename_table[dwarf_line.filename_size].name = tcc_strdup(filename); dwarf_line.cur_file = dwarf_line.filename_size++ + index_offset; return; } #if 0 static int dwarf_uleb128_size (unsigned long long value) { int size = 0; do { value >>= 7; size++; } while (value != 0); return size; } #endif static int dwarf_sleb128_size (long long value) { int size = 0; long long end = value >> 63; unsigned char last = end & 0x40; unsigned char byte; do { byte = value & 0x7f; value >>= 7; size++; } while (value != end || (byte & 0x40) != last); return size; } static void dwarf_uleb128 (Section *s, unsigned long long value) { do { unsigned char byte = value & 0x7f; value >>= 7; dwarf_data1(s, byte | (value ? 0x80 : 0)); } while (value != 0); } static void dwarf_sleb128 (Section *s, long long value) { int more; long long end = value >> 63; unsigned char last = end & 0x40; do { unsigned char byte = value & 0x7f; value >>= 7; more = value != end || (byte & 0x40) != last; dwarf_data1(s, byte | (0x80 * more)); } while (more); } static void dwarf_uleb128_op (TCCState *s1, unsigned long long value) { do { unsigned char byte = value & 0x7f; value >>= 7; dwarf_line_op(s1, byte | (value ? 0x80 : 0)); } while (value != 0); } static void dwarf_sleb128_op (TCCState *s1, long long value) { int more; long long end = value >> 63; unsigned char last = end & 0x40; do { unsigned char byte = value & 0x7f; value >>= 7; more = value != end || (byte & 0x40) != last; dwarf_line_op(s1, byte | (0x80 * more)); } while (more); } /* start of translation unit info */ ST_FUNC void tcc_debug_start(TCCState *s1) { int i; char buf[512]; char *filename; /* we might currently #include the */ filename = file->prev ? file->prev->filename : file->filename; /* an elf symbol of type STT_FILE must be put so that STB_LOCAL symbols can be safely used */ put_elf_sym(symtab_section, 0, 0, ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, SHN_ABS, filename); if (s1->do_debug) { new_file = last_line_num = 0; debug_next_type = N_DEFAULT_DEBUG; debug_hash = NULL; debug_anon_hash = NULL; n_debug_hash = 0; n_debug_anon_hash = 0; getcwd(buf, sizeof(buf)); #ifdef _WIN32 normalize_slashes(buf); #endif if (s1->dwarf) { int start_abbrev; unsigned char *ptr; char *undo; /* dwarf_abbrev */ start_abbrev = dwarf_abbrev_section->data_offset; ptr = section_ptr_add(dwarf_abbrev_section, sizeof(dwarf_abbrev_init)); memcpy(ptr, dwarf_abbrev_init, sizeof(dwarf_abbrev_init)); if (s1->dwarf < 5) { while (*ptr) { ptr += 3; while (*ptr) { if (ptr[1] == DW_FORM_line_strp) ptr[1] = DW_FORM_strp; if (s1->dwarf < 4) { /* These are compatable for DW_TAG_compile_unit DW_AT_stmt_list. */ if (ptr[1] == DW_FORM_sec_offset) ptr[1] = DW_FORM_data4; /* This code uses only size < 0x80 so these are compatible. */ if (ptr[1] == DW_FORM_exprloc) ptr[1] = DW_FORM_block1; } ptr += 2; } ptr += 2; } } dwarf_sym.info = dwarf_get_section_sym(dwarf_info_section); dwarf_sym.abbrev = dwarf_get_section_sym(dwarf_abbrev_section); dwarf_sym.line = dwarf_get_section_sym(dwarf_line_section); dwarf_sym.str = dwarf_get_section_sym(dwarf_str_section); if (tcc_state->dwarf >= 5) dwarf_sym.line_str = dwarf_get_section_sym(dwarf_line_str_section); else { dwarf_line_str_section = dwarf_str_section; dwarf_sym.line_str = dwarf_sym.str; } section_sym = dwarf_get_section_sym(text_section); /* dwarf_info */ dwarf_info.start = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); // size dwarf_data2(dwarf_info_section, s1->dwarf); // version if (s1->dwarf >= 5) { dwarf_data1(dwarf_info_section, DW_UT_compile); // unit type dwarf_data1(dwarf_info_section, PTR_SIZE); dwarf_reloc(dwarf_info_section, dwarf_sym.abbrev, R_DATA_32DW); dwarf_data4(dwarf_info_section, start_abbrev); } else { dwarf_reloc(dwarf_info_section, dwarf_sym.abbrev, R_DATA_32DW); dwarf_data4(dwarf_info_section, start_abbrev); dwarf_data1(dwarf_info_section, PTR_SIZE); } dwarf_data1(dwarf_info_section, DWARF_ABBREV_COMPILE_UNIT); dwarf_strp(dwarf_info_section, "tcc " TCC_VERSION); dwarf_data1(dwarf_info_section, DW_LANG_C11); dwarf_line_strp(dwarf_info_section, filename); dwarf_line_strp(dwarf_info_section, buf); dwarf_reloc(dwarf_info_section, section_sym, R_DATA_PTR); #if PTR_SIZE == 4 dwarf_data4(dwarf_info_section, ind); // low pc dwarf_data4(dwarf_info_section, 0); // high pc #else dwarf_data8(dwarf_info_section, ind); // low pc dwarf_data8(dwarf_info_section, 0); // high pc #endif dwarf_reloc(dwarf_info_section, dwarf_sym.line, R_DATA_32DW); dwarf_data4(dwarf_info_section, dwarf_line_section->data_offset); // stmt_list /* dwarf_line */ dwarf_line.start = dwarf_line_section->data_offset; dwarf_data4(dwarf_line_section, 0); // length dwarf_data2(dwarf_line_section, s1->dwarf); // version if (s1->dwarf >= 5) { dwarf_data1(dwarf_line_section, PTR_SIZE); // address size dwarf_data1(dwarf_line_section, 0); // segment selector } dwarf_data4(dwarf_line_section, 0); // prologue Length dwarf_data1(dwarf_line_section, DWARF_MIN_INSTR_LEN); if (s1->dwarf >= 4) dwarf_data1(dwarf_line_section, 1); // maximum ops per instruction dwarf_data1(dwarf_line_section, 1); // Initial value of 'is_stmt' dwarf_data1(dwarf_line_section, DWARF_LINE_BASE); dwarf_data1(dwarf_line_section, DWARF_LINE_RANGE); dwarf_data1(dwarf_line_section, DWARF_OPCODE_BASE); ptr = section_ptr_add(dwarf_line_section, sizeof(dwarf_line_opcodes)); memcpy(ptr, dwarf_line_opcodes, sizeof(dwarf_line_opcodes)); undo = strrchr(filename, '/'); if (undo) *undo = 0; dwarf_line.dir_size = 1 + (undo != NULL); dwarf_line.dir_table = (char **) tcc_malloc(sizeof (char *) * dwarf_line.dir_size); dwarf_line.dir_table[0] = tcc_strdup(buf); if (undo) dwarf_line.dir_table[1] = tcc_strdup(filename); dwarf_line.filename_size = 2; dwarf_line.filename_table = (struct dwarf_filename_struct *) tcc_malloc(2*sizeof (struct dwarf_filename_struct)); dwarf_line.filename_table[0].dir_entry = 0; if (undo) { dwarf_line.filename_table[0].name = tcc_strdup(undo + 1); dwarf_line.filename_table[1].dir_entry = 1; dwarf_line.filename_table[1].name = tcc_strdup(undo + 1); *undo = '/'; } else { dwarf_line.filename_table[0].name = tcc_strdup(filename); dwarf_line.filename_table[1].dir_entry = 0; dwarf_line.filename_table[1].name = tcc_strdup(filename); } dwarf_line.line_size = dwarf_line.line_max_size = 0; dwarf_line.line_data = NULL; dwarf_line.cur_file = 1; dwarf_line.last_file = 0; dwarf_line.last_pc = 0; dwarf_line.last_line = 1; dwarf_line_op(s1, 0); // extended dwarf_uleb128_op(s1, 1 + PTR_SIZE); // extended size dwarf_line_op(s1, DW_LNE_set_address); for (i = 0; i < PTR_SIZE; i++) dwarf_line_op(s1, 0); memset(&dwarf_info.base_type_used, 0, sizeof(dwarf_info.base_type_used)); } else { /* file info: full path + filename */ pstrcat(buf, sizeof(buf), "/"); section_sym = put_elf_sym(symtab_section, 0, 0, ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0, text_section->sh_num, NULL); put_stabs_r(s1, buf, N_SO, 0, 0, text_section->data_offset, text_section, section_sym); put_stabs_r(s1, filename, N_SO, 0, 0, text_section->data_offset, text_section, section_sym); for (i = 0; i < N_DEFAULT_DEBUG; i++) put_stabs(s1, default_debug[i].name, N_LSYM, 0, 0, 0); } /* we're currently 'including' the */ tcc_debug_bincl(s1); } } /* put end of translation unit info */ ST_FUNC void tcc_debug_end(TCCState *s1) { if (!s1->do_debug || debug_next_type == 0) return; if (debug_info_root) tcc_debug_funcend(s1, 0); /* free stuff in case of errors */ if (s1->dwarf) { int i, j; int start_aranges; unsigned char *ptr; int text_size = text_section->data_offset; /* dwarf_info */ for (i = 0; i < n_debug_anon_hash; i++) { Sym *t = debug_anon_hash[i].type; int pos = dwarf_info_section->data_offset; dwarf_data1(dwarf_info_section, IS_UNION (t->type.t) ? DWARF_ABBREV_UNION_EMPTY_TYPE : DWARF_ABBREV_STRUCTURE_EMPTY_TYPE); dwarf_strp(dwarf_info_section, (t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM ? "" : get_tok_str(t->v, NULL)); dwarf_uleb128(dwarf_info_section, 0); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, file->line_num); for (j = 0; j < debug_anon_hash[i].n_debug_type; j++) write32le(dwarf_info_section->data + debug_anon_hash[i].debug_type[j], pos - dwarf_info.start); tcc_free (debug_anon_hash[i].debug_type); } tcc_free (debug_anon_hash); dwarf_data1(dwarf_info_section, 0); ptr = dwarf_info_section->data + dwarf_info.start; write32le(ptr, dwarf_info_section->data_offset - dwarf_info.start - 4); write32le(ptr + 25 + (s1->dwarf >= 5) + PTR_SIZE, text_size); /* dwarf_aranges */ start_aranges = dwarf_aranges_section->data_offset; dwarf_data4(dwarf_aranges_section, 0); // size dwarf_data2(dwarf_aranges_section, 2); // version dwarf_reloc(dwarf_aranges_section, dwarf_sym.info, R_DATA_32DW); dwarf_data4(dwarf_aranges_section, 0); // dwarf_info #if PTR_SIZE == 4 dwarf_data1(dwarf_aranges_section, 4); // address size #else dwarf_data1(dwarf_aranges_section, 8); // address size #endif dwarf_data1(dwarf_aranges_section, 0); // segment selector size dwarf_data4(dwarf_aranges_section, 0); // padding dwarf_reloc(dwarf_aranges_section, section_sym, R_DATA_PTR); #if PTR_SIZE == 4 dwarf_data4(dwarf_aranges_section, 0); // Begin dwarf_data4(dwarf_aranges_section, text_size); // End dwarf_data4(dwarf_aranges_section, 0); // End list dwarf_data4(dwarf_aranges_section, 0); // End list #else dwarf_data8(dwarf_aranges_section, 0); // Begin dwarf_data8(dwarf_aranges_section, text_size); // End dwarf_data8(dwarf_aranges_section, 0); // End list dwarf_data8(dwarf_aranges_section, 0); // End list #endif ptr = dwarf_aranges_section->data + start_aranges; write32le(ptr, dwarf_aranges_section->data_offset - start_aranges - 4); /* dwarf_line */ if (s1->dwarf >= 5) { dwarf_data1(dwarf_line_section, 1); /* col */ dwarf_uleb128(dwarf_line_section, DW_LNCT_path); dwarf_uleb128(dwarf_line_section, DW_FORM_line_strp); dwarf_uleb128(dwarf_line_section, dwarf_line.dir_size); for (i = 0; i < dwarf_line.dir_size; i++) dwarf_line_strp(dwarf_line_section, dwarf_line.dir_table[i]); dwarf_data1(dwarf_line_section, 2); /* col */ dwarf_uleb128(dwarf_line_section, DW_LNCT_path); dwarf_uleb128(dwarf_line_section, DW_FORM_line_strp); dwarf_uleb128(dwarf_line_section, DW_LNCT_directory_index); dwarf_uleb128(dwarf_line_section, DW_FORM_udata); dwarf_uleb128(dwarf_line_section, dwarf_line.filename_size); for (i = 0; i < dwarf_line.filename_size; i++) { dwarf_line_strp(dwarf_line_section, dwarf_line.filename_table[i].name); dwarf_uleb128(dwarf_line_section, dwarf_line.filename_table[i].dir_entry); } } else { int len; for (i = 0; i < dwarf_line.dir_size; i++) { len = strlen(dwarf_line.dir_table[i]) + 1; ptr = section_ptr_add(dwarf_line_section, len); memmove(ptr, dwarf_line.dir_table[i], len); } dwarf_data1(dwarf_line_section, 0); /* end dir */ for (i = 0; i < dwarf_line.filename_size; i++) { len = strlen(dwarf_line.filename_table[i].name) + 1; ptr = section_ptr_add(dwarf_line_section, len); memmove(ptr, dwarf_line.filename_table[i].name, len); dwarf_uleb128(dwarf_line_section, dwarf_line.filename_table[i].dir_entry); dwarf_uleb128(dwarf_line_section, 0); /* time */ dwarf_uleb128(dwarf_line_section, 0); /* size */ } dwarf_data1(dwarf_line_section, 0); /* end file */ } for (i = 0; i < dwarf_line.dir_size; i++) tcc_free(dwarf_line.dir_table[i]); tcc_free(dwarf_line.dir_table); for (i = 0; i < dwarf_line.filename_size; i++) tcc_free(dwarf_line.filename_table[i].name); tcc_free(dwarf_line.filename_table); dwarf_line_op(s1, 0); // extended dwarf_uleb128_op(s1, 1); // extended size dwarf_line_op(s1, DW_LNE_end_sequence); i = (s1->dwarf >= 5) * 2; write32le(&dwarf_line_section->data[dwarf_line.start + 6 + i], dwarf_line_section->data_offset - dwarf_line.start - (10 + i)); section_ptr_add(dwarf_line_section, 3); dwarf_reloc(dwarf_line_section, section_sym, R_DATA_PTR); ptr = section_ptr_add(dwarf_line_section, dwarf_line.line_size - 3); memmove(ptr - 3, dwarf_line.line_data, dwarf_line.line_size); tcc_free(dwarf_line.line_data); write32le(dwarf_line_section->data + dwarf_line.start, dwarf_line_section->data_offset - dwarf_line.start - 4); } else { put_stabs_r(s1, NULL, N_SO, 0, 0, text_section->data_offset, text_section, section_sym); } tcc_free(debug_hash); debug_next_type = 0; } static BufferedFile* put_new_file(TCCState *s1) { BufferedFile *f = file; /* use upper file if from inline ":asm:" */ if (f->filename[0] == ':') f = f->prev; if (f && new_file) { new_file = last_line_num = 0; if (s1->dwarf) dwarf_file(s1); else put_stabs_r(s1, f->filename, N_SOL, 0, 0, ind, text_section, section_sym); } return f; } /* put alternative filename */ ST_FUNC void tcc_debug_putfile(TCCState *s1, const char *filename) { if (0 == strcmp(file->filename, filename)) return; pstrcpy(file->filename, sizeof(file->filename), filename); if (!s1->do_debug) return; if (s1->dwarf) dwarf_file(s1); new_file = 1; } /* begin of #include */ ST_FUNC void tcc_debug_bincl(TCCState *s1) { if (!s1->do_debug) return; if (s1->dwarf) dwarf_file(s1); else put_stabs(s1, file->filename, N_BINCL, 0, 0, 0); new_file = 1; } /* end of #include */ ST_FUNC void tcc_debug_eincl(TCCState *s1) { if (!s1->do_debug) return; if (s1->dwarf) dwarf_file(s1); else put_stabn(s1, N_EINCL, 0, 0, 0); new_file = 1; } /* generate line number info */ ST_FUNC void tcc_debug_line(TCCState *s1) { BufferedFile *f; if (!s1->do_debug) return; if (cur_text_section != text_section || nocode_wanted) return; f = put_new_file(s1); if (!f) return; if (last_line_num == f->line_num) return; last_line_num = f->line_num; if (s1->dwarf) { int len_pc = (ind - dwarf_line.last_pc) / DWARF_MIN_INSTR_LEN; int len_line = f->line_num - dwarf_line.last_line; int n = len_pc * DWARF_LINE_RANGE + len_line + DWARF_OPCODE_BASE - DWARF_LINE_BASE; if (dwarf_line.cur_file != dwarf_line.last_file) { dwarf_line.last_file = dwarf_line.cur_file; dwarf_line_op(s1, DW_LNS_set_file); dwarf_uleb128_op(s1, dwarf_line.cur_file); } if (len_pc && len_line >= DWARF_LINE_BASE && len_line <= (DWARF_OPCODE_BASE + DWARF_LINE_BASE) && n >= DWARF_OPCODE_BASE && n <= 255) dwarf_line_op(s1, n); else { if (len_pc) { n = len_pc * DWARF_LINE_RANGE + 0 + DWARF_OPCODE_BASE - DWARF_LINE_BASE; if (n >= DWARF_OPCODE_BASE && n <= 255) dwarf_line_op(s1, n); else { dwarf_line_op(s1, DW_LNS_advance_pc); dwarf_uleb128_op(s1, len_pc); } } if (len_line) { n = 0 * DWARF_LINE_RANGE + len_line + DWARF_OPCODE_BASE - DWARF_LINE_BASE; if (len_line >= DWARF_LINE_BASE && len_line <= (DWARF_OPCODE_BASE + DWARF_LINE_BASE) && n >= DWARF_OPCODE_BASE && n <= 255) dwarf_line_op(s1, n); else { dwarf_line_op(s1, DW_LNS_advance_line); dwarf_sleb128_op(s1, len_line); } } } dwarf_line.last_pc = ind; dwarf_line.last_line = f->line_num; } else { if (func_ind != -1) { put_stabn(s1, N_SLINE, 0, f->line_num, ind - func_ind); } else { /* from tcc_assemble */ put_stabs_r(s1, NULL, N_SLINE, 0, f->line_num, ind, text_section, section_sym); } } } static void tcc_debug_stabs (TCCState *s1, const char *str, int type, unsigned long value, Section *sec, int sym_index, int info) { struct debug_sym *s; if (debug_info) { debug_info->sym = (struct debug_sym *)tcc_realloc (debug_info->sym, sizeof(struct debug_sym) * (debug_info->n_sym + 1)); s = debug_info->sym + debug_info->n_sym++; s->type = type; s->value = value; s->str = tcc_strdup(str); s->sec = sec; s->sym_index = sym_index; s->info = info; s->file = dwarf_line.cur_file; s->line = file->line_num; } else if (sec) put_stabs_r (s1, str, type, 0, 0, value, sec, sym_index); else put_stabs (s1, str, type, 0, 0, value); } ST_FUNC void tcc_debug_stabn(TCCState *s1, int type, int value) { if (!s1->do_debug) return; if (type == N_LBRAC) { struct _debug_info *info = (struct _debug_info *) tcc_mallocz(sizeof (*info)); info->start = value; info->parent = debug_info; if (debug_info) { if (debug_info->child) { if (debug_info->child->last) debug_info->child->last->next = info; else debug_info->child->next = info; debug_info->child->last = info; } else debug_info->child = info; } else debug_info_root = info; debug_info = info; } else { debug_info->end = value; debug_info = debug_info->parent; } } static int tcc_debug_find(TCCState *s1, Sym *t, int dwarf) { int i; if (!debug_info && dwarf && (t->type.t & VT_BTYPE) == VT_STRUCT && t->c == -1) { for (i = 0; i < n_debug_anon_hash; i++) if (t == debug_anon_hash[i].type) return 0; debug_anon_hash = (struct _debug_anon_hash *) tcc_realloc (debug_anon_hash, (n_debug_anon_hash + 1) * sizeof(*debug_anon_hash)); debug_anon_hash[n_debug_anon_hash].n_debug_type = 0; debug_anon_hash[n_debug_anon_hash].debug_type = NULL; debug_anon_hash[n_debug_anon_hash++].type = t; return 0; } for (i = 0; i < n_debug_hash; i++) if (t == debug_hash[i].type) return debug_hash[i].debug_type; return -1; } static int tcc_get_dwarf_info(TCCState *s1, Sym *s); static void tcc_debug_check_anon(TCCState *s1, Sym *t, int debug_type) { int i; if (!debug_info && (t->type.t & VT_BTYPE) == VT_STRUCT && t->type.ref->c == -1) for (i = 0; i < n_debug_anon_hash; i++) if (t->type.ref == debug_anon_hash[i].type) { debug_anon_hash[i].debug_type = tcc_realloc(debug_anon_hash[i].debug_type, (debug_anon_hash[i].n_debug_type + 1) * sizeof(int)); debug_anon_hash[i].debug_type[debug_anon_hash[i].n_debug_type++] = debug_type; } } ST_FUNC void tcc_debug_fix_anon(TCCState *s1, CType *t) { int i, j, debug_type; if (!(s1->do_debug & 2) || !s1->dwarf || debug_info) return; if ((t->t & VT_BTYPE) == VT_STRUCT && t->ref->c != -1) for (i = 0; i < n_debug_anon_hash; i++) if (t->ref == debug_anon_hash[i].type) { Sym sym = {0}; sym .type = *t ; /* Trick to not hash this struct */ debug_info = (struct _debug_info *) t; debug_type = tcc_get_dwarf_info(s1, &sym); debug_info = NULL; for (j = 0; j < debug_anon_hash[i].n_debug_type; j++) write32le(dwarf_info_section->data + debug_anon_hash[i].debug_type[j], debug_type - dwarf_info.start); tcc_free(debug_anon_hash[i].debug_type); n_debug_anon_hash--; for (; i < n_debug_anon_hash; i++) debug_anon_hash[i] = debug_anon_hash[i + 1]; } } static int tcc_debug_add(TCCState *s1, Sym *t, int dwarf) { int offset = dwarf ? dwarf_info_section->data_offset : ++debug_next_type; debug_hash = (struct _debug_hash *) tcc_realloc (debug_hash, (n_debug_hash + 1) * sizeof(*debug_hash)); debug_hash[n_debug_hash].debug_type = offset; debug_hash[n_debug_hash++].type = t; return offset; } static void tcc_debug_remove(TCCState *s1, Sym *t) { int i; for (i = 0; i < n_debug_hash; i++) if (t == debug_hash[i].type) { n_debug_hash--; for (; i < n_debug_hash; i++) debug_hash[i] = debug_hash[i+1]; } } #define STRUCT_NODEBUG(s) \ (s->a.nodebug || \ ((s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM && \ ((s->type.t & VT_BTYPE) == VT_BYTE || \ (s->type.t & VT_BTYPE) == VT_BOOL || \ (s->type.t & VT_BTYPE) == VT_SHORT || \ (s->type.t & VT_BTYPE) == VT_INT || \ (s->type.t & VT_BTYPE) == VT_LLONG))) static void tcc_get_debug_info(TCCState *s1, Sym *s, CString *result) { int type; int n = 0; int debug_type = -1; Sym *t = s; CString str; for (;;) { type = t->type.t & ~(VT_STORAGE | VT_CONSTANT | VT_VOLATILE | VT_VLA); if ((type & VT_BTYPE) != VT_BYTE) type &= ~VT_DEFSIGN; if (type == VT_PTR || type == (VT_PTR | VT_ARRAY)) n++, t = t->type.ref; else break; } if ((type & VT_BTYPE) == VT_STRUCT) { Sym *e = t; t = t->type.ref; debug_type = tcc_debug_find(s1, t, 0); if (debug_type == -1) { debug_type = tcc_debug_add(s1, t, 0); cstr_new (&str); cstr_printf (&str, "%s:T%d=%c%d", (t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM ? "" : get_tok_str(t->v, NULL), debug_type, IS_UNION (t->type.t) ? 'u' : 's', t->c); while (t->next) { int pos, size, align; t = t->next; if (STRUCT_NODEBUG(t)) continue; cstr_printf (&str, "%s:", get_tok_str(t->v, NULL)); tcc_get_debug_info (s1, t, &str); if (t->type.t & VT_BITFIELD) { pos = t->c * 8 + BIT_POS(t->type.t); size = BIT_SIZE(t->type.t); } else { pos = t->c * 8; size = type_size(&t->type, &align) * 8; } cstr_printf (&str, ",%d,%d;", pos, size); } cstr_printf (&str, ";"); tcc_debug_stabs(s1, str.data, N_LSYM, 0, NULL, 0, 0); cstr_free (&str); if (debug_info) tcc_debug_remove(s1, e); } } else if (IS_ENUM(type)) { Sym *e = t = t->type.ref; debug_type = tcc_debug_find(s1, t, 0); if (debug_type == -1) { debug_type = tcc_debug_add(s1, t, 0); cstr_new (&str); cstr_printf (&str, "%s:T%d=e", (t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM ? "" : get_tok_str(t->v, NULL), debug_type); while (t->next) { t = t->next; cstr_printf (&str, "%s:", (t->v & ~SYM_FIELD) >= SYM_FIRST_ANOM ? "" : get_tok_str(t->v, NULL)); cstr_printf (&str, e->type.t & VT_UNSIGNED ? "%u," : "%d,", (int)t->enum_val); } cstr_printf (&str, ";"); tcc_debug_stabs(s1, str.data, N_LSYM, 0, NULL, 0, 0); cstr_free (&str); if (debug_info) tcc_debug_remove(s1, e); } } else if ((type & VT_BTYPE) != VT_FUNC) { type &= ~VT_STRUCT_MASK; for (debug_type = 1; debug_type <= N_DEFAULT_DEBUG; debug_type++) if (default_debug[debug_type - 1].type == type) break; if (debug_type > N_DEFAULT_DEBUG) return; } if (n > 0) cstr_printf (result, "%d=", ++debug_next_type); t = s; for (;;) { type = t->type.t & ~(VT_STORAGE | VT_CONSTANT | VT_VOLATILE | VT_VLA); if ((type & VT_BTYPE) != VT_BYTE) type &= ~VT_DEFSIGN; if (type == VT_PTR) cstr_printf (result, "%d=*", ++debug_next_type); else if (type == (VT_PTR | VT_ARRAY)) cstr_printf (result, "%d=ar1;0;%d;", ++debug_next_type, t->type.ref->c - 1); else if (type == VT_FUNC) { cstr_printf (result, "%d=f", ++debug_next_type); tcc_get_debug_info (s1, t->type.ref, result); return; } else break; t = t->type.ref; } cstr_printf (result, "%d", debug_type); } static int tcc_get_dwarf_info(TCCState *s1, Sym *s) { int type; int debug_type = -1; Sym *e, *t = s; int i; int last_pos = -1; int retval; if (new_file) put_new_file(s1); for (;;) { type = t->type.t & ~(VT_STORAGE | VT_CONSTANT | VT_VOLATILE | VT_VLA); if ((type & VT_BTYPE) != VT_BYTE) type &= ~VT_DEFSIGN; if (type == VT_PTR || type == (VT_PTR | VT_ARRAY)) t = t->type.ref; else break; } if ((type & VT_BTYPE) == VT_STRUCT) { t = t->type.ref; debug_type = tcc_debug_find(s1, t, 1); if (debug_type == -1) { int pos_sib = 0, i, *pos_type; debug_type = tcc_debug_add(s1, t, 1); e = t; i = 0; while (e->next) { e = e->next; if (STRUCT_NODEBUG(e)) continue; i++; } pos_type = (int *) tcc_malloc(i * sizeof(int)); dwarf_data1(dwarf_info_section, IS_UNION (t->type.t) ? t->next ? DWARF_ABBREV_UNION_TYPE : DWARF_ABBREV_UNION_EMPTY_TYPE : t->next ? DWARF_ABBREV_STRUCTURE_TYPE : DWARF_ABBREV_STRUCTURE_EMPTY_TYPE); dwarf_strp(dwarf_info_section, (t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM ? "" : get_tok_str(t->v, NULL)); dwarf_uleb128(dwarf_info_section, t->c); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, file->line_num); if (t->next) { pos_sib = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); } e = t; i = 0; while (e->next) { e = e->next; if (STRUCT_NODEBUG(e)) continue; dwarf_data1(dwarf_info_section, e->type.t & VT_BITFIELD ? DWARF_ABBREV_MEMBER_BF : DWARF_ABBREV_MEMBER); dwarf_strp(dwarf_info_section, get_tok_str(e->v, NULL)); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, file->line_num); pos_type[i++] = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); if (e->type.t & VT_BITFIELD) { int pos = e->c * 8 + BIT_POS(e->type.t); int size = BIT_SIZE(e->type.t); dwarf_uleb128(dwarf_info_section, size); dwarf_uleb128(dwarf_info_section, pos); } else dwarf_uleb128(dwarf_info_section, e->c); } if (t->next) { dwarf_data1(dwarf_info_section, 0); write32le(dwarf_info_section->data + pos_sib, dwarf_info_section->data_offset - dwarf_info.start); } e = t; i = 0; while (e->next) { e = e->next; if (STRUCT_NODEBUG(e)) continue; type = tcc_get_dwarf_info(s1, e); tcc_debug_check_anon(s1, e, pos_type[i]); write32le(dwarf_info_section->data + pos_type[i++], type - dwarf_info.start); } tcc_free(pos_type); if (debug_info) tcc_debug_remove(s1, t); } } else if (IS_ENUM(type)) { t = t->type.ref; debug_type = tcc_debug_find(s1, t, 1); if (debug_type == -1) { int pos_sib, pos_type; Sym sym = {0}; sym.type.t = VT_INT | (type & VT_UNSIGNED); pos_type = tcc_get_dwarf_info(s1, &sym); debug_type = tcc_debug_add(s1, t, 1); dwarf_data1(dwarf_info_section, DWARF_ABBREV_ENUMERATION_TYPE); dwarf_strp(dwarf_info_section, (t->v & ~SYM_STRUCT) >= SYM_FIRST_ANOM ? "" : get_tok_str(t->v, NULL)); dwarf_data1(dwarf_info_section, type & VT_UNSIGNED ? DW_ATE_unsigned : DW_ATE_signed ); dwarf_data1(dwarf_info_section, 4); dwarf_data4(dwarf_info_section, pos_type - dwarf_info.start); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, file->line_num); pos_sib = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); e = t; while (e->next) { e = e->next; dwarf_data1(dwarf_info_section, type & VT_UNSIGNED ? DWARF_ABBREV_ENUMERATOR_UNSIGNED : DWARF_ABBREV_ENUMERATOR_SIGNED); dwarf_strp(dwarf_info_section, (e->v & ~SYM_FIELD) >= SYM_FIRST_ANOM ? "" : get_tok_str(e->v, NULL)); if (type & VT_UNSIGNED) dwarf_uleb128(dwarf_info_section, e->enum_val); else dwarf_sleb128(dwarf_info_section, e->enum_val); } dwarf_data1(dwarf_info_section, 0); write32le(dwarf_info_section->data + pos_sib, dwarf_info_section->data_offset - dwarf_info.start); if (debug_info) tcc_debug_remove(s1, t); } } else if ((type & VT_BTYPE) != VT_FUNC) { type &= ~VT_STRUCT_MASK; for (i = 1; i <= N_DEFAULT_DEBUG; i++) if (default_debug[i - 1].type == type) break; if (i > N_DEFAULT_DEBUG) return 0; debug_type = dwarf_info.base_type_used[i - 1]; if (debug_type == 0) { char name[100]; debug_type = dwarf_info_section->data_offset; dwarf_data1(dwarf_info_section, DWARF_ABBREV_BASE_TYPE); dwarf_uleb128(dwarf_info_section, default_debug[i - 1].size); dwarf_data1(dwarf_info_section, default_debug[i - 1].encoding); strncpy(name, default_debug[i - 1].name, sizeof(name) -1); *strchr(name, ':') = 0; dwarf_strp(dwarf_info_section, name); dwarf_info.base_type_used[i - 1] = debug_type; } } retval = debug_type; e = NULL; t = s; for (;;) { type = t->type.t & ~(VT_STORAGE | VT_CONSTANT | VT_VOLATILE | VT_VLA); if ((type & VT_BTYPE) != VT_BYTE) type &= ~VT_DEFSIGN; if (type == VT_PTR) { i = dwarf_info_section->data_offset; if (retval == debug_type) retval = i; dwarf_data1(dwarf_info_section, DWARF_ABBREV_POINTER); dwarf_data1(dwarf_info_section, PTR_SIZE); if (last_pos != -1) { tcc_debug_check_anon(s1, e, last_pos); write32le(dwarf_info_section->data + last_pos, i - dwarf_info.start); } last_pos = dwarf_info_section->data_offset; e = t->type.ref; dwarf_data4(dwarf_info_section, 0); } else if (type == (VT_PTR | VT_ARRAY)) { int sib_pos, sub_type; #if LONG_SIZE == 4 Sym sym = {0}; sym.type.t = VT_LONG | VT_INT | VT_UNSIGNED; #else Sym sym = {0}; sym.type.t = VT_LLONG | VT_LONG | VT_UNSIGNED; #endif sub_type = tcc_get_dwarf_info(s1, &sym); i = dwarf_info_section->data_offset; if (retval == debug_type) retval = i; dwarf_data1(dwarf_info_section, DWARF_ABBREV_ARRAY_TYPE); if (last_pos != -1) { tcc_debug_check_anon(s1, e, last_pos); write32le(dwarf_info_section->data + last_pos, i - dwarf_info.start); } last_pos = dwarf_info_section->data_offset; e = t->type.ref; dwarf_data4(dwarf_info_section, 0); sib_pos = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); for (;;) { dwarf_data1(dwarf_info_section, DWARF_ABBREV_SUBRANGE_TYPE); dwarf_data4(dwarf_info_section, sub_type - dwarf_info.start); dwarf_uleb128(dwarf_info_section, t->type.ref->c - 1); s = t->type.ref; type = s->type.t & ~(VT_STORAGE | VT_CONSTANT | VT_VOLATILE); if (type != (VT_PTR | VT_ARRAY)) break; t = s; } dwarf_data1(dwarf_info_section, 0); write32le(dwarf_info_section->data + sib_pos, dwarf_info_section->data_offset - dwarf_info.start); } else if (type == VT_FUNC) { int sib_pos = 0, *pos_type; Sym *f; i = dwarf_info_section->data_offset; debug_type = tcc_get_dwarf_info(s1, t->type.ref); if (retval == debug_type) retval = i; dwarf_data1(dwarf_info_section, t->type.ref->next ? DWARF_ABBREV_SUBROUTINE_TYPE : DWARF_ABBREV_SUBROUTINE_EMPTY_TYPE); if (last_pos != -1) { tcc_debug_check_anon(s1, e, last_pos); write32le(dwarf_info_section->data + last_pos, i - dwarf_info.start); } last_pos = dwarf_info_section->data_offset; e = t->type.ref; dwarf_data4(dwarf_info_section, 0); if (t->type.ref->next) { sib_pos = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); } f = t->type.ref; i = 0; while (f->next) { f = f->next; i++; } pos_type = (int *) tcc_malloc(i * sizeof(int)); f = t->type.ref; i = 0; while (f->next) { f = f->next; dwarf_data1(dwarf_info_section, DWARF_ABBREV_FORMAL_PARAMETER2); pos_type[i++] = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); } if (t->type.ref->next) { dwarf_data1(dwarf_info_section, 0); write32le(dwarf_info_section->data + sib_pos, dwarf_info_section->data_offset - dwarf_info.start); } f = t->type.ref; i = 0; while (f->next) { f = f->next; type = tcc_get_dwarf_info(s1, f); tcc_debug_check_anon(s1, f, pos_type[i]); write32le(dwarf_info_section->data + pos_type[i++], type - dwarf_info.start); } tcc_free(pos_type); } else { if (last_pos != -1) { tcc_debug_check_anon(s1, e, last_pos); write32le(dwarf_info_section->data + last_pos, debug_type - dwarf_info.start); } break; } t = t->type.ref; } return retval; } static void tcc_debug_finish (TCCState *s1, struct _debug_info *cur) { while (cur) { struct _debug_info *next = cur->next; int i; if (s1->dwarf) { for (i = cur->n_sym - 1; i >= 0; i--) { struct debug_sym *s = &cur->sym[i]; dwarf_data1(dwarf_info_section, s->type == N_PSYM ? DWARF_ABBREV_FORMAL_PARAMETER : s->type == N_GSYM ? DWARF_ABBREV_VARIABLE_EXTERNAL : s->type == N_STSYM ? DWARF_ABBREV_VARIABLE_STATIC : DWARF_ABBREV_VARIABLE_LOCAL); dwarf_strp(dwarf_info_section, s->str); if (s->type == N_GSYM || s->type == N_STSYM) { dwarf_uleb128(dwarf_info_section, s->file); dwarf_uleb128(dwarf_info_section, s->line); } dwarf_data4(dwarf_info_section, s->info - dwarf_info.start); if (s->type == N_GSYM || s->type == N_STSYM) { /* global/static */ if (s->type == N_GSYM) dwarf_data1(dwarf_info_section, 1); dwarf_data1(dwarf_info_section, PTR_SIZE + 1); dwarf_data1(dwarf_info_section, DW_OP_addr); if (s->type == N_STSYM) dwarf_reloc(dwarf_info_section, section_sym, R_DATA_PTR); #if PTR_SIZE == 4 dwarf_data4(dwarf_info_section, s->value); #else dwarf_data8(dwarf_info_section, s->value); #endif } else { /* param/local */ dwarf_data1(dwarf_info_section, dwarf_sleb128_size(s->value) + 1); dwarf_data1(dwarf_info_section, DW_OP_fbreg); dwarf_sleb128(dwarf_info_section, s->value); } tcc_free (s->str); } tcc_free (cur->sym); dwarf_data1(dwarf_info_section, cur->child ? DWARF_ABBREV_LEXICAL_BLOCK : DWARF_ABBREV_LEXICAL_EMPTY_BLOCK); dwarf_reloc(dwarf_info_section, section_sym, R_DATA_PTR); #if PTR_SIZE == 4 dwarf_data4(dwarf_info_section, func_ind + cur->start); dwarf_data4(dwarf_info_section, cur->end - cur->start); #else dwarf_data8(dwarf_info_section, func_ind + cur->start); dwarf_data8(dwarf_info_section, cur->end - cur->start); #endif tcc_debug_finish (s1, cur->child); if (cur->child) dwarf_data1(dwarf_info_section, 0); } else { for (i = 0; i < cur->n_sym; i++) { struct debug_sym *s = &cur->sym[i]; if (s->sec) put_stabs_r(s1, s->str, s->type, 0, 0, s->value, s->sec, s->sym_index); else put_stabs(s1, s->str, s->type, 0, 0, s->value); tcc_free (s->str); } tcc_free (cur->sym); put_stabn(s1, N_LBRAC, 0, 0, cur->start); tcc_debug_finish (s1, cur->child); put_stabn(s1, N_RBRAC, 0, 0, cur->end); } tcc_free (cur); cur = next; } } ST_FUNC void tcc_add_debug_info(TCCState *s1, int param, Sym *s, Sym *e) { CString debug_str; if (!(s1->do_debug & 2)) return; cstr_new (&debug_str); for (; s != e; s = s->prev) { if (!s->v || (s->r & VT_VALMASK) != VT_LOCAL) continue; if (s1->dwarf) { tcc_debug_stabs(s1, get_tok_str(s->v, NULL), param ? N_PSYM : N_LSYM, s->c, NULL, 0, tcc_get_dwarf_info(s1, s)); } else { cstr_reset (&debug_str); cstr_printf (&debug_str, "%s:%s", get_tok_str(s->v, NULL), param ? "p" : ""); tcc_get_debug_info(s1, s, &debug_str); tcc_debug_stabs(s1, debug_str.data, param ? N_PSYM : N_LSYM, s->c, NULL, 0, 0); } } cstr_free (&debug_str); } /* put function symbol */ ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym) { CString debug_str; BufferedFile *f; if (!s1->do_debug) return; debug_info_root = NULL; debug_info = NULL; tcc_debug_stabn(s1, N_LBRAC, ind - func_ind); f = put_new_file(s1); if (!f) return; if (s1->dwarf) { tcc_debug_line(s1); dwarf_info.func = sym; dwarf_info.line = file->line_num; if (s1->do_backtrace) { int i, len; dwarf_line_op(s1, 0); // extended dwarf_uleb128_op(s1, strlen(funcname) + 2); dwarf_line_op(s1, DW_LNE_hi_user - 1); len = strlen(funcname) + 1; for (i = 0; i < len; i++) dwarf_line_op(s1, funcname[i]); } } else { cstr_new (&debug_str); cstr_printf(&debug_str, "%s:%c", funcname, sym->type.t & VT_STATIC ? 'f' : 'F'); tcc_get_debug_info(s1, sym->type.ref, &debug_str); put_stabs_r(s1, debug_str.data, N_FUN, 0, f->line_num, 0, cur_text_section, sym->c); cstr_free (&debug_str); tcc_debug_line(s1); } } ST_FUNC void tcc_debug_prolog_epilog(TCCState *s1, int value) { if (!s1->do_debug) return; if (s1->dwarf) { dwarf_line_op(s1, value == 0 ? DW_LNS_set_prologue_end : DW_LNS_set_epilogue_begin); } } /* put function size */ ST_FUNC void tcc_debug_funcend(TCCState *s1, int size) { /* lldb does not like function end and next function start at same pc */ int min_instr_len; if (!s1->do_debug) return; min_instr_len = dwarf_line.last_pc == ind ? 0 : DWARF_MIN_INSTR_LEN; ind -= min_instr_len; tcc_debug_line(s1); ind += min_instr_len; tcc_debug_stabn(s1, N_RBRAC, size); if (s1->dwarf) { int func_sib = 0; Sym *sym = dwarf_info.func; int n_debug_info = tcc_get_dwarf_info(s1, sym->type.ref); dwarf_data1(dwarf_info_section, sym->type.t & VT_STATIC ? DWARF_ABBREV_SUBPROGRAM_STATIC : DWARF_ABBREV_SUBPROGRAM_EXTERNAL); if ((sym->type.t & VT_STATIC) == 0) dwarf_data1(dwarf_info_section, 1); dwarf_strp(dwarf_info_section, funcname); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, dwarf_info.line); tcc_debug_check_anon(s1, sym->type.ref, dwarf_info_section->data_offset); dwarf_data4(dwarf_info_section, n_debug_info - dwarf_info.start); dwarf_reloc(dwarf_info_section, section_sym, R_DATA_PTR); #if PTR_SIZE == 4 dwarf_data4(dwarf_info_section, func_ind); // low_pc dwarf_data4(dwarf_info_section, size); // high_pc #else dwarf_data8(dwarf_info_section, func_ind); // low_pc dwarf_data8(dwarf_info_section, size); // high_pc #endif func_sib = dwarf_info_section->data_offset; dwarf_data4(dwarf_info_section, 0); // sibling dwarf_data1(dwarf_info_section, 1); #if defined(TCC_TARGET_I386) dwarf_data1(dwarf_info_section, DW_OP_reg5); // ebp #elif defined(TCC_TARGET_X86_64) dwarf_data1(dwarf_info_section, DW_OP_reg6); // rbp #elif defined TCC_TARGET_ARM dwarf_data1(dwarf_info_section, DW_OP_reg13); // sp #elif defined TCC_TARGET_ARM64 dwarf_data1(dwarf_info_section, DW_OP_reg29); // reg 29 #elif defined TCC_TARGET_RISCV64 dwarf_data1(dwarf_info_section, DW_OP_reg8); // r8(s0) #else dwarf_data1(dwarf_info_section, DW_OP_call_frame_cfa); #endif tcc_debug_finish (s1, debug_info_root); dwarf_data1(dwarf_info_section, 0); write32le(dwarf_info_section->data + func_sib, dwarf_info_section->data_offset - dwarf_info.start); } else { tcc_debug_finish (s1, debug_info_root); } debug_info_root = 0; } ST_FUNC void tcc_debug_extern_sym(TCCState *s1, Sym *sym, int sh_num, int sym_bind, int sym_type) { if (!(s1->do_debug & 2)) return; if (sym_type == STT_FUNC || sym->v >= SYM_FIRST_ANOM) return; if (s1->dwarf) { int debug_type; debug_type = tcc_get_dwarf_info(s1, sym); dwarf_data1(dwarf_info_section, sym_bind == STB_GLOBAL ? DWARF_ABBREV_VARIABLE_EXTERNAL : DWARF_ABBREV_VARIABLE_STATIC); dwarf_strp(dwarf_info_section, get_tok_str(sym->v, NULL)); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, file->line_num); tcc_debug_check_anon(s1, sym, dwarf_info_section->data_offset); dwarf_data4(dwarf_info_section, debug_type - dwarf_info.start); if (sym_bind == STB_GLOBAL) dwarf_data1(dwarf_info_section, 1); dwarf_data1(dwarf_info_section, PTR_SIZE + 1); dwarf_data1(dwarf_info_section, DW_OP_addr); greloca(dwarf_info_section, sym, dwarf_info_section->data_offset, R_DATA_PTR, 0); #if PTR_SIZE == 4 dwarf_data4(dwarf_info_section, 0); #else dwarf_data8(dwarf_info_section, 0); #endif } else { Section *s = sh_num == SHN_COMMON ? common_section : s1->sections[sh_num]; CString str; cstr_new (&str); cstr_printf (&str, "%s:%c", get_tok_str(sym->v, NULL), sym_bind == STB_GLOBAL ? 'G' : func_ind != -1 ? 'V' : 'S' ); tcc_get_debug_info(s1, sym, &str); if (sym_bind == STB_GLOBAL) tcc_debug_stabs(s1, str.data, N_GSYM, 0, NULL, 0, 0); else tcc_debug_stabs(s1, str.data, (sym->type.t & VT_STATIC) && data_section == s ? N_STSYM : N_LCSYM, 0, s, sym->c, 0); cstr_free (&str); } } ST_FUNC void tcc_debug_typedef(TCCState *s1, Sym *sym) { if (!(s1->do_debug & 2)) return; if (s1->dwarf) { int debug_type; debug_type = tcc_get_dwarf_info(s1, sym); if (debug_type != -1) { dwarf_data1(dwarf_info_section, DWARF_ABBREV_TYPEDEF); dwarf_strp(dwarf_info_section, get_tok_str(sym->v, NULL)); dwarf_uleb128(dwarf_info_section, dwarf_line.cur_file); dwarf_uleb128(dwarf_info_section, file->line_num); tcc_debug_check_anon(s1, sym, dwarf_info_section->data_offset); dwarf_data4(dwarf_info_section, debug_type - dwarf_info.start); } } else { CString str; cstr_new (&str); cstr_printf (&str, "%s:t", (sym->v & ~SYM_FIELD) >= SYM_FIRST_ANOM ? "" : get_tok_str(sym->v, NULL)); tcc_get_debug_info(s1, sym, &str); tcc_debug_stabs(s1, str.data, N_LSYM, 0, NULL, 0, 0); cstr_free (&str); } } /* ------------------------------------------------------------------------- */ /* for section layout see lib/tcov.c */ ST_FUNC void tcc_tcov_block_end(TCCState *s1, int line); ST_FUNC void tcc_tcov_block_begin(TCCState *s1) { SValue sv; void *ptr; unsigned long last_offset = tcov_data.offset; tcc_tcov_block_end (tcc_state, 0); if (s1->test_coverage == 0 || nocode_wanted) return; if (tcov_data.last_file_name == 0 || strcmp ((const char *)(tcov_section->data + tcov_data.last_file_name), file->true_filename) != 0) { char wd[1024]; CString cstr; if (tcov_data.last_func_name) section_ptr_add(tcov_section, 1); if (tcov_data.last_file_name) section_ptr_add(tcov_section, 1); tcov_data.last_func_name = 0; cstr_new (&cstr); if (file->true_filename[0] == '/') { tcov_data.last_file_name = tcov_section->data_offset; cstr_printf (&cstr, "%s", file->true_filename); } else { getcwd (wd, sizeof(wd)); tcov_data.last_file_name = tcov_section->data_offset + strlen(wd) + 1; cstr_printf (&cstr, "%s/%s", wd, file->true_filename); } ptr = section_ptr_add(tcov_section, cstr.size + 1); strcpy((char *)ptr, cstr.data); #ifdef _WIN32 normalize_slashes((char *)ptr); #endif cstr_free (&cstr); } if (tcov_data.last_func_name == 0 || strcmp ((const char *)(tcov_section->data + tcov_data.last_func_name), funcname) != 0) { size_t len; if (tcov_data.last_func_name) section_ptr_add(tcov_section, 1); tcov_data.last_func_name = tcov_section->data_offset; len = strlen (funcname); ptr = section_ptr_add(tcov_section, len + 1); strcpy((char *)ptr, funcname); section_ptr_add(tcov_section, -tcov_section->data_offset & 7); ptr = section_ptr_add(tcov_section, 8); write64le (ptr, file->line_num); } if (ind == tcov_data.ind && tcov_data.line == file->line_num) tcov_data.offset = last_offset; else { Sym label = {0}; label.type.t = VT_LLONG | VT_STATIC; ptr = section_ptr_add(tcov_section, 16); tcov_data.line = file->line_num; write64le (ptr, (tcov_data.line << 8) | 0xff); put_extern_sym(&label, tcov_section, ((unsigned char *)ptr - tcov_section->data) + 8, 0); sv.type = label.type; sv.r = VT_SYM | VT_LVAL | VT_CONST; sv.r2 = VT_CONST; sv.c.i = 0; sv.sym = &label; #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || \ defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64 || \ defined TCC_TARGET_RISCV64 gen_increment_tcov (&sv); #else vpushv(&sv); inc(0, TOK_INC); vpop(); #endif tcov_data.offset = (unsigned char *)ptr - tcov_section->data; tcov_data.ind = ind; } } ST_FUNC void tcc_tcov_block_end(TCCState *s1, int line) { if (s1->test_coverage == 0) return; if (line == -1) line = tcov_data.line; if (tcov_data.offset) { void *ptr = tcov_section->data + tcov_data.offset; unsigned long long nline = line ? line : file->line_num; write64le (ptr, (read64le (ptr) & 0xfffffffffull) | (nline << 36)); tcov_data.offset = 0; } } ST_FUNC void tcc_tcov_check_line(TCCState *s1, int start) { if (s1->test_coverage == 0) return; if (tcov_data.line != file->line_num) { if ((tcov_data.line + 1) != file->line_num) { tcc_tcov_block_end (s1, -1); if (start) tcc_tcov_block_begin (s1); } else tcov_data.line = file->line_num; } } ST_FUNC void tcc_tcov_start(TCCState *s1) { if (s1->test_coverage == 0) return; if (!s1->dState) s1->dState = tcc_mallocz(sizeof *s1->dState); memset (&tcov_data, 0, sizeof (tcov_data)); if (tcov_section == NULL) { tcov_section = new_section(tcc_state, ".tcov", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); section_ptr_add(tcov_section, 4); // pointer to executable name } } ST_FUNC void tcc_tcov_end(TCCState *s1) { if (s1->test_coverage == 0) return; if (tcov_data.last_func_name) section_ptr_add(tcov_section, 1); if (tcov_data.last_file_name) section_ptr_add(tcov_section, 1); } ST_FUNC void tcc_tcov_reset_ind(TCCState *s1) { tcov_data.ind = 0; } /* ------------------------------------------------------------------------- */ #undef last_line_num #undef new_file #undef section_sym #undef debug_next_type #undef debug_hash #undef n_debug_hash #undef debug_anon_hash #undef n_debug_anon_hash #undef debug_info #undef debug_info_root #undef dwarf_sym #undef dwarf_line #undef dwarf_info #undef tcov_data