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:
parent
cd75ca692a
commit
452045422b
5 changed files with 108 additions and 44 deletions
5
libtcc.c
5
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;
|
||||
|
|
3
tcc.c
3
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
|
||||
|
|
2
tccdbg.c
2
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)
|
||||
|
|
59
tccgen.c
59
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();
|
||||
}
|
||||
|
|
83
tccpe.c
83
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 {
|
||||
|
|
Loading…
Reference in a new issue