|
@@ -1789,6 +1789,258 @@ void kcore_extract__delete(struct kcore_extract *kce)
|
|
|
unlink(kce->extract_filename);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * populate_sdt_note : Parse raw data and identify SDT note
|
|
|
+ * @elf: elf of the opened file
|
|
|
+ * @data: raw data of a section with description offset applied
|
|
|
+ * @len: note description size
|
|
|
+ * @type: type of the note
|
|
|
+ * @sdt_notes: List to add the SDT note
|
|
|
+ *
|
|
|
+ * Responsible for parsing the @data in section .note.stapsdt in @elf and
|
|
|
+ * if its an SDT note, it appends to @sdt_notes list.
|
|
|
+ */
|
|
|
+static int populate_sdt_note(Elf **elf, const char *data, size_t len,
|
|
|
+ struct list_head *sdt_notes)
|
|
|
+{
|
|
|
+ const char *provider, *name;
|
|
|
+ struct sdt_note *tmp = NULL;
|
|
|
+ GElf_Ehdr ehdr;
|
|
|
+ GElf_Addr base_off = 0;
|
|
|
+ GElf_Shdr shdr;
|
|
|
+ int ret = -EINVAL;
|
|
|
+
|
|
|
+ union {
|
|
|
+ Elf64_Addr a64[NR_ADDR];
|
|
|
+ Elf32_Addr a32[NR_ADDR];
|
|
|
+ } buf;
|
|
|
+
|
|
|
+ Elf_Data dst = {
|
|
|
+ .d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
|
|
|
+ .d_size = gelf_fsize((*elf), ELF_T_ADDR, NR_ADDR, EV_CURRENT),
|
|
|
+ .d_off = 0, .d_align = 0
|
|
|
+ };
|
|
|
+ Elf_Data src = {
|
|
|
+ .d_buf = (void *) data, .d_type = ELF_T_ADDR,
|
|
|
+ .d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
|
|
|
+ .d_align = 0
|
|
|
+ };
|
|
|
+
|
|
|
+ tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note));
|
|
|
+ if (!tmp) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&tmp->note_list);
|
|
|
+
|
|
|
+ if (len < dst.d_size + 3)
|
|
|
+ goto out_free_note;
|
|
|
+
|
|
|
+ /* Translation from file representation to memory representation */
|
|
|
+ if (gelf_xlatetom(*elf, &dst, &src,
|
|
|
+ elf_getident(*elf, NULL)[EI_DATA]) == NULL) {
|
|
|
+ pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1));
|
|
|
+ goto out_free_note;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Populate the fields of sdt_note */
|
|
|
+ provider = data + dst.d_size;
|
|
|
+
|
|
|
+ name = (const char *)memchr(provider, '\0', data + len - provider);
|
|
|
+ if (name++ == NULL)
|
|
|
+ goto out_free_note;
|
|
|
+
|
|
|
+ tmp->provider = strdup(provider);
|
|
|
+ if (!tmp->provider) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out_free_note;
|
|
|
+ }
|
|
|
+ tmp->name = strdup(name);
|
|
|
+ if (!tmp->name) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out_free_prov;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gelf_getclass(*elf) == ELFCLASS32) {
|
|
|
+ memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr));
|
|
|
+ tmp->bit32 = true;
|
|
|
+ } else {
|
|
|
+ memcpy(&tmp->addr, &buf, 3 * sizeof(Elf64_Addr));
|
|
|
+ tmp->bit32 = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!gelf_getehdr(*elf, &ehdr)) {
|
|
|
+ pr_debug("%s : cannot get elf header.\n", __func__);
|
|
|
+ ret = -EBADF;
|
|
|
+ goto out_free_name;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Adjust the prelink effect :
|
|
|
+ * Find out the .stapsdt.base section.
|
|
|
+ * This scn will help us to handle prelinking (if present).
|
|
|
+ * Compare the retrieved file offset of the base section with the
|
|
|
+ * base address in the description of the SDT note. If its different,
|
|
|
+ * then accordingly, adjust the note location.
|
|
|
+ */
|
|
|
+ if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) {
|
|
|
+ base_off = shdr.sh_offset;
|
|
|
+ if (base_off) {
|
|
|
+ if (tmp->bit32)
|
|
|
+ tmp->addr.a32[0] = tmp->addr.a32[0] + base_off -
|
|
|
+ tmp->addr.a32[1];
|
|
|
+ else
|
|
|
+ tmp->addr.a64[0] = tmp->addr.a64[0] + base_off -
|
|
|
+ tmp->addr.a64[1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ list_add_tail(&tmp->note_list, sdt_notes);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_free_name:
|
|
|
+ free(tmp->name);
|
|
|
+out_free_prov:
|
|
|
+ free(tmp->provider);
|
|
|
+out_free_note:
|
|
|
+ free(tmp);
|
|
|
+out_err:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * construct_sdt_notes_list : constructs a list of SDT notes
|
|
|
+ * @elf : elf to look into
|
|
|
+ * @sdt_notes : empty list_head
|
|
|
+ *
|
|
|
+ * Scans the sections in 'elf' for the section
|
|
|
+ * .note.stapsdt. It, then calls populate_sdt_note to find
|
|
|
+ * out the SDT events and populates the 'sdt_notes'.
|
|
|
+ */
|
|
|
+static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
|
|
|
+{
|
|
|
+ GElf_Ehdr ehdr;
|
|
|
+ Elf_Scn *scn = NULL;
|
|
|
+ Elf_Data *data;
|
|
|
+ GElf_Shdr shdr;
|
|
|
+ size_t shstrndx, next;
|
|
|
+ GElf_Nhdr nhdr;
|
|
|
+ size_t name_off, desc_off, offset;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
|
|
|
+ ret = -EBADF;
|
|
|
+ goto out_ret;
|
|
|
+ }
|
|
|
+ if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
|
|
|
+ ret = -EBADF;
|
|
|
+ goto out_ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Look for the required section */
|
|
|
+ scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
|
|
|
+ if (!scn) {
|
|
|
+ ret = -ENOENT;
|
|
|
+ goto out_ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {
|
|
|
+ ret = -ENOENT;
|
|
|
+ goto out_ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ data = elf_getdata(scn, NULL);
|
|
|
+
|
|
|
+ /* Get the SDT notes */
|
|
|
+ for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
|
|
|
+ &desc_off)) > 0; offset = next) {
|
|
|
+ if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
|
|
|
+ !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
|
|
|
+ sizeof(SDT_NOTE_NAME))) {
|
|
|
+ /* Check the type of the note */
|
|
|
+ if (nhdr.n_type != SDT_NOTE_TYPE)
|
|
|
+ goto out_ret;
|
|
|
+
|
|
|
+ ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
|
|
|
+ nhdr.n_descsz, sdt_notes);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (list_empty(sdt_notes))
|
|
|
+ ret = -ENOENT;
|
|
|
+
|
|
|
+out_ret:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * get_sdt_note_list : Wrapper to construct a list of sdt notes
|
|
|
+ * @head : empty list_head
|
|
|
+ * @target : file to find SDT notes from
|
|
|
+ *
|
|
|
+ * This opens the file, initializes
|
|
|
+ * the ELF and then calls construct_sdt_notes_list.
|
|
|
+ */
|
|
|
+int get_sdt_note_list(struct list_head *head, const char *target)
|
|
|
+{
|
|
|
+ Elf *elf;
|
|
|
+ int fd, ret;
|
|
|
+
|
|
|
+ fd = open(target, O_RDONLY);
|
|
|
+ if (fd < 0)
|
|
|
+ return -EBADF;
|
|
|
+
|
|
|
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
|
|
|
+ if (!elf) {
|
|
|
+ ret = -EBADF;
|
|
|
+ goto out_close;
|
|
|
+ }
|
|
|
+ ret = construct_sdt_notes_list(elf, head);
|
|
|
+ elf_end(elf);
|
|
|
+out_close:
|
|
|
+ close(fd);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * cleanup_sdt_note_list : free the sdt notes' list
|
|
|
+ * @sdt_notes: sdt notes' list
|
|
|
+ *
|
|
|
+ * Free up the SDT notes in @sdt_notes.
|
|
|
+ * Returns the number of SDT notes free'd.
|
|
|
+ */
|
|
|
+int cleanup_sdt_note_list(struct list_head *sdt_notes)
|
|
|
+{
|
|
|
+ struct sdt_note *tmp, *pos;
|
|
|
+ int nr_free = 0;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
|
|
|
+ list_del(&pos->note_list);
|
|
|
+ free(pos->name);
|
|
|
+ free(pos->provider);
|
|
|
+ free(pos);
|
|
|
+ nr_free++;
|
|
|
+ }
|
|
|
+ return nr_free;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * sdt_notes__get_count: Counts the number of sdt events
|
|
|
+ * @start: list_head to sdt_notes list
|
|
|
+ *
|
|
|
+ * Returns the number of SDT notes in a list
|
|
|
+ */
|
|
|
+int sdt_notes__get_count(struct list_head *start)
|
|
|
+{
|
|
|
+ struct sdt_note *sdt_ptr;
|
|
|
+ int count = 0;
|
|
|
+
|
|
|
+ list_for_each_entry(sdt_ptr, start, note_list)
|
|
|
+ count++;
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
void symbol__elf_init(void)
|
|
|
{
|
|
|
elf_version(EV_CURRENT);
|