Add memory size, build id, and debug link info to statedump and dl
authorAntoine Busque <abusque@efficios.com>
Thu, 2 Jul 2015 15:55:53 +0000 (11:55 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Sat, 29 Aug 2015 02:13:53 +0000 (22:13 -0400)
Implement a minimal ELF parser allowing to compute an executable's
in-memory size and to read its build id and debug link info, if any,
at runtime. This is performed during base address statedump, and on
dlopen when using the `liblttng-ust-dl.so` helper.

This adds a `memsz` field to `lttng_ust_statedump:soinfo` and
`lttng_ust_dl:dlopen`. Also adds two events to both providers,
`build_id` and `debug_link`, which are only traced when the
corresponding information is found in the executable file. They can be
matched with the corresponding `soinfo` or `dlopen` via the `baddr`
field shared amongst all the events. Build ID and debug link are
standard methods of identifying and retrieving debug information
corresponding to a specific version of an executable.

The fields `size` and `mtime` from the existing `soinfo` and `dlopen`
events have been removed as they provided no valuable information for
analysis, whereas build ID or debug link allow unambiguous retrieval
of the debug information, and the on-disk `size` is superseded by the
in-memory `memsz`.

Signed-off-by: Antoine Busque <abusque@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
include/lttng/ust-elf.h [new file with mode: 0644]
liblttng-ust-dl/lttng-ust-dl.c
liblttng-ust-dl/ust_dl.h
liblttng-ust/Makefile.am
liblttng-ust/lttng-ust-elf.c [new file with mode: 0644]
liblttng-ust/lttng-ust-statedump-provider.h
liblttng-ust/lttng-ust-statedump.c
liblttng-ust/lttng-ust-statedump.h

diff --git a/include/lttng/ust-elf.h b/include/lttng/ust-elf.h
new file mode 100644 (file)
index 0000000..41e3c9b
--- /dev/null
@@ -0,0 +1,228 @@
+#ifndef _LTTNG_UST_ELF_H
+#define _LTTNG_UST_ELF_H
+/*
+ * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <byteswap.h>
+#include <elf.h>
+#include <lttng/ust-endian.h>
+
+/*
+ * Determine native endianness in order to convert when reading an ELF
+ * file if there is a mismatch.
+ */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define NATIVE_ELF_ENDIANNESS ELFDATA2LSB
+#else
+#define NATIVE_ELF_ENDIANNESS ELFDATA2MSB
+#endif
+
+/*
+ * The size in bytes of the debug link CRC as contained in an ELF
+ * section.
+ */
+#define ELF_CRC_SIZE           4
+/*
+ * ELF notes are aligned on 4 bytes. ref: ELF specification version
+ * 1.1 p. 2-5.
+ */
+#define ELF_NOTE_ENTRY_ALIGN   4
+/*
+ * Within an ELF note, the `desc` field is also aligned on 4
+ * bytes. ref: ELF specification version 1.1 p. 2-5.
+ */
+#define ELF_NOTE_DESC_ALIGN    4
+
+#define bswap(x)                               \
+       do {                                    \
+               switch (sizeof(x)) {            \
+               case 8:                         \
+                       x = bswap_64(x);        \
+                       break;                  \
+               case 4:                         \
+                       x = bswap_32(x);        \
+                       break;                  \
+               case 2:                         \
+                       x = bswap_16(x);        \
+                       break;                  \
+               case 1:                         \
+                       break;                  \
+               default:                        \
+                       abort();                \
+               }                               \
+       } while (0)
+
+#define bswap_phdr(phdr)               \
+       do {                            \
+               bswap((phdr).p_type);   \
+               bswap((phdr).p_offset); \
+               bswap((phdr).p_filesz); \
+               bswap((phdr).p_memsz);  \
+               bswap((phdr).p_align);  \
+       } while (0)
+
+#define bswap_shdr(shdr)                   \
+       do {                                \
+               bswap((shdr).sh_name);      \
+               bswap((shdr).sh_type);      \
+               bswap((shdr).sh_flags);     \
+               bswap((shdr).sh_addr);      \
+               bswap((shdr).sh_offset);    \
+               bswap((shdr).sh_size);      \
+               bswap((shdr).sh_link);      \
+               bswap((shdr).sh_info);      \
+               bswap((shdr).sh_addralign); \
+               bswap((shdr).sh_entsize);   \
+       } while (0)
+
+#define bswap_ehdr(ehdr)                               \
+       do {                                            \
+               bswap((ehdr).e_type);                   \
+               bswap((ehdr).e_machine);                \
+               bswap((ehdr).e_version);                \
+               bswap((ehdr).e_entry);                  \
+               bswap((ehdr).e_phoff);                  \
+               bswap((ehdr).e_shoff);                  \
+               bswap((ehdr).e_flags);                  \
+               bswap((ehdr).e_ehsize);                 \
+               bswap((ehdr).e_phentsize);              \
+               bswap((ehdr).e_phnum);                  \
+               bswap((ehdr).e_shentsize);              \
+               bswap((ehdr).e_shnum);                  \
+               bswap((ehdr).e_shstrndx);               \
+       } while (0)
+
+#define copy_phdr(src_phdr, dst_phdr)                          \
+       do {                                                    \
+               (dst_phdr).p_type = (src_phdr).p_type;          \
+               (dst_phdr).p_offset = (src_phdr).p_offset;      \
+               (dst_phdr).p_filesz = (src_phdr).p_filesz;      \
+               (dst_phdr).p_memsz = (src_phdr).p_memsz;        \
+               (dst_phdr).p_align = (src_phdr).p_align;        \
+       } while (0)
+
+#define copy_shdr(src_shdr, dst_shdr)                                  \
+       do {                                                            \
+               (dst_shdr).sh_name = (src_shdr).sh_name;                \
+               (dst_shdr).sh_type = (src_shdr).sh_type;                \
+               (dst_shdr).sh_flags = (src_shdr).sh_flags;              \
+               (dst_shdr).sh_addr = (src_shdr).sh_addr;                \
+               (dst_shdr).sh_offset = (src_shdr).sh_offset;            \
+               (dst_shdr).sh_size = (src_shdr).sh_size;                \
+               (dst_shdr).sh_link = (src_shdr).sh_link;                \
+               (dst_shdr).sh_info = (src_shdr).sh_info;                \
+               (dst_shdr).sh_addralign = (src_shdr).sh_addralign;      \
+               (dst_shdr).sh_entsize = (src_shdr).sh_entsize;          \
+       } while (0)
+
+#define copy_ehdr(src_ehdr, dst_ehdr)                                  \
+       do {                                                            \
+               (dst_ehdr).e_type = (src_ehdr).e_type;                  \
+               (dst_ehdr).e_machine = (src_ehdr).e_machine;            \
+               (dst_ehdr).e_version = (src_ehdr).e_version;            \
+               (dst_ehdr).e_entry = (src_ehdr).e_entry;                \
+               (dst_ehdr).e_phoff = (src_ehdr).e_phoff;                \
+               (dst_ehdr).e_shoff = (src_ehdr).e_shoff;                \
+               (dst_ehdr).e_flags = (src_ehdr).e_flags;                \
+               (dst_ehdr).e_ehsize = (src_ehdr).e_ehsize;              \
+               (dst_ehdr).e_phentsize = (src_ehdr).e_phentsize;        \
+               (dst_ehdr).e_phnum = (src_ehdr).e_phnum;                \
+               (dst_ehdr).e_shentsize = (src_ehdr).e_shentsize;        \
+               (dst_ehdr).e_shnum = (src_ehdr).e_shnum;                \
+               (dst_ehdr).e_shstrndx = (src_ehdr).e_shstrndx;          \
+       } while (0)
+
+struct lttng_ust_elf_ehdr {
+       uint16_t e_type;
+       uint16_t e_machine;
+       uint32_t e_version;
+       uint64_t e_entry;
+       uint64_t e_phoff;
+       uint64_t e_shoff;
+       uint32_t e_flags;
+       uint16_t e_ehsize;
+       uint16_t e_phentsize;
+       uint16_t e_phnum;
+       uint16_t e_shentsize;
+       uint16_t e_shnum;
+       uint16_t e_shstrndx;
+};
+
+struct lttng_ust_elf_phdr {
+       uint32_t p_type;
+       uint64_t p_offset;
+       uint64_t p_filesz;
+       uint64_t p_memsz;
+       uint64_t p_align;
+};
+
+struct lttng_ust_elf_shdr {
+       uint32_t sh_name;
+       uint32_t sh_type;
+       uint64_t sh_flags;
+       uint64_t sh_addr;
+       uint64_t sh_offset;
+       uint64_t sh_size;
+       uint32_t sh_link;
+       uint32_t sh_info;
+       uint64_t sh_addralign;
+       uint64_t sh_entsize;
+};
+
+struct lttng_ust_elf_nhdr {
+       uint32_t n_namesz;
+       uint32_t n_descsz;
+       uint32_t n_type;
+};
+
+struct lttng_ust_elf {
+       /* Offset in bytes to start of section names string table. */
+       uint64_t section_names_offset;
+       /* Size in bytes of section names string table. */
+       uint64_t section_names_size;
+       char *path;
+       FILE *file;
+       struct lttng_ust_elf_ehdr *ehdr;
+       uint8_t bitness;
+       uint8_t endianness;
+};
+
+static inline
+int is_elf_32_bit(struct lttng_ust_elf *elf)
+{
+       return elf->bitness == ELFCLASS32;
+}
+
+static inline
+int is_elf_native_endian(struct lttng_ust_elf *elf)
+{
+       return elf->endianness == NATIVE_ELF_ENDIANNESS;
+}
+
+struct lttng_ust_elf *lttng_ust_elf_create(const char *path);
+void lttng_ust_elf_destroy(struct lttng_ust_elf *elf);
+int lttng_ust_elf_get_memsz(struct lttng_ust_elf *elf, uint64_t *memsz);
+int lttng_ust_elf_get_build_id(struct lttng_ust_elf *elf, uint8_t **build_id,
+                       size_t *length, int *found);
+int lttng_ust_elf_get_debug_link(struct lttng_ust_elf *elf, char **filename,
+                       uint32_t *crc, int *found);
+
+#endif /* _LTTNG_UST_ELF_H */
index d366f64f8f46c2195bd0bc3a1ba8af69b3ec28ec..d6b807149861415e30212a5ca72100dfef8db4c7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013  Paul Woegerer <paul.woegerer@mentor.com>
+ * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 #define _LGPL_SOURCE
 #define _GNU_SOURCE
-#include <lttng/ust-dlfcn.h>
-#include <inttypes.h>
-#include <link.h>
-#include <unistd.h>
-#include <stdio.h>
+
 #include <limits.h>
+#include <stdio.h>
 #include <sys/types.h>
-#include <sys/stat.h>
-#include <signal.h>
-#include <sched.h>
-#include <stdarg.h>
+#include <unistd.h>
+
+#include <lttng/ust-dlfcn.h>
+#include <lttng/ust-elf.h>
 #include "usterr-signal-safe.h"
 
-#include <lttng/ust-compiler.h>
-#include <lttng/ust.h>
+/* Include link.h last else it conflicts with ust-dlfcn. */
+#include <link.h>
 
 #define TRACEPOINT_DEFINE
 #include "ust_dl.h"
@@ -45,7 +43,7 @@ void *_lttng_ust_dl_libc_dlopen(const char *filename, int flag)
 {
        if (!__lttng_ust_plibc_dlopen) {
                __lttng_ust_plibc_dlopen = dlsym(RTLD_NEXT, "dlopen");
-               if (__lttng_ust_plibc_dlopen == NULL) {
+               if (!__lttng_ust_plibc_dlopen) {
                        fprintf(stderr, "%s\n", dlerror());
                        return NULL;
                }
@@ -58,7 +56,7 @@ int _lttng_ust_dl_libc_dlclose(void *handle)
 {
        if (!__lttng_ust_plibc_dlclose) {
                __lttng_ust_plibc_dlclose = dlsym(RTLD_NEXT, "dlclose");
-               if (__lttng_ust_plibc_dlclose == NULL) {
+               if (!__lttng_ust_plibc_dlclose) {
                        fprintf(stderr, "%s\n", dlerror());
                        return -1;
                }
@@ -70,33 +68,77 @@ static
 void lttng_ust_dl_dlopen(void *so_base, const char *so_name, void *ip)
 {
        char resolved_path[PATH_MAX];
-       struct stat sostat;
+       struct lttng_ust_elf *elf;
+       uint64_t memsz;
+       uint8_t *build_id;
+       size_t build_id_len;
+       char *dbg_file;
+       uint32_t crc;
+       int has_build_id = 0, has_debug_link = 0;
+       int ret;
 
        if (!realpath(so_name, resolved_path)) {
                ERR("could not resolve path '%s'", so_name);
                return;
        }
 
-       if (stat(resolved_path, &sostat)) {
-               ERR("could not access file status for %s", resolved_path);
+       elf = lttng_ust_elf_create(resolved_path);
+       if (!elf) {
+               ERR("could not acces file %s", resolved_path);
                return;
        }
 
+       ret = lttng_ust_elf_get_memsz(elf, &memsz);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_ust_elf_get_build_id(
+               elf, &build_id, &build_id_len, &has_build_id);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_ust_elf_get_debug_link(
+               elf, &dbg_file, &crc, &has_debug_link);
+       if (ret) {
+               goto end;
+       }
+
        tracepoint(lttng_ust_dl, dlopen,
-               so_base, resolved_path, sostat.st_size, sostat.st_mtime, ip);
+               ip, so_base, resolved_path, memsz);
+
+       if (has_build_id) {
+               tracepoint(lttng_ust_dl, build_id,
+                       ip, so_base, build_id, build_id_len);
+               free(build_id);
+       }
+
+       if (has_debug_link) {
+               tracepoint(lttng_ust_dl, debug_link,
+                       ip, so_base, dbg_file, crc);
+               free(dbg_file);
+       }
+
+end:
+       lttng_ust_elf_destroy(elf);
        return;
 }
 
 void *dlopen(const char *filename, int flag)
 {
-       void *handle = _lttng_ust_dl_libc_dlopen(filename, flag);
+       void *handle;
+
+       handle = _lttng_ust_dl_libc_dlopen(filename, flag);
        if (__tracepoint_ptrs_registered && handle) {
                struct link_map *p = NULL;
-               if (dlinfo(handle, RTLD_DI_LINKMAP, &p) != -1 && p != NULL
-                               && p->l_addr != 0)
+               int ret;
+
+               ret = dlinfo(handle, RTLD_DI_LINKMAP, &p);
+               if (ret != -1 && p != NULL && p->l_addr != 0) {
                        lttng_ust_dl_dlopen((void *) p->l_addr, p->l_name,
                                __builtin_return_address(0));
+               }
        }
+
        return handle;
 }
 
@@ -104,10 +146,15 @@ int dlclose(void *handle)
 {
        if (__tracepoint_ptrs_registered && handle) {
                struct link_map *p = NULL;
-               if (dlinfo(handle, RTLD_DI_LINKMAP, &p) != -1 && p != NULL
-                               && p->l_addr != 0)
-                       tracepoint(lttng_ust_dl, dlclose, (void *) p->l_addr,
-                               __builtin_return_address(0));
+               int ret;
+
+               ret = dlinfo(handle, RTLD_DI_LINKMAP, &p);
+               if (ret != -1 && p != NULL && p->l_addr != 0) {
+                       tracepoint(lttng_ust_dl, dlclose,
+                               __builtin_return_address(0),
+                               (void *) p->l_addr);
+               }
        }
+
        return _lttng_ust_dl_libc_dlclose(handle);
 }
index ddab530649c42df75e169c70b73dfb4353f934b3..e87ec122e0c1f85b4568a409c9d921c35b9ffb3f 100644 (file)
@@ -10,6 +10,7 @@ extern "C" {
 
 /*
  * Copyright (C) 2013  Paul Woegerer <paul_woegerer@mentor.com>
+ * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -37,18 +38,45 @@ extern "C" {
 #include <lttng/tracepoint.h>
 
 TRACEPOINT_EVENT(lttng_ust_dl, dlopen,
-       TP_ARGS(void *, baddr, const char*, sopath, int64_t, size,
-               int64_t, mtime, void *, ip),
+       TP_ARGS(void *, ip, void *, baddr, const char*, sopath,
+               uint64_t, memsz),
        TP_FIELDS(
                ctf_integer_hex(void *, baddr, baddr)
+               ctf_integer(uint64_t, memsz, memsz)
                ctf_string(sopath, sopath)
-               ctf_integer(int64_t, size, size)
-               ctf_integer(int64_t, mtime, mtime)
+       )
+)
+
+TRACEPOINT_EVENT(lttng_ust_dl, build_id,
+       TP_ARGS(
+               void *, ip,
+               void *, baddr,
+               uint8_t *, build_id,
+               size_t, build_id_len
+       ),
+       TP_FIELDS(
+               ctf_integer_hex(void *, baddr, baddr)
+               ctf_sequence_hex(uint8_t, build_id, build_id,
+                       size_t, build_id_len)
+       )
+)
+
+TRACEPOINT_EVENT(lttng_ust_dl, debug_link,
+       TP_ARGS(
+               void *, ip,
+               void *, baddr,
+               char *, filename,
+               uint32_t, crc
+       ),
+       TP_FIELDS(
+               ctf_integer_hex(void *, baddr, baddr)
+               ctf_integer(uint32_t, crc, crc)
+               ctf_string(filename, filename)
        )
 )
 
 TRACEPOINT_EVENT(lttng_ust_dl, dlclose,
-       TP_ARGS(void *, baddr, void *, ip),
+       TP_ARGS(void *, ip, void *, baddr),
        TP_FIELDS(
                ctf_integer_hex(void *, baddr, baddr)
        )
index 199fa03d81d803abd57d40e4e2d2eced8d7d5919..9166896e1a14b4db88268b5f0afa86f3ac15c4f9 100644 (file)
@@ -36,6 +36,8 @@ liblttng_ust_runtime_la_SOURCES = \
        lttng-filter-interpreter.c \
        filter-bytecode.h \
        lttng-hash-helper.h \
+       lttng-ust-elf.c \
+       lttng-ust-elf.h \
        lttng-ust-statedump.c \
        lttng-ust-statedump.h \
        lttng-ust-statedump-provider.h \
diff --git a/liblttng-ust/lttng-ust-elf.c b/liblttng-ust/lttng-ust-elf.c
new file mode 100644 (file)
index 0000000..f5c4f1a
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <helper.h>
+#include <string.h>
+#include <lttng/align.h>
+#include <lttng/ust-elf.h>
+
+/*
+ * Retrieve the nth (where n is the `index` argument) phdr (program
+ * header) from the given elf instance.
+ *
+ * A pointer to the phdr is returned on success, NULL on failure.
+ */
+static
+struct lttng_ust_elf_phdr *lttng_ust_elf_get_phdr(struct lttng_ust_elf *elf,
+                                               uint16_t index)
+{
+       struct lttng_ust_elf_phdr *phdr = NULL;
+       long offset;
+
+       if (!elf) {
+               goto error;
+       }
+
+       if (index >= elf->ehdr->e_phnum) {
+               goto error;
+       }
+
+       phdr = zmalloc(sizeof(struct lttng_ust_elf_phdr));
+       if (!phdr) {
+               goto error;
+       }
+
+       offset = elf->ehdr->e_phoff + index * elf->ehdr->e_phentsize;
+       if (fseek(elf->file, offset, SEEK_SET)) {
+               goto error;
+       }
+
+       if (is_elf_32_bit(elf)) {
+               Elf32_Phdr elf_phdr;
+
+               if (!fread(&elf_phdr, sizeof(elf_phdr), 1, elf->file)) {
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_phdr(elf_phdr);
+               }
+               copy_phdr(elf_phdr, *phdr);
+       } else {
+               Elf64_Phdr elf_phdr;
+
+               if (!fread(&elf_phdr, sizeof(elf_phdr), 1, elf->file)) {
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_phdr(elf_phdr);
+               }
+               copy_phdr(elf_phdr, *phdr);
+       }
+
+       return phdr;
+
+error:
+       free(phdr);
+       return NULL;
+}
+
+/*
+ * Retrieve the nth (where n is the `index` argument) shdr (section
+ * header) from the given elf instance.
+ *
+ * A pointer to the shdr is returned on success, NULL on failure.
+ */
+static
+struct lttng_ust_elf_shdr *lttng_ust_elf_get_shdr(struct lttng_ust_elf *elf,
+                                               uint16_t index)
+{
+       struct lttng_ust_elf_shdr *shdr = NULL;
+       long offset;
+
+       if (!elf) {
+               goto error;
+       }
+
+       if (index >= elf->ehdr->e_shnum) {
+               goto error;
+       }
+
+       shdr = zmalloc(sizeof(struct lttng_ust_elf_shdr));
+       if (!shdr) {
+               goto error;
+       }
+
+       offset = elf->ehdr->e_shoff + index * elf->ehdr->e_shentsize;
+       if (fseek(elf->file, offset, SEEK_SET)) {
+               goto error;
+       }
+
+       if (is_elf_32_bit(elf)) {
+               Elf32_Shdr elf_shdr;
+
+               if (!fread(&elf_shdr, sizeof(elf_shdr), 1, elf->file)) {
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_shdr(elf_shdr);
+               }
+               copy_shdr(elf_shdr, *shdr);
+       } else {
+               Elf64_Shdr elf_shdr;
+
+               if (!fread(&elf_shdr, sizeof(elf_shdr), 1, elf->file)) {
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_shdr(elf_shdr);
+               }
+               copy_shdr(elf_shdr, *shdr);
+       }
+
+       return shdr;
+
+error:
+       free(shdr);
+       return NULL;
+}
+
+/*
+ * Lookup a section's name from a given offset (usually from an shdr's
+ * sh_name value) in bytes relative to the beginning of the section
+ * names string table.
+ *
+ * If no name is found, NULL is returned.
+ */
+static
+char *lttng_ust_elf_get_section_name(struct lttng_ust_elf *elf, uint32_t offset)
+{
+       char *name = NULL;
+       size_t len;
+
+       if (!elf) {
+               goto error;
+       }
+
+       if (offset >= elf->section_names_size) {
+               goto error;
+       }
+
+       if (fseek(elf->file, elf->section_names_offset + offset, SEEK_SET)) {
+               goto error;
+       }
+       /* Note that len starts at 1, it is not an index. */
+       for (len = 1; offset + len <= elf->section_names_size; ++len) {
+               switch (fgetc(elf->file)) {
+               case EOF:
+                       goto error;
+               case '\0':
+                       goto end;
+               default:
+                       break;
+               }
+       }
+
+       /* No name was found before the end of the table. */
+       goto error;
+
+end:
+       name = zmalloc(sizeof(char) * len);
+       if (!name) {
+               goto error;
+       }
+       if (fseek(elf->file, elf->section_names_offset + offset,
+               SEEK_SET)) {
+               goto error;
+       }
+       if (!fgets(name, len, elf->file)) {
+               goto error;
+       }
+
+       return name;
+
+error:
+       free(name);
+       return NULL;
+}
+
+/*
+ * Create an instance of lttng_ust_elf for the ELF file located at
+ * `path`.
+ *
+ * Return a pointer to the instance on success, NULL on failure.
+ */
+struct lttng_ust_elf *lttng_ust_elf_create(const char *path)
+{
+       uint8_t e_ident[EI_NIDENT];
+       struct lttng_ust_elf_shdr *section_names_shdr;
+       struct lttng_ust_elf *elf;
+
+       elf = zmalloc(sizeof(struct lttng_ust_elf));
+       if (!elf) {
+               goto error;
+       }
+
+       elf->path = strdup(path);
+       if (!elf->path) {
+               goto error;
+       }
+
+       elf->file = fopen(elf->path, "rb");
+       if (!elf->file) {
+               goto error;
+       }
+
+       if (!fread(e_ident, 1, EI_NIDENT, elf->file)) {
+               goto error;
+       }
+       elf->bitness = e_ident[EI_CLASS];
+       elf->endianness = e_ident[EI_DATA];
+       rewind(elf->file);
+
+       elf->ehdr = zmalloc(sizeof(struct lttng_ust_elf_ehdr));
+       if (!elf->ehdr) {
+               goto error;
+       }
+
+       if (is_elf_32_bit(elf)) {
+               Elf32_Ehdr elf_ehdr;
+
+               if (!fread(&elf_ehdr, sizeof(elf_ehdr), 1, elf->file)) {
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_ehdr(elf_ehdr);
+               }
+               copy_ehdr(elf_ehdr, *(elf->ehdr));
+       } else {
+               Elf64_Ehdr elf_ehdr;
+
+               if (!fread(&elf_ehdr, sizeof(elf_ehdr), 1, elf->file)) {
+                       goto error;
+               }
+               if (!is_elf_native_endian(elf)) {
+                       bswap_ehdr(elf_ehdr);
+               }
+               copy_ehdr(elf_ehdr, *(elf->ehdr));
+       }
+
+       section_names_shdr = lttng_ust_elf_get_shdr(elf, elf->ehdr->e_shstrndx);
+       if (!section_names_shdr) {
+               goto error;
+       }
+
+       elf->section_names_offset = section_names_shdr->sh_offset;
+       elf->section_names_size = section_names_shdr->sh_size;
+
+       free(section_names_shdr);
+
+       return elf;
+
+error:
+       if (elf) {
+               free(elf->ehdr);
+               fclose(elf->file);
+               free(elf->path);
+       }
+       free(elf);
+       return NULL;
+}
+
+/*
+ * Destroy the given lttng_ust_elf instance.
+ */
+void lttng_ust_elf_destroy(struct lttng_ust_elf *elf)
+{
+       if (!elf) {
+               return;
+       }
+
+       free(elf->ehdr);
+       fclose(elf->file);
+       free(elf->path);
+       free(elf);
+}
+
+/*
+ * Compute the total in-memory size of the ELF file, in bytes.
+ *
+ * Returns 0 if successful, -1 if not. On success, the memory size is
+ * returned through the out parameter `memsz`.
+ */
+int lttng_ust_elf_get_memsz(struct lttng_ust_elf *elf, uint64_t *memsz)
+{
+       uint16_t i;
+       uint64_t _memsz = 0;
+
+       if (!elf || !memsz) {
+               goto error;
+       }
+
+       for (i = 0; i < elf->ehdr->e_phnum; ++i) {
+               struct lttng_ust_elf_phdr *phdr;
+               uint64_t align;
+
+               phdr = lttng_ust_elf_get_phdr(elf, i);
+               if (!phdr) {
+                       goto error;
+               }
+
+               /*
+                * Only PT_LOAD segments contribute to memsz. Skip
+                * other segments.
+                */
+               if (phdr->p_type != PT_LOAD) {
+                       goto next_loop;
+               }
+
+               /*
+                * A p_align of 0 means no alignment, i.e. aligned to
+                * 1 byte.
+                */
+               align = phdr->p_align == 0 ? 1 : phdr->p_align;
+               /* Align the start of the segment. */
+               _memsz += offset_align(_memsz, align);
+               _memsz += phdr->p_memsz;
+               /*
+                * Add padding at the end of the segment, so it ends
+                * on a multiple of the align value (which usually
+                * means a page boundary). This makes the computation
+                * valid even in cases where p_align would change from
+                * one segment to the next.
+                */
+               _memsz += offset_align(_memsz, align);
+       next_loop:
+               free(phdr);
+       }
+
+       *memsz = _memsz;
+       return 0;
+error:
+       return -1;
+}
+
+/*
+ * Internal method used to try and get the build_id from a PT_NOTE
+ * segment ranging from `offset` to `segment_end`.
+ *
+ * If the function returns successfully, the out parameter `found`
+ * indicates whether the build id information was present in the
+ * segment or not. If `found` is not 0, the out parameters `build_id`
+ * and `length` will both have been set with the retrieved
+ * information.
+ *
+ * Returns 0 on success, -1 if an error occurred.
+ */
+static
+int lttng_ust_elf_get_build_id_from_segment(
+       struct lttng_ust_elf *elf, uint8_t **build_id, size_t *length,
+       uint64_t offset, uint64_t segment_end, int *found)
+{
+       uint8_t *_build_id;
+       size_t _length;
+       int _found = 0;
+
+       while (offset < segment_end) {
+               struct lttng_ust_elf_nhdr nhdr;
+
+               /* Align start of note entry */
+               offset += offset_align(offset, ELF_NOTE_ENTRY_ALIGN);
+               if (offset >= segment_end) {
+                       break;
+               }
+               /*
+                * We seek manually because if the note isn't the
+                * build id the data following the header will not
+                * have been read.
+                */
+               if (fseek(elf->file, offset, SEEK_SET)) {
+                       goto error;
+               }
+               if (!fread(&nhdr, sizeof(nhdr), 1, elf->file)) {
+                       goto error;
+               }
+
+               if (!is_elf_native_endian(elf)) {
+                       nhdr.n_namesz = bswap_32(nhdr.n_namesz);
+                       nhdr.n_descsz = bswap_32(nhdr.n_descsz);
+                       nhdr.n_type = bswap_32(nhdr.n_type);
+               }
+
+               offset += sizeof(nhdr) + nhdr.n_namesz;
+               /* Align start of desc entry */
+               offset += offset_align(offset, ELF_NOTE_DESC_ALIGN);
+
+               if (nhdr.n_type != NT_GNU_BUILD_ID) {
+                       /*
+                        * Ignore non build id notes but still
+                        * increase the offset.
+                        */
+                       offset += nhdr.n_descsz;
+                       continue;
+               }
+
+               _length = nhdr.n_descsz;
+               _build_id = zmalloc(sizeof(uint8_t) * _length);
+               if (!build_id) {
+                       goto error;
+               }
+
+               if (fseek(elf->file, offset, SEEK_SET)) {
+                       goto error;
+               }
+               if (!fread(_build_id, sizeof(*_build_id), _length, elf->file)) {
+                       goto error;
+               }
+
+               _found = 1;
+               break;
+       }
+
+       if (_found) {
+               *build_id = _build_id;
+               *length = _length;
+       }
+
+       *found = _found;
+       return 0;
+error:
+       return -1;
+}
+
+/*
+ * Retrieve a build ID (an array of bytes) from the corresponding
+ * section in the ELF file. The length of the build ID can be either
+ * 16 or 20 bytes depending on the method used to generate it, hence
+ * the length out parameter.
+ *
+ * If the function returns successfully, the out parameter `found`
+ * indicates whether the build id information was present in the ELF
+ * file or not. If `found` is not 0, the out parameters `build_id` and
+ * `length` will both have been set with the retrieved information.
+ *
+ * Returns 0 on success, -1 if an error occurred.
+ */
+int lttng_ust_elf_get_build_id(struct lttng_ust_elf *elf, uint8_t **build_id,
+                       size_t *length, int *found)
+{
+       uint16_t i;
+       uint8_t *_build_id;
+       size_t _length;
+       int _found = 0;
+
+       if (!elf || !build_id || !length || !found) {
+               goto error;
+       }
+
+       for (i = 0; i < elf->ehdr->e_phnum; ++i) {
+               uint64_t offset, segment_end;
+               struct lttng_ust_elf_phdr *phdr;
+               int ret;
+
+               phdr = lttng_ust_elf_get_phdr(elf, i);
+               if (!phdr) {
+                       goto error;
+               }
+
+               /* Build ID will be contained in a PT_NOTE segment. */
+               if (phdr->p_type != PT_NOTE) {
+                       goto next_loop;
+               }
+
+               offset = phdr->p_offset;
+               segment_end = offset + phdr->p_filesz;
+               ret = lttng_ust_elf_get_build_id_from_segment(
+                       elf, &_build_id, &_length, offset, segment_end,
+                       &_found);
+       next_loop:
+               free(phdr);
+               if (ret) {
+                       goto error;
+               }
+               if (_found) {
+                       break;
+               }
+       }
+
+       if (_found) {
+               *build_id = _build_id;
+               *length = _length;
+       }
+
+       *found = _found;
+       return 0;
+error:
+       return -1;
+}
+
+/*
+ * Try to retrieve filename and CRC from given ELF section `shdr`.
+ *
+ * If the function returns successfully, the out parameter `found`
+ * indicates whether the debug link information was present in the ELF
+ * section or not. If `found` is not 0, the out parameters `filename` and
+ * `crc` will both have been set with the retrieved information.
+ *
+ * Returns 0 on success, -1 if an error occurred.
+ */
+static
+int lttng_ust_elf_get_debug_link_from_section(struct lttng_ust_elf *elf,
+                                       char **filename, uint32_t *crc,
+                                       int *found,
+                                       struct lttng_ust_elf_shdr *shdr)
+{
+       int _found = 0;
+       char *_filename;
+       char *section_name = NULL;
+       uint32_t _crc;
+
+       if (!elf || !filename || !crc || !found || !shdr) {
+               goto error;
+       }
+
+       /*
+        * The .gnu_debuglink section is of type SHT_PROGBITS,
+        * skip the other sections.
+        */
+       if (shdr->sh_type != SHT_PROGBITS) {
+               goto end;
+       }
+
+       section_name = lttng_ust_elf_get_section_name(elf,
+                                               shdr->sh_name);
+       if (!section_name) {
+               goto end;
+       }
+       if (strcmp(section_name, ".gnu_debuglink")) {
+               goto end;
+       }
+
+       /*
+        * The length of the filename is the sh_size excluding the CRC
+        * which comes after it in the section.
+        */
+       _filename = zmalloc(sizeof(char) * (shdr->sh_size - ELF_CRC_SIZE));
+       if (!_filename) {
+               goto error;
+       }
+       if (fseek(elf->file, shdr->sh_offset, SEEK_SET)) {
+               goto error;
+       }
+       if (!fread(_filename, sizeof(*_filename), shdr->sh_size - ELF_CRC_SIZE,
+               elf->file)) {
+               goto error;
+       }
+       if (!fread(&_crc, sizeof(_crc), 1, elf->file)) {
+               goto error;
+       }
+       if (!is_elf_native_endian(elf)) {
+               _crc = bswap_32(_crc);
+       }
+
+       _found = 1;
+
+end:
+       free(section_name);
+       if (_found) {
+               *filename = _filename;
+               *crc = _crc;
+       }
+       *found = _found;
+
+       return 0;
+
+error:
+       if (section_name) {
+               free(section_name);
+       }
+
+       return -1;
+}
+
+/*
+ * Retrieve filename and CRC from ELF's .gnu_debuglink section, if any.
+ *
+ * If the function returns successfully, the out parameter `found`
+ * indicates whether the debug link information was present in the ELF
+ * file or not. If `found` is not 0, the out parameters `filename` and
+ * `crc` will both have been set with the retrieved information.
+ *
+ * Returns 0 on success, -1 if an error occurred.
+ */
+int lttng_ust_elf_get_debug_link(struct lttng_ust_elf *elf, char **filename,
+                               uint32_t *crc, int *found)
+{
+       int ret;
+       uint16_t i;
+       int _found = 0;
+       char *_filename;
+       uint32_t _crc;
+
+       if (!elf || !filename || !crc || !found) {
+               goto error;
+       }
+
+       for (i = 0; i < elf->ehdr->e_shnum; ++i) {
+               struct lttng_ust_elf_shdr *shdr = NULL;
+
+               shdr = lttng_ust_elf_get_shdr(elf, i);
+               if (!shdr) {
+                       goto error;
+               }
+
+               ret = lttng_ust_elf_get_debug_link_from_section(
+                       elf, &_filename, &_crc, &_found, shdr);
+               free(shdr);
+
+               if (ret) {
+                       goto error;
+               }
+               if (_found) {
+                       break;
+               }
+       }
+
+       if (_found) {
+               *filename = _filename;
+               *crc = _crc;
+       }
+
+       *found = _found;
+       return 0;
+error:
+       return -1;
+}
index adfbf9b294bd95a898d4ff6423c8f4ca11d5571a..5e212d9e3cce0e79cd6f8ffb9b4807c5b64fc825 100644 (file)
@@ -48,14 +48,40 @@ TRACEPOINT_EVENT(lttng_ust_statedump, soinfo,
                struct lttng_session *, session,
                void *, baddr,
                const char*, sopath,
-               int64_t, size,
-               int64_t, mtime
-               ),
+               uint64_t, memsz
+       ),
        TP_FIELDS(
                ctf_integer_hex(void *, baddr, baddr)
+               ctf_integer(uint64_t, memsz, memsz)
                ctf_string(sopath, sopath)
-               ctf_integer(int64_t, size, size)
-               ctf_integer(int64_t, mtime, mtime)
+       )
+)
+
+TRACEPOINT_EVENT(lttng_ust_statedump, build_id,
+       TP_ARGS(
+               struct lttng_session *, session,
+               void *, baddr,
+               uint8_t *, build_id,
+               size_t, build_id_len
+       ),
+       TP_FIELDS(
+               ctf_integer_hex(void *, baddr, baddr)
+               ctf_sequence_hex(uint8_t, build_id, build_id,
+                       size_t, build_id_len)
+       )
+)
+
+TRACEPOINT_EVENT(lttng_ust_statedump, debug_link,
+       TP_ARGS(
+               struct lttng_session *, session,
+               void *, baddr,
+               char *, filename,
+               uint32_t, crc
+       ),
+       TP_FIELDS(
+               ctf_integer_hex(void *, baddr, baddr)
+               ctf_integer(uint32_t, crc, crc)
+               ctf_string(filename, filename)
        )
 )
 
index 37f067ffa4cb5d3daf4bbeb1260ca1cc0c8b5110..0c0a7bddaa5329004e2ced770a0dfc1e7a018577 100644 (file)
 
 #define _LGPL_SOURCE
 #define _GNU_SOURCE
-#include <link.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <link.h>
 #include <limits.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stddef.h>
 #include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
 
-#include <usterr-signal-safe.h>
+#include <lttng/ust-elf.h>
 #include "lttng-tracer-core.h"
 #include "lttng-ust-statedump.h"
 
@@ -49,9 +46,12 @@ struct soinfo_data {
        void *owner;
        void *base_addr_ptr;
        const char *resolved_path;
+       char *dbg_file;
+       uint8_t *build_id;
+       uint64_t memsz;
+       size_t build_id_len;
        int vdso;
-       off_t size;
-       time_t mtime;
+       uint32_t crc;
 };
 
 typedef void (*tracepoint_cb)(struct lttng_session *session, void *priv);
@@ -92,9 +92,28 @@ void trace_soinfo_cb(struct lttng_session *session, void *priv)
        struct soinfo_data *so_data = (struct soinfo_data *) priv;
 
        tracepoint(lttng_ust_statedump, soinfo,
-                  session, so_data->base_addr_ptr,
-                  so_data->resolved_path, so_data->size,
-                  so_data->mtime);
+               session, so_data->base_addr_ptr,
+               so_data->resolved_path, so_data->memsz);
+}
+
+static
+void trace_build_id_cb(struct lttng_session *session, void *priv)
+{
+       struct soinfo_data *so_data = (struct soinfo_data *) priv;
+
+       tracepoint(lttng_ust_statedump, build_id,
+               session, so_data->base_addr_ptr,
+               so_data->build_id, so_data->build_id_len);
+}
+
+static
+void trace_debug_link_cb(struct lttng_session *session, void *priv)
+{
+       struct soinfo_data *so_data = (struct soinfo_data *) priv;
+
+       tracepoint(lttng_ust_statedump, debug_link,
+               session, so_data->base_addr_ptr,
+               so_data->dbg_file, so_data->crc);
 }
 
 static
@@ -109,20 +128,78 @@ void trace_end_cb(struct lttng_session *session, void *priv)
        tracepoint(lttng_ust_statedump, end, session);
 }
 
+static
+int get_elf_info(struct soinfo_data *so_data, int *has_build_id,
+               int *has_debug_link) {
+       struct lttng_ust_elf *elf;
+       int ret = 0;
+
+       elf = lttng_ust_elf_create(so_data->resolved_path);
+       if (!elf) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_ust_elf_get_memsz(elf, &so_data->memsz);
+       if (ret) {
+               goto end;
+       }
+
+       ret = lttng_ust_elf_get_build_id(elf, &so_data->build_id,
+                                       &so_data->build_id_len, has_build_id);
+       if (ret) {
+               goto end;
+       }
+       ret = lttng_ust_elf_get_debug_link(elf, &so_data->dbg_file,
+                                       &so_data->crc, has_debug_link);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       lttng_ust_elf_destroy(elf);
+       return ret;
+}
+
 static
 int trace_baddr(struct soinfo_data *so_data)
 {
-       struct stat sostat;
+       int ret = 0, has_build_id = 0, has_debug_link = 0;
+
+       if (!so_data->vdso) {
+               ret = get_elf_info(so_data, &has_build_id, &has_debug_link);
+               if (ret) {
+                       goto end;
+               }
+       } else {
+               so_data->memsz = 0;
+       }
 
-       if (so_data->vdso || stat(so_data->resolved_path, &sostat)) {
-               sostat.st_size = 0;
-               sostat.st_mtime = -1;
+       ret = trace_statedump_event(trace_soinfo_cb, so_data->owner, so_data);
+       if (ret) {
+               goto end;
        }
 
-       so_data->size = sostat.st_size;
-       so_data->mtime = sostat.st_mtime;
+       if (has_build_id) {
+               ret = trace_statedump_event(
+                       trace_build_id_cb, so_data->owner, so_data);
+               free(so_data->build_id);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       if (has_debug_link) {
+               ret = trace_statedump_event(
+                       trace_debug_link_cb, so_data->owner, so_data);
+               free(so_data->dbg_file);
+               if (ret) {
+                       goto end;
+               }
+       }
 
-       return trace_statedump_event(trace_soinfo_cb, so_data->owner, so_data);
+end:
+       return ret;
 }
 
 static
index e78774d54521ee60b3dcfa3499221eb975b820b9..48deb96d98c5cd1befa65317ff9e21054dae7fd7 100644 (file)
@@ -3,6 +3,7 @@
 
 /*
  * Copyright (C) 2013  Paul Woegerer <paul_woegerer@mentor.com>
+ * Copyright (C) 2015  Antoine Busque <abusque@efficios.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
This page took 0.039986 seconds and 4 git commands to generate.