summaryrefslogtreecommitdiff
path: root/tools/perf/util/symbol-elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/symbol-elf.c')
-rw-r--r--tools/perf/util/symbol-elf.c607
1 files changed, 6 insertions, 601 deletions
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index eed0b96..a9c829b 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -8,7 +8,7 @@
#include "symbol.h"
#include "debug.h"
-#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT
+#ifndef HAVE_ELF_GETPHDRNUM
static int elf_getphdrnum(Elf *elf, size_t *dst)
{
GElf_Ehdr gehdr;
@@ -487,27 +487,27 @@ int filename__read_debuglink(const char *filename, char *debuglink,
ek = elf_kind(elf);
if (ek != ELF_K_ELF)
- goto out_elf_end;
+ goto out_close;
if (gelf_getehdr(elf, &ehdr) == NULL) {
pr_err("%s: cannot get elf header.\n", __func__);
- goto out_elf_end;
+ goto out_close;
}
sec = elf_section_by_name(elf, &ehdr, &shdr,
".gnu_debuglink", NULL);
if (sec == NULL)
- goto out_elf_end;
+ goto out_close;
data = elf_getdata(sec, NULL);
if (data == NULL)
- goto out_elf_end;
+ goto out_close;
/* the start of this section is a zero-terminated string */
strncpy(debuglink, data->d_buf, size);
-out_elf_end:
elf_end(elf);
+
out_close:
close(fd);
out:
@@ -1018,601 +1018,6 @@ int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
return err;
}
-static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len)
-{
- ssize_t r;
- size_t n;
- int err = -1;
- char *buf = malloc(page_size);
-
- if (buf == NULL)
- return -1;
-
- if (lseek(to, to_offs, SEEK_SET) != to_offs)
- goto out;
-
- if (lseek(from, from_offs, SEEK_SET) != from_offs)
- goto out;
-
- while (len) {
- n = page_size;
- if (len < n)
- n = len;
- /* Use read because mmap won't work on proc files */
- r = read(from, buf, n);
- if (r < 0)
- goto out;
- if (!r)
- break;
- n = r;
- r = write(to, buf, n);
- if (r < 0)
- goto out;
- if ((size_t)r != n)
- goto out;
- len -= n;
- }
-
- err = 0;
-out:
- free(buf);
- return err;
-}
-
-struct kcore {
- int fd;
- int elfclass;
- Elf *elf;
- GElf_Ehdr ehdr;
-};
-
-static int kcore__open(struct kcore *kcore, const char *filename)
-{
- GElf_Ehdr *ehdr;
-
- kcore->fd = open(filename, O_RDONLY);
- if (kcore->fd == -1)
- return -1;
-
- kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL);
- if (!kcore->elf)
- goto out_close;
-
- kcore->elfclass = gelf_getclass(kcore->elf);
- if (kcore->elfclass == ELFCLASSNONE)
- goto out_end;
-
- ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr);
- if (!ehdr)
- goto out_end;
-
- return 0;
-
-out_end:
- elf_end(kcore->elf);
-out_close:
- close(kcore->fd);
- return -1;
-}
-
-static int kcore__init(struct kcore *kcore, char *filename, int elfclass,
- bool temp)
-{
- GElf_Ehdr *ehdr;
-
- kcore->elfclass = elfclass;
-
- if (temp)
- kcore->fd = mkstemp(filename);
- else
- kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400);
- if (kcore->fd == -1)
- return -1;
-
- kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL);
- if (!kcore->elf)
- goto out_close;
-
- if (!gelf_newehdr(kcore->elf, elfclass))
- goto out_end;
-
- ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr);
- if (!ehdr)
- goto out_end;
-
- return 0;
-
-out_end:
- elf_end(kcore->elf);
-out_close:
- close(kcore->fd);
- unlink(filename);
- return -1;
-}
-
-static void kcore__close(struct kcore *kcore)
-{
- elf_end(kcore->elf);
- close(kcore->fd);
-}
-
-static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count)
-{
- GElf_Ehdr *ehdr = &to->ehdr;
- GElf_Ehdr *kehdr = &from->ehdr;
-
- memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT);
- ehdr->e_type = kehdr->e_type;
- ehdr->e_machine = kehdr->e_machine;
- ehdr->e_version = kehdr->e_version;
- ehdr->e_entry = 0;
- ehdr->e_shoff = 0;
- ehdr->e_flags = kehdr->e_flags;
- ehdr->e_phnum = count;
- ehdr->e_shentsize = 0;
- ehdr->e_shnum = 0;
- ehdr->e_shstrndx = 0;
-
- if (from->elfclass == ELFCLASS32) {
- ehdr->e_phoff = sizeof(Elf32_Ehdr);
- ehdr->e_ehsize = sizeof(Elf32_Ehdr);
- ehdr->e_phentsize = sizeof(Elf32_Phdr);
- } else {
- ehdr->e_phoff = sizeof(Elf64_Ehdr);
- ehdr->e_ehsize = sizeof(Elf64_Ehdr);
- ehdr->e_phentsize = sizeof(Elf64_Phdr);
- }
-
- if (!gelf_update_ehdr(to->elf, ehdr))
- return -1;
-
- if (!gelf_newphdr(to->elf, count))
- return -1;
-
- return 0;
-}
-
-static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset,
- u64 addr, u64 len)
-{
- GElf_Phdr gphdr;
- GElf_Phdr *phdr;
-
- phdr = gelf_getphdr(kcore->elf, idx, &gphdr);
- if (!phdr)
- return -1;
-
- phdr->p_type = PT_LOAD;
- phdr->p_flags = PF_R | PF_W | PF_X;
- phdr->p_offset = offset;
- phdr->p_vaddr = addr;
- phdr->p_paddr = 0;
- phdr->p_filesz = len;
- phdr->p_memsz = len;
- phdr->p_align = page_size;
-
- if (!gelf_update_phdr(kcore->elf, idx, phdr))
- return -1;
-
- return 0;
-}
-
-static off_t kcore__write(struct kcore *kcore)
-{
- return elf_update(kcore->elf, ELF_C_WRITE);
-}
-
-struct phdr_data {
- off_t offset;
- u64 addr;
- u64 len;
-};
-
-struct kcore_copy_info {
- u64 stext;
- u64 etext;
- u64 first_symbol;
- u64 last_symbol;
- u64 first_module;
- u64 last_module_symbol;
- struct phdr_data kernel_map;
- struct phdr_data modules_map;
-};
-
-static int kcore_copy__process_kallsyms(void *arg, const char *name, char type,
- u64 start)
-{
- struct kcore_copy_info *kci = arg;
-
- if (!symbol_type__is_a(type, MAP__FUNCTION))
- return 0;
-
- if (strchr(name, '[')) {
- if (start > kci->last_module_symbol)
- kci->last_module_symbol = start;
- return 0;
- }
-
- if (!kci->first_symbol || start < kci->first_symbol)
- kci->first_symbol = start;
-
- if (!kci->last_symbol || start > kci->last_symbol)
- kci->last_symbol = start;
-
- if (!strcmp(name, "_stext")) {
- kci->stext = start;
- return 0;
- }
-
- if (!strcmp(name, "_etext")) {
- kci->etext = start;
- return 0;
- }
-
- return 0;
-}
-
-static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci,
- const char *dir)
-{
- char kallsyms_filename[PATH_MAX];
-
- scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir);
-
- if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms"))
- return -1;
-
- if (kallsyms__parse(kallsyms_filename, kci,
- kcore_copy__process_kallsyms) < 0)
- return -1;
-
- return 0;
-}
-
-static int kcore_copy__process_modules(void *arg,
- const char *name __maybe_unused,
- u64 start)
-{
- struct kcore_copy_info *kci = arg;
-
- if (!kci->first_module || start < kci->first_module)
- kci->first_module = start;
-
- return 0;
-}
-
-static int kcore_copy__parse_modules(struct kcore_copy_info *kci,
- const char *dir)
-{
- char modules_filename[PATH_MAX];
-
- scnprintf(modules_filename, PATH_MAX, "%s/modules", dir);
-
- if (symbol__restricted_filename(modules_filename, "/proc/modules"))
- return -1;
-
- if (modules__parse(modules_filename, kci,
- kcore_copy__process_modules) < 0)
- return -1;
-
- return 0;
-}
-
-static void kcore_copy__map(struct phdr_data *p, u64 start, u64 end, u64 pgoff,
- u64 s, u64 e)
-{
- if (p->addr || s < start || s >= end)
- return;
-
- p->addr = s;
- p->offset = (s - start) + pgoff;
- p->len = e < end ? e - s : end - s;
-}
-
-static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data)
-{
- struct kcore_copy_info *kci = data;
- u64 end = start + len;
-
- kcore_copy__map(&kci->kernel_map, start, end, pgoff, kci->stext,
- kci->etext);
-
- kcore_copy__map(&kci->modules_map, start, end, pgoff, kci->first_module,
- kci->last_module_symbol);
-
- return 0;
-}
-
-static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf)
-{
- if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0)
- return -1;
-
- return 0;
-}
-
-static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir,
- Elf *elf)
-{
- if (kcore_copy__parse_kallsyms(kci, dir))
- return -1;
-
- if (kcore_copy__parse_modules(kci, dir))
- return -1;
-
- if (kci->stext)
- kci->stext = round_down(kci->stext, page_size);
- else
- kci->stext = round_down(kci->first_symbol, page_size);
-
- if (kci->etext) {
- kci->etext = round_up(kci->etext, page_size);
- } else if (kci->last_symbol) {
- kci->etext = round_up(kci->last_symbol, page_size);
- kci->etext += page_size;
- }
-
- kci->first_module = round_down(kci->first_module, page_size);
-
- if (kci->last_module_symbol) {
- kci->last_module_symbol = round_up(kci->last_module_symbol,
- page_size);
- kci->last_module_symbol += page_size;
- }
-
- if (!kci->stext || !kci->etext)
- return -1;
-
- if (kci->first_module && !kci->last_module_symbol)
- return -1;
-
- return kcore_copy__read_maps(kci, elf);
-}
-
-static int kcore_copy__copy_file(const char *from_dir, const char *to_dir,
- const char *name)
-{
- char from_filename[PATH_MAX];
- char to_filename[PATH_MAX];
-
- scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name);
- scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name);
-
- return copyfile_mode(from_filename, to_filename, 0400);
-}
-
-static int kcore_copy__unlink(const char *dir, const char *name)
-{
- char filename[PATH_MAX];
-
- scnprintf(filename, PATH_MAX, "%s/%s", dir, name);
-
- return unlink(filename);
-}
-
-static int kcore_copy__compare_fds(int from, int to)
-{
- char *buf_from;
- char *buf_to;
- ssize_t ret;
- size_t len;
- int err = -1;
-
- buf_from = malloc(page_size);
- buf_to = malloc(page_size);
- if (!buf_from || !buf_to)
- goto out;
-
- while (1) {
- /* Use read because mmap won't work on proc files */
- ret = read(from, buf_from, page_size);
- if (ret < 0)
- goto out;
-
- if (!ret)
- break;
-
- len = ret;
-
- if (readn(to, buf_to, len) != (int)len)
- goto out;
-
- if (memcmp(buf_from, buf_to, len))
- goto out;
- }
-
- err = 0;
-out:
- free(buf_to);
- free(buf_from);
- return err;
-}
-
-static int kcore_copy__compare_files(const char *from_filename,
- const char *to_filename)
-{
- int from, to, err = -1;
-
- from = open(from_filename, O_RDONLY);
- if (from < 0)
- return -1;
-
- to = open(to_filename, O_RDONLY);
- if (to < 0)
- goto out_close_from;
-
- err = kcore_copy__compare_fds(from, to);
-
- close(to);
-out_close_from:
- close(from);
- return err;
-}
-
-static int kcore_copy__compare_file(const char *from_dir, const char *to_dir,
- const char *name)
-{
- char from_filename[PATH_MAX];
- char to_filename[PATH_MAX];
-
- scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name);
- scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name);
-
- return kcore_copy__compare_files(from_filename, to_filename);
-}
-
-/**
- * kcore_copy - copy kallsyms, modules and kcore from one directory to another.
- * @from_dir: from directory
- * @to_dir: to directory
- *
- * This function copies kallsyms, modules and kcore files from one directory to
- * another. kallsyms and modules are copied entirely. Only code segments are
- * copied from kcore. It is assumed that two segments suffice: one for the
- * kernel proper and one for all the modules. The code segments are determined
- * from kallsyms and modules files. The kernel map starts at _stext or the
- * lowest function symbol, and ends at _etext or the highest function symbol.
- * The module map starts at the lowest module address and ends at the highest
- * module symbol. Start addresses are rounded down to the nearest page. End
- * addresses are rounded up to the nearest page. An extra page is added to the
- * highest kernel symbol and highest module symbol to, hopefully, encompass that
- * symbol too. Because it contains only code sections, the resulting kcore is
- * unusual. One significant peculiarity is that the mapping (start -> pgoff)
- * is not the same for the kernel map and the modules map. That happens because
- * the data is copied adjacently whereas the original kcore has gaps. Finally,
- * kallsyms and modules files are compared with their copies to check that
- * modules have not been loaded or unloaded while the copies were taking place.
- *
- * Return: %0 on success, %-1 on failure.
- */
-int kcore_copy(const char *from_dir, const char *to_dir)
-{
- struct kcore kcore;
- struct kcore extract;
- size_t count = 2;
- int idx = 0, err = -1;
- off_t offset = page_size, sz, modules_offset = 0;
- struct kcore_copy_info kci = { .stext = 0, };
- char kcore_filename[PATH_MAX];
- char extract_filename[PATH_MAX];
-
- if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms"))
- return -1;
-
- if (kcore_copy__copy_file(from_dir, to_dir, "modules"))
- goto out_unlink_kallsyms;
-
- scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir);
- scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir);
-
- if (kcore__open(&kcore, kcore_filename))
- goto out_unlink_modules;
-
- if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf))
- goto out_kcore_close;
-
- if (kcore__init(&extract, extract_filename, kcore.elfclass, false))
- goto out_kcore_close;
-
- if (!kci.modules_map.addr)
- count -= 1;
-
- if (kcore__copy_hdr(&kcore, &extract, count))
- goto out_extract_close;
-
- if (kcore__add_phdr(&extract, idx++, offset, kci.kernel_map.addr,
- kci.kernel_map.len))
- goto out_extract_close;
-
- if (kci.modules_map.addr) {
- modules_offset = offset + kci.kernel_map.len;
- if (kcore__add_phdr(&extract, idx, modules_offset,
- kci.modules_map.addr, kci.modules_map.len))
- goto out_extract_close;
- }
-
- sz = kcore__write(&extract);
- if (sz < 0 || sz > offset)
- goto out_extract_close;
-
- if (copy_bytes(kcore.fd, kci.kernel_map.offset, extract.fd, offset,
- kci.kernel_map.len))
- goto out_extract_close;
-
- if (modules_offset && copy_bytes(kcore.fd, kci.modules_map.offset,
- extract.fd, modules_offset,
- kci.modules_map.len))
- goto out_extract_close;
-
- if (kcore_copy__compare_file(from_dir, to_dir, "modules"))
- goto out_extract_close;
-
- if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms"))
- goto out_extract_close;
-
- err = 0;
-
-out_extract_close:
- kcore__close(&extract);
- if (err)
- unlink(extract_filename);
-out_kcore_close:
- kcore__close(&kcore);
-out_unlink_modules:
- if (err)
- kcore_copy__unlink(to_dir, "modules");
-out_unlink_kallsyms:
- if (err)
- kcore_copy__unlink(to_dir, "kallsyms");
-
- return err;
-}
-
-int kcore_extract__create(struct kcore_extract *kce)
-{
- struct kcore kcore;
- struct kcore extract;
- size_t count = 1;
- int idx = 0, err = -1;
- off_t offset = page_size, sz;
-
- if (kcore__open(&kcore, kce->kcore_filename))
- return -1;
-
- strcpy(kce->extract_filename, PERF_KCORE_EXTRACT);
- if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true))
- goto out_kcore_close;
-
- if (kcore__copy_hdr(&kcore, &extract, count))
- goto out_extract_close;
-
- if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len))
- goto out_extract_close;
-
- sz = kcore__write(&extract);
- if (sz < 0 || sz > offset)
- goto out_extract_close;
-
- if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len))
- goto out_extract_close;
-
- err = 0;
-
-out_extract_close:
- kcore__close(&extract);
- if (err)
- unlink(kce->extract_filename);
-out_kcore_close:
- kcore__close(&kcore);
-
- return err;
-}
-
-void kcore_extract__delete(struct kcore_extract *kce)
-{
- unlink(kce->extract_filename);
-}
-
void symbol__elf_init(void)
{
elf_version(EV_CURRENT);