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.
This commit is contained in:
grischka 2023-06-18 19:58:16 +02:00
parent cd75ca692a
commit 452045422b
5 changed files with 108 additions and 44 deletions

View file

@ -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;

3
tcc.c
View file

@ -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

View file

@ -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)

View file

@ -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();
}

83
tccpe.c
View file

@ -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 {