On windows, create a .pdb file with option "-g.pdb" Such executables created by tcc can be debugged with "ollydbg" or "x64dbg" This currently relies on the 3rd party tool cv2pdb from https://github.com/rainers/cv2pdb which again relies on mspdbsrv.exe mspdbcore.dll msobj80.dll mspdb80.dll from a MSVC installation. cv2pdb.exe + the ms* files may be put in the path or in the same directory as tcc.exe.
Unified · Split
| @@ -1976,9 +1976,12 @@ dorun: · 1975 unmodified lines | |||
| 1976 | case TCC_OPTION_g: | 1976 | case TCC_OPTION_g: |
| 1977 | s->do_debug = 1; | 1977 | s->do_debug = 1; |
| 1978 | s->dwarf = DWARF_VERSION; | 1978 | s->dwarf = DWARF_VERSION; |
| 1979 | |||
| 1980 | if (strstart("dwarf", &optarg)) | 1979 | if (strstart("dwarf", &optarg)) |
| 1981 | s->dwarf = (*optarg) ? (0 - atoi(optarg)) : DEFAULT_DWARF_VERSION; | 1980 | s->dwarf = (*optarg) ? (0 - atoi(optarg)) : DEFAULT_DWARF_VERSION; |
| 1981 | #ifdef TCC_TARGET_PE | ||
| 1982 | else if (0 == strcmp(".pdb", optarg)) | ||
| 1983 | s->dwarf = 5, s->do_debug |= 16; | ||
| 1984 | #endif | ||
| 1982 | break; | 1985 | break; |
| 1983 | case TCC_OPTION_c: | 1986 | case TCC_OPTION_c: |
| 1984 | x = TCC_OUTPUT_OBJ; | 1987 | x = TCC_OUTPUT_OBJ; |
| @@ -59,6 +59,9 @@ static const char help[] = · 58 unmodified lines | |||
| 59 | "Debugger options:\n" | 59 | "Debugger options:\n" |
| 60 | " -g generate stab runtime debug info\n" | 60 | " -g generate stab runtime debug info\n" |
| 61 | " -gdwarf[-x] generate dwarf runtime debug info\n" | 61 | " -gdwarf[-x] generate dwarf runtime debug info\n" |
| 62 | #ifdef TCC_TARGET_PE | ||
| 63 | " -g.pdb create .pdb debug database\n" | ||
| 64 | #endif | ||
| 62 | #ifdef CONFIG_TCC_BCHECK | 65 | #ifdef CONFIG_TCC_BCHECK |
| 63 | " -b compile with built-in memory and bounds checker (implies -g)\n" | 66 | " -b compile with built-in memory and bounds checker (implies -g)\n" |
| 64 | #endif | 67 | #endif |
| @@ -1086,7 +1086,7 @@ ST_FUNC void tcc_debug_line(TCCState *s1) · 1085 unmodified lines | |||
| 1086 | 1086 | ||
| 1087 | if (!s1->do_debug) | 1087 | if (!s1->do_debug) |
| 1088 | return; | 1088 | return; |
| 1089 | if (cur_text_section != text_section) | 1089 | if (cur_text_section != text_section || nocode_wanted) |
| 1090 | return; | 1090 | return; |
| 1091 | f = put_new_file(s1); | 1091 | f = put_new_file(s1); |
| 1092 | if (!f) | 1092 | if (!f) |
| @@ -5500,25 +5500,12 @@ ST_FUNC void unary(void) · 5499 unmodified lines | |||
| 5500 | goto tok_identifier; | 5500 | goto tok_identifier; |
| 5501 | /* fall thru */ | 5501 | /* fall thru */ |
| 5502 | case TOK___FUNC__: | 5502 | case TOK___FUNC__: |
| 5503 | { | 5503 | tok = TOK_STR; |
| 5504 | Section *sec; | 5504 | cstr_reset(&tokcstr); |
| 5505 | int len; | 5505 | cstr_cat(&tokcstr, funcname, 0); |
| 5506 | /* special function name identifier */ | 5506 | tokc.str.size = tokcstr.size; |
| 5507 | len = strlen(funcname) + 1; | 5507 | tokc.str.data = tokcstr.data; |
| 5508 | /* generate char[len] type */ | 5508 | goto case_TOK_STR; |
| 5509 | type.t = char_type.t; | ||
| 5510 | if (tcc_state->warn_write_strings & WARN_ON) | ||
| 5511 | type.t |= VT_CONSTANT; | ||
| 5512 | mk_pointer(&type); | ||
| 5513 | type.t |= VT_ARRAY; | ||
| 5514 | type.ref->c = len; | ||
| 5515 | sec = rodata_section; | ||
| 5516 | vpush_ref(&type, sec, sec->data_offset, len); | ||
| 5517 | if (!NODATA_WANTED) | ||
| 5518 | memcpy(section_ptr_add(sec, len), funcname, len); | ||
| 5519 | next(); | ||
| 5520 | } | ||
| 5521 | break; | ||
| 5522 | case TOK_LSTR: | 5509 | case TOK_LSTR: |
| 5523 | #ifdef TCC_TARGET_PE | 5510 | #ifdef TCC_TARGET_PE |
| 5524 | t = VT_SHORT | VT_UNSIGNED; | 5511 | t = VT_SHORT | VT_UNSIGNED; |
| @@ -5527,6 +5514,7 @@ ST_FUNC void unary(void) · 2 unmodified lines | |||
| 5527 | #endif | 5514 | #endif |
| 5528 | goto str_init; | 5515 | goto str_init; |
| 5529 | case TOK_STR: | 5516 | case TOK_STR: |
| 5517 | case_TOK_STR: | ||
| 5530 | /* string parsing */ | 5518 | /* string parsing */ |
| 5531 | t = char_type.t; | 5519 | t = char_type.t; |
| 5532 | str_init: | 5520 | str_init: |
| @@ -7767,7 +7755,7 @@ static void decl_initializer(init_params *p, CType *type, unsigned long c, int f · 2234 unmodified lines | |||
| 7767 | CType *t1; | 7755 | CType *t1; |
| 7768 | 7756 | ||
| 7769 | /* generate line number info */ | 7757 | /* generate line number info */ |
| 7770 | if (debug_modes && !p->sec) | 7758 | if (debug_modes && !(flags & DIF_SIZE_ONLY) && !p->sec) |
| 7771 | tcc_debug_line(tcc_state), tcc_tcov_check_line (tcc_state, 1); | 7759 | tcc_debug_line(tcc_state), tcc_tcov_check_line (tcc_state, 1); |
| 7772 | 7760 | ||
| 7773 | if (!(flags & DIF_HAVE_ELEM) && tok != '{' && | 7761 | if (!(flags & DIF_HAVE_ELEM) && tok != '{' && |
| @@ -8288,50 +8276,60 @@ static void gen_function(Sym *sym) · 514 unmodified lines | |||
| 8288 | struct scope f = { 0 }; | 8276 | struct scope f = { 0 }; |
| 8289 | cur_scope = root_scope = &f; | 8277 | cur_scope = root_scope = &f; |
| 8290 | nocode_wanted = 0; | 8278 | nocode_wanted = 0; |
| 8279 | |||
| 8291 | ind = cur_text_section->data_offset; | 8280 | ind = cur_text_section->data_offset; |
| 8292 | if (sym->a.aligned) { | 8281 | if (sym->a.aligned) { |
| 8293 | size_t newoff = section_add(cur_text_section, 0, | 8282 | size_t newoff = section_add(cur_text_section, 0, |
| 8294 | 1 << (sym->a.aligned - 1)); | 8283 | 1 << (sym->a.aligned - 1)); |
| 8295 | gen_fill_nops(newoff - ind); | 8284 | gen_fill_nops(newoff - ind); |
| 8296 | } | 8285 | } |
| 8286 | |||
| 8287 | funcname = get_tok_str(sym->v, NULL); | ||
| 8288 | func_ind = ind; | ||
| 8289 | func_vt = sym->type.ref->type; | ||
| 8290 | func_var = sym->type.ref->f.func_type == FUNC_ELLIPSIS; | ||
| 8291 | |||
| 8297 | /* NOTE: we patch the symbol size later */ | 8292 | /* NOTE: we patch the symbol size later */ |
| 8298 | put_extern_sym(sym, cur_text_section, ind, 0); | 8293 | put_extern_sym(sym, cur_text_section, ind, 0); |
| 8294 | |||
| 8299 | if (sym->type.ref->f.func_ctor) | 8295 | if (sym->type.ref->f.func_ctor) |
| 8300 | add_array (tcc_state, ".init_array", sym->c); | 8296 | add_array (tcc_state, ".init_array", sym->c); |
| 8301 | if (sym->type.ref->f.func_dtor) | 8297 | if (sym->type.ref->f.func_dtor) |
| 8302 | add_array (tcc_state, ".fini_array", sym->c); | 8298 | add_array (tcc_state, ".fini_array", sym->c); |
| 8303 | 8299 | ||
| 8304 | funcname = get_tok_str(sym->v, NULL); | ||
| 8305 | func_ind = ind; | ||
| 8306 | func_vt = sym->type.ref->type; | ||
| 8307 | func_var = sym->type.ref->f.func_type == FUNC_ELLIPSIS; | ||
| 8308 | |||
| 8309 | /* put debug symbol */ | 8300 | /* put debug symbol */ |
| 8310 | tcc_debug_funcstart(tcc_state, sym); | 8301 | tcc_debug_funcstart(tcc_state, sym); |
| 8302 | |||
| 8311 | /* push a dummy symbol to enable local sym storage */ | 8303 | /* push a dummy symbol to enable local sym storage */ |
| 8312 | sym_push2(&local_stack, SYM_FIELD, 0, 0); | 8304 | sym_push2(&local_stack, SYM_FIELD, 0, 0); |
| 8313 | local_scope = 1; /* for function parameters */ | 8305 | local_scope = 1; /* for function parameters */ |
| 8314 | gfunc_prolog(sym); | 8306 | gfunc_prolog(sym); |
| 8315 | tcc_debug_prolog_epilog(tcc_state, 0); | 8307 | tcc_debug_prolog_epilog(tcc_state, 0); |
| 8308 | |||
| 8316 | local_scope = 0; | 8309 | local_scope = 0; |
| 8317 | rsym = 0; | 8310 | rsym = 0; |
| 8318 | clear_temp_local_var_list(); | 8311 | clear_temp_local_var_list(); |
| 8319 | func_vla_arg(sym); | 8312 | func_vla_arg(sym); |
| 8320 | block(0); | 8313 | block(0); |
| 8321 | gsym(rsym); | 8314 | gsym(rsym); |
| 8315 | |||
| 8322 | nocode_wanted = 0; | 8316 | nocode_wanted = 0; |
| 8323 | /* reset local stack */ | 8317 | /* reset local stack */ |
| 8324 | pop_local_syms(NULL, 0); | 8318 | pop_local_syms(NULL, 0); |
| 8325 | tcc_debug_prolog_epilog(tcc_state, 1); | 8319 | tcc_debug_prolog_epilog(tcc_state, 1); |
| 8326 | gfunc_epilog(); | 8320 | gfunc_epilog(); |
| 8321 | |||
| 8322 | /* end of function */ | ||
| 8323 | tcc_debug_funcend(tcc_state, ind - func_ind); | ||
| 8324 | |||
| 8325 | /* patch symbol size */ | ||
| 8326 | elfsym(sym)->st_size = ind - func_ind; | ||
| 8327 | |||
| 8327 | cur_text_section->data_offset = ind; | 8328 | cur_text_section->data_offset = ind; |
| 8328 | local_scope = 0; | 8329 | local_scope = 0; |
| 8329 | label_pop(&global_label_stack, NULL, 0); | 8330 | label_pop(&global_label_stack, NULL, 0); |
| 8330 | sym_pop(&all_cleanups, NULL, 0); | 8331 | sym_pop(&all_cleanups, NULL, 0); |
| 8331 | /* patch symbol size */ | 8332 | |
| 8332 | elfsym(sym)->st_size = ind - func_ind; | ||
| 8333 | /* end of function */ | ||
| 8334 | tcc_debug_funcend(tcc_state, ind - func_ind); | ||
| 8335 | /* It's better to crash than to generate wrong code */ | 8333 | /* It's better to crash than to generate wrong code */ |
| 8336 | cur_text_section = NULL; | 8334 | cur_text_section = NULL; |
| 8337 | funcname = ""; /* for safety */ | 8335 | funcname = ""; /* for safety */ |
| @@ -8341,6 +8339,7 @@ static void gen_function(Sym *sym) · 3 unmodified lines | |||
| 8341 | func_ind = -1; | 8339 | func_ind = -1; |
| 8342 | nocode_wanted = DATA_ONLY_WANTED; | 8340 | nocode_wanted = DATA_ONLY_WANTED; |
| 8343 | check_vstack(); | 8341 | check_vstack(); |
| 8342 | |||
| 8344 | /* do this after funcend debug info */ | 8343 | /* do this after funcend debug info */ |
| 8345 | next(); | 8344 | next(); |
| 8346 | } | 8345 | } |
| @@ -301,8 +301,7 @@ enum { · 300 unmodified lines | |||
| 301 | sec_pdata , | 301 | sec_pdata , |
| 302 | sec_other , | 302 | sec_other , |
| 303 | sec_rsrc , | 303 | sec_rsrc , |
| 304 | sec_stab , | 304 | sec_debug , |
| 305 | sec_stabstr , | ||
| 306 | sec_reloc , | 305 | sec_reloc , |
| 307 | sec_last | 306 | sec_last |
| 308 | }; | 307 | }; |
| @@ -475,9 +474,9 @@ struct pe_file { · 166 unmodified lines | |||
| 475 | unsigned pos; | 474 | unsigned pos; |
| 476 | }; | 475 | }; |
| 477 | 476 | ||
| 478 | static int pe_fwrite(void *data, int len, struct pe_file *pf) | 477 | static int pe_fwrite(const void *data, int len, struct pe_file *pf) |
| 479 | { | 478 | { |
| 480 | WORD *p = data; | 479 | const WORD *p = data; |
| 481 | DWORD sum; | 480 | DWORD sum; |
| 482 | int ret, i; | 481 | int ret, i; |
| 483 | pf->pos += (ret = fwrite(data, 1, len, pf->op)); | 482 | pf->pos += (ret = fwrite(data, 1, len, pf->op)); |
| @@ -502,6 +501,51 @@ static void pe_fpad(struct pe_file *pf, DWORD new_pos) · 18 unmodified lines | |||
| 502 | pf->pos = new_pos; | 501 | pf->pos = new_pos; |
| 503 | } | 502 | } |
| 504 | 503 | ||
| 504 | /*----------------------------------------------------------------------------*/ | ||
| 505 | /* PE-DWARF/COFF support | ||
| 506 | does not work with a mingw-gdb really but works with cv2pdb | ||
| 507 | (https://github.com/rainers/cv2pdb) */ | ||
| 508 | |||
| 509 | #define N_COFF_SYMS 0 | ||
| 510 | |||
| 511 | static const char dwarf_secs[] = | ||
| 512 | { | ||
| 513 | ".debug_info\0" | ||
| 514 | ".debug_abbrev\0" | ||
| 515 | ".debug_line\0" | ||
| 516 | ".debug_aranges\0" | ||
| 517 | ".debug_str\0" | ||
| 518 | ".debug_line_str\0" | ||
| 519 | }; | ||
| 520 | |||
| 521 | static const unsigned coff_strtab_size = 4 + sizeof dwarf_secs - 1; | ||
| 522 | |||
| 523 | static int pe_put_long_secname(char *secname, const char *name) | ||
| 524 | { | ||
| 525 | const char *d = dwarf_secs; | ||
| 526 | do { | ||
| 527 | if (0 == strcmp(d, name)) { | ||
| 528 | sprintf(secname, "/%d", (int)(d - dwarf_secs + 4)); | ||
| 529 | return 1; | ||
| 530 | } | ||
| 531 | d = strchr(d, 0) + 1; | ||
| 532 | } while (*d); | ||
| 533 | return 0; | ||
| 534 | } | ||
| 535 | |||
| 536 | static void pe_create_pdb(TCCState *s1, const char *exename) | ||
| 537 | { | ||
| 538 | char buf[300]; int r; | ||
| 539 | snprintf(buf, sizeof buf, "cv2pdb.exe %s", exename); | ||
| 540 | r = system(buf); | ||
| 541 | strcpy(tcc_fileextension(strcpy(buf, exename)), ".pdb"); | ||
| 542 | if (r) { | ||
| 543 | tcc_error_noabort("could not create '%s'\n(need working cv2pdb from https://github.com/rainers/cv2pdb)", buf); | ||
| 544 | } else if (s1->verbose) { | ||
| 545 | printf("<- %s\n", buf); | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 505 | /*----------------------------------------------------------------------------*/ | 549 | /*----------------------------------------------------------------------------*/ |
| 506 | static int pe_write(struct pe_info *pe) | 550 | static int pe_write(struct pe_info *pe) |
| 507 | { | 551 | { |
| @@ -616,6 +660,7 @@ static int pe_write(struct pe_info *pe) · 108 unmodified lines | |||
| 616 | struct section_info *si; | 660 | struct section_info *si; |
| 617 | IMAGE_SECTION_HEADER *psh; | 661 | IMAGE_SECTION_HEADER *psh; |
| 618 | TCCState *s1 = pe->s1; | 662 | TCCState *s1 = pe->s1; |
| 663 | int need_strtab = 0; | ||
| 619 | 664 | ||
| 620 | pf.op = fopen(pe->filename, "wb"); | 665 | pf.op = fopen(pe->filename, "wb"); |
| 621 | if (NULL == pf.op) | 666 | if (NULL == pf.op) |
| @@ -686,6 +731,8 @@ static int pe_write(struct pe_info *pe) · 64 unmodified lines | |||
| 686 | } | 731 | } |
| 687 | 732 | ||
| 688 | memcpy(psh->Name, sh_name, umin(strlen(sh_name), sizeof psh->Name)); | 733 | memcpy(psh->Name, sh_name, umin(strlen(sh_name), sizeof psh->Name)); |
| 734 | if (si->cls == sec_debug) | ||
| 735 | need_strtab += pe_put_long_secname(psh->Name, sh_name); | ||
| 689 | 736 | ||
| 690 | psh->Characteristics = si->pe_flags; | 737 | psh->Characteristics = si->pe_flags; |
| 691 | psh->VirtualAddress = addr; | 738 | psh->VirtualAddress = addr; |
| @@ -715,7 +762,10 @@ static int pe_write(struct pe_info *pe) · 23 unmodified lines | |||
| 715 | if (PE_DLL == pe->type) | 762 | if (PE_DLL == pe->type) |
| 716 | pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; | 763 | pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; |
| 717 | pe_header.filehdr.Characteristics |= pe->s1->pe_characteristics; | 764 | pe_header.filehdr.Characteristics |= pe->s1->pe_characteristics; |
| 718 | 765 | if (need_strtab) { | |
| 766 | pe_header.filehdr.PointerToSymbolTable = file_offset; | ||
| 767 | pe_header.filehdr.NumberOfSymbols = N_COFF_SYMS; | ||
| 768 | } | ||
| 719 | pe_fwrite(&pe_header, sizeof pe_header, &pf); | 769 | pe_fwrite(&pe_header, sizeof pe_header, &pf); |
| 720 | for (i = 0; i < pe->sec_count; ++i) | 770 | for (i = 0; i < pe->sec_count; ++i) |
| 721 | pe_fwrite(&pe->sec_info[i]->ish, sizeof(IMAGE_SECTION_HEADER), &pf); | 771 | pe_fwrite(&pe->sec_info[i]->ish, sizeof(IMAGE_SECTION_HEADER), &pf); |
| @@ -736,6 +786,13 @@ static int pe_write(struct pe_info *pe) · 14 unmodified lines | |||
| 736 | pe_fpad(&pf, file_offset); | 786 | pe_fpad(&pf, file_offset); |
| 737 | } | 787 | } |
| 738 | 788 | ||
| 789 | if (need_strtab) { | ||
| 790 | /* create a tiny COFF string table with the long section names */ | ||
| 791 | pe_fwrite(&coff_strtab_size, sizeof coff_strtab_size, &pf); | ||
| 792 | pe_fwrite(dwarf_secs, sizeof dwarf_secs - 1, &pf); | ||
| 793 | file_offset = pf.pos; | ||
| 794 | } | ||
| 795 | |||
| 739 | pf.sum += file_offset; | 796 | pf.sum += file_offset; |
| 740 | fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET); | 797 | fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET); |
| 741 | pe_fwrite(&pf.sum, sizeof (DWORD), &pf); | 798 | pe_fwrite(&pf.sum, sizeof (DWORD), &pf); |
| @@ -750,6 +807,8 @@ static int pe_write(struct pe_info *pe) · 8 unmodified lines | |||
| 750 | if (pe->s1->verbose) | 807 | if (pe->s1->verbose) |
| 751 | printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset); | 808 | printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset); |
| 752 | 809 | ||
| 810 | if (s1->do_debug & 16) | ||
| 811 | pe_create_pdb(s1, pe->filename); | ||
| 753 | return 0; | 812 | return 0; |
| 754 | } | 813 | } |
| 755 | 814 | ||
| @@ -1072,12 +1131,12 @@ static int pe_section_class(Section *s) · 316 unmodified lines | |||
| 1072 | type = s->sh_type; | 1131 | type = s->sh_type; |
| 1073 | flags = s->sh_flags; | 1132 | flags = s->sh_flags; |
| 1074 | name = s->name; | 1133 | name = s->name; |
| 1075 | if (0 == memcmp(name, ".stab", 5)) { | 1134 | |
| 1076 | if (0 == s->s1->do_debug) | 1135 | if (0 == memcmp(name, ".stab", 5) || 0 == memcmp(name, ".debug_", 7)) { |
| 1077 | return sec_last; | 1136 | if (s->s1->do_debug) |
| 1078 | return name[5] ? sec_stabstr : sec_stab; | 1137 | return sec_debug; |
| 1079 | } | 1138 | |
| 1080 | if (flags & SHF_ALLOC) { | 1139 | } else if (flags & SHF_ALLOC) { |
| 1081 | if (type == SHT_PROGBITS | 1140 | if (type == SHT_PROGBITS |
| 1082 | || type == SHT_INIT_ARRAY | 1141 | || type == SHT_INIT_ARRAY |
| 1083 | || type == SHT_FINI_ARRAY) { | 1142 | || type == SHT_FINI_ARRAY) { |
| @@ -1135,7 +1194,7 @@ static int pe_assign_addresses (struct pe_info *pe) · 51 unmodified lines | |||
| 1135 | if (PE_MERGE_DATA && c == sec_bss) | 1194 | if (PE_MERGE_DATA && c == sec_bss) |
| 1136 | c = sec_data; | 1195 | c = sec_data; |
| 1137 | 1196 | ||
| 1138 | if (si && c == si->cls) { | 1197 | if (si && c == si->cls && c != sec_debug) { |
| 1139 | /* merge with previous section */ | 1198 | /* merge with previous section */ |
| 1140 | s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1; | 1199 | s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1; |
| 1141 | } else { | 1200 | } else { |