Skip to content

Commit 2db8654

Browse files
Expose libtcc symbol listing
1 parent de04109 commit 2db8654

7 files changed

Lines changed: 128 additions & 0 deletions

File tree

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export(tcc_lib_paths)
4747
export(tcc_library)
4848
export(tcc_library_path)
4949
export(tcc_link)
50+
export(tcc_list_symbols)
5051
export(tcc_malloc)
5152
export(tcc_map_c_type_to_ffi)
5253
export(tcc_null_ptr)

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Rtinycc 0.1.11
22

3+
- Add `tcc_list_symbols()` to inspect global symbol names and resolved hexadecimal addresses known to a libtcc state.
34
- Fix `PROTECT` and memory-balance hygiene bugs in internal C state initialization (`RC_libtcc_state_new`, `RC_libtcc_get_symbol`, and callback registration paths). Previously, class string attributes could be inadvertently allocated without proper protection.
45
- Fix a stack-depth and protection imbalance bug in `RC_invoke_callback_internal` where callback execution could unexpectedly unprotect the `call` object prior to evaluation.
56

R/tinycc.R

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,21 @@ tcc_get_symbol <- function(state, name) {
307307
.Call(RC_libtcc_get_symbol, state, name)
308308
}
309309

310+
#' List symbols known to a libtcc state
311+
#'
312+
#' Return the global symbols currently known to a libtcc state. This is a
313+
#' symbol-table inspection helper for compiled/linked TCC states, not a DLL
314+
#' export scanner and not a C signature discovery API. For meaningful runtime
315+
#' addresses, call it after [tcc_relocate()].
316+
#'
317+
#' @param state A `tcc_state`.
318+
#' @return A data frame with columns `name` and `address`, where `address` is a
319+
#' hexadecimal character string.
320+
#' @export
321+
tcc_list_symbols <- function(state) {
322+
.Call(RC_libtcc_list_symbols, state)
323+
}
324+
310325

311326
#' Call a zero-argument symbol with a specified return type
312327
#' @param state A `tcc_state`.

inst/tinytest/test_libtcc.R

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ expect_true(tcc_symbol_is_valid(sym_ptr))
2727
addr <- get_external_ptr_addr(sym_ptr)
2828
cat(sprintf("symbol 'forty_two' address: %f\n", addr))
2929
cat(sprintf("address %% 8: %f\n", addr %% 8))
30+
symbols <- tcc_list_symbols(state)
31+
expect_true(is.data.frame(symbols))
32+
expect_true(identical(names(symbols), c("name", "address")))
33+
expect_true("forty_two" %in% symbols$name)
34+
expect_true(all(grepl("^0x[0-9a-fA-F]+$", symbols$address)))
3035
expect_equal(tcc_call_symbol(state, "forty_two", return = "int"), 42L)
3136
# CLI compile to object
3237
# forty_two.c uses stdio.h / printf which are UCRT-inline on Windows,

man/tcc_list_symbols.Rd

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/RC_libtcc.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <R_ext/Parse.h>
1313
#include <stdint.h>
1414
#include <inttypes.h>
15+
#include <limits.h>
1516
#include <math.h>
1617
#include <stdlib.h>
1718
#include <string.h>
@@ -46,6 +47,7 @@ SEXP RC_libtcc_compile_string(SEXP ext, SEXP code);
4647
SEXP RC_libtcc_add_symbol(SEXP ext, SEXP name, SEXP addr);
4748
SEXP RC_libtcc_relocate(SEXP ext);
4849
SEXP RC_libtcc_get_symbol(SEXP ext, SEXP name);
50+
SEXP RC_libtcc_list_symbols(SEXP ext);
4951
SEXP RC_libtcc_call_symbol(SEXP ext, SEXP name, SEXP ret_type);
5052
SEXP RC_libtcc_ptr_valid(SEXP ptr);
5153
SEXP RC_libtcc_output_file(SEXP ext, SEXP filename);
@@ -553,6 +555,87 @@ SEXP RC_libtcc_get_symbol(SEXP ext, SEXP name) {
553555
return ptr;
554556
}
555557

558+
typedef struct {
559+
R_xlen_t count;
560+
} RC_tcc_symbol_count_t;
561+
562+
typedef struct {
563+
SEXP names;
564+
SEXP addresses;
565+
R_xlen_t index;
566+
} RC_tcc_symbol_collect_t;
567+
568+
static void RC_libtcc_count_symbol_cb(void *ctx, const char *name, const void *val) {
569+
(void) name;
570+
(void) val;
571+
RC_tcc_symbol_count_t *counter = (RC_tcc_symbol_count_t *) ctx;
572+
counter->count++;
573+
}
574+
575+
static void RC_libtcc_collect_symbol_cb(void *ctx, const char *name, const void *val) {
576+
RC_tcc_symbol_collect_t *collect = (RC_tcc_symbol_collect_t *) ctx;
577+
R_xlen_t i = collect->index++;
578+
if (i >= XLENGTH(collect->names)) {
579+
return;
580+
}
581+
582+
char addr_buf[2 + (sizeof(uintptr_t) * 2) + 1];
583+
if (val == NULL) {
584+
snprintf(addr_buf, sizeof(addr_buf), "0x0");
585+
} else {
586+
snprintf(addr_buf, sizeof(addr_buf), "0x%" PRIxPTR, (uintptr_t) val);
587+
}
588+
589+
SET_STRING_ELT(
590+
collect->names,
591+
i,
592+
Rf_mkCharCE(name == NULL ? "" : name, CE_UTF8)
593+
);
594+
SET_STRING_ELT(collect->addresses, i, Rf_mkChar(addr_buf));
595+
}
596+
597+
/**
598+
* List global symbols known to a TCC state.
599+
* Ownership: none.
600+
* Allocation: result data frame only.
601+
* Protection: PROTECT(6), UNPROTECT(6).
602+
*/
603+
SEXP RC_libtcc_list_symbols(SEXP ext) {
604+
TCCState *s = RC_tcc_state(ext);
605+
606+
RC_tcc_symbol_count_t counter = {0};
607+
tcc_list_symbols(s, &counter, RC_libtcc_count_symbol_cb);
608+
if (counter.count > INT_MAX) {
609+
Rf_error("too many TCC symbols to return as a data frame");
610+
}
611+
612+
SEXP names_vec = PROTECT(Rf_allocVector(STRSXP, counter.count));
613+
SEXP addresses_vec = PROTECT(Rf_allocVector(STRSXP, counter.count));
614+
615+
RC_tcc_symbol_collect_t collect = {names_vec, addresses_vec, 0};
616+
tcc_list_symbols(s, &collect, RC_libtcc_collect_symbol_cb);
617+
618+
SEXP out = PROTECT(Rf_allocVector(VECSXP, 2));
619+
SET_VECTOR_ELT(out, 0, names_vec);
620+
SET_VECTOR_ELT(out, 1, addresses_vec);
621+
622+
SEXP out_names = PROTECT(Rf_allocVector(STRSXP, 2));
623+
SET_STRING_ELT(out_names, 0, Rf_mkChar("name"));
624+
SET_STRING_ELT(out_names, 1, Rf_mkChar("address"));
625+
Rf_setAttrib(out, R_NamesSymbol, out_names);
626+
627+
SEXP row_names = PROTECT(Rf_allocVector(INTSXP, 2));
628+
INTEGER(row_names)[0] = NA_INTEGER;
629+
INTEGER(row_names)[1] = -(int) counter.count;
630+
Rf_setAttrib(out, R_RowNamesSymbol, row_names);
631+
632+
SEXP class_str = PROTECT(Rf_mkString("data.frame"));
633+
Rf_setAttrib(out, R_ClassSymbol, class_str);
634+
635+
UNPROTECT(6);
636+
return out;
637+
}
638+
556639
/**
557640
* Call a zero-argument symbol, casting to the requested return type.
558641
* Ownership: none.

src/init.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ SEXP RC_libtcc_add_host_symbols(SEXP ext);
2424
SEXP RC_libtcc_relocate(SEXP ext);
2525
SEXP RC_libtcc_call_symbol(SEXP ext, SEXP name, SEXP ret_type);
2626
SEXP RC_libtcc_get_symbol(SEXP ext, SEXP name);
27+
SEXP RC_libtcc_list_symbols(SEXP ext);
2728

2829
SEXP RC_libtcc_ptr_valid(SEXP ptr);
2930
SEXP RC_get_external_ptr_addr(SEXP ext);
@@ -96,6 +97,7 @@ static const R_CallMethodDef CallEntries[] = {
9697
{"RC_libtcc_relocate", (DL_FUNC) &RC_libtcc_relocate, 1},
9798
{"RC_libtcc_call_symbol", (DL_FUNC) &RC_libtcc_call_symbol, 3},
9899
{"RC_libtcc_get_symbol", (DL_FUNC) &RC_libtcc_get_symbol, 2},
100+
{"RC_libtcc_list_symbols", (DL_FUNC) &RC_libtcc_list_symbols, 1},
99101

100102
{"RC_libtcc_ptr_valid", (DL_FUNC) &RC_libtcc_ptr_valid, 1},
101103
{"RC_get_external_ptr_addr", (DL_FUNC) &RC_get_external_ptr_addr, 1},

0 commit comments

Comments
 (0)