/*
- * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
- * Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright (C) 2017 Erica Bugden <erica.bugden@efficios.com>
+ * Copyright (C) 2015 Antoine Busque <abusque@efficios.com>
+ * Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright (C) 2017 Erica Bugden <erica.bugden@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.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
*
- * 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 <common/compat/endian.h>
#define TEXT_SECTION_NAME ".text"
#define SYMBOL_TAB_SECTION_NAME ".symtab"
#define STRING_TAB_SECTION_NAME ".strtab"
+#define DYNAMIC_SYMBOL_TAB_SECTION_NAME ".dynsym"
+#define DYNAMIC_STRING_TAB_SECTION_NAME ".dynstr"
#define NOTE_STAPSDT_SECTION_NAME ".note.stapsdt"
#define NOTE_STAPSDT_NAME "stapsdt"
#define NOTE_STAPSDT_TYPE 3
+#define MAX_SECTION_DATA_SIZE 512 * 1024 * 1024
#if BYTE_ORDER == LITTLE_ENDIAN
#define NATIVE_ELF_ENDIANNESS ELFDATA2LSB
#define NATIVE_ELF_ENDIANNESS ELFDATA2MSB
#endif
+#define next_4bytes_boundary(x) (typeof(x)) ((((uint64_t)x) + 3) & ~0x03)
+
#define bswap(x) \
do { \
switch (sizeof(x)) { \
case 8: \
- x = be64toh(x); \
+ x = be64toh((uint64_t)x); \
break; \
case 4: \
- x = be32toh(x); \
+ x = be32toh((uint32_t)x); \
break; \
case 2: \
- x = be16toh(x); \
+ x = be16toh((uint16_t)x); \
break; \
case 1: \
break; \
dst_sym.st_size = src_sym.st_size; \
} while (0)
-/* Both 32bit and 64bit use the same 1 byte field for type. (See elf.h) */
-#define ELF_ST_TYPE(val) ELF32_ST_TYPE(val)
+#ifndef ELFCLASSNUM
+#define ELFCLASSNUM 3
+#endif
+
+#ifndef ELFDATANUM
+#define ELFDATANUM 3
+#endif
+
+#ifndef EV_NUM
+#define EV_NUM 2
+#endif
struct lttng_elf_ehdr {
uint16_t e_type;
struct lttng_elf {
int fd;
+ size_t file_size;
uint8_t bitness;
uint8_t endianness;
/* Offset in bytes to start of section names string table. */
* 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.
+ * 0 is returned on succes, -1 on failure.
*/
static
-struct lttng_elf_shdr *lttng_elf_get_section_hdr(struct lttng_elf *elf,
- uint16_t index)
+int lttng_elf_get_section_hdr(struct lttng_elf *elf,
+ uint16_t index, struct lttng_elf_shdr *out_header)
{
- struct lttng_elf_shdr *section_header = NULL;
int ret = 0;
if (!elf) {
+ ret = -1;
goto error;
}
if (index >= elf->ehdr->e_shnum) {
+ ret = -1;
goto error;
}
- section_header = zmalloc(sizeof(struct lttng_elf_shdr));
- if (!section_header) {
- goto error;
- }
-
- ret = populate_section_header(elf, section_header, index);
+ ret = populate_section_header(elf, out_header, index);
if (ret) {
- ret = LTTNG_ERR_ELF_PARSING;
DBG("Error populating section header.");
goto error;
}
- return section_header;
error:
- free(section_header);
- return NULL;
+ return ret;
}
/*
static
struct lttng_elf *lttng_elf_create(int fd)
{
- struct lttng_elf_shdr *section_names_shdr;
+ struct lttng_elf_shdr section_names_shdr;
struct lttng_elf *elf = NULL;
int ret;
+ struct stat stat_buf;
if (fd < 0) {
goto error;
}
+ ret = fstat(fd, &stat_buf);
+ if (ret) {
+ PERROR("Failed to determine size of elf file");
+ goto error;
+ }
+ if (!S_ISREG(stat_buf.st_mode)) {
+ ERR("Refusing to initialize lttng_elf from non-regular file");
+ goto error;
+ }
+
elf = zmalloc(sizeof(struct lttng_elf));
if (!elf) {
PERROR("Error allocating struct lttng_elf");
goto error;
}
+ elf->file_size = (size_t) stat_buf.st_size;
elf->fd = dup(fd);
if (elf->fd < 0) {
goto error;
}
- section_names_shdr = lttng_elf_get_section_hdr(elf, elf->ehdr->e_shstrndx);
- if (!section_names_shdr) {
+ ret = lttng_elf_get_section_hdr(
+ elf, elf->ehdr->e_shstrndx, §ion_names_shdr);
+ if (ret) {
goto error;
}
- elf->section_names_offset = section_names_shdr->sh_offset;
- elf->section_names_size = section_names_shdr->sh_size;
-
- free(section_names_shdr);
+ elf->section_names_offset = section_names_shdr.sh_offset;
+ elf->section_names_size = section_names_shdr.sh_size;
return elf;
error:
static
int lttng_elf_get_section_hdr_by_name(struct lttng_elf *elf,
- const char *section_name, struct lttng_elf_shdr **section_hdr)
+ const char *section_name, struct lttng_elf_shdr *section_hdr)
{
int i;
char *curr_section_name;
+
for (i = 0; i < elf->ehdr->e_shnum; ++i) {
- *section_hdr = lttng_elf_get_section_hdr(elf, i);
- curr_section_name = lttng_elf_get_section_name(elf,
- (*section_hdr)->sh_name);
+ bool name_equal;
+ int ret = lttng_elf_get_section_hdr(elf, i, section_hdr);
+ if (ret) {
+ break;
+ }
+ curr_section_name = lttng_elf_get_section_name(elf,
+ section_hdr->sh_name);
if (!curr_section_name) {
continue;
}
- if (strcmp(curr_section_name, section_name) == 0) {
+ name_equal = strcmp(curr_section_name, section_name) == 0;
+ free(curr_section_name);
+ if (name_equal) {
return 0;
}
}
int ret;
off_t section_offset;
char *data;
+ size_t max_alloc_size;
if (!elf || !shdr) {
goto error;
}
+ max_alloc_size = min_t(size_t, MAX_SECTION_DATA_SIZE, elf->file_size);
+
section_offset = shdr->sh_offset;
if (lseek(elf->fd, section_offset, SEEK_SET) < 0) {
PERROR("Error seeking to section offset");
goto error;
}
+ if (shdr->sh_size > max_alloc_size) {
+ ERR("ELF section size exceeds maximal allowed size of %zu bytes",
+ max_alloc_size);
+ goto error;
+ }
data = zmalloc(shdr->sh_size);
if (!data) {
PERROR("Error allocating buffer for ELF section data");
off_t text_section_addr_beg;
off_t text_section_addr_end;
off_t offset_in_section;
- struct lttng_elf_shdr *text_section_hdr = NULL;
+ struct lttng_elf_shdr text_section_hdr;
if (!elf_handle) {
DBG("Invalid ELF handle.");
goto error;
}
- text_section_offset = text_section_hdr->sh_offset;
- text_section_addr_beg = text_section_hdr->sh_addr;
- text_section_addr_end = text_section_addr_beg + text_section_hdr->sh_size;
+ text_section_offset = text_section_hdr.sh_offset;
+ text_section_addr_beg = text_section_hdr.sh_addr;
+ text_section_addr_end =
+ text_section_addr_beg + text_section_hdr.sh_size;
/*
* Verify that the address is within the .text section boundaries.
char *curr_sym_str = NULL;
char *symbol_table_data = NULL;
char *string_table_data = NULL;
- struct lttng_elf_shdr *symtab_hdr = NULL;
- struct lttng_elf_shdr *strtab_hdr = NULL;
+ const char *string_table_name = NULL;
+ struct lttng_elf_shdr symtab_hdr;
+ struct lttng_elf_shdr strtab_hdr;
struct lttng_elf *elf = NULL;
if (!symbol || !offset ) {
goto end;
}
- /* Get the symbol table section header. */
+ /*
+ * The .symtab section might not exist on stripped binaries.
+ * Try to get the symbol table section header first. If it's absent,
+ * try to get the dynamic symbol table. All symbols in the dynamic
+ * symbol tab are in the (normal) symbol table if it exists.
+ */
ret = lttng_elf_get_section_hdr_by_name(elf, SYMBOL_TAB_SECTION_NAME,
&symtab_hdr);
if (ret) {
- DBG("Cannot get ELF Symbol Table section.");
- ret = LTTNG_ERR_ELF_PARSING;
- goto destroy_elf;
+ DBG("Cannot get ELF Symbol Table section. Trying to get ELF Dynamic Symbol Table section.");
+ /* Get the dynamic symbol table section header. */
+ ret = lttng_elf_get_section_hdr_by_name(elf, DYNAMIC_SYMBOL_TAB_SECTION_NAME,
+ &symtab_hdr);
+ if (ret) {
+ DBG("Cannot get ELF Symbol Table nor Dynamic Symbol Table sections.");
+ ret = LTTNG_ERR_ELF_PARSING;
+ goto destroy_elf;
+ }
+ string_table_name = DYNAMIC_STRING_TAB_SECTION_NAME;
+ } else {
+ string_table_name = STRING_TAB_SECTION_NAME;
}
+
/* Get the data associated with the symbol table section. */
- symbol_table_data = lttng_elf_get_section_data(elf, symtab_hdr);
+ symbol_table_data = lttng_elf_get_section_data(elf, &symtab_hdr);
if (symbol_table_data == NULL) {
DBG("Cannot get ELF Symbol Table data.");
ret = LTTNG_ERR_ELF_PARSING;
}
/* Get the string table section header. */
- ret = lttng_elf_get_section_hdr_by_name(elf, STRING_TAB_SECTION_NAME,
+ ret = lttng_elf_get_section_hdr_by_name(elf, string_table_name,
&strtab_hdr);
if (ret) {
DBG("Cannot get ELF string table section.");
}
/* Get the data associated with the string table section. */
- string_table_data = lttng_elf_get_section_data(elf, strtab_hdr);
+ string_table_data = lttng_elf_get_section_data(elf, &strtab_hdr);
if (string_table_data == NULL) {
DBG("Cannot get ELF string table section data.");
ret = LTTNG_ERR_ELF_PARSING;
}
/* Get the number of symbol in the table for the iteration. */
- sym_count = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
+ sym_count = symtab_hdr.sh_size / symtab_hdr.sh_entsize;
/* Loop over all symbol. */
for (sym_idx = 0; sym_idx < sym_count; sym_idx++) {
/*
* If the current symbol is not a function; skip to the next symbol.
*/
- if (ELF_ST_TYPE(curr_sym.st_info) != STT_FUNC) {
+ /* Both 32bit and 64bit use the same 1 byte field for type. (See elf.h) */
+ if (ELF32_ST_TYPE(curr_sym.st_info) != STT_FUNC) {
continue;
}
*/
ret = lttng_elf_convert_addr_in_text_to_offset(elf, addr, offset);
if (ret) {
- DBG("Cannot convet addr to offset.");
+ DBG("Cannot convert addr to offset.");
goto free_string_table_data;
}
end:
return ret;
}
+
+/*
+ * Compute the offsets of SDT probes from the begining of the ELF binary.
+ *
+ * On success, returns 0 and the nb_probes parameter is set to the number of
+ * offsets found and the offsets parameter points to an array of offsets where
+ * the SDT probes are.
+ * On failure, returns -1.
+ */
+int lttng_elf_get_sdt_probe_offsets(int fd, const char *provider_name,
+ const char *probe_name, uint64_t **offsets, uint32_t *nb_probes)
+{
+ int ret = 0, nb_match = 0;
+ struct lttng_elf_shdr stap_note_section_hdr;
+ struct lttng_elf *elf = NULL;
+ char *stap_note_section_data = NULL;
+ char *curr_note_section_begin, *curr_data_ptr, *curr_probe, *curr_provider;
+ char *next_note_ptr;
+ uint32_t name_size, desc_size, note_type;
+ uint64_t curr_probe_location, curr_probe_offset, curr_semaphore_location;
+ uint64_t *probe_locs = NULL, *new_probe_locs = NULL;
+
+ if (!provider_name || !probe_name || !nb_probes || !offsets) {
+ DBG("Invalid arguments.");
+ ret = LTTNG_ERR_ELF_PARSING;
+ goto error;
+ }
+
+ elf = lttng_elf_create(fd);
+ if (!elf) {
+ DBG("Error allocation ELF.");
+ ret = LTTNG_ERR_ELF_PARSING;
+ goto error;
+ }
+
+ /* Get the stap note section header. */
+ ret = lttng_elf_get_section_hdr_by_name(elf, NOTE_STAPSDT_SECTION_NAME,
+ &stap_note_section_hdr);
+ if (ret) {
+ DBG("Cannot get ELF stap note section.");
+ goto destroy_elf_error;
+ }
+
+ /* Get the data associated with the stap note section. */
+ stap_note_section_data =
+ lttng_elf_get_section_data(elf, &stap_note_section_hdr);
+ if (stap_note_section_data == NULL) {
+ DBG("Cannot get ELF stap note section data.");
+ ret = LTTNG_ERR_ELF_PARSING;
+ goto destroy_elf_error;
+ }
+
+ next_note_ptr = stap_note_section_data;
+ curr_note_section_begin = stap_note_section_data;
+
+ *offsets = NULL;
+ while (1) {
+ curr_data_ptr = next_note_ptr;
+ /* Check if we have reached the end of the note section. */
+ if (curr_data_ptr >=
+ curr_note_section_begin +
+ stap_note_section_hdr.sh_size) {
+ *nb_probes = nb_match;
+ *offsets = probe_locs;
+ ret = 0;
+ break;
+ }
+ /* Get name size field. */
+ name_size = next_4bytes_boundary(*(uint32_t*) curr_data_ptr);
+ curr_data_ptr += sizeof(uint32_t);
+
+ /* Sanity check; a zero name_size is reserved. */
+ if (name_size == 0) {
+ DBG("Invalid name size field in SDT probe descriptions"
+ "section.");
+ ret = -1;
+ goto realloc_error;
+ }
+
+ /* Get description size field. */
+ desc_size = next_4bytes_boundary(*(uint32_t*) curr_data_ptr);
+ curr_data_ptr += sizeof(uint32_t);
+
+ /* Get type field. */
+ note_type = *(uint32_t *) curr_data_ptr;
+ curr_data_ptr += sizeof(uint32_t);
+
+ /*
+ * Move the pointer to the next note to be ready for the next
+ * iteration. The current note is made of 3 unsigned 32bit
+ * integers (name size, descriptor size and note type), the
+ * name and the descriptor. To move to the next note, we move
+ * the pointer according to those values.
+ */
+ next_note_ptr = next_note_ptr +
+ (3 * sizeof(uint32_t)) + desc_size + name_size;
+
+ /*
+ * Move ptr to the end of the name string (we don't need it)
+ * and go to the next 4 byte alignement.
+ */
+ if (note_type != NOTE_STAPSDT_TYPE ||
+ strncmp(curr_data_ptr, NOTE_STAPSDT_NAME, name_size) != 0) {
+ continue;
+ }
+
+ curr_data_ptr += name_size;
+
+ /* Get probe location. */
+ curr_probe_location = *(uint64_t *) curr_data_ptr;
+ curr_data_ptr += sizeof(uint64_t);
+
+ /* Pass over the base. Not needed. */
+ curr_data_ptr += sizeof(uint64_t);
+
+ /* Get semaphore location. */
+ curr_semaphore_location = *(uint64_t *) curr_data_ptr;
+ curr_data_ptr += sizeof(uint64_t);
+ /* Get provider name. */
+ curr_provider = curr_data_ptr;
+ curr_data_ptr += strlen(curr_provider) + 1;
+
+ /* Get probe name. */
+ curr_probe = curr_data_ptr;
+
+ /* Check if the provider and probe name match */
+ if (strcmp(provider_name, curr_provider) == 0 &&
+ strcmp(probe_name, curr_probe) == 0) {
+ int new_size;
+
+ /*
+ * We currently don't support SDT probes with semaphores. Return
+ * success as we found a matching probe but it's guarded by a
+ * semaphore.
+ */
+ if (curr_semaphore_location != 0) {
+ ret = LTTNG_ERR_SDT_PROBE_SEMAPHORE;
+ goto realloc_error;
+ }
+
+ new_size = (++nb_match) * sizeof(uint64_t);
+
+ /*
+ * Found a match with not semaphore, we need to copy the
+ * probe_location to the output parameter.
+ */
+ new_probe_locs = realloc(probe_locs, new_size);
+ if (!new_probe_locs) {
+ /* Error allocating a larger buffer */
+ DBG("Allocation error in SDT.");
+ ret = LTTNG_ERR_NOMEM;
+ goto realloc_error;
+ }
+ probe_locs = new_probe_locs;
+ new_probe_locs = NULL;
+
+ /*
+ * Use the virtual address of the probe to compute the offset of
+ * this probe from the beginning of the executable file.
+ */
+ ret = lttng_elf_convert_addr_in_text_to_offset(elf,
+ curr_probe_location, &curr_probe_offset);
+ if (ret) {
+ DBG("Conversion error in SDT.");
+ goto realloc_error;
+ }
+
+ probe_locs[nb_match - 1] = curr_probe_offset;
+ }
+ }
+
+end:
+ free(stap_note_section_data);
+destroy_elf_error:
+ lttng_elf_destroy(elf);
+error:
+ return ret;
+realloc_error:
+ free(probe_locs);
+ goto end;
+}