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 | 1976 | case TCC_OPTION_g: |
| 1977 | 1977 | s->do_debug = 1; |
| 1978 | 1978 | s->dwarf = DWARF_VERSION; |
| 1979 | − | |
| 1980 | 1979 | if (strstart("dwarf", &optarg)) |
| 1981 | 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 | 1985 | break; |
| 1983 | 1986 | case TCC_OPTION_c: |
| 1984 | 1987 | x = TCC_OUTPUT_OBJ; |
| @@ -59,6 +59,9 @@ static const char help[] = · 58 unmodified lines | ||
| 59 | 59 | "Debugger options:\n" |
| 60 | 60 | " -g generate stab runtime debug info\n" |
| 61 | 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 | 65 | #ifdef CONFIG_TCC_BCHECK |
| 63 | 66 | " -b compile with built-in memory and bounds checker (implies -g)\n" |
| 64 | 67 | #endif |
| @@ -1086,7 +1086,7 @@ ST_FUNC void tcc_debug_line(TCCState *s1) · 1085 unmodified lines | ||
| 1086 | 1086 | |
| 1087 | 1087 | if (!s1->do_debug) |
| 1088 | 1088 | return; |
| 1089 | − if (cur_text_section != text_section) | |
| 1089 | + if (cur_text_section != text_section || nocode_wanted) | |
| 1090 | 1090 | return; |
| 1091 | 1091 | f = put_new_file(s1); |
| 1092 | 1092 | if (!f) |
| @@ -5500,25 +5500,12 @@ ST_FUNC void unary(void) · 5499 unmodified lines | ||
| 5500 | 5500 | goto tok_identifier; |
| 5501 | 5501 | /* fall thru */ |
| 5502 | 5502 | case TOK___FUNC__: |
| 5503 | − { | |
| 5504 | − Section *sec; | |
| 5505 | − int len; | |
| 5506 | − /* special function name identifier */ | |
| 5507 | − len = strlen(funcname) + 1; | |
| 5508 | − /* generate char[len] type */ | |
| 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; | |
| 5503 | + tok = TOK_STR; | |
| 5504 | + cstr_reset(&tokcstr); | |
| 5505 | + cstr_cat(&tokcstr, funcname, 0); | |
| 5506 | + tokc.str.size = tokcstr.size; | |
| 5507 | + tokc.str.data = tokcstr.data; | |
| 5508 | + goto case_TOK_STR; | |
| 5522 | 5509 | case TOK_LSTR: |
| 5523 | 5510 | #ifdef TCC_TARGET_PE |
| 5524 | 5511 | t = VT_SHORT | VT_UNSIGNED; |
| @@ -5527,6 +5514,7 @@ ST_FUNC void unary(void) · 2 unmodified lines | ||
| 5527 | 5514 | #endif |
| 5528 | 5515 | goto str_init; |
| 5529 | 5516 | case TOK_STR: |
| 5517 | + case_TOK_STR: | |
| 5530 | 5518 | /* string parsing */ |
| 5531 | 5519 | t = char_type.t; |
| 5532 | 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 | 7755 | CType *t1; |
| 7768 | 7756 | |
| 7769 | 7757 | /* generate line number info */ |
| 7770 | − if (debug_modes && !p->sec) | |
| 7758 | + if (debug_modes && !(flags & DIF_SIZE_ONLY) && !p->sec) | |
| 7771 | 7759 | tcc_debug_line(tcc_state), tcc_tcov_check_line (tcc_state, 1); |
| 7772 | 7760 | |
| 7773 | 7761 | if (!(flags & DIF_HAVE_ELEM) && tok != '{' && |
| @@ -8288,50 +8276,60 @@ static void gen_function(Sym *sym) · 514 unmodified lines | ||
| 8288 | 8276 | struct scope f = { 0 }; |
| 8289 | 8277 | cur_scope = root_scope = &f; |
| 8290 | 8278 | nocode_wanted = 0; |
| 8279 | + | |
| 8291 | 8280 | ind = cur_text_section->data_offset; |
| 8292 | 8281 | if (sym->a.aligned) { |
| 8293 | 8282 | size_t newoff = section_add(cur_text_section, 0, |
| 8294 | 8283 | 1 << (sym->a.aligned - 1)); |
| 8295 | 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 | 8292 | /* NOTE: we patch the symbol size later */ |
| 8298 | 8293 | put_extern_sym(sym, cur_text_section, ind, 0); |
| 8294 | + | |
| 8299 | 8295 | if (sym->type.ref->f.func_ctor) |
| 8300 | 8296 | add_array (tcc_state, ".init_array", sym->c); |
| 8301 | 8297 | if (sym->type.ref->f.func_dtor) |
| 8302 | 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 | 8300 | /* put debug symbol */ |
| 8310 | 8301 | tcc_debug_funcstart(tcc_state, sym); |
| 8302 | + | |
| 8311 | 8303 | /* push a dummy symbol to enable local sym storage */ |
| 8312 | 8304 | sym_push2(&local_stack, SYM_FIELD, 0, 0); |
| 8313 | 8305 | local_scope = 1; /* for function parameters */ |
| 8314 | 8306 | gfunc_prolog(sym); |
| 8315 | 8307 | tcc_debug_prolog_epilog(tcc_state, 0); |
| 8308 | + | |
| 8316 | 8309 | local_scope = 0; |
| 8317 | 8310 | rsym = 0; |
| 8318 | 8311 | clear_temp_local_var_list(); |
| 8319 | 8312 | func_vla_arg(sym); |
| 8320 | 8313 | block(0); |
| 8321 | 8314 | gsym(rsym); |
| 8315 | + | |
| 8322 | 8316 | nocode_wanted = 0; |
| 8323 | 8317 | /* reset local stack */ |
| 8324 | 8318 | pop_local_syms(NULL, 0); |
| 8325 | 8319 | tcc_debug_prolog_epilog(tcc_state, 1); |
| 8326 | 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 | 8328 | cur_text_section->data_offset = ind; |
| 8328 | 8329 | local_scope = 0; |
| 8329 | 8330 | label_pop(&global_label_stack, NULL, 0); |
| 8330 | 8331 | sym_pop(&all_cleanups, NULL, 0); |
| 8331 | − /* patch symbol size */ | |
| 8332 | − elfsym(sym)->st_size = ind - func_ind; | |
| 8333 | − /* end of function */ | |
| 8334 | − tcc_debug_funcend(tcc_state, ind - func_ind); | |
| 8332 | + | |
| 8335 | 8333 | /* It's better to crash than to generate wrong code */ |
| 8336 | 8334 | cur_text_section = NULL; |
| 8337 | 8335 | funcname = ""; /* for safety */ |
| @@ -8341,6 +8339,7 @@ static void gen_function(Sym *sym) · 3 unmodified lines | ||
| 8341 | 8339 | func_ind = -1; |
| 8342 | 8340 | nocode_wanted = DATA_ONLY_WANTED; |
| 8343 | 8341 | check_vstack(); |
| 8342 | + | |
| 8344 | 8343 | /* do this after funcend debug info */ |
| 8345 | 8344 | next(); |
| 8346 | 8345 | } |
| @@ -301,8 +301,7 @@ enum { · 300 unmodified lines | ||
| 301 | 301 | sec_pdata , |
| 302 | 302 | sec_other , |
| 303 | 303 | sec_rsrc , |
| 304 | − sec_stab , | |
| 305 | − sec_stabstr , | |
| 304 | + sec_debug , | |
| 306 | 305 | sec_reloc , |
| 307 | 306 | sec_last |
| 308 | 307 | }; |
| @@ -475,9 +474,9 @@ struct pe_file { · 166 unmodified lines | ||
| 475 | 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 | 480 | DWORD sum; |
| 482 | 481 | int ret, i; |
| 483 | 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 | 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 | 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 | 660 | struct section_info *si; |
| 617 | 661 | IMAGE_SECTION_HEADER *psh; |
| 618 | 662 | TCCState *s1 = pe->s1; |
| 663 | + int need_strtab = 0; | |
| 619 | 664 | |
| 620 | 665 | pf.op = fopen(pe->filename, "wb"); |
| 621 | 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 | 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 | 737 | psh->Characteristics = si->pe_flags; |
| 691 | 738 | psh->VirtualAddress = addr; |
| @@ -715,7 +762,10 @@ static int pe_write(struct pe_info *pe) · 23 unmodified lines | ||
| 715 | 762 | if (PE_DLL == pe->type) |
| 716 | 763 | pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; |
| 717 | 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 | 769 | pe_fwrite(&pe_header, sizeof pe_header, &pf); |
| 720 | 770 | for (i = 0; i < pe->sec_count; ++i) |
| 721 | 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 | 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 | 796 | pf.sum += file_offset; |
| 740 | 797 | fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET); |
| 741 | 798 | pe_fwrite(&pf.sum, sizeof (DWORD), &pf); |
| @@ -750,6 +807,8 @@ static int pe_write(struct pe_info *pe) · 8 unmodified lines | ||
| 750 | 807 | if (pe->s1->verbose) |
| 751 | 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 | 812 | return 0; |
| 754 | 813 | } |
| 755 | 814 | |
| @@ -1072,12 +1131,12 @@ static int pe_section_class(Section *s) · 316 unmodified lines | ||
| 1072 | 1131 | type = s->sh_type; |
| 1073 | 1132 | flags = s->sh_flags; |
| 1074 | 1133 | name = s->name; |
| 1075 | − if (0 == memcmp(name, ".stab", 5)) { | |
| 1076 | − if (0 == s->s1->do_debug) | |
| 1077 | − return sec_last; | |
| 1078 | − return name[5] ? sec_stabstr : sec_stab; | |
| 1079 | − } | |
| 1080 | − if (flags & SHF_ALLOC) { | |
| 1134 | + | |
| 1135 | + if (0 == memcmp(name, ".stab", 5) || 0 == memcmp(name, ".debug_", 7)) { | |
| 1136 | + if (s->s1->do_debug) | |
| 1137 | + return sec_debug; | |
| 1138 | + | |
| 1139 | + } else if (flags & SHF_ALLOC) { | |
| 1081 | 1140 | if (type == SHT_PROGBITS |
| 1082 | 1141 | || type == SHT_INIT_ARRAY |
| 1083 | 1142 | || type == SHT_FINI_ARRAY) { |
| @@ -1135,7 +1194,7 @@ static int pe_assign_addresses (struct pe_info *pe) · 51 unmodified lines | ||
| 1135 | 1194 | if (PE_MERGE_DATA && c == sec_bss) |
| 1136 | 1195 | c = sec_data; |
| 1137 | 1196 | |
| 1138 | − if (si && c == si->cls) { | |
| 1197 | + if (si && c == si->cls && c != sec_debug) { | |
| 1139 | 1198 | /* merge with previous section */ |
| 1140 | 1199 | s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1; |
| 1141 | 1200 | } else { |