userspace/kp_symbol.c - ktap

Global variables defined

Functions defined

Macros defined

Source code

  1. /*
  2. * symbol.c
  3. *
  4. * This file is part of ktap by Jovi Zhangwei.
  5. *
  6. * Copyright (C) 2013 Azat Khuzhin <a3at.mail@gmail.com>.
  7. *
  8. * ktap is free software; you can redistribute it and/or modify it
  9. * under the terms and conditions of the GNU General Public License,
  10. * version 2, as published by the Free Software Foundation.
  11. *
  12. * ktap is distributed in the hope it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15. * more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along with
  18. * this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  20. */

  21. #include <stdio.h>
  22. #include <stdlib.h>

  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #include <unistd.h>
  26. #include <fcntl.h>
  27. #include <string.h>
  28. #include <linux/limits.h>

  29. #include <libelf.h>

  30. #include "../include/ktap_types.h"
  31. #include "kp_symbol.h"

  32. const char *dbg_link_name = ".gnu_debuglink";
  33. const char *dbg_bin_dir = "/usr/lib/debug";

  34. static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
  35.                     GElf_Shdr *shp, const char *name)
  36. {
  37.     Elf_Scn *scn = NULL;

  38.     /* Elf is corrupted/truncated, avoid calling elf_strptr. */
  39.     if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
  40.         return NULL;

  41.     while ((scn = elf_nextscn(elf, scn)) != NULL) {
  42.         char *str;

  43.         gelf_getshdr(scn, shp);
  44.         str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
  45.         if (!strcmp(name, str))
  46.             break;
  47.     }

  48.     return scn;
  49. }

  50. /**
  51. * @return v_addr of "LOAD" program header, that have zero offset.
  52. */
  53. static int find_load_address(Elf *elf, vaddr_t *load_address)
  54. {
  55.     GElf_Phdr phdr;
  56.     size_t i, phdrnum;

  57.     if (elf_getphdrnum(elf, &phdrnum))
  58.         return -1;

  59.     for (i = 0; i < phdrnum; i++) {
  60.         if (gelf_getphdr(elf, i, &phdr) == NULL)
  61.             return -1;

  62.         if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)
  63.             continue;

  64.         *load_address = phdr.p_vaddr;
  65.         return 0;
  66.     }

  67.     /* cannot found load address */
  68.     return -1;
  69. }

  70. static size_t elf_symbols(GElf_Shdr shdr)
  71. {
  72.     return shdr.sh_size / shdr.sh_entsize;
  73. }

  74. static int dso_symbols(Elf *elf, symbol_actor actor, void *arg)
  75. {
  76.     Elf_Data *elf_data = NULL;
  77.     Elf_Scn *scn = NULL;
  78.     GElf_Sym sym;
  79.     GElf_Shdr shdr;
  80.     int symbols_count = 0;
  81.     vaddr_t load_address;

  82.     if (find_load_address(elf, &load_address))
  83.         return -1;

  84.     while ((scn = elf_nextscn(elf, scn))) {
  85.         int i;

  86.         gelf_getshdr(scn, &shdr);

  87.         if (shdr.sh_type != SHT_SYMTAB)
  88.             continue;

  89.         elf_data = elf_getdata(scn, elf_data);

  90.         for (i = 0; i < elf_symbols(shdr); i++) {
  91.             char *name;
  92.             vaddr_t addr;
  93.             int ret;

  94.             gelf_getsym(elf_data, i, &sym);

  95.             if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
  96.                 continue;

  97.             name = elf_strptr(elf, shdr.sh_link, sym.st_name);
  98.             addr = sym.st_value - load_address;

  99.             ret = actor(name, addr, arg);
  100.             if (ret)
  101.                 return ret;

  102.             ++symbols_count;
  103.         }
  104.     }

  105.     return symbols_count;
  106. }

  107. #define SDT_NOTE_TYPE 3
  108. #define SDT_NOTE_COUNT 3
  109. #define SDT_NOTE_SCN ".note.stapsdt"
  110. #define SDT_NOTE_NAME "stapsdt"

  111. static vaddr_t sdt_note_addr(Elf *elf, const char *data, size_t len, int type)
  112. {
  113.     vaddr_t vaddr;

  114.     /*
  115.      * Three addresses need to be obtained :
  116.      * Marker location, address of base section and semaphore location
  117.      */
  118.     union {
  119.         Elf64_Addr a64[3];
  120.         Elf32_Addr a32[3];
  121.     } buf;

  122.     /*
  123.      * dst and src are required for translation from file to memory
  124.      * representation
  125.      */
  126.     Elf_Data dst = {
  127.         .d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
  128.         .d_size = gelf_fsize(elf, ELF_T_ADDR, SDT_NOTE_COUNT, EV_CURRENT),
  129.         .d_off = 0, .d_align = 0
  130.     };

  131.     Elf_Data src = {
  132.         .d_buf = (void *) data, .d_type = ELF_T_ADDR,
  133.         .d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
  134.         .d_align = 0
  135.     };

  136.     /* Check the type of each of the notes */
  137.     if (type != SDT_NOTE_TYPE)
  138.         return 0;

  139.     if (len < dst.d_size + SDT_NOTE_COUNT)
  140.         return 0;

  141.     /* Translation from file representation to memory representation */
  142.     if (gelf_xlatetom(elf, &dst, &src,
  143.               elf_getident(elf, NULL)[EI_DATA]) == NULL)
  144.         return 0; /* TODO */

  145.     memcpy(&vaddr, &buf, sizeof(vaddr));

  146.     return vaddr;
  147. }

  148. static const char *sdt_note_name(Elf *elf, GElf_Nhdr *nhdr, const char *data)
  149. {
  150.     const char *provider = data + gelf_fsize(elf,
  151.         ELF_T_ADDR, SDT_NOTE_COUNT, EV_CURRENT);
  152.     const char *name = (const char *)memchr(provider, '\0',
  153.         data + nhdr->n_descsz - provider);

  154.     if (name++ == NULL)
  155.         return NULL;

  156.     return name;
  157. }

  158. static const char *sdt_note_data(const Elf_Data *data, size_t off)
  159. {
  160.     return ((data->d_buf) + off);
  161. }

  162. static int dso_sdt_notes(Elf *elf, symbol_actor actor, void *arg)
  163. {
  164.     GElf_Ehdr ehdr;
  165.     Elf_Scn *scn = NULL;
  166.     Elf_Data *data;
  167.     GElf_Shdr shdr;
  168.     size_t shstrndx;
  169.     size_t next;
  170.     GElf_Nhdr nhdr;
  171.     size_t name_off, desc_off, offset;
  172.     vaddr_t vaddr = 0;
  173.     int symbols_count = 0;

  174.     if (gelf_getehdr(elf, &ehdr) == NULL)
  175.         return 0;
  176.     if (elf_getshdrstrndx(elf, &shstrndx) != 0)
  177.         return 0;

  178.     /*
  179.      * Look for section type = SHT_NOTE, flags = no SHF_ALLOC
  180.      * and name = .note.stapsdt
  181.      */
  182.     scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN);
  183.     if (!scn)
  184.         return 0;
  185.     if (!(shdr.sh_type == SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC))
  186.         return 0;

  187.     data = elf_getdata(scn, NULL);

  188.     for (offset = 0;
  189.         (next = gelf_getnote(data, offset, &nhdr, &name_off, &desc_off)) > 0;
  190.         offset = next) {
  191.         const char *name;
  192.         int ret;

  193.         if (nhdr.n_namesz != sizeof(SDT_NOTE_NAME) ||
  194.             memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
  195.                 sizeof(SDT_NOTE_NAME)))
  196.             continue;

  197.         name = sdt_note_name(elf, &nhdr, sdt_note_data(data, desc_off));
  198.         if (!name)
  199.             continue;

  200.         vaddr = sdt_note_addr(elf, sdt_note_data(data, desc_off),
  201.                     nhdr.n_descsz, nhdr.n_type);
  202.         if (!vaddr)
  203.             continue;

  204.         ret = actor(name, vaddr, arg);
  205.         if (ret)
  206.             return ret;

  207.         ++symbols_count;
  208.     }

  209.     return symbols_count;
  210. }

  211. int dso_follow_debuglink(Elf *elf,
  212.              const char *orig_exec,
  213.              int type,
  214.              symbol_actor actor,
  215.              void *arg)
  216. {
  217.     GElf_Ehdr ehdr;
  218.     size_t shstrndx, orig_exec_dir_len;
  219.     GElf_Shdr shdr;
  220.     Elf_Scn *dbg_link_scn;
  221.     Elf_Data *dbg_link_scn_data;
  222.     char *dbg_link, *dbg_bin, *last_slash;
  223.     int symbols_count;

  224.     /* First try to find the .gnu_debuglink section in the binary. */
  225.     if (gelf_getehdr(elf, &ehdr) == NULL)
  226.         return 0;
  227.     if (elf_getshdrstrndx(elf, &shstrndx) != 0)
  228.         return 0;

  229.     dbg_link_scn = elf_section_by_name(elf, &ehdr, &shdr, dbg_link_name);
  230.     if (dbg_link_scn == NULL)
  231.         return 0;

  232.     /* Debug link section found, read of the content (only get the first
  233.        string, no checksum checking atm). This is debug binary file name. */
  234.     dbg_link_scn_data = elf_getdata(dbg_link_scn, NULL);
  235.     if (dbg_link_scn_data == NULL ||
  236.         dbg_link_scn_data->d_size <= 0 ||
  237.         dbg_link_scn_data->d_buf == NULL)
  238.         return 0;

  239.     /* Now compose debug executable name */
  240.     dbg_link = (char *)(dbg_link_scn_data->d_buf);
  241.     dbg_bin = malloc(strlen(dbg_bin_dir) + 1 +
  242.              strlen(orig_exec) + 1 +
  243.              strlen(dbg_link) + 1);
  244.     if (!dbg_bin)
  245.         return 0;

  246.     orig_exec_dir_len = PATH_MAX;
  247.     last_slash = strrchr(orig_exec, '/');
  248.     if (last_slash != NULL)
  249.         orig_exec_dir_len = last_slash - orig_exec;

  250.     sprintf(dbg_bin, "%s/%.*s/%s",
  251.         dbg_bin_dir, (int)orig_exec_dir_len, orig_exec, dbg_link);

  252.     /* Retry symbol seach with the debug binary */
  253.     symbols_count = parse_dso_symbols(dbg_bin, type, actor, arg);

  254.     free(dbg_bin);

  255.     return symbols_count;
  256. }

  257. int parse_dso_symbols(const char *exec, int type, symbol_actor actor, void *arg)
  258. {
  259.     int symbols_count = 0;
  260.     Elf *elf;
  261.     int fd;

  262.     if (elf_version(EV_CURRENT) == EV_NONE)
  263.         return -1;

  264.     fd = open(exec, O_RDONLY);
  265.     if (fd < 0)
  266.         return -1;

  267.     elf = elf_begin(fd, ELF_C_READ, NULL);
  268.     if (elf) {
  269.         switch (type) {
  270.         case FIND_SYMBOL:
  271.             symbols_count = dso_symbols(elf, actor, arg);
  272.             if (symbols_count != 0)
  273.                 break;
  274.             /* If no symbols found, try in the debuglink binary. */
  275.             symbols_count = dso_follow_debuglink(elf,
  276.                                  exec,
  277.                                  type,
  278.                                  actor,
  279.                                  arg);
  280.             break;
  281.         case FIND_STAPSDT_NOTE:
  282.             symbols_count = dso_sdt_notes(elf, actor, arg);
  283.             break;
  284.         }

  285.         elf_end(elf);
  286.     }

  287.     close(fd);
  288.     return symbols_count;
  289. }