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:
|
case TCC_OPTION_g:
|
||||||
s->do_debug = 1;
|
s->do_debug = 1;
|
||||||
s->dwarf = DWARF_VERSION;
|
s->dwarf = DWARF_VERSION;
|
||||||
|
|
||||||
if (strstart("dwarf", &optarg))
|
if (strstart("dwarf", &optarg))
|
||||||
s->dwarf = (*optarg) ? (0 - atoi(optarg)) : DEFAULT_DWARF_VERSION;
|
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;
|
break;
|
||||||
case TCC_OPTION_c:
|
case TCC_OPTION_c:
|
||||||
x = TCC_OUTPUT_OBJ;
|
x = TCC_OUTPUT_OBJ;
|
||||||
|
|
3
tcc.c
3
tcc.c
|
@ -59,6 +59,9 @@ static const char help[] =
|
||||||
"Debugger options:\n"
|
"Debugger options:\n"
|
||||||
" -g generate stab runtime debug info\n"
|
" -g generate stab runtime debug info\n"
|
||||||
" -gdwarf[-x] generate dwarf 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
|
#ifdef CONFIG_TCC_BCHECK
|
||||||
" -b compile with built-in memory and bounds checker (implies -g)\n"
|
" -b compile with built-in memory and bounds checker (implies -g)\n"
|
||||||
#endif
|
#endif
|
||||||
|
|
2
tccdbg.c
2
tccdbg.c
|
@ -1086,7 +1086,7 @@ ST_FUNC void tcc_debug_line(TCCState *s1)
|
||||||
|
|
||||||
if (!s1->do_debug)
|
if (!s1->do_debug)
|
||||||
return;
|
return;
|
||||||
if (cur_text_section != text_section)
|
if (cur_text_section != text_section || nocode_wanted)
|
||||||
return;
|
return;
|
||||||
f = put_new_file(s1);
|
f = put_new_file(s1);
|
||||||
if (!f)
|
if (!f)
|
||||||
|
|
59
tccgen.c
59
tccgen.c
|
@ -5500,25 +5500,12 @@ ST_FUNC void unary(void)
|
||||||
goto tok_identifier;
|
goto tok_identifier;
|
||||||
/* fall thru */
|
/* fall thru */
|
||||||
case TOK___FUNC__:
|
case TOK___FUNC__:
|
||||||
{
|
tok = TOK_STR;
|
||||||
Section *sec;
|
cstr_reset(&tokcstr);
|
||||||
int len;
|
cstr_cat(&tokcstr, funcname, 0);
|
||||||
/* special function name identifier */
|
tokc.str.size = tokcstr.size;
|
||||||
len = strlen(funcname) + 1;
|
tokc.str.data = tokcstr.data;
|
||||||
/* generate char[len] type */
|
goto case_TOK_STR;
|
||||||
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;
|
|
||||||
case TOK_LSTR:
|
case TOK_LSTR:
|
||||||
#ifdef TCC_TARGET_PE
|
#ifdef TCC_TARGET_PE
|
||||||
t = VT_SHORT | VT_UNSIGNED;
|
t = VT_SHORT | VT_UNSIGNED;
|
||||||
|
@ -5527,6 +5514,7 @@ ST_FUNC void unary(void)
|
||||||
#endif
|
#endif
|
||||||
goto str_init;
|
goto str_init;
|
||||||
case TOK_STR:
|
case TOK_STR:
|
||||||
|
case_TOK_STR:
|
||||||
/* string parsing */
|
/* string parsing */
|
||||||
t = char_type.t;
|
t = char_type.t;
|
||||||
str_init:
|
str_init:
|
||||||
|
@ -7767,7 +7755,7 @@ static void decl_initializer(init_params *p, CType *type, unsigned long c, int f
|
||||||
CType *t1;
|
CType *t1;
|
||||||
|
|
||||||
/* generate line number info */
|
/* 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);
|
tcc_debug_line(tcc_state), tcc_tcov_check_line (tcc_state, 1);
|
||||||
|
|
||||||
if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
|
if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
|
||||||
|
@ -8288,50 +8276,60 @@ static void gen_function(Sym *sym)
|
||||||
struct scope f = { 0 };
|
struct scope f = { 0 };
|
||||||
cur_scope = root_scope = &f;
|
cur_scope = root_scope = &f;
|
||||||
nocode_wanted = 0;
|
nocode_wanted = 0;
|
||||||
|
|
||||||
ind = cur_text_section->data_offset;
|
ind = cur_text_section->data_offset;
|
||||||
if (sym->a.aligned) {
|
if (sym->a.aligned) {
|
||||||
size_t newoff = section_add(cur_text_section, 0,
|
size_t newoff = section_add(cur_text_section, 0,
|
||||||
1 << (sym->a.aligned - 1));
|
1 << (sym->a.aligned - 1));
|
||||||
gen_fill_nops(newoff - ind);
|
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);
|
funcname = get_tok_str(sym->v, NULL);
|
||||||
func_ind = ind;
|
func_ind = ind;
|
||||||
func_vt = sym->type.ref->type;
|
func_vt = sym->type.ref->type;
|
||||||
func_var = sym->type.ref->f.func_type == FUNC_ELLIPSIS;
|
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 */
|
/* put debug symbol */
|
||||||
tcc_debug_funcstart(tcc_state, sym);
|
tcc_debug_funcstart(tcc_state, sym);
|
||||||
|
|
||||||
/* push a dummy symbol to enable local sym storage */
|
/* push a dummy symbol to enable local sym storage */
|
||||||
sym_push2(&local_stack, SYM_FIELD, 0, 0);
|
sym_push2(&local_stack, SYM_FIELD, 0, 0);
|
||||||
local_scope = 1; /* for function parameters */
|
local_scope = 1; /* for function parameters */
|
||||||
gfunc_prolog(sym);
|
gfunc_prolog(sym);
|
||||||
tcc_debug_prolog_epilog(tcc_state, 0);
|
tcc_debug_prolog_epilog(tcc_state, 0);
|
||||||
|
|
||||||
local_scope = 0;
|
local_scope = 0;
|
||||||
rsym = 0;
|
rsym = 0;
|
||||||
clear_temp_local_var_list();
|
clear_temp_local_var_list();
|
||||||
func_vla_arg(sym);
|
func_vla_arg(sym);
|
||||||
block(0);
|
block(0);
|
||||||
gsym(rsym);
|
gsym(rsym);
|
||||||
|
|
||||||
nocode_wanted = 0;
|
nocode_wanted = 0;
|
||||||
/* reset local stack */
|
/* reset local stack */
|
||||||
pop_local_syms(NULL, 0);
|
pop_local_syms(NULL, 0);
|
||||||
tcc_debug_prolog_epilog(tcc_state, 1);
|
tcc_debug_prolog_epilog(tcc_state, 1);
|
||||||
gfunc_epilog();
|
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;
|
cur_text_section->data_offset = ind;
|
||||||
local_scope = 0;
|
local_scope = 0;
|
||||||
label_pop(&global_label_stack, NULL, 0);
|
label_pop(&global_label_stack, NULL, 0);
|
||||||
sym_pop(&all_cleanups, 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 */
|
/* It's better to crash than to generate wrong code */
|
||||||
cur_text_section = NULL;
|
cur_text_section = NULL;
|
||||||
funcname = ""; /* for safety */
|
funcname = ""; /* for safety */
|
||||||
|
@ -8341,6 +8339,7 @@ static void gen_function(Sym *sym)
|
||||||
func_ind = -1;
|
func_ind = -1;
|
||||||
nocode_wanted = DATA_ONLY_WANTED;
|
nocode_wanted = DATA_ONLY_WANTED;
|
||||||
check_vstack();
|
check_vstack();
|
||||||
|
|
||||||
/* do this after funcend debug info */
|
/* do this after funcend debug info */
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
83
tccpe.c
83
tccpe.c
|
@ -301,8 +301,7 @@ enum {
|
||||||
sec_pdata ,
|
sec_pdata ,
|
||||||
sec_other ,
|
sec_other ,
|
||||||
sec_rsrc ,
|
sec_rsrc ,
|
||||||
sec_stab ,
|
sec_debug ,
|
||||||
sec_stabstr ,
|
|
||||||
sec_reloc ,
|
sec_reloc ,
|
||||||
sec_last
|
sec_last
|
||||||
};
|
};
|
||||||
|
@ -475,9 +474,9 @@ struct pe_file {
|
||||||
unsigned pos;
|
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;
|
DWORD sum;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
pf->pos += (ret = fwrite(data, 1, len, pf->op));
|
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;
|
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)
|
static int pe_write(struct pe_info *pe)
|
||||||
{
|
{
|
||||||
|
@ -616,6 +660,7 @@ static int pe_write(struct pe_info *pe)
|
||||||
struct section_info *si;
|
struct section_info *si;
|
||||||
IMAGE_SECTION_HEADER *psh;
|
IMAGE_SECTION_HEADER *psh;
|
||||||
TCCState *s1 = pe->s1;
|
TCCState *s1 = pe->s1;
|
||||||
|
int need_strtab = 0;
|
||||||
|
|
||||||
pf.op = fopen(pe->filename, "wb");
|
pf.op = fopen(pe->filename, "wb");
|
||||||
if (NULL == pf.op)
|
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));
|
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->Characteristics = si->pe_flags;
|
||||||
psh->VirtualAddress = addr;
|
psh->VirtualAddress = addr;
|
||||||
|
@ -715,7 +762,10 @@ static int pe_write(struct pe_info *pe)
|
||||||
if (PE_DLL == pe->type)
|
if (PE_DLL == pe->type)
|
||||||
pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL;
|
pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL;
|
||||||
pe_header.filehdr.Characteristics |= pe->s1->pe_characteristics;
|
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);
|
pe_fwrite(&pe_header, sizeof pe_header, &pf);
|
||||||
for (i = 0; i < pe->sec_count; ++i)
|
for (i = 0; i < pe->sec_count; ++i)
|
||||||
pe_fwrite(&pe->sec_info[i]->ish, sizeof(IMAGE_SECTION_HEADER), &pf);
|
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);
|
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;
|
pf.sum += file_offset;
|
||||||
fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET);
|
fseek(pf.op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET);
|
||||||
pe_fwrite(&pf.sum, sizeof (DWORD), &pf);
|
pe_fwrite(&pf.sum, sizeof (DWORD), &pf);
|
||||||
|
@ -750,6 +807,8 @@ static int pe_write(struct pe_info *pe)
|
||||||
if (pe->s1->verbose)
|
if (pe->s1->verbose)
|
||||||
printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset);
|
printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset);
|
||||||
|
|
||||||
|
if (s1->do_debug & 16)
|
||||||
|
pe_create_pdb(s1, pe->filename);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1072,12 +1131,12 @@ static int pe_section_class(Section *s)
|
||||||
type = s->sh_type;
|
type = s->sh_type;
|
||||||
flags = s->sh_flags;
|
flags = s->sh_flags;
|
||||||
name = s->name;
|
name = s->name;
|
||||||
if (0 == memcmp(name, ".stab", 5)) {
|
|
||||||
if (0 == s->s1->do_debug)
|
if (0 == memcmp(name, ".stab", 5) || 0 == memcmp(name, ".debug_", 7)) {
|
||||||
return sec_last;
|
if (s->s1->do_debug)
|
||||||
return name[5] ? sec_stabstr : sec_stab;
|
return sec_debug;
|
||||||
}
|
|
||||||
if (flags & SHF_ALLOC) {
|
} else if (flags & SHF_ALLOC) {
|
||||||
if (type == SHT_PROGBITS
|
if (type == SHT_PROGBITS
|
||||||
|| type == SHT_INIT_ARRAY
|
|| type == SHT_INIT_ARRAY
|
||||||
|| type == SHT_FINI_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)
|
if (PE_MERGE_DATA && c == sec_bss)
|
||||||
c = sec_data;
|
c = sec_data;
|
||||||
|
|
||||||
if (si && c == si->cls) {
|
if (si && c == si->cls && c != sec_debug) {
|
||||||
/* merge with previous section */
|
/* merge with previous section */
|
||||||
s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1;
|
s->sh_addr = addr = ((addr - 1) | (16 - 1)) + 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Reference in a new issue