commit 8595e20e2ca09d579b4937910c720aabb49412be Author: Denis Tereshkin Date: Sun May 18 20:33:28 2025 +0700 Initial commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2e69d83 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.10) +project(ftracetool C) + +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED ON) + +find_package(PkgConfig) +pkg_search_module(LIBELF REQUIRED libelf) +pkg_search_module(CHIBI_SCHEME REQUIRED chibi-scheme) + +if(NOT CHIBI_SCHEME_FOUND) + message(FATAL_ERROR "chibi-scheme not found!") +endif() + +if(NOT LIBELF_FOUND) + message(FATAL_ERROR "libelf not found!") +endif() + +file(GLOB SOURCES "src/*.c") + +message(STATUS "Libelf include dirs: ${LIBELF_INCLUDE_DIRS}") +message(STATUS "Libelf libraries: ${LIBELF_LIBRARIES}") + +message(STATUS "Chibi-scheme include dirs: ${CHIBI_SCHEME_INCLUDE_DIRS}") +message(STATUS "Chibi-scheme libraries: ${CHIBI_SCHEME_LIBRARIES}") + +add_executable(ftracetool ${SOURCES}) + +target_include_directories(ftracetool PRIVATE ${LIBELF_INCLUDE_DIRS} ${CHIBI_SCHEME_INCLUDE_DIRS}) +target_link_libraries(ftracetool PRIVATE ${LIBELF_LIBRARIES} ${CHIBI_SCHEME_LIBRARIES}) + diff --git a/src/ftracetool.c b/src/ftracetool.c new file mode 100644 index 0000000..964e2b3 --- /dev/null +++ b/src/ftracetool.c @@ -0,0 +1,331 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "version.h" + +#define CLEANUP(f) __attribute__((cleanup(f))) + +static void show_version() +{ + printf("ftracetool v%s\n", VERSION_STRING); +} + +static void show_usage() +{ + printf("ftracetool v%s\n \ + -s Show symbols in elf file\n \ + -v Show version\n \ + -h Show help\n", + VERSION_STRING); +} + +static void close_fd_ptr(const int *const fd) +{ + close(*fd); +} + +unsigned long lookup_symbol_in_file(const char *const filename, + const char *const symbol) +{ + Elf *elf = NULL; + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + GElf_Shdr shdr = {}; + size_t strtabndx = 0; + + int const fd CLEANUP(close_fd_ptr) = open(filename, O_RDONLY); + if (fd == -1) + { + perror("open() failed"); + return EXIT_FAILURE; + } + (void)elf_version(EV_CURRENT); + + if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + { + fprintf(stderr, "elf_begin() failed: %s\n", elf_errmsg(elf_errno())); + return EXIT_FAILURE; + } + + while ((scn = elf_nextscn(elf, scn)) != NULL) + { + if (gelf_getshdr(scn, &shdr) != &shdr) + { + fprintf( + stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(elf_errno())); + continue; + } + + if (shdr.sh_type != SHT_SYMTAB && shdr.sh_type != SHT_DYNSYM) + { + continue; + } + + strtabndx = shdr.sh_link; + Elf_Scn *const strtab_scn = elf_getscn(elf, strtabndx); + if (!strtab_scn) + { + fprintf( + stderr, "elf_getscn() failed %s\n", elf_errmsg(elf_errno())); + continue; + } + + Elf_Data *const strtab_data = elf_getdata(strtab_scn, NULL); + if (!strtab_data || !strtab_data->d_buf) + { + fprintf( + stderr, "elf_getdata() failed %s\n", elf_errmsg(elf_errno())); + continue; + } + + data = elf_getdata(scn, NULL); + if (!data) + { + continue; + } + + size_t const count = (shdr.sh_size / shdr.sh_entsize); + for (size_t i = 0; i < count; i++) + { + GElf_Sym sym = {}; + if (gelf_getsym(data, i, &sym) != &sym) + { + continue; + } + + if (sym.st_name == 0 || sym.st_value == 0) + { + continue; + } + + const char *name = (const char *)strtab_data->d_buf + sym.st_name; + if (strcmp(name, symbol) == 0) + { + return sym.st_value; + } + } + } + elf_end(elf); + + return -1; +} + +static int do_show_symbols_in_file(const char *const filename) +{ + Elf *elf = NULL; + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + GElf_Shdr shdr = {}; + size_t strtabndx = 0; + + int const fd CLEANUP(close_fd_ptr) = open(filename, O_RDONLY); + if (fd == -1) + { + perror("open() failed"); + return EXIT_FAILURE; + } + (void)elf_version(EV_CURRENT); + + if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + { + fprintf(stderr, "elf_begin() failed: %s\n", elf_errmsg(elf_errno())); + return EXIT_FAILURE; + } + + while ((scn = elf_nextscn(elf, scn)) != NULL) + { + if (gelf_getshdr(scn, &shdr) != &shdr) + { + fprintf( + stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(elf_errno())); + continue; + } + + if (shdr.sh_type != SHT_SYMTAB && shdr.sh_type != SHT_DYNSYM) + { + continue; + } + + strtabndx = shdr.sh_link; + Elf_Scn *const strtab_scn = elf_getscn(elf, strtabndx); + if (!strtab_scn) + { + fprintf( + stderr, "elf_getscn() failed %s\n", elf_errmsg(elf_errno())); + continue; + } + + Elf_Data *const strtab_data = elf_getdata(strtab_scn, NULL); + if (!strtab_data || !strtab_data->d_buf) + { + fprintf( + stderr, "elf_getdata() failed %s\n", elf_errmsg(elf_errno())); + continue; + } + + data = elf_getdata(scn, NULL); + if (!data) + { + continue; + } + + size_t const count = (shdr.sh_size / shdr.sh_entsize); + for (size_t i = 0; i < count; i++) + { + GElf_Sym sym = {}; + if (gelf_getsym(data, i, &sym) != &sym) + { + continue; + } + + if (sym.st_name == 0 || sym.st_value == 0) + { + continue; + } + + const char *name = (const char *)strtab_data->d_buf + sym.st_name; + printf("0x%08lx %s\n", (unsigned long)sym.st_value, name); + } + } + elf_end(elf); + + return EXIT_SUCCESS; +} + +static sexp sexp_lookup_symbol_in_file_stub( + sexp ctx, sexp self, sexp_sint_t n, sexp arg0, sexp arg1) +{ + sexp res; + if (!sexp_stringp(arg0)) + return sexp_type_exception(ctx, self, SEXP_STRING, arg0); + if (!sexp_stringp(arg1)) + return sexp_type_exception(ctx, self, SEXP_STRING, arg1); + res = sexp_make_unsigned_integer( + ctx, + lookup_symbol_in_file(sexp_string_data(arg0), sexp_string_data(arg1))); + return res; +} + +static int register_ftracetool_api(sexp ctx) +{ + sexp_gc_var1(op); + sexp_gc_preserve1(ctx, op); + op = sexp_define_foreign(ctx, + sexp_context_env(ctx), + "lookup-symbol-in-file", + 2, + sexp_lookup_symbol_in_file_stub); + if (sexp_opcodep(op)) + { + sexp_opcode_return_type(op) = sexp_make_fixnum(SEXP_FIXNUM); + sexp_opcode_arg1_type(op) = sexp_make_fixnum(SEXP_STRING); + sexp_opcode_arg2_type(op) = sexp_make_fixnum(SEXP_STRING); + } + sexp_gc_release3(ctx); + + return 0; +} + +static int do_execute_script(const char *const filename) +{ + int ret = EXIT_SUCCESS; + sexp ctx; + sexp scheme_ret; + sexp_scheme_init(); + ctx = sexp_make_eval_context(NULL, NULL, NULL, 0, 0); + sexp_load_standard_env(ctx, NULL, SEXP_SEVEN); + sexp_load_standard_ports(ctx, NULL, stdin, stdout, stderr, 1); + + sexp_gc_var1(filename_obj); + + if (register_ftracetool_api(ctx) != 0) + { + fprintf(stderr, "Unable to register ftracetool API\n"); + return EXIT_FAILURE; + } + + filename_obj = sexp_c_string(ctx, filename, -1); + scheme_ret = sexp_load(ctx, filename_obj, NULL); + if (sexp_exceptionp(scheme_ret)) + { + fprintf(stderr, "Exception occured while executing %s:\n", filename); + sexp_print_exception(ctx, scheme_ret, sexp_current_output_port(ctx)); + ret = EXIT_FAILURE; + } + + sexp_gc_release1(filename_obj); + + sexp_destroy_context(ctx); + + return ret; +} + +int main(int argc, char **argv) +{ + int opt = 0; + char filename[NAME_MAX] = {}; + bool execute_script = false; + bool show_symbols_in_file = false; + while ((opt = getopt(argc, argv, "vhs:e:")) != -1) + { + switch (opt) + { + case 'v': + { + show_version(); + return 0; + } + case 'h': + { + show_usage(); + return 0; + } + case 'e': + { + strncpy(filename, optarg, NAME_MAX - 1); + execute_script = true; + break; + } + case 's': + { + strncpy(filename, optarg, NAME_MAX - 1); + show_symbols_in_file = true; + break; + } + default: + { + show_usage(); + return EXIT_FAILURE; + } + } + } + + if (execute_script && show_symbols_in_file) + { + fprintf(stderr, "-e and -s are mutually exclusive\n"); + exit(EXIT_FAILURE); + } + + if (show_symbols_in_file) + { + return do_show_symbols_in_file(filename); + } + else if (execute_script) + { + return do_execute_script(filename); + } + + return 0; +} diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..01b7267 --- /dev/null +++ b/src/version.h @@ -0,0 +1,6 @@ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_STRING "0.1" + +#endif