commit
8595e20e2c
3 changed files with 368 additions and 0 deletions
@ -0,0 +1,31 @@
@@ -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 @@
@@ -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