mirror of
git://repo.or.cz/tinycc.git
synced 2026-06-17 15:44:18 +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. */
|
||||
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
|
||||
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) {
|
||||
int len = strlen(str);
|
||||
tcc_open_bf(s1, "<string>", len);
|
||||
tcc_open_bf(s1, filename ? filename : "<string>", len);
|
||||
memcpy(file->buffer, str, len);
|
||||
if (s1->do_debug && filename) {
|
||||
FILE *fp = fopen(filename, "w");
|
||||
|
||||
if (fp) {
|
||||
fputs(str, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tcc_open_bf(s1, str, 0);
|
||||
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)
|
||||
{
|
||||
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" */
|
||||
@ -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);
|
||||
|
||||
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)
|
||||
|
||||
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);
|
||||
#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 */
|
||||
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*);
|
||||
|
||||
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 */
|
||||
static int elf_output_obj(TCCState *s1, const char *filename)
|
||||
LIBTCCAPI int elf_output_obj(TCCState *s1, const char *filename)
|
||||
{
|
||||
Section *s;
|
||||
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 */
|
||||
alloc_sec_names(s1, 1);
|
||||
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);
|
||||
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 */
|
||||
prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
|
||||
close(fd);
|
||||
@ -299,7 +299,7 @@ static void cleanup_sections(TCCState *s1)
|
||||
do {
|
||||
for (i = --f; i < p->nb_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);
|
||||
} else {
|
||||
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) */
|
||||
/* 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
|
||||
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. */
|
||||
|
||||
#ifndef CONFIG_RUNMEM_RO
|
||||
# ifdef __APPLE__
|
||||
# define CONFIG_RUNMEM_RO 1
|
||||
# define CONFIG_RUNMEM_RO 2
|
||||
# else
|
||||
# define CONFIG_RUNMEM_RO 0
|
||||
# endif
|
||||
@ -355,12 +356,13 @@ redo:
|
||||
if (copy == 3)
|
||||
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;
|
||||
for(i = 1; i < s1->nb_sections; i++) {
|
||||
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];
|
||||
if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
|
||||
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