Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  elf.c   Sprache: C

 
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <libelf.h>
#include <gelf.h>
#include <fcntl.h>
#include <linux/kernel.h>

#include "libbpf_internal.h"
#include "str_error.h"

/* A SHT_GNU_versym section holds 16-bit words. This bit is set if
 * the symbol is hidden and can only be seen when referenced using an
 * explicit version number. This is a GNU extension.
 */

#define VERSYM_HIDDEN 0x8000

/* This is the mask for the rest of the data in a word read from a
 * SHT_GNU_versym section.
 */

#define VERSYM_VERSION 0x7fff

int elf_open(const char *binary_path, struct elf_fd *elf_fd)
{
 int fd, ret;
 Elf *elf;

 elf_fd->elf = NULL;
 elf_fd->fd = -1;

 if (elf_version(EV_CURRENT) == EV_NONE) {
  pr_warn("elf: failed to init libelf for %s\n", binary_path);
  return -LIBBPF_ERRNO__LIBELF;
 }
 fd = open(binary_path, O_RDONLY | O_CLOEXEC);
 if (fd < 0) {
  ret = -errno;
  pr_warn("elf: failed to open %s: %s\n", binary_path, errstr(ret));
  return ret;
 }
 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
 if (!elf) {
  pr_warn("elf: could not read elf from %s: %s\n", binary_path, elf_errmsg(-1));
  close(fd);
  return -LIBBPF_ERRNO__FORMAT;
 }
 elf_fd->fd = fd;
 elf_fd->elf = elf;
 return 0;
}

void elf_close(struct elf_fd *elf_fd)
{
 if (!elf_fd)
  return;
 elf_end(elf_fd->elf);
 close(elf_fd->fd);
}

/* Return next ELF section of sh_type after scn, or first of that type if scn is NULL. */
static Elf_Scn *elf_find_next_scn_by_type(Elf *elf, int sh_type, Elf_Scn *scn)
{
 while ((scn = elf_nextscn(elf, scn)) != NULL) {
  GElf_Shdr sh;

  if (!gelf_getshdr(scn, &sh))
   continue;
  if (sh.sh_type == sh_type)
   return scn;
 }
 return NULL;
}

struct elf_sym {
 const char *name;
 GElf_Sym sym;
 GElf_Shdr sh;
 int ver;
 bool hidden;
};

struct elf_sym_iter {
 Elf *elf;
 Elf_Data *syms;
 Elf_Data *versyms;
 Elf_Data *verdefs;
 size_t nr_syms;
 size_t strtabidx;
 size_t verdef_strtabidx;
 size_t next_sym_idx;
 struct elf_sym sym;
 int st_type;
};

static int elf_sym_iter_new(struct elf_sym_iter *iter,
       Elf *elf, const char *binary_path,
       int sh_type, int st_type)
{
 Elf_Scn *scn = NULL;
 GElf_Ehdr ehdr;
 GElf_Shdr sh;

 memset(iter, 0, sizeof(*iter));

 if (!gelf_getehdr(elf, &ehdr)) {
  pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
  return -EINVAL;
 }

 scn = elf_find_next_scn_by_type(elf, sh_type, NULL);
 if (!scn) {
  pr_debug("elf: failed to find symbol table ELF sections in '%s'\n",
    binary_path);
  return -ENOENT;
 }

 if (!gelf_getshdr(scn, &sh))
  return -EINVAL;

 iter->strtabidx = sh.sh_link;
 iter->syms = elf_getdata(scn, 0);
 if (!iter->syms) {
  pr_warn("elf: failed to get symbols for symtab section in '%s': %s\n",
   binary_path, elf_errmsg(-1));
  return -EINVAL;
 }
 iter->nr_syms = iter->syms->d_size / sh.sh_entsize;
 iter->elf = elf;
 iter->st_type = st_type;

 /* Version symbol table is meaningful to dynsym only */
 if (sh_type != SHT_DYNSYM)
  return 0;

 scn = elf_find_next_scn_by_type(elf, SHT_GNU_versym, NULL);
 if (!scn)
  return 0;
 iter->versyms = elf_getdata(scn, 0);

 scn = elf_find_next_scn_by_type(elf, SHT_GNU_verdef, NULL);
 if (!scn)
  return 0;

 iter->verdefs = elf_getdata(scn, 0);
 if (!iter->verdefs || !gelf_getshdr(scn, &sh)) {
  pr_warn("elf: failed to get verdef ELF section in '%s'\n", binary_path);
  return -EINVAL;
 }
 iter->verdef_strtabidx = sh.sh_link;

 return 0;
}

static struct elf_sym *elf_sym_iter_next(struct elf_sym_iter *iter)
{
 struct elf_sym *ret = &iter->sym;
 GElf_Sym *sym = &ret->sym;
 const char *name = NULL;
 GElf_Versym versym;
 Elf_Scn *sym_scn;
 size_t idx;

 for (idx = iter->next_sym_idx; idx < iter->nr_syms; idx++) {
  if (!gelf_getsym(iter->syms, idx, sym))
   continue;
  if (GELF_ST_TYPE(sym->st_info) != iter->st_type)
   continue;
  name = elf_strptr(iter->elf, iter->strtabidx, sym->st_name);
  if (!name)
   continue;
  sym_scn = elf_getscn(iter->elf, sym->st_shndx);
  if (!sym_scn)
   continue;
  if (!gelf_getshdr(sym_scn, &ret->sh))
   continue;

  iter->next_sym_idx = idx + 1;
  ret->name = name;
  ret->ver = 0;
  ret->hidden = false;

  if (iter->versyms) {
   if (!gelf_getversym(iter->versyms, idx, &versym))
    continue;
   ret->ver = versym & VERSYM_VERSION;
   ret->hidden = versym & VERSYM_HIDDEN;
  }
  return ret;
 }

 return NULL;
}

static const char *elf_get_vername(struct elf_sym_iter *iter, int ver)
{
 GElf_Verdaux verdaux;
 GElf_Verdef verdef;
 int offset;

 if (!iter->verdefs)
  return NULL;

 offset = 0;
 while (gelf_getverdef(iter->verdefs, offset, &verdef)) {
  if (verdef.vd_ndx != ver) {
   if (!verdef.vd_next)
    break;

   offset += verdef.vd_next;
   continue;
  }

  if (!gelf_getverdaux(iter->verdefs, offset + verdef.vd_aux, &verdaux))
   break;

  return elf_strptr(iter->elf, iter->verdef_strtabidx, verdaux.vda_name);

 }
 return NULL;
}

static bool symbol_match(struct elf_sym_iter *iter, int sh_type, struct elf_sym *sym,
    const char *name, size_t name_len, const char *lib_ver)
{
 const char *ver_name;

 /* Symbols are in forms of func, func@LIB_VER or func@@LIB_VER
 * make sure the func part matches the user specified name
 */

 if (strncmp(sym->name, name, name_len) != 0)
  return false;

 /* ...but we don't want a search for "foo" to match 'foo2" also, so any
 * additional characters in sname should be of the form "@@LIB".
 */

 if (sym->name[name_len] != '\0' && sym->name[name_len] != '@')
  return false;

 /* If user does not specify symbol version, then we got a match */
 if (!lib_ver)
  return true;

 /* If user specifies symbol version, for dynamic symbols,
 * get version name from ELF verdef section for comparison.
 */

 if (sh_type == SHT_DYNSYM) {
  ver_name = elf_get_vername(iter, sym->ver);
  if (!ver_name)
   return false;
  return strcmp(ver_name, lib_ver) == 0;
 }

 /* For normal symbols, it is already in form of func@LIB_VER */
 return strcmp(sym->name, name) == 0;
}

/* Transform symbol's virtual address (absolute for binaries and relative
 * for shared libs) into file offset, which is what kernel is expecting
 * for uprobe/uretprobe attachment.
 * See Documentation/trace/uprobetracer.rst for more details. This is done
 * by looking up symbol's containing section's header and using iter's virtual
 * address (sh_addr) and corresponding file offset (sh_offset) to transform
 * sym.st_value (virtual address) into desired final file offset.
 */

static unsigned long elf_sym_offset(struct elf_sym *sym)
{
 return sym->sym.st_value - sym->sh.sh_addr + sym->sh.sh_offset;
}

/* Find offset of function name in the provided ELF object. "binary_path" is
 * the path to the ELF binary represented by "elf", and only used for error
 * reporting matters. "name" matches symbol name or name@@LIB for library
 * functions.
 */

long elf_find_func_offset(Elf *elf, const char *binary_path, const char *name)
{
 int i, sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
 const char *at_symbol, *lib_ver;
 bool is_shared_lib;
 long ret = -ENOENT;
 size_t name_len;
 GElf_Ehdr ehdr;

 if (!gelf_getehdr(elf, &ehdr)) {
  pr_warn("elf: failed to get ehdr from %s: %s\n", binary_path, elf_errmsg(-1));
  ret = -LIBBPF_ERRNO__FORMAT;
  goto out;
 }
 /* for shared lib case, we do not need to calculate relative offset */
 is_shared_lib = ehdr.e_type == ET_DYN;

 /* Does name specify "@@LIB_VER" or "@LIB_VER" ? */
 at_symbol = strchr(name, '@');
 if (at_symbol) {
  name_len = at_symbol - name;
  /* skip second @ if it's @@LIB_VER case */
  if (at_symbol[1] == '@')
   at_symbol++;
  lib_ver = at_symbol + 1;
 } else {
  name_len = strlen(name);
  lib_ver = NULL;
 }

 /* Search SHT_DYNSYM, SHT_SYMTAB for symbol. This search order is used because if
 * a binary is stripped, it may only have SHT_DYNSYM, and a fully-statically
 * linked binary may not have SHT_DYMSYM, so absence of a section should not be
 * reported as a warning/error.
 */

 for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
  struct elf_sym_iter iter;
  struct elf_sym *sym;
  int last_bind = -1;
  int cur_bind;

  ret = elf_sym_iter_new(&iter, elf, binary_path, sh_types[i], STT_FUNC);
  if (ret == -ENOENT)
   continue;
  if (ret)
   goto out;

  while ((sym = elf_sym_iter_next(&iter))) {
   if (!symbol_match(&iter, sh_types[i], sym, name, name_len, lib_ver))
    continue;

   cur_bind = GELF_ST_BIND(sym->sym.st_info);

   if (ret > 0) {
    /* handle multiple matches */
    if (elf_sym_offset(sym) == ret) {
     /* same offset, no problem */
     continue;
    } else if (last_bind != STB_WEAK && cur_bind != STB_WEAK) {
     /* Only accept one non-weak bind. */
     pr_warn("elf: ambiguous match for '%s', '%s' in '%s'\n",
      sym->name, name, binary_path);
     ret = -LIBBPF_ERRNO__FORMAT;
     goto out;
    } else if (cur_bind == STB_WEAK) {
     /* already have a non-weak bind, and
 * this is a weak bind, so ignore.
 */

     continue;
    }
   }

   ret = elf_sym_offset(sym);
   last_bind = cur_bind;
  }
  if (ret > 0)
   break;
 }

 if (ret > 0) {
  pr_debug("elf: symbol address match for '%s' in '%s': 0x%lx\n", name, binary_path,
    ret);
 } else {
  if (ret == 0) {
   pr_warn("elf: '%s' is 0 in symtab for '%s': %s\n", name, binary_path,
    is_shared_lib ? "should not be 0 in a shared library" :
      "try using shared library path instead");
   ret = -ENOENT;
  } else {
   pr_warn("elf: failed to find symbol '%s' in '%s'\n", name, binary_path);
  }
 }
out:
 return ret;
}

/* Find offset of function name in ELF object specified by path. "name" matches
 * symbol name or name@@LIB for library functions.
 */

long elf_find_func_offset_from_file(const char *binary_path, const char *name)
{
 struct elf_fd elf_fd;
 long ret = -ENOENT;

 ret = elf_open(binary_path, &elf_fd);
 if (ret)
  return ret;
 ret = elf_find_func_offset(elf_fd.elf, binary_path, name);
 elf_close(&elf_fd);
 return ret;
}

struct symbol {
 const char *name;
 int bind;
 int idx;
};

static int symbol_cmp(const void *a, const void *b)
{
 const struct symbol *sym_a = a;
 const struct symbol *sym_b = b;

 return strcmp(sym_a->name, sym_b->name);
}

/*
 * Return offsets in @poffsets for symbols specified in @syms array argument.
 * On success returns 0 and offsets are returned in allocated array with @cnt
 * size, that needs to be released by the caller.
 */

int elf_resolve_syms_offsets(const char *binary_path, int cnt,
        const char **syms, unsigned long **poffsets,
        int st_type)
{
 int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
 int err = 0, i, cnt_done = 0;
 unsigned long *offsets;
 struct symbol *symbols;
 struct elf_fd elf_fd;

 err = elf_open(binary_path, &elf_fd);
 if (err)
  return err;

 offsets = calloc(cnt, sizeof(*offsets));
 symbols = calloc(cnt, sizeof(*symbols));

 if (!offsets || !symbols) {
  err = -ENOMEM;
  goto out;
 }

 for (i = 0; i < cnt; i++) {
  symbols[i].name = syms[i];
  symbols[i].idx = i;
 }

 qsort(symbols, cnt, sizeof(*symbols), symbol_cmp);

 for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
  struct elf_sym_iter iter;
  struct elf_sym *sym;

  err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], st_type);
  if (err == -ENOENT)
   continue;
  if (err)
   goto out;

  while ((sym = elf_sym_iter_next(&iter))) {
   unsigned long sym_offset = elf_sym_offset(sym);
   int bind = GELF_ST_BIND(sym->sym.st_info);
   struct symbol *found, tmp = {
    .name = sym->name,
   };
   unsigned long *offset;

   found = bsearch(&tmp, symbols, cnt, sizeof(*symbols), symbol_cmp);
   if (!found)
    continue;

   offset = &offsets[found->idx];
   if (*offset > 0) {
    /* same offset, no problem */
    if (*offset == sym_offset)
     continue;
    /* handle multiple matches */
    if (found->bind != STB_WEAK && bind != STB_WEAK) {
     /* Only accept one non-weak bind. */
     pr_warn("elf: ambiguous match found '%s@%lu' in '%s' previous offset %lu\n",
      sym->name, sym_offset, binary_path, *offset);
     err = -ESRCH;
     goto out;
    } else if (bind == STB_WEAK) {
     /* already have a non-weak bind, and
 * this is a weak bind, so ignore.
 */

     continue;
    }
   } else {
    cnt_done++;
   }
   *offset = sym_offset;
   found->bind = bind;
  }
 }

 if (cnt != cnt_done) {
  err = -ENOENT;
  goto out;
 }

 *poffsets = offsets;

out:
 free(symbols);
 if (err)
  free(offsets);
 elf_close(&elf_fd);
 return err;
}

/*
 * Return offsets in @poffsets for symbols specified by @pattern argument.
 * On success returns 0 and offsets are returned in allocated @poffsets
 * array with the @pctn size, that needs to be released by the caller.
 */

int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern,
    unsigned long **poffsets, size_t *pcnt)
{
 int sh_types[2] = { SHT_SYMTAB, SHT_DYNSYM };
 unsigned long *offsets = NULL;
 size_t cap = 0, cnt = 0;
 struct elf_fd elf_fd;
 int err = 0, i;

 err = elf_open(binary_path, &elf_fd);
 if (err)
  return err;

 for (i = 0; i < ARRAY_SIZE(sh_types); i++) {
  struct elf_sym_iter iter;
  struct elf_sym *sym;

  err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
  if (err == -ENOENT)
   continue;
  if (err)
   goto out;

  while ((sym = elf_sym_iter_next(&iter))) {
   if (!glob_match(sym->name, pattern))
    continue;

   err = libbpf_ensure_mem((void **) &offsets, &cap, sizeof(*offsets),
      cnt + 1);
   if (err)
    goto out;

   offsets[cnt++] = elf_sym_offset(sym);
  }

  /* If we found anything in the first symbol section,
 * do not search others to avoid duplicates.
 */

  if (cnt)
   break;
 }

 if (cnt) {
  *poffsets = offsets;
  *pcnt = cnt;
 } else {
  err = -ENOENT;
 }

out:
 if (err)
  free(offsets);
 elf_close(&elf_fd);
 return err;
}

Messung V0.5
C=98 H=92 G=94

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge