From 452045422bdc0d25acdae2ffe688796c2dcb2516 Mon Sep 17 00:00:00 2001 From: grischka Date: Sun, 18 Jun 2023 19:58:16 +0200 Subject: [PATCH] win32: pe dwarf sections & option -g.pdb 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. --- libtcc.c | 5 +++- tcc.c | 3 ++ tccdbg.c | 2 +- tccgen.c | 59 ++++++++++++++++++++-------------------- tccpe.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 108 insertions(+), 44 deletions(-) diff --git a/libtcc.c b/libtcc.c index 85ed5d32..8b425667 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1976,9 +1976,12 @@ dorun: case TCC_OPTION_g: s->do_debug = 1; s->dwarf = DWARF_VERSION; - if (strstart("dwarf", &optarg)) s->dwarf = (*optarg) ? (0 - atoi(optarg)) : DEFAULT_DWARF_VERSION; +#ifdef TCC_TARGET_PE + else if (0 == strcmp(".pdb", optarg)) + s->dwarf = 5, s->do_debug |= 16; +#endif break; case TCC_OPTION_c: x = TCC_OUTPUT_OBJ; diff --git a/tcc.c b/tcc.c index 9d06a477..b38dae9b 100644 --- a/tcc.c +++ b/tcc.c @@ -59,6 +59,9 @@ static const char help[] = "Debugger options:\n" " -g generate stab runtime debug info\n" " -gdwarf[-x] generate dwarf runtime debug info\n" +#ifdef TCC_TARGET_PE + " -g.pdb create .pdb debug database\n" +#endif #ifdef CONFIG_TCC_BCHECK " -b compile with built-in memory and bounds checker (implies -g)\n" #endif diff --git a/tccdbg.c b/tccdbg.c index 18c2cb10..56d878df 100644 --- a/tccdbg.c +++ b/tccdbg.c @@ -1086,7 +1086,7 @@ ST_FUNC void tcc_debug_line(TCCState *s1) if (!s1->do_debug) return; - if (cur_text_section != text_section) + if (cur_text_section != text_section || nocode_wanted) return; f = put_new_file(s1); if (!f) diff --git a/tccgen.c b/tccgen.c index 8f259b8b..fd0680fc 100644 --- a/tccgen.c +++ b/tccgen.c @@ -5500,25 +5500,12 @@ ST_FUNC void unary(void) goto tok_identifier; /* fall thru */ case TOK___FUNC__: - { - Section *sec; - int len; - /* special function name identifier */ - len = strlen(funcname) + 1; - /* generate char[len] type */ - type.t = char_type.t; - if (tcc_state->warn_write_strings & WARN_ON) - type.t |= VT_CONSTANT; - mk_pointer(&type); - type.t |= VT_ARRAY; - type.ref->c = len; - sec = rodata_section; - vpush_ref(&type, sec, sec->data_offset, len); - if (!NODATA_WANTED) - memcpy(section_ptr_add(sec, len), funcname, len); - next(); - } - break; + tok = TOK_STR; + cstr_reset(&tokcstr); + cstr_cat(&tokcstr, funcname, 0); + tokc.str.size = tokcstr.size; + tokc.str.data = tokcstr.data; + goto case_TOK_STR; case TOK_LSTR: #ifdef TCC_TARGET_PE t = VT_SHORT | VT_UNSIGNED; @@ -5527,6 +5514,7 @@ ST_FUNC void unary(void) #endif goto str_init; case TOK_STR: + case_TOK_STR: /* string parsing */ t = char_type.t; str_init: @@ -7767,7 +7755,7 @@ static void decl_initializer(init_params *p, CType *type, unsigned long c, int f CType *t1; /* generate line number info */ - if (debug_modes && !p->sec) + if (debug_modes && !(flags & DIF_SIZE_ONLY) && !p->sec) tcc_debug_line(tcc_state), tcc_tcov_check_line (tcc_state, 1); if (!(flags & DIF_HAVE_ELEM) && tok != '{' && @@ -8288,50 +8276,60 @@ static void gen_function(Sym *sym) struct scope f = { 0 }; cur_scope = root_scope = &f; nocode_wanted = 0; + ind = cur_text_section->data_offset; if (sym->a.aligned) { size_t newoff = section_add(cur_text_section, 0, 1 << (sym->a.aligned - 1)); gen_fill_nops(newoff - ind); } - /* NOTE: we patch the symbol size later */ - put_extern_sym(sym, cur_text_section, ind, 0); - if (sym->type.ref->f.func_ctor) - add_array (tcc_state, ".init_array", sym->c); - if (sym->type.ref->f.func_dtor) - add_array (tcc_state, ".fini_array", sym->c); funcname = get_tok_str(sym->v, NULL); func_ind = ind; func_vt = sym->type.ref->type; func_var = sym->type.ref->f.func_type == FUNC_ELLIPSIS; + /* NOTE: we patch the symbol size later */ + put_extern_sym(sym, cur_text_section, ind, 0); + + if (sym->type.ref->f.func_ctor) + add_array (tcc_state, ".init_array", sym->c); + if (sym->type.ref->f.func_dtor) + add_array (tcc_state, ".fini_array", sym->c); + /* put debug symbol */ tcc_debug_funcstart(tcc_state, sym); + /* push a dummy symbol to enable local sym storage */ sym_push2(&local_stack, SYM_FIELD, 0, 0); local_scope = 1; /* for function parameters */ gfunc_prolog(sym); tcc_debug_prolog_epilog(tcc_state, 0); + local_scope = 0; rsym = 0; clear_temp_local_var_list(); func_vla_arg(sym); block(0); gsym(rsym); + nocode_wanted = 0; /* reset local stack */ pop_local_syms(NULL, 0); tcc_debug_prolog_epilog(tcc_state, 1); gfunc_epilog(); + + /* end of function */ + tcc_debug_funcend(tcc_state, ind - func_ind); + + /* patch symbol size */ + elfsym(sym)->st_size = ind - func_ind; + cur_text_section->data_offset = ind; local_scope = 0; label_pop(&global_label_stack, NULL, 0); sym_pop(&all_cleanups, NULL, 0); - /* patch symbol size */ - elfsym(sym)->st_size = ind - func_ind; - /* end of function */ - tcc_debug_funcend(tcc_state, ind - func_ind); + /* It's better to crash than to generate wrong code */ cur_text_section = NULL; funcname = ""; /* for safety */ @@ -8341,6 +8339,7 @@ static void gen_function(Sym *sym) func_ind = -1; nocode_wanted = DATA_ONLY_WANTED; check_vstack(); + /* do this after funcend debug info */ next(); } diff --git a/tccpe.c b/tccpe.c index ab610af1..11ec6df6 100644 --- a/tccpe.c +++ b/tccpe.c @@ -301,8 +301,7 @@ enum { sec_pdata , sec_other , sec_rsrc , - sec_stab , - sec_stabstr , + sec_debug , sec_reloc , sec_last }; @@ -475,9 +474,9 @@ struct pe_file { unsigned pos; }; -static int pe_fwrite(void *data, int len, struct pe_file *pf) +static int pe_fwrite(const void *data, int len, struct pe_file *pf) { - WORD *p = data; + const WORD *p = data; DWORD sum; int ret, i; pf->pos += (ret = fwrite(data, 1, len, pf->op)); @@ -502,6 +501,51 @@ static void pe_fpad(struct pe_file *pf, DWORD new_pos) pf->pos = new_pos; } +/*----------------------------------------------------------------------------*/ +/* PE-DWARF/COFF support + does not work with a mingw-gdb really but works with cv2pdb + (https://github.com/rainers/cv2pdb) */ + +#define N_COFF_SYMS 0 + +static const char dwarf_secs[] = +{ + ".debug_info\0" + ".debug_abbrev\0" + ".debug_line\0" + ".debug_aranges\0" + ".debug_str\0" + ".debug_line_str\0" +}; + +static const unsigned coff_strtab_size = 4 + sizeof dwarf_secs - 1; + +static int pe_put_long_secname(char *secname, const char *name) +{ + const char *d = dwarf_secs; + do { + if (0 == strcmp(d, name)) { + sprintf(secname, "/%d", (int)(d - dwarf_secs + 4)); + return 1; + } + d = strchr(d, 0) + 1; + } while (*d); + return 0; +} + +static void pe_create_pdb(TCCState *s1, const char *exename) +{ + char buf[300]; int r; + snprintf(buf, sizeof buf, "cv2pdb.exe %s", exename); + r = system(buf); + strcpy(tcc_fileextension(strcpy(buf, exename)), ".pdb"); + if (r) { + tcc_error_noabort("could not create '%s'\n(need working cv2pdb from https://github.com/rainers/cv2pdb)", buf); + } else if (s1->verbose) { + printf("<- %s\n", buf); + } +} + /*----------------------------------------------------------------------------*/ static int pe_write(struct pe_info *pe) { @@ -616,6 +660,7 @@ static int pe_write(struct pe_info *pe) struct section_info *si; IMAGE_SECTION_HEADER *psh; TCCState *s1 = pe->s1; + int need_strtab = 0; pf.op = fopen(pe->filename, "wb"); if (NULL == pf.op) @@ -686,6 +731,8 @@ static int pe_write(struct pe_info *pe) } memcpy(psh->Name, sh_name, umin(strlen(sh_name), sizeof psh->Name)); + if (si->cls == sec_debug) + need_strtab += pe_put_long_secname(psh->Name, sh_name); psh->Characteristics = si->pe_flags; psh->VirtualAddress = addr; @@ -715,7 +762,10 @@ static int pe_write(struct pe_info *pe) if (PE_DLL == pe->type) pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL; pe_header.filehdr.Characteristics |= pe->s1->pe_characteristics; - + if (need_strtab) { + pe_header.filehdr.PointerToSymbolTable = file_offset; + pe_header.filehdr.NumberOfSymbols = N_COFF_SYMS; + } pe_fwrite(&pe_header, sizeof pe_header, &pf); for (i = 0; i < pe->sec_count; ++i) pe_fwrite(&pe->sec_info[i]->ish, sizeof(IMAGE_SECTION_HEADER), &pf); @@ -736,6 +786,13 @@ static int pe_write(struct pe_info *pe) pe_fpad(&pf, file_offset); } + if (need_strtab) { + /* create a tiny COFF string table with the long section names */ + pe_fwrite(&coff_strtab_size, sizeof coff_strtab_size, &pf); + pe_fwrite(dwarf_secs, sizeof dwarf_secs - 1, &pf); + file_offset = pf.pos; + } + pf.sum += file_offset; fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET); pe_fwrite(&pf.sum, sizeof (DWORD), &pf); @@ -750,6 +807,8 @@ static int pe_write(struct pe_info *pe) if (pe->s1->verbose) printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset); + if (s1->do_debug & 16) + pe_create_pdb(s1, pe->filename); return 0; } @@ -1072,12 +1131,12 @@ static int pe_section_class(Section *s) type = s->sh_type; flags = s->sh_flags; name = s->name; - if (0 == memcmp(name, ".stab", 5)) { - if (0 == s->s1->do_debug) - return sec_last; - return name[5] ? sec_stabstr : sec_stab; - } - if (flags & SHF_ALLOC) { + + if (0 == memcmp(name, ".stab", 5) || 0 == memcmp(name, ".debug_", 7)) { + if (s->s1->do_debug) + return sec_debug; + + } else if (flags & SHF_ALLOC) { if (type == SHT_PROGBITS || type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY) { @@ -1135,7 +1194,7 @@ static int pe_assign_addresses (struct pe_info *pe) if (PE_MERGE_DATA && c == sec_bss) c = sec_data; - if (si && c == si->cls) { + if (si && c == si->cls && c != sec_debug) { /* merge with previous section */ s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1; } else {