Commit b8f0f092 authored by Vicent Marti's avatar Vicent Marti

cc: Add a set of ELF-related APIs based on libelf

The following APIs have been implemented inside `libbcc`, which
programatically replicate the previous usage of `binutils` tools like
`objdump` and `elfdump`.

- bcc_elf_foreach_usdt: finds all USDT probes inside a given binary
- bcc_elf_loadadddr: find the load address for a shared object
- bcc_elf_foreach_sym: lists all symbols in an ELF binary or SO
- bcc_elf_shared_obj: returns whether a given path is an ELF shared
  object, a binary, or neither
parent 0612db5f
...@@ -33,11 +33,11 @@ if (CMAKE_COMPILER_IS_GNUCC) ...@@ -33,11 +33,11 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif() endif()
endif() endif()
add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc) add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc bcc_elf.c)
set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0) set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0)
set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc) set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc)
add_library(bcc-loader-static libbpf.c perf_reader.c) add_library(bcc-loader-static libbpf.c perf_reader.c bcc_elf.c)
add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc) add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc)
set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc) set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc)
......
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <gelf.h>
#include "bcc_helpers.h"
#define NT_STAPSDT 3
static int openelf(const char *path, Elf **elf_out, int *fd_out)
{
if (elf_version(EV_CURRENT) == EV_NONE)
return -1;
*fd_out = open(path, O_RDONLY);
if (*fd_out < 0)
return -1;
*elf_out = elf_begin(*fd_out, ELF_C_READ, 0);
if (*elf_out == 0) {
close(*fd_out);
return -1;
}
return 0;
}
static const char *
parse_stapsdt_note(struct bcc_elf_usdt *probe, const char *desc, int elf_class)
{
if (elf_class == ELFCLASS32) {
probe->pc = *((uint32_t *)(desc));
probe->base_addr = *((uint32_t *)(desc + 4));
probe->semaphore = *((uint32_t *)(desc + 8));
desc = desc + 12;
} else {
probe->pc = *((uint64_t *)(desc));
probe->base_addr = *((uint64_t *)(desc + 8));
probe->semaphore = *((uint64_t *)(desc + 16));
desc = desc + 24;
}
probe->provider = desc;
desc += strlen(desc) + 1;
probe->name = desc;
desc += strlen(desc) + 1;
probe->arg_fmt = desc;
desc += strlen(desc) + 1;
return desc;
}
static int do_note_segment(
Elf_Scn *section, int elf_class,
bcc_elf_probecb callback, const char *binpath, void *payload)
{
Elf_Data *data = NULL;
while ((data = elf_getdata(section, data)) != 0) {
size_t offset = 0;
GElf_Nhdr hdr;
size_t name_off, desc_off;
while ((offset = gelf_getnote(data, offset, &hdr, &name_off, &desc_off)) != 0) {
const char *desc, *desc_end;
struct bcc_elf_usdt probe;
if (hdr.n_type != NT_STAPSDT)
continue;
if (hdr.n_namesz != 8)
continue;
if (memcmp((const char *)data->d_buf + name_off, "stapsdt", 8) != 0)
continue;
desc = (const char *)data->d_buf + desc_off;
desc_end = desc + hdr.n_descsz;
if (parse_stapsdt_note(&probe, desc, elf_class) == desc_end)
callback(binpath, &probe, payload);
}
}
return 0;
}
static int listprobes(Elf *e, bcc_elf_probecb callback, const char *binpath, void *payload)
{
Elf_Scn *section = NULL;
size_t stridx;
int elf_class = gelf_getclass(e);
if (elf_getshdrstrndx(e, &stridx) != 0)
return -1;
while ((section = elf_nextscn(e, section)) != 0) {
GElf_Shdr header;
char *name;
if (!gelf_getshdr(section, &header))
continue;
if (header.sh_type != SHT_NOTE)
continue;
name = elf_strptr(e, stridx, header.sh_name);
if (name && !strcmp(name, ".note.stapsdt")) {
if (do_note_segment(section, elf_class, callback, binpath, payload) < 0)
return -1;
}
}
return 0;
}
int bcc_elf_foreach_usdt(const char *path, bcc_elf_probecb callback, void *payload)
{
Elf *e;
int fd, res;
if (openelf(path, &e, &fd) < 0)
return -1;
res = listprobes(e, callback, path, payload);
elf_end(e);
close(fd);
return res;
}
static int list_in_scn(
Elf *e, Elf_Scn *section,
size_t stridx, size_t symsize,
bcc_elf_symcb callback, void *payload)
{
Elf_Data *data = NULL;
while ((data = elf_getdata(section, data)) != 0) {
size_t i, symcount = data->d_size / symsize;
if (data->d_size % symsize)
return -1;
for (i = 0; i < symcount; ++i) {
GElf_Sym sym;
const char *name;
if (!gelf_getsym(data, (int)i, &sym))
continue;
if ((name = elf_strptr(e, stridx, sym.st_name)) == NULL)
continue;
if (callback(name, sym.st_value, sym.st_size, sym.st_info, payload) < 0)
break;
}
}
return 0;
}
static int listsymbols(Elf *e, bcc_elf_symcb callback, void *payload)
{
Elf_Scn *section = NULL;
while ((section = elf_nextscn(e, section)) != 0) {
GElf_Shdr header;
if (!gelf_getshdr(section, &header))
continue;
if (header.sh_type != SHT_SYMTAB && header.sh_type != SHT_DYNSYM)
continue;
if (list_in_scn(e, section,
header.sh_link, header.sh_entsize, callback, payload) < 0)
return -1;
}
return 0;
}
int bcc_elf_foreach_sym(const char *path, bcc_elf_symcb callback, void *payload)
{
Elf *e;
int fd, res;
if (openelf(path, &e, &fd) < 0)
return -1;
res = listsymbols(e, callback, payload);
elf_end(e);
close(fd);
return res;
}
static int loadaddr(Elf *e, uint64_t *addr)
{
size_t phnum, i;
if (elf_getphdrnum(e, &phnum) != 0)
return -1;
for (i = 0; i < phnum; ++i) {
GElf_Phdr header;
if (!gelf_getphdr(e, (int)i, &header))
continue;
if (header.p_type != PT_LOAD)
continue;
*addr = (uint64_t)header.p_vaddr;
return 0;
}
return -1;
}
int bcc_elf_loadaddr(const char *path, uint64_t *address)
{
Elf *e;
int fd, res;
if (openelf(path, &e, &fd) < 0)
return -1;
res = loadaddr(e, address);
elf_end(e);
close(fd);
return res;
}
int bcc_elf_is_shared_obj(const char *path)
{
Elf *e;
GElf_Ehdr hdr;
int fd, res = -1;
if (openelf(path, &e, &fd) < 0)
return -1;
if (gelf_getehdr(e, &hdr))
res = (hdr.e_type == ET_DYN);
elf_end(e);
close(fd);
return res;
}
#if 0
#include <stdio.h>
int main(int argc, char *argv[])
{
uint64_t addr;
if (bcc_elf_findsym(argv[1], argv[2], -1, STT_FUNC, &addr) < 0)
return -1;
printf("%s: %p\n", argv[2], (void *)addr);
return 0;
}
#endif
#ifndef LIBBCC_ELF_H
#define LIBBCC_ELF_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
struct bcc_symbol {
const char *name;
const char *module;
uint64_t offset;
};
struct bcc_elf_usdt {
uint64_t pc;
uint64_t base_addr;
uint64_t semaphore;
const char *provider;
const char *name;
const char *arg_fmt;
};
typedef void (*bcc_elf_probecb)(const char *, const struct bcc_elf_usdt *, void *);
typedef int (*bcc_elf_symcb)(const char *, uint64_t, uint64_t, int, void *);
int bcc_elf_foreach_usdt(const char *path, bcc_elf_probecb callback, void *payload);
int bcc_elf_loadaddr(const char *path, uint64_t *address);
int bcc_elf_foreach_sym(const char *path, bcc_elf_symcb callback, void *payload);
int bcc_elf_is_shared_obj(const char *path);
typedef void (*bcc_procutils_modulecb)(const char *, uint64_t, uint64_t, void *);
typedef void (*bcc_procutils_ksymcb)(const char *, uint64_t, void *);
const char *bcc_procutils_which_so(const char *libname);
char *bcc_procutils_which(const char *binpath);
int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback, void *payload);
int bcc_procutils_each_ksym(bcc_procutils_ksymcb callback, void *payload);
int bcc_resolve_symname(const char *module, const char *symname, const uint64_t addr,
struct bcc_symbol *sym);
void *bcc_symcache_new(int pid);
int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym);
int bcc_symcache_resolve_name(void *resolver, const char *name, uint64_t *addr);
void bcc_symcache_refresh(void *resolver);
#ifdef __cplusplus
}
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment