commit
8595e20e2c
3 changed files with 368 additions and 0 deletions
@ -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}) |
||||||
|
|
||||||
@ -0,0 +1,331 @@ |
|||||||
|
|
||||||
|
#include <chibi/sexp.h> |
||||||
|
#include <elf.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <gelf.h> |
||||||
|
#include <libelf.h> |
||||||
|
#include <limits.h> |
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#include <chibi/eval.h> |
||||||
|
|
||||||
|
#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; |
||||||
|
} |
||||||
Loading…
Reference in new issue