Fix: leak of lttng_elf_shdr in lttng-elf.c
[lttng-tools.git] / src / common / lttng-elf.c
index 779beee012929c5fa3a927f78a5a15cb0b527b3e..b2aa88487e1dff286c21d0442f6cdf175a68681f 100644 (file)
@@ -38,6 +38,8 @@
 #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 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;                  \
@@ -305,39 +309,32 @@ error:
  * 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;
 }
 
 /*
@@ -531,7 +528,7 @@ end:
 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;
 
@@ -556,15 +553,14 @@ struct lttng_elf *lttng_elf_create(int fd)
                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, &section_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:
@@ -603,15 +599,19 @@ void lttng_elf_destroy(struct lttng_elf *elf)
 
 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);
+               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;
                }
@@ -675,7 +675,7 @@ int lttng_elf_convert_addr_in_text_to_offset(struct lttng_elf *elf_handle,
        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.");
@@ -692,9 +692,10 @@ int lttng_elf_convert_addr_in_text_to_offset(struct lttng_elf *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.
@@ -735,8 +736,9 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        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;
+       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 ) {
@@ -750,16 +752,31 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *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;
@@ -767,7 +784,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        }
 
        /* 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.");
@@ -775,7 +792,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        }
 
        /* 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;
@@ -783,7 +800,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
        }
 
        /* 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++) {
@@ -842,7 +859,7 @@ int lttng_elf_get_symbol_offset(int fd, char *symbol, uint64_t *offset)
         */
        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;
        }
 
@@ -856,3 +873,184 @@ destroy_elf:
 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;
+}
This page took 0.029164 seconds and 4 git commands to generate.