mirror of
git://repo.or.cz/tinycc.git
synced 2026-11-04 21:26:36 +08:00
Add support to debug libtcc code
Some checks are pending
build and test / test-x86_64-linux (push) Waiting to run
build and test / test-x86_64-osx (push) Waiting to run
build and test / test-aarch64-osx (push) Waiting to run
build and test / test-x86_64-win32 (push) Waiting to run
build and test / test-i386-win32 (push) Waiting to run
build and test / test-armv7-linux (push) Waiting to run
build and test / test-aarch64-linux (push) Waiting to run
build and test / test-riscv64-linux (push) Waiting to run
Some checks are pending
build and test / test-x86_64-linux (push) Waiting to run
build and test / test-x86_64-osx (push) Waiting to run
build and test / test-aarch64-osx (push) Waiting to run
build and test / test-x86_64-win32 (push) Waiting to run
build and test / test-i386-win32 (push) Waiting to run
build and test / test-armv7-linux (push) Waiting to run
build and test / test-aarch64-linux (push) Waiting to run
build and test / test-riscv64-linux (push) Waiting to run
I tried several gdb/lldb options to debug libtcc generated code. But gdb/lldb complained that they could not load symbols or that module does not exist. There was also another problem. The code itself was in memory (string) and gdb/lldb do not have functions to acces it. So I came up with a new api for debugging libtcc code. First you enable debugging with: tcc_set_options(s, "-g") Then compile the code with: tcc_compile_string_file(s, program, "<file>.c") Then call tcc_relocate(). And finaly write the object file to disk: elf_output_obj(s, "<file>.o") Now you can start the debugger and put an breakpoint after the elf_output_obj() code. Then use gdb command add-symbol-file <file>.o and from there on you can set breakpoints in the libtcc generated code. You can also step/print variables/... I could not find a simular function in lldb yet. When debugging is done you remove the tcc_set_options(s, "-g"). All other code can remain because tcc_compile_string_file and elf_output_obj do not output any file any more is debug is not set. See also tests/libtcc_debug.c
This commit is contained in:
parent
8a8388c6ff
commit
1fe3e3bff5
21
libtcc.c
21
libtcc.c
@ -791,7 +791,7 @@ ST_FUNC int tcc_open(TCCState *s1, const char *filename)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* compile the file opened in 'file'. Return non zero if errors. */
|
/* compile the file opened in 'file'. Return non zero if errors. */
|
||||||
static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
|
static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd, const char *filename)
|
||||||
{
|
{
|
||||||
/* Here we enter the code section where we use the global variables for
|
/* Here we enter the code section where we use the global variables for
|
||||||
parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c).
|
parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c).
|
||||||
@ -807,8 +807,16 @@ static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
|
|||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
int len = strlen(str);
|
int len = strlen(str);
|
||||||
tcc_open_bf(s1, "<string>", len);
|
tcc_open_bf(s1, filename ? filename : "<string>", len);
|
||||||
memcpy(file->buffer, str, len);
|
memcpy(file->buffer, str, len);
|
||||||
|
if (s1->do_debug && filename) {
|
||||||
|
FILE *fp = fopen(filename, "w");
|
||||||
|
|
||||||
|
if (fp) {
|
||||||
|
fputs(str, fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tcc_open_bf(s1, str, 0);
|
tcc_open_bf(s1, str, 0);
|
||||||
file->fd = fd;
|
file->fd = fd;
|
||||||
@ -838,7 +846,12 @@ static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
|
|||||||
|
|
||||||
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
|
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
|
||||||
{
|
{
|
||||||
return tcc_compile(s, s->filetype, str, -1);
|
return tcc_compile(s, s->filetype, str, -1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBTCCAPI int tcc_compile_string_file(TCCState *s, const char *str, const char *filename)
|
||||||
|
{
|
||||||
|
return tcc_compile(s, s->filetype, str, -1, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* define a preprocessor symbol. value can be NULL, sym can be "sym=val" */
|
/* define a preprocessor symbol. value can be NULL, sym can be "sym=val" */
|
||||||
@ -1233,7 +1246,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
|
|||||||
return tcc_add_binary(s1, flags, filename, fd);
|
return tcc_add_binary(s1, flags, filename, fd);
|
||||||
|
|
||||||
dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename));
|
dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename));
|
||||||
return tcc_compile(s1, flags, filename, fd);
|
return tcc_compile(s1, flags, filename, fd, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
|
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
|
||||||
|
|||||||
12
libtcc.h
12
libtcc.h
@ -105,6 +105,18 @@ LIBTCCAPI void tcc_list_symbols(TCCState *s, void *ctx,
|
|||||||
LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *jmp_buf, void *top_func, void *longjmp);
|
LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *jmp_buf, void *top_func, void *longjmp);
|
||||||
#define tcc_setjmp(s1,jb,f) setjmp(_tcc_setjmp(s1, jb, f, longjmp))
|
#define tcc_setjmp(s1,jb,f) setjmp(_tcc_setjmp(s1, jb, f, longjmp))
|
||||||
|
|
||||||
|
/* debugging */
|
||||||
|
/* For debugging to work you have to enable it with tcc_set_options */
|
||||||
|
|
||||||
|
/* compile a string containing a C source. Return -1 if error.
|
||||||
|
Write the string to file filename if debug is set. */
|
||||||
|
LIBTCCAPI int tcc_compile_string_file(TCCState *s, const char *buf, const char *filename);
|
||||||
|
|
||||||
|
/* Output object file. This must be done after tcc_relocate.
|
||||||
|
It only generates the file if debug is set.
|
||||||
|
The filename can be loaded with gdb command add-symbol-file */
|
||||||
|
LIBTCCAPI int elf_output_obj(TCCState *s1, const char *filename);
|
||||||
|
|
||||||
/* custom error printer for runtime exceptions. Returning 0 stops backtrace */
|
/* custom error printer for runtime exceptions. Returning 0 stops backtrace */
|
||||||
typedef int TCCBtFunc(void *udata, void *pc, const char *file, int line, const char* func, const char *msg);
|
typedef int TCCBtFunc(void *udata, void *pc, const char *file, int line, const char* func, const char *msg);
|
||||||
LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, void* userdata, TCCBtFunc*);
|
LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, void* userdata, TCCBtFunc*);
|
||||||
|
|||||||
5
tccelf.c
5
tccelf.c
@ -3095,10 +3095,13 @@ static void alloc_sec_names(TCCState *s1, int is_obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Output an elf .o file */
|
/* Output an elf .o file */
|
||||||
static int elf_output_obj(TCCState *s1, const char *filename)
|
LIBTCCAPI int elf_output_obj(TCCState *s1, const char *filename)
|
||||||
{
|
{
|
||||||
Section *s;
|
Section *s;
|
||||||
int i, ret, file_offset;
|
int i, ret, file_offset;
|
||||||
|
for(i = 1; i < s1->nb_sections; i++)
|
||||||
|
if (s1->sections[i] == NULL)
|
||||||
|
return -2; /* debugging and TCC_OUTPUT_MEMORY and do_debug = 0 */
|
||||||
/* Allocate strings for section names */
|
/* Allocate strings for section names */
|
||||||
alloc_sec_names(s1, 1);
|
alloc_sec_names(s1, 1);
|
||||||
file_offset = (sizeof (ElfW(Ehdr)) + 3) & -4;
|
file_offset = (sizeof (ElfW(Ehdr)) + 3) & -4;
|
||||||
|
|||||||
16
tccrun.c
16
tccrun.c
@ -120,7 +120,7 @@ static int rt_mem(TCCState *s1, int size)
|
|||||||
unlink(tmpfname);
|
unlink(tmpfname);
|
||||||
ftruncate(fd, size);
|
ftruncate(fd, size);
|
||||||
|
|
||||||
ptr = mmap(NULL, size * 2, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
|
ptr = mmap(NULL, size * 2, PROT_READ|PROT_EXEC|(s1->do_debug ? PROT_WRITE : 0), MAP_SHARED, fd, 0);
|
||||||
/* mmap RW memory at fixed distance */
|
/* mmap RW memory at fixed distance */
|
||||||
prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
|
prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -299,7 +299,7 @@ static void cleanup_sections(TCCState *s1)
|
|||||||
do {
|
do {
|
||||||
for (i = --f; i < p->nb_secs; i++) {
|
for (i = --f; i < p->nb_secs; i++) {
|
||||||
Section *s = p->secs[i];
|
Section *s = p->secs[i];
|
||||||
if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
|
if (s1->do_debug || s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash) {
|
||||||
s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
|
s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
|
||||||
} else {
|
} else {
|
||||||
free_section(s), tcc_free(s), p->secs[i] = NULL;
|
free_section(s), tcc_free(s), p->secs[i] = NULL;
|
||||||
@ -311,15 +311,16 @@ static void cleanup_sections(TCCState *s1)
|
|||||||
/* ------------------------------------------------------------- */
|
/* ------------------------------------------------------------- */
|
||||||
/* 0 = .text rwx other rw (memory >= 2 pages a 4096 bytes) */
|
/* 0 = .text rwx other rw (memory >= 2 pages a 4096 bytes) */
|
||||||
/* 1 = .text rx other rw (memory >= 3 pages) */
|
/* 1 = .text rx other rw (memory >= 3 pages) */
|
||||||
/* 2 = .text rx .rdata ro .data/.bss rw (memory >= 4 pages) */
|
/* 2 = .debug .debug ro (optional) */
|
||||||
|
/* 3 = .text rx .rdata ro .data/.bss rw (memory >= 4 pages) */
|
||||||
|
|
||||||
/* Some targets implement secutiry options that do not allow write in
|
/* Some targets implement secutiry options that do not allow write in
|
||||||
executable code. These targets need CONFIG_RUNMEM_RO=1.
|
executable code. These targets need CONFIG_RUNMEM_RO=2.
|
||||||
The disadvantage of this is that it requires a little bit more memory. */
|
The disadvantage of this is that it requires a little bit more memory. */
|
||||||
|
|
||||||
#ifndef CONFIG_RUNMEM_RO
|
#ifndef CONFIG_RUNMEM_RO
|
||||||
# ifdef __APPLE__
|
# ifdef __APPLE__
|
||||||
# define CONFIG_RUNMEM_RO 1
|
# define CONFIG_RUNMEM_RO 2
|
||||||
# else
|
# else
|
||||||
# define CONFIG_RUNMEM_RO 0
|
# define CONFIG_RUNMEM_RO 0
|
||||||
# endif
|
# endif
|
||||||
@ -355,12 +356,13 @@ redo:
|
|||||||
if (copy == 3)
|
if (copy == 3)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
|
for (k = 0; k < 4; ++k) { /* 0:rx, 1:ro, 2:ro debug , 3:rw sections */
|
||||||
n = 0; addr = 0;
|
n = 0; addr = 0;
|
||||||
for(i = 1; i < s1->nb_sections; i++) {
|
for(i = 1; i < s1->nb_sections; i++) {
|
||||||
static const char shf[] = {
|
static const char shf[] = {
|
||||||
SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE
|
SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, 0, SHF_ALLOC|SHF_WRITE
|
||||||
};
|
};
|
||||||
|
if (k == 2 && s1->do_debug == 0) continue;
|
||||||
s = s1->sections[i];
|
s = s1->sections[i];
|
||||||
if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
|
if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
58
tests/libtcc_debug.c
Normal file
58
tests/libtcc_debug.c
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "libtcc.h"
|
||||||
|
|
||||||
|
static const char program[] =
|
||||||
|
"#include <stdio.h>\n"
|
||||||
|
"int fib(int n)\n"
|
||||||
|
"{\n"
|
||||||
|
" if (n <= 2)\n"
|
||||||
|
" return 1;\n"
|
||||||
|
" else\n"
|
||||||
|
" return fib(n-1) + fib(n-2);\n"
|
||||||
|
"}\n"
|
||||||
|
"int tst(void)\n"
|
||||||
|
"{\n"
|
||||||
|
" int i;\n"
|
||||||
|
" for (i = 2; i < 20; i++)\n"
|
||||||
|
" printf(\"%d \", fib(i));\n"
|
||||||
|
" printf(\"\\n\");\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
void handle_error(void *opaque, const char *msg)
|
||||||
|
{
|
||||||
|
fprintf(opaque, "%s\n", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
int (*func)(void);
|
||||||
|
TCCState *s = tcc_new();
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
fprintf(stderr, __FILE__ ": could not create tcc state\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
/* If -g option is not set the debugging files tst.c en tst.o will
|
||||||
|
not be created. */
|
||||||
|
tcc_set_options(s, "-g");
|
||||||
|
#endif
|
||||||
|
tcc_set_error_func(s, stdout, handle_error);
|
||||||
|
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
|
||||||
|
if (tcc_compile_string_file(s, program, "tst.c") == -1)
|
||||||
|
return 1;
|
||||||
|
if (tcc_relocate(s) < 0)
|
||||||
|
return 1;
|
||||||
|
elf_output_obj(s, "tst.o");
|
||||||
|
/* set breakpoint on next line. and load symbol file with
|
||||||
|
gdb command add-symbol-file.
|
||||||
|
Then set breakpoint on tst and continue. */
|
||||||
|
if ((func = tcc_get_symbol(s, "tst")))
|
||||||
|
func();
|
||||||
|
tcc_delete(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user