Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 1 | #include "kvm/symbol.h" |
| 2 | |
| 3 | #include "kvm/kvm.h" |
| 4 | |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 5 | #include <linux/err.h> |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 6 | #include <stdlib.h> |
| 7 | #include <string.h> |
| 8 | #include <stdio.h> |
| 9 | #include <bfd.h> |
| 10 | |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 11 | static bfd *abfd; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 12 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 13 | int symbol_init(struct kvm *kvm) |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 14 | { |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 15 | int ret = 0; |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 16 | |
| 17 | if (!kvm->vmlinux) |
Sasha Levin | 49a8afd | 2012-09-17 10:03:30 +0200 | [diff] [blame] | 18 | return 0; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 19 | |
| 20 | bfd_init(); |
| 21 | |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 22 | abfd = bfd_openr(kvm->vmlinux, NULL); |
| 23 | if (abfd == NULL) { |
| 24 | bfd_error_type err = bfd_get_error(); |
| 25 | |
| 26 | switch (err) { |
| 27 | case bfd_error_no_memory: |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 28 | ret = -ENOMEM; |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 29 | break; |
| 30 | case bfd_error_invalid_target: |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 31 | ret = -EINVAL; |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 32 | break; |
| 33 | default: |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 34 | ret = -EFAULT; |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 35 | break; |
| 36 | } |
| 37 | } |
| 38 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 39 | return ret; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 40 | } |
Sasha Levin | 49a8afd | 2012-09-17 10:03:30 +0200 | [diff] [blame] | 41 | late_init(symbol_init); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 42 | |
| 43 | static asymbol *lookup(asymbol **symbols, int nr_symbols, const char *symbol_name) |
| 44 | { |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 45 | int i, ret; |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 46 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 47 | ret = -ENOENT; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 48 | |
| 49 | for (i = 0; i < nr_symbols; i++) { |
| 50 | asymbol *symbol = symbols[i]; |
| 51 | |
| 52 | if (!strcmp(bfd_asymbol_name(symbol), symbol_name)) |
| 53 | return symbol; |
| 54 | } |
| 55 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 56 | return ERR_PTR(ret); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 57 | } |
| 58 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 59 | char *symbol_lookup(struct kvm *kvm, unsigned long addr, char *sym, size_t size) |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 60 | { |
| 61 | const char *filename; |
| 62 | bfd_vma sym_offset; |
| 63 | bfd_vma sym_start; |
| 64 | asection *section; |
| 65 | unsigned int line; |
| 66 | const char *func; |
| 67 | long symtab_size; |
| 68 | asymbol *symbol; |
| 69 | asymbol **syms; |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 70 | int nr_syms, ret; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 71 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 72 | ret = -ENOENT; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 73 | if (!abfd) |
| 74 | goto not_found; |
| 75 | |
| 76 | if (!bfd_check_format(abfd, bfd_object)) |
| 77 | goto not_found; |
| 78 | |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 79 | symtab_size = bfd_get_symtab_upper_bound(abfd); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 80 | if (!symtab_size) |
| 81 | goto not_found; |
| 82 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 83 | ret = -ENOMEM; |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 84 | syms = malloc(symtab_size); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 85 | if (!syms) |
| 86 | goto not_found; |
| 87 | |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 88 | nr_syms = bfd_canonicalize_symtab(abfd, syms); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 89 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 90 | ret = -ENOENT; |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 91 | section = bfd_get_section_by_name(abfd, ".debug_aranges"); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 92 | if (!section) |
| 93 | goto not_found; |
| 94 | |
| 95 | if (!bfd_find_nearest_line(abfd, section, NULL, addr, &filename, &func, &line)) |
| 96 | goto not_found; |
| 97 | |
| 98 | if (!func) |
| 99 | goto not_found; |
| 100 | |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 101 | symbol = lookup(syms, nr_syms, func); |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 102 | if (IS_ERR(symbol)) |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 103 | goto not_found; |
| 104 | |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 105 | sym_start = bfd_asymbol_value(symbol); |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 106 | |
Sasha Levin | 3a60be0 | 2011-12-16 10:40:06 +0200 | [diff] [blame] | 107 | sym_offset = addr - sym_start; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 108 | |
| 109 | snprintf(sym, size, "%s+%llx (%s:%i)", func, (long long) sym_offset, filename, line); |
| 110 | |
| 111 | sym[size - 1] = '\0'; |
| 112 | |
| 113 | free(syms); |
| 114 | |
| 115 | return sym; |
| 116 | |
| 117 | not_found: |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 118 | return ERR_PTR(ret); |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 119 | } |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 120 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 121 | int symbol_exit(struct kvm *kvm) |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 122 | { |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 123 | bfd_boolean ret = TRUE; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 124 | |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 125 | if (abfd) |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 126 | ret = bfd_close(abfd); |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 127 | |
Cyrill Gorcunov | 807b77b | 2012-02-01 12:41:05 +0400 | [diff] [blame] | 128 | if (ret == TRUE) |
Sasha Levin | 4932d17 | 2012-01-29 09:37:23 -0500 | [diff] [blame] | 129 | return 0; |
| 130 | |
| 131 | return -EFAULT; |
Pekka Enberg | b0b42ba | 2011-05-11 19:14:39 +0300 | [diff] [blame] | 132 | } |
Sasha Levin | 49a8afd | 2012-09-17 10:03:30 +0200 | [diff] [blame] | 133 | late_exit(symbol_exit); |