On 12/29/25 22:02, [email protected] wrote:
i've been building a C package manager where the packages are written
in C and jitted as opposed to some crazy dsl or external scripting
language. hence my interest in tcc. it does pretty much everything i
need stock, but i was experimenting with how to debug the jitted code
with gdb.
it looks like this boils down to using gdb's builtin jit stuff
(__jit_debug_register_code, etc). you just give it a pointer to the
elf data and everything works nicely. and tcc obviously has a ton of
nice code for outputting to elf.
but the problem is twofold. (1) tcc only outputs elf objects to files.
nothing in the api for outputting to a buffer/realloc'ing a buffer and
returning. (2) i REALLY want the resulting object to be relocated, but
when tcc outputs an object it's obviously not relocated, since it's
expected to be linked in to some host program later.
i made a proof of concept where i do the relocation myself. but it's
very hairy and moreover feels wrong -- i just jitted the code. i know
where it is in memory. i know the offsets. why go through this dance
of generating an unrelocated object just to relocate it later?
i believe the fundamental mismatch is that tcc operates in either/or
mode. either you want an object file or you want to run it directly.
there's no idea of "i want to run directly, but also i want debug
symbols". this is understandable...
my question is just whether there's any chance of this work being
accepted as a patch to tcc, or if i should just fork. i have
everything working; it's not a lot of code, just another entry in
libtcc.h that outputs the elf to mem, relocated. i am happy to clean
it up and submit it...but if you dont think it will be useful/you just
dont want it then i'll just keep my fork.
in any case, thanks for the great library and thanks for reading
I tried a lot of debugging jit code for gdb/lldb but none of it really
worked.
So I did the next thing and output the object file and a text file to disk.
Then loaded the symbol table with gdb add-symbol-file. Gdb then finds
the text file from the object file. See tst_run.c how this works.
See attachments.
Do not apply this. It is just a first working version.
Herman
diff --git a/libtcc.h b/libtcc.h
index 5949c80..738c7d5 100644
--- a/libtcc.h
+++ b/libtcc.h
@@ -105,6 +105,16 @@ 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 */
+
+/* Output object file. This must be done after tcc_relocate.
+ The filename can be loaded with gdb command add-symbol-file */
+LIBTCCAPI int elf_output_obj(TCCState *s1, const char *filename);
+
+/* Write source file. If filename is NULL '<string>' will be used */
+LIBTCCAPI int tcc_write_source(const char *filename, const char *text);
+
/* 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*);
diff --git a/tccelf.c b/tccelf.c
index e078b91..70a2084 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -3095,10 +3095,14 @@ 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 tcc_error_noabort("Probably forget to set debug option with
tccapi");
+ }
/* Allocate strings for section names */
alloc_sec_names(s1, 1);
file_offset = (sizeof (ElfW(Ehdr)) + 3) & -4;
@@ -3115,6 +3119,17 @@ static int elf_output_obj(TCCState *s1, const char
*filename)
return ret;
}
+LIBTCCAPI int tcc_write_source(const char *filename, const char *text)
+{
+ FILE *fp = fopen(filename ? filename : "<string>", "w");
+
+ if (fp) {
+ fputs(text, fp);
+ fclose(fp);
+ }
+ return fp ? 0 : -1;
+}
+
LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
{
s->nb_errors = 0;
diff --git a/tccrun.c b/tccrun.c
index 1fbbf6d..7fe06c1 100644
--- a/tccrun.c
+++ b/tccrun.c
@@ -160,6 +160,8 @@ LIBTCCAPI int tcc_relocate(TCCState *s1)
ret = tcc_relocate_ex(s1, s1->run_ptr, ptr_diff);
if (ret == 0)
st_link(s1);
+ if (s1->do_debug)
+ protect_pages((void*)PAGEALIGN(s1->run_ptr), s1->run_size - PAGESIZE,
3 /*rwx*/);
return ret;
}
@@ -299,7 +301,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 +313,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 +358,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;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libtcc.h"
static const char program[] =
"#include <stdio.h>\n"
"int value;\n"
"int tst(void)\n"
"{\n"
" fprintf(stderr, \"Hello World\\n\");\n"
" value = 10;\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;
}
tcc_set_options(s, "-g");
tcc_set_error_func(s, stdout, handle_error);
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
if (tcc_compile_string(s, program) == -1)
return 1;
if (tcc_relocate(s) < 0)
return 1;
elf_output_obj(s, "tst.o");
tcc_write_source(NULL, program);
/* set breakpoint on next line. and load symbol file with
gdb command add-symbol-file.
The set breakpoint on tst and continue. */
func = tcc_get_symbol(s, "tst");
if (!func)
return 1;
func();
tcc_delete(s);
return 0;
}
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel