1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2e5a1845fSNamhyung Kim #include <fcntl.h>
3e5a1845fSNamhyung Kim #include <stdio.h>
4e5a1845fSNamhyung Kim #include <errno.h>
5215a0d30SArnaldo Carvalho de Melo #include <stdlib.h>
6e5a1845fSNamhyung Kim #include <string.h>
7e5a1845fSNamhyung Kim #include <unistd.h>
8e5a1845fSNamhyung Kim #include <inttypes.h>
9e5a1845fSNamhyung Kim
1009aa3b00SArnaldo Carvalho de Melo #include "dso.h"
111101f69aSArnaldo Carvalho de Melo #include "map.h"
12c54d241bSArnaldo Carvalho de Melo #include "maps.h"
13e5a1845fSNamhyung Kim #include "symbol.h"
14b1d1b094SArnaldo Carvalho de Melo #include "symsrc.h"
153b4e4efeSIan Rogers #include "demangle-cxx.h"
16cef7af25SFabian Hemmer #include "demangle-ocaml.h"
17e9c4bcddSStephane Eranian #include "demangle-java.h"
18cae15db7SDavid Tolnay #include "demangle-rust.h"
198fa7d87fSWaiman Long #include "machine.h"
20922d0e4dSVladimir Nikulichev #include "vdso.h"
21e5a1845fSNamhyung Kim #include "debug.h"
2232ff3fecSArnaldo Carvalho de Melo #include "util/copyfile.h"
233052ba56SArnaldo Carvalho de Melo #include <linux/ctype.h>
24fb71c86cSArnaldo Carvalho de Melo #include <linux/kernel.h>
257f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
263d689ed6SArnaldo Carvalho de Melo #include <symbol/kallsyms.h>
27fb71c86cSArnaldo Carvalho de Melo #include <internal/lib.h>
28e5a1845fSNamhyung Kim
293b4e4efeSIan Rogers #ifdef HAVE_LIBBFD_SUPPORT
303b4e4efeSIan Rogers #define PACKAGE 'perf'
313b4e4efeSIan Rogers #include <bfd.h>
323b4e4efeSIan Rogers #endif
333b4e4efeSIan Rogers
3465cd8e55SIan Rogers #if defined(HAVE_LIBBFD_SUPPORT) || defined(HAVE_CPLUS_DEMANGLE_SUPPORT)
3565cd8e55SIan Rogers #ifndef DMGL_PARAMS
3665cd8e55SIan Rogers #define DMGL_PARAMS (1 << 0) /* Include function args */
3765cd8e55SIan Rogers #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
3865cd8e55SIan Rogers #endif
3965cd8e55SIan Rogers #endif
4065cd8e55SIan Rogers
41e370a3d5SDavid Ahern #ifndef EM_AARCH64
42e370a3d5SDavid Ahern #define EM_AARCH64 183 /* ARM 64 bit */
43e370a3d5SDavid Ahern #endif
44e370a3d5SDavid Ahern
45765be32bSTiezhu Yang #ifndef EM_LOONGARCH
46765be32bSTiezhu Yang #define EM_LOONGARCH 258
47765be32bSTiezhu Yang #endif
48765be32bSTiezhu Yang
49843cf70eSArnaldo Carvalho de Melo #ifndef ELF32_ST_VISIBILITY
50843cf70eSArnaldo Carvalho de Melo #define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
51843cf70eSArnaldo Carvalho de Melo #endif
52843cf70eSArnaldo Carvalho de Melo
53843cf70eSArnaldo Carvalho de Melo /* For ELF64 the definitions are the same. */
54843cf70eSArnaldo Carvalho de Melo #ifndef ELF64_ST_VISIBILITY
55843cf70eSArnaldo Carvalho de Melo #define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
56843cf70eSArnaldo Carvalho de Melo #endif
57843cf70eSArnaldo Carvalho de Melo
58843cf70eSArnaldo Carvalho de Melo /* How to extract information held in the st_other field. */
59843cf70eSArnaldo Carvalho de Melo #ifndef GELF_ST_VISIBILITY
60843cf70eSArnaldo Carvalho de Melo #define GELF_ST_VISIBILITY(val) ELF64_ST_VISIBILITY (val)
61843cf70eSArnaldo Carvalho de Melo #endif
62843cf70eSArnaldo Carvalho de Melo
63cc31078cSArnaldo Carvalho de Melo typedef Elf64_Nhdr GElf_Nhdr;
64e370a3d5SDavid Ahern
65aaba4e12SArnaldo Carvalho de Melo
6689fe808aSIngo Molnar #ifndef HAVE_ELF_GETPHDRNUM_SUPPORT
elf_getphdrnum(Elf * elf,size_t * dst)67179f36ddSArnaldo Carvalho de Melo static int elf_getphdrnum(Elf *elf, size_t *dst)
68e955d5c4SAdrian Hunter {
69e955d5c4SAdrian Hunter GElf_Ehdr gehdr;
70e955d5c4SAdrian Hunter GElf_Ehdr *ehdr;
71e955d5c4SAdrian Hunter
72e955d5c4SAdrian Hunter ehdr = gelf_getehdr(elf, &gehdr);
73e955d5c4SAdrian Hunter if (!ehdr)
74e955d5c4SAdrian Hunter return -1;
75e955d5c4SAdrian Hunter
76e955d5c4SAdrian Hunter *dst = ehdr->e_phnum;
77e955d5c4SAdrian Hunter
78e955d5c4SAdrian Hunter return 0;
79e955d5c4SAdrian Hunter }
80e955d5c4SAdrian Hunter #endif
81e955d5c4SAdrian Hunter
822492c465SArnaldo Carvalho de Melo #ifndef HAVE_ELF_GETSHDRSTRNDX_SUPPORT
elf_getshdrstrndx(Elf * elf __maybe_unused,size_t * dst __maybe_unused)832492c465SArnaldo Carvalho de Melo static int elf_getshdrstrndx(Elf *elf __maybe_unused, size_t *dst __maybe_unused)
842492c465SArnaldo Carvalho de Melo {
852492c465SArnaldo Carvalho de Melo pr_err("%s: update your libelf to > 0.140, this one lacks elf_getshdrstrndx().\n", __func__);
862492c465SArnaldo Carvalho de Melo return -1;
872492c465SArnaldo Carvalho de Melo }
882492c465SArnaldo Carvalho de Melo #endif
892492c465SArnaldo Carvalho de Melo
90e5a1845fSNamhyung Kim #ifndef NT_GNU_BUILD_ID
91e5a1845fSNamhyung Kim #define NT_GNU_BUILD_ID 3
92e5a1845fSNamhyung Kim #endif
93e5a1845fSNamhyung Kim
94e5a1845fSNamhyung Kim /**
95e5a1845fSNamhyung Kim * elf_symtab__for_each_symbol - iterate thru all the symbols
96e5a1845fSNamhyung Kim *
97e5a1845fSNamhyung Kim * @syms: struct elf_symtab instance to iterate
98e5a1845fSNamhyung Kim * @idx: uint32_t idx
99e5a1845fSNamhyung Kim * @sym: GElf_Sym iterator
100e5a1845fSNamhyung Kim */
101e5a1845fSNamhyung Kim #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
102e5a1845fSNamhyung Kim for (idx = 0, gelf_getsym(syms, idx, &sym);\
103e5a1845fSNamhyung Kim idx < nr_syms; \
104e5a1845fSNamhyung Kim idx++, gelf_getsym(syms, idx, &sym))
105e5a1845fSNamhyung Kim
elf_sym__type(const GElf_Sym * sym)106e5a1845fSNamhyung Kim static inline uint8_t elf_sym__type(const GElf_Sym *sym)
107e5a1845fSNamhyung Kim {
108e5a1845fSNamhyung Kim return GELF_ST_TYPE(sym->st_info);
109e5a1845fSNamhyung Kim }
110e5a1845fSNamhyung Kim
elf_sym__visibility(const GElf_Sym * sym)11159a17706SJiri Olsa static inline uint8_t elf_sym__visibility(const GElf_Sym *sym)
11259a17706SJiri Olsa {
11359a17706SJiri Olsa return GELF_ST_VISIBILITY(sym->st_other);
11459a17706SJiri Olsa }
11559a17706SJiri Olsa
1164e31050fSVinson Lee #ifndef STT_GNU_IFUNC
1174e31050fSVinson Lee #define STT_GNU_IFUNC 10
1184e31050fSVinson Lee #endif
1194e31050fSVinson Lee
elf_sym__is_function(const GElf_Sym * sym)120e5a1845fSNamhyung Kim static inline int elf_sym__is_function(const GElf_Sym *sym)
121e5a1845fSNamhyung Kim {
122a2f3b6bfSAdrian Hunter return (elf_sym__type(sym) == STT_FUNC ||
123a2f3b6bfSAdrian Hunter elf_sym__type(sym) == STT_GNU_IFUNC) &&
124e5a1845fSNamhyung Kim sym->st_name != 0 &&
125e5a1845fSNamhyung Kim sym->st_shndx != SHN_UNDEF;
126e5a1845fSNamhyung Kim }
127e5a1845fSNamhyung Kim
elf_sym__is_object(const GElf_Sym * sym)128e5a1845fSNamhyung Kim static inline bool elf_sym__is_object(const GElf_Sym *sym)
129e5a1845fSNamhyung Kim {
130e5a1845fSNamhyung Kim return elf_sym__type(sym) == STT_OBJECT &&
131e5a1845fSNamhyung Kim sym->st_name != 0 &&
132e5a1845fSNamhyung Kim sym->st_shndx != SHN_UNDEF;
133e5a1845fSNamhyung Kim }
134e5a1845fSNamhyung Kim
elf_sym__is_label(const GElf_Sym * sym)135e5a1845fSNamhyung Kim static inline int elf_sym__is_label(const GElf_Sym *sym)
136e5a1845fSNamhyung Kim {
137e5a1845fSNamhyung Kim return elf_sym__type(sym) == STT_NOTYPE &&
138e5a1845fSNamhyung Kim sym->st_name != 0 &&
139e5a1845fSNamhyung Kim sym->st_shndx != SHN_UNDEF &&
14059a17706SJiri Olsa sym->st_shndx != SHN_ABS &&
14159a17706SJiri Olsa elf_sym__visibility(sym) != STV_HIDDEN &&
14259a17706SJiri Olsa elf_sym__visibility(sym) != STV_INTERNAL;
143e5a1845fSNamhyung Kim }
144e5a1845fSNamhyung Kim
elf_sym__filter(GElf_Sym * sym)1453183f8caSArnaldo Carvalho de Melo static bool elf_sym__filter(GElf_Sym *sym)
146e5a1845fSNamhyung Kim {
1473183f8caSArnaldo Carvalho de Melo return elf_sym__is_function(sym) || elf_sym__is_object(sym);
148e5a1845fSNamhyung Kim }
149e5a1845fSNamhyung Kim
elf_sym__name(const GElf_Sym * sym,const Elf_Data * symstrs)150e5a1845fSNamhyung Kim static inline const char *elf_sym__name(const GElf_Sym *sym,
151e5a1845fSNamhyung Kim const Elf_Data *symstrs)
152e5a1845fSNamhyung Kim {
153e5a1845fSNamhyung Kim return symstrs->d_buf + sym->st_name;
154e5a1845fSNamhyung Kim }
155e5a1845fSNamhyung Kim
elf_sec__name(const GElf_Shdr * shdr,const Elf_Data * secstrs)156e5a1845fSNamhyung Kim static inline const char *elf_sec__name(const GElf_Shdr *shdr,
157e5a1845fSNamhyung Kim const Elf_Data *secstrs)
158e5a1845fSNamhyung Kim {
159e5a1845fSNamhyung Kim return secstrs->d_buf + shdr->sh_name;
160e5a1845fSNamhyung Kim }
161e5a1845fSNamhyung Kim
elf_sec__is_text(const GElf_Shdr * shdr,const Elf_Data * secstrs)162e5a1845fSNamhyung Kim static inline int elf_sec__is_text(const GElf_Shdr *shdr,
163e5a1845fSNamhyung Kim const Elf_Data *secstrs)
164e5a1845fSNamhyung Kim {
165e5a1845fSNamhyung Kim return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
166e5a1845fSNamhyung Kim }
167e5a1845fSNamhyung Kim
elf_sec__is_data(const GElf_Shdr * shdr,const Elf_Data * secstrs)168e5a1845fSNamhyung Kim static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
169e5a1845fSNamhyung Kim const Elf_Data *secstrs)
170e5a1845fSNamhyung Kim {
171e5a1845fSNamhyung Kim return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
172e5a1845fSNamhyung Kim }
173e5a1845fSNamhyung Kim
elf_sec__filter(GElf_Shdr * shdr,Elf_Data * secstrs)1743183f8caSArnaldo Carvalho de Melo static bool elf_sec__filter(GElf_Shdr *shdr, Elf_Data *secstrs)
175e5a1845fSNamhyung Kim {
1763183f8caSArnaldo Carvalho de Melo return elf_sec__is_text(shdr, secstrs) ||
1773183f8caSArnaldo Carvalho de Melo elf_sec__is_data(shdr, secstrs);
178e5a1845fSNamhyung Kim }
179e5a1845fSNamhyung Kim
elf_addr_to_index(Elf * elf,GElf_Addr addr)180e5a1845fSNamhyung Kim static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
181e5a1845fSNamhyung Kim {
182e5a1845fSNamhyung Kim Elf_Scn *sec = NULL;
183e5a1845fSNamhyung Kim GElf_Shdr shdr;
184e5a1845fSNamhyung Kim size_t cnt = 1;
185e5a1845fSNamhyung Kim
186e5a1845fSNamhyung Kim while ((sec = elf_nextscn(elf, sec)) != NULL) {
187e5a1845fSNamhyung Kim gelf_getshdr(sec, &shdr);
188e5a1845fSNamhyung Kim
189e5a1845fSNamhyung Kim if ((addr >= shdr.sh_addr) &&
190e5a1845fSNamhyung Kim (addr < (shdr.sh_addr + shdr.sh_size)))
191e5a1845fSNamhyung Kim return cnt;
192e5a1845fSNamhyung Kim
193e5a1845fSNamhyung Kim ++cnt;
194e5a1845fSNamhyung Kim }
195e5a1845fSNamhyung Kim
196e5a1845fSNamhyung Kim return -1;
197e5a1845fSNamhyung Kim }
198e5a1845fSNamhyung Kim
elf_section_by_name(Elf * elf,GElf_Ehdr * ep,GElf_Shdr * shp,const char * name,size_t * idx)19999ca4233SMasami Hiramatsu Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
20099ca4233SMasami Hiramatsu GElf_Shdr *shp, const char *name, size_t *idx)
201e5a1845fSNamhyung Kim {
202e5a1845fSNamhyung Kim Elf_Scn *sec = NULL;
203e5a1845fSNamhyung Kim size_t cnt = 1;
204e5a1845fSNamhyung Kim
20570e79866SAlexey Dobriyan /* ELF is corrupted/truncated, avoid calling elf_strptr. */
20649274654SCody P Schafer if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
20749274654SCody P Schafer return NULL;
20849274654SCody P Schafer
209e5a1845fSNamhyung Kim while ((sec = elf_nextscn(elf, sec)) != NULL) {
210e5a1845fSNamhyung Kim char *str;
211e5a1845fSNamhyung Kim
212e5a1845fSNamhyung Kim gelf_getshdr(sec, shp);
213e5a1845fSNamhyung Kim str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
214155b3a13SJiri Olsa if (str && !strcmp(name, str)) {
215e5a1845fSNamhyung Kim if (idx)
216e5a1845fSNamhyung Kim *idx = cnt;
217155b3a13SJiri Olsa return sec;
218e5a1845fSNamhyung Kim }
219e5a1845fSNamhyung Kim ++cnt;
220e5a1845fSNamhyung Kim }
221e5a1845fSNamhyung Kim
222155b3a13SJiri Olsa return NULL;
223e5a1845fSNamhyung Kim }
224e5a1845fSNamhyung Kim
filename__has_section(const char * filename,const char * sec)22506ea72a4SNamhyung Kim bool filename__has_section(const char *filename, const char *sec)
22606ea72a4SNamhyung Kim {
22706ea72a4SNamhyung Kim int fd;
22806ea72a4SNamhyung Kim Elf *elf;
22906ea72a4SNamhyung Kim GElf_Ehdr ehdr;
23006ea72a4SNamhyung Kim GElf_Shdr shdr;
23106ea72a4SNamhyung Kim bool found = false;
23206ea72a4SNamhyung Kim
23306ea72a4SNamhyung Kim fd = open(filename, O_RDONLY);
23406ea72a4SNamhyung Kim if (fd < 0)
23506ea72a4SNamhyung Kim return false;
23606ea72a4SNamhyung Kim
23706ea72a4SNamhyung Kim elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
23806ea72a4SNamhyung Kim if (elf == NULL)
23906ea72a4SNamhyung Kim goto out;
24006ea72a4SNamhyung Kim
24106ea72a4SNamhyung Kim if (gelf_getehdr(elf, &ehdr) == NULL)
24206ea72a4SNamhyung Kim goto elf_out;
24306ea72a4SNamhyung Kim
24406ea72a4SNamhyung Kim found = !!elf_section_by_name(elf, &ehdr, &shdr, sec, NULL);
24506ea72a4SNamhyung Kim
24606ea72a4SNamhyung Kim elf_out:
24706ea72a4SNamhyung Kim elf_end(elf);
24806ea72a4SNamhyung Kim out:
24906ea72a4SNamhyung Kim close(fd);
25006ea72a4SNamhyung Kim return found;
25106ea72a4SNamhyung Kim }
25206ea72a4SNamhyung Kim
elf_read_program_header(Elf * elf,u64 vaddr,GElf_Phdr * phdr)2532d86612aSLeo Yan static int elf_read_program_header(Elf *elf, u64 vaddr, GElf_Phdr *phdr)
2542d86612aSLeo Yan {
2552d86612aSLeo Yan size_t i, phdrnum;
2562d86612aSLeo Yan u64 sz;
2572d86612aSLeo Yan
2582d86612aSLeo Yan if (elf_getphdrnum(elf, &phdrnum))
2592d86612aSLeo Yan return -1;
2602d86612aSLeo Yan
2612d86612aSLeo Yan for (i = 0; i < phdrnum; i++) {
2622d86612aSLeo Yan if (gelf_getphdr(elf, i, phdr) == NULL)
2632d86612aSLeo Yan return -1;
2642d86612aSLeo Yan
2652d86612aSLeo Yan if (phdr->p_type != PT_LOAD)
2662d86612aSLeo Yan continue;
2672d86612aSLeo Yan
2682d86612aSLeo Yan sz = max(phdr->p_memsz, phdr->p_filesz);
2692d86612aSLeo Yan if (!sz)
2702d86612aSLeo Yan continue;
2712d86612aSLeo Yan
2722d86612aSLeo Yan if (vaddr >= phdr->p_vaddr && (vaddr < phdr->p_vaddr + sz))
2732d86612aSLeo Yan return 0;
2742d86612aSLeo Yan }
2752d86612aSLeo Yan
2762d86612aSLeo Yan /* Not found any valid program header */
2772d86612aSLeo Yan return -1;
2782d86612aSLeo Yan }
2792d86612aSLeo Yan
want_demangle(bool is_kernel_sym)2802a8d41b4SMilian Wolff static bool want_demangle(bool is_kernel_sym)
2812a8d41b4SMilian Wolff {
2822a8d41b4SMilian Wolff return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle;
2832a8d41b4SMilian Wolff }
2842a8d41b4SMilian Wolff
28565cd8e55SIan Rogers /*
28665cd8e55SIan Rogers * Demangle C++ function signature, typically replaced by demangle-cxx.cpp
28765cd8e55SIan Rogers * version.
28865cd8e55SIan Rogers */
cxx_demangle_sym(const char * str __maybe_unused,bool params __maybe_unused,bool modifiers __maybe_unused)28965cd8e55SIan Rogers __weak char *cxx_demangle_sym(const char *str __maybe_unused, bool params __maybe_unused,
29065cd8e55SIan Rogers bool modifiers __maybe_unused)
29165cd8e55SIan Rogers {
29265cd8e55SIan Rogers #ifdef HAVE_LIBBFD_SUPPORT
29365cd8e55SIan Rogers int flags = (params ? DMGL_PARAMS : 0) | (modifiers ? DMGL_ANSI : 0);
29465cd8e55SIan Rogers
29565cd8e55SIan Rogers return bfd_demangle(NULL, str, flags);
29665cd8e55SIan Rogers #elif defined(HAVE_CPLUS_DEMANGLE_SUPPORT)
29765cd8e55SIan Rogers int flags = (params ? DMGL_PARAMS : 0) | (modifiers ? DMGL_ANSI : 0);
29865cd8e55SIan Rogers
29965cd8e55SIan Rogers return cplus_demangle(str, flags);
30065cd8e55SIan Rogers #else
30165cd8e55SIan Rogers return NULL;
30265cd8e55SIan Rogers #endif
30365cd8e55SIan Rogers }
30465cd8e55SIan Rogers
demangle_sym(struct dso * dso,int kmodule,const char * elf_name)3052a8d41b4SMilian Wolff static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
3062a8d41b4SMilian Wolff {
3072a8d41b4SMilian Wolff char *demangled = NULL;
3082a8d41b4SMilian Wolff
3092a8d41b4SMilian Wolff /*
3102a8d41b4SMilian Wolff * We need to figure out if the object was created from C++ sources
3112a8d41b4SMilian Wolff * DWARF DW_compile_unit has this, but we don't always have access
3122a8d41b4SMilian Wolff * to it...
3132a8d41b4SMilian Wolff */
3142a8d41b4SMilian Wolff if (!want_demangle(dso->kernel || kmodule))
3152a8d41b4SMilian Wolff return demangled;
3162a8d41b4SMilian Wolff
3173b4e4efeSIan Rogers demangled = cxx_demangle_sym(elf_name, verbose > 0, verbose > 0);
318cef7af25SFabian Hemmer if (demangled == NULL) {
319cef7af25SFabian Hemmer demangled = ocaml_demangle_sym(elf_name);
320cef7af25SFabian Hemmer if (demangled == NULL) {
3212a8d41b4SMilian Wolff demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET);
322cef7af25SFabian Hemmer }
323cef7af25SFabian Hemmer }
3242a8d41b4SMilian Wolff else if (rust_is_mangled(demangled))
3252a8d41b4SMilian Wolff /*
3262a8d41b4SMilian Wolff * Input to Rust demangling is the BFD-demangled
3272a8d41b4SMilian Wolff * name which it Rust-demangles in place.
3282a8d41b4SMilian Wolff */
3292a8d41b4SMilian Wolff rust_demangle_sym(demangled);
3302a8d41b4SMilian Wolff
3312a8d41b4SMilian Wolff return demangled;
3322a8d41b4SMilian Wolff }
3332a8d41b4SMilian Wolff
334375a4481SAdrian Hunter struct rel_info {
33578250284SAdrian Hunter u32 nr_entries;
33678250284SAdrian Hunter u32 *sorted;
337375a4481SAdrian Hunter bool is_rela;
338375a4481SAdrian Hunter Elf_Data *reldata;
339375a4481SAdrian Hunter GElf_Rela rela;
340375a4481SAdrian Hunter GElf_Rel rel;
341375a4481SAdrian Hunter };
342375a4481SAdrian Hunter
get_rel_symidx(struct rel_info * ri,u32 idx)343375a4481SAdrian Hunter static u32 get_rel_symidx(struct rel_info *ri, u32 idx)
344375a4481SAdrian Hunter {
34578250284SAdrian Hunter idx = ri->sorted ? ri->sorted[idx] : idx;
346375a4481SAdrian Hunter if (ri->is_rela) {
347375a4481SAdrian Hunter gelf_getrela(ri->reldata, idx, &ri->rela);
348375a4481SAdrian Hunter return GELF_R_SYM(ri->rela.r_info);
349375a4481SAdrian Hunter }
350375a4481SAdrian Hunter gelf_getrel(ri->reldata, idx, &ri->rel);
351375a4481SAdrian Hunter return GELF_R_SYM(ri->rel.r_info);
352375a4481SAdrian Hunter }
353375a4481SAdrian Hunter
get_rel_offset(struct rel_info * ri,u32 x)35478250284SAdrian Hunter static u64 get_rel_offset(struct rel_info *ri, u32 x)
35578250284SAdrian Hunter {
35678250284SAdrian Hunter if (ri->is_rela) {
35778250284SAdrian Hunter GElf_Rela rela;
35878250284SAdrian Hunter
35978250284SAdrian Hunter gelf_getrela(ri->reldata, x, &rela);
36078250284SAdrian Hunter return rela.r_offset;
36178250284SAdrian Hunter } else {
36278250284SAdrian Hunter GElf_Rel rel;
36378250284SAdrian Hunter
36478250284SAdrian Hunter gelf_getrel(ri->reldata, x, &rel);
36578250284SAdrian Hunter return rel.r_offset;
36678250284SAdrian Hunter }
36778250284SAdrian Hunter }
36878250284SAdrian Hunter
rel_cmp(const void * a,const void * b,void * r)36978250284SAdrian Hunter static int rel_cmp(const void *a, const void *b, void *r)
37078250284SAdrian Hunter {
37178250284SAdrian Hunter struct rel_info *ri = r;
37278250284SAdrian Hunter u64 a_offset = get_rel_offset(ri, *(const u32 *)a);
37378250284SAdrian Hunter u64 b_offset = get_rel_offset(ri, *(const u32 *)b);
37478250284SAdrian Hunter
37578250284SAdrian Hunter return a_offset < b_offset ? -1 : (a_offset > b_offset ? 1 : 0);
37678250284SAdrian Hunter }
37778250284SAdrian Hunter
sort_rel(struct rel_info * ri)37878250284SAdrian Hunter static int sort_rel(struct rel_info *ri)
37978250284SAdrian Hunter {
38078250284SAdrian Hunter size_t sz = sizeof(ri->sorted[0]);
38178250284SAdrian Hunter u32 i;
38278250284SAdrian Hunter
38378250284SAdrian Hunter ri->sorted = calloc(ri->nr_entries, sz);
38478250284SAdrian Hunter if (!ri->sorted)
38578250284SAdrian Hunter return -1;
38678250284SAdrian Hunter for (i = 0; i < ri->nr_entries; i++)
38778250284SAdrian Hunter ri->sorted[i] = i;
38878250284SAdrian Hunter qsort_r(ri->sorted, ri->nr_entries, sz, rel_cmp, ri);
38978250284SAdrian Hunter return 0;
39078250284SAdrian Hunter }
39178250284SAdrian Hunter
392b7dbc0beSAdrian Hunter /*
393b7dbc0beSAdrian Hunter * For x86_64, the GNU linker is putting IFUNC information in the relocation
394b7dbc0beSAdrian Hunter * addend.
395b7dbc0beSAdrian Hunter */
addend_may_be_ifunc(GElf_Ehdr * ehdr,struct rel_info * ri)396b7dbc0beSAdrian Hunter static bool addend_may_be_ifunc(GElf_Ehdr *ehdr, struct rel_info *ri)
397b7dbc0beSAdrian Hunter {
398b7dbc0beSAdrian Hunter return ehdr->e_machine == EM_X86_64 && ri->is_rela &&
399b7dbc0beSAdrian Hunter GELF_R_TYPE(ri->rela.r_info) == R_X86_64_IRELATIVE;
400b7dbc0beSAdrian Hunter }
401b7dbc0beSAdrian Hunter
get_ifunc_name(Elf * elf,struct dso * dso,GElf_Ehdr * ehdr,struct rel_info * ri,char * buf,size_t buf_sz)402b7dbc0beSAdrian Hunter static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
403b7dbc0beSAdrian Hunter struct rel_info *ri, char *buf, size_t buf_sz)
404b7dbc0beSAdrian Hunter {
405b7dbc0beSAdrian Hunter u64 addr = ri->rela.r_addend;
406b7dbc0beSAdrian Hunter struct symbol *sym;
407b7dbc0beSAdrian Hunter GElf_Phdr phdr;
408b7dbc0beSAdrian Hunter
409b7dbc0beSAdrian Hunter if (!addend_may_be_ifunc(ehdr, ri))
410b7dbc0beSAdrian Hunter return false;
411b7dbc0beSAdrian Hunter
412b7dbc0beSAdrian Hunter if (elf_read_program_header(elf, addr, &phdr))
413b7dbc0beSAdrian Hunter return false;
414b7dbc0beSAdrian Hunter
415b7dbc0beSAdrian Hunter addr -= phdr.p_vaddr - phdr.p_offset;
416b7dbc0beSAdrian Hunter
417b7dbc0beSAdrian Hunter sym = dso__find_symbol_nocache(dso, addr);
418b7dbc0beSAdrian Hunter
419b7dbc0beSAdrian Hunter /* Expecting the address to be an IFUNC or IFUNC alias */
420b7dbc0beSAdrian Hunter if (!sym || sym->start != addr || (sym->type != STT_GNU_IFUNC && !sym->ifunc_alias))
421b7dbc0beSAdrian Hunter return false;
422b7dbc0beSAdrian Hunter
423b7dbc0beSAdrian Hunter snprintf(buf, buf_sz, "%s@plt", sym->name);
424b7dbc0beSAdrian Hunter
425b7dbc0beSAdrian Hunter return true;
426b7dbc0beSAdrian Hunter }
427b7dbc0beSAdrian Hunter
exit_rel(struct rel_info * ri)42878250284SAdrian Hunter static void exit_rel(struct rel_info *ri)
42978250284SAdrian Hunter {
430d729163dSArnaldo Carvalho de Melo zfree(&ri->sorted);
43178250284SAdrian Hunter }
43278250284SAdrian Hunter
get_plt_sizes(struct dso * dso,GElf_Ehdr * ehdr,GElf_Shdr * shdr_plt,u64 * plt_header_size,u64 * plt_entry_size)433b08b20c3SAdrian Hunter static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
434c2d066c0SAdrian Hunter u64 *plt_header_size, u64 *plt_entry_size)
435c2d066c0SAdrian Hunter {
436c2d066c0SAdrian Hunter switch (ehdr->e_machine) {
437c2d066c0SAdrian Hunter case EM_ARM:
438c2d066c0SAdrian Hunter *plt_header_size = 20;
439c2d066c0SAdrian Hunter *plt_entry_size = 12;
440b08b20c3SAdrian Hunter return true;
441c2d066c0SAdrian Hunter case EM_AARCH64:
442c2d066c0SAdrian Hunter *plt_header_size = 32;
443c2d066c0SAdrian Hunter *plt_entry_size = 16;
444b08b20c3SAdrian Hunter return true;
445765be32bSTiezhu Yang case EM_LOONGARCH:
446765be32bSTiezhu Yang *plt_header_size = 32;
447765be32bSTiezhu Yang *plt_entry_size = 16;
448765be32bSTiezhu Yang return true;
449c2d066c0SAdrian Hunter case EM_SPARC:
450c2d066c0SAdrian Hunter *plt_header_size = 48;
451c2d066c0SAdrian Hunter *plt_entry_size = 12;
452b08b20c3SAdrian Hunter return true;
453c2d066c0SAdrian Hunter case EM_SPARCV9:
454c2d066c0SAdrian Hunter *plt_header_size = 128;
455c2d066c0SAdrian Hunter *plt_entry_size = 32;
456b08b20c3SAdrian Hunter return true;
45766fe2d53SAdrian Hunter case EM_386:
45866fe2d53SAdrian Hunter case EM_X86_64:
45966fe2d53SAdrian Hunter *plt_entry_size = shdr_plt->sh_entsize;
46066fe2d53SAdrian Hunter /* Size is 8 or 16, if not, assume alignment indicates size */
46166fe2d53SAdrian Hunter if (*plt_entry_size != 8 && *plt_entry_size != 16)
46266fe2d53SAdrian Hunter *plt_entry_size = shdr_plt->sh_addralign == 8 ? 8 : 16;
46366fe2d53SAdrian Hunter *plt_header_size = *plt_entry_size;
46466fe2d53SAdrian Hunter break;
465c2d066c0SAdrian Hunter default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */
466c2d066c0SAdrian Hunter *plt_header_size = shdr_plt->sh_entsize;
467c2d066c0SAdrian Hunter *plt_entry_size = shdr_plt->sh_entsize;
46866fe2d53SAdrian Hunter break;
46966fe2d53SAdrian Hunter }
470b08b20c3SAdrian Hunter if (*plt_entry_size)
471b08b20c3SAdrian Hunter return true;
472b08b20c3SAdrian Hunter pr_debug("Missing PLT entry size for %s\n", dso->long_name);
473b08b20c3SAdrian Hunter return false;
474c2d066c0SAdrian Hunter }
475c2d066c0SAdrian Hunter
machine_is_x86(GElf_Half e_machine)476b2529f82SAdrian Hunter static bool machine_is_x86(GElf_Half e_machine)
477b2529f82SAdrian Hunter {
478b2529f82SAdrian Hunter return e_machine == EM_386 || e_machine == EM_X86_64;
479b2529f82SAdrian Hunter }
480b2529f82SAdrian Hunter
481ce4c8e79SAdrian Hunter struct rela_dyn {
482ce4c8e79SAdrian Hunter GElf_Addr offset;
483ce4c8e79SAdrian Hunter u32 sym_idx;
484ce4c8e79SAdrian Hunter };
485ce4c8e79SAdrian Hunter
486ce4c8e79SAdrian Hunter struct rela_dyn_info {
487ce4c8e79SAdrian Hunter struct dso *dso;
488ce4c8e79SAdrian Hunter Elf_Data *plt_got_data;
489ce4c8e79SAdrian Hunter u32 nr_entries;
490ce4c8e79SAdrian Hunter struct rela_dyn *sorted;
491ce4c8e79SAdrian Hunter Elf_Data *dynsym_data;
492ce4c8e79SAdrian Hunter Elf_Data *dynstr_data;
493ce4c8e79SAdrian Hunter Elf_Data *rela_dyn_data;
494ce4c8e79SAdrian Hunter };
495ce4c8e79SAdrian Hunter
exit_rela_dyn(struct rela_dyn_info * di)496ce4c8e79SAdrian Hunter static void exit_rela_dyn(struct rela_dyn_info *di)
497ce4c8e79SAdrian Hunter {
498d729163dSArnaldo Carvalho de Melo zfree(&di->sorted);
499ce4c8e79SAdrian Hunter }
500ce4c8e79SAdrian Hunter
cmp_offset(const void * a,const void * b)501ce4c8e79SAdrian Hunter static int cmp_offset(const void *a, const void *b)
502ce4c8e79SAdrian Hunter {
503ce4c8e79SAdrian Hunter const struct rela_dyn *va = a;
504ce4c8e79SAdrian Hunter const struct rela_dyn *vb = b;
505ce4c8e79SAdrian Hunter
506ce4c8e79SAdrian Hunter return va->offset < vb->offset ? -1 : (va->offset > vb->offset ? 1 : 0);
507ce4c8e79SAdrian Hunter }
508ce4c8e79SAdrian Hunter
sort_rela_dyn(struct rela_dyn_info * di)509ce4c8e79SAdrian Hunter static int sort_rela_dyn(struct rela_dyn_info *di)
510ce4c8e79SAdrian Hunter {
511ce4c8e79SAdrian Hunter u32 i, n;
512ce4c8e79SAdrian Hunter
513ce4c8e79SAdrian Hunter di->sorted = calloc(di->nr_entries, sizeof(di->sorted[0]));
514ce4c8e79SAdrian Hunter if (!di->sorted)
515ce4c8e79SAdrian Hunter return -1;
516ce4c8e79SAdrian Hunter
517ce4c8e79SAdrian Hunter /* Get data for sorting: the offset and symbol index */
518ce4c8e79SAdrian Hunter for (i = 0, n = 0; i < di->nr_entries; i++) {
519ce4c8e79SAdrian Hunter GElf_Rela rela;
520ce4c8e79SAdrian Hunter u32 sym_idx;
521ce4c8e79SAdrian Hunter
522ce4c8e79SAdrian Hunter gelf_getrela(di->rela_dyn_data, i, &rela);
523ce4c8e79SAdrian Hunter sym_idx = GELF_R_SYM(rela.r_info);
524ce4c8e79SAdrian Hunter if (sym_idx) {
525ce4c8e79SAdrian Hunter di->sorted[n].sym_idx = sym_idx;
526ce4c8e79SAdrian Hunter di->sorted[n].offset = rela.r_offset;
527ce4c8e79SAdrian Hunter n += 1;
528ce4c8e79SAdrian Hunter }
529ce4c8e79SAdrian Hunter }
530ce4c8e79SAdrian Hunter
531ce4c8e79SAdrian Hunter /* Sort by offset */
532ce4c8e79SAdrian Hunter di->nr_entries = n;
533ce4c8e79SAdrian Hunter qsort(di->sorted, n, sizeof(di->sorted[0]), cmp_offset);
534ce4c8e79SAdrian Hunter
535ce4c8e79SAdrian Hunter return 0;
536ce4c8e79SAdrian Hunter }
537ce4c8e79SAdrian Hunter
get_rela_dyn_info(Elf * elf,GElf_Ehdr * ehdr,struct rela_dyn_info * di,Elf_Scn * scn)538ce4c8e79SAdrian Hunter static void get_rela_dyn_info(Elf *elf, GElf_Ehdr *ehdr, struct rela_dyn_info *di, Elf_Scn *scn)
539ce4c8e79SAdrian Hunter {
540ce4c8e79SAdrian Hunter GElf_Shdr rela_dyn_shdr;
541ce4c8e79SAdrian Hunter GElf_Shdr shdr;
542ce4c8e79SAdrian Hunter
543ce4c8e79SAdrian Hunter di->plt_got_data = elf_getdata(scn, NULL);
544ce4c8e79SAdrian Hunter
545ce4c8e79SAdrian Hunter scn = elf_section_by_name(elf, ehdr, &rela_dyn_shdr, ".rela.dyn", NULL);
546ce4c8e79SAdrian Hunter if (!scn || !rela_dyn_shdr.sh_link || !rela_dyn_shdr.sh_entsize)
547ce4c8e79SAdrian Hunter return;
548ce4c8e79SAdrian Hunter
549ce4c8e79SAdrian Hunter di->nr_entries = rela_dyn_shdr.sh_size / rela_dyn_shdr.sh_entsize;
550ce4c8e79SAdrian Hunter di->rela_dyn_data = elf_getdata(scn, NULL);
551ce4c8e79SAdrian Hunter
552ce4c8e79SAdrian Hunter scn = elf_getscn(elf, rela_dyn_shdr.sh_link);
553ce4c8e79SAdrian Hunter if (!scn || !gelf_getshdr(scn, &shdr) || !shdr.sh_link)
554ce4c8e79SAdrian Hunter return;
555ce4c8e79SAdrian Hunter
556ce4c8e79SAdrian Hunter di->dynsym_data = elf_getdata(scn, NULL);
557ce4c8e79SAdrian Hunter di->dynstr_data = elf_getdata(elf_getscn(elf, shdr.sh_link), NULL);
558ce4c8e79SAdrian Hunter
559ce4c8e79SAdrian Hunter if (!di->plt_got_data || !di->dynstr_data || !di->dynsym_data || !di->rela_dyn_data)
560ce4c8e79SAdrian Hunter return;
561ce4c8e79SAdrian Hunter
562ce4c8e79SAdrian Hunter /* Sort into offset order */
563ce4c8e79SAdrian Hunter sort_rela_dyn(di);
564ce4c8e79SAdrian Hunter }
565ce4c8e79SAdrian Hunter
566ce4c8e79SAdrian Hunter /* Get instruction displacement from a plt entry for x86_64 */
get_x86_64_plt_disp(const u8 * p)567ce4c8e79SAdrian Hunter static u32 get_x86_64_plt_disp(const u8 *p)
568ce4c8e79SAdrian Hunter {
569ce4c8e79SAdrian Hunter u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
570ce4c8e79SAdrian Hunter int n = 0;
571ce4c8e79SAdrian Hunter
572ce4c8e79SAdrian Hunter /* Skip endbr64 */
573ce4c8e79SAdrian Hunter if (!memcmp(p, endbr64, sizeof(endbr64)))
574ce4c8e79SAdrian Hunter n += sizeof(endbr64);
575ce4c8e79SAdrian Hunter /* Skip bnd prefix */
576ce4c8e79SAdrian Hunter if (p[n] == 0xf2)
577ce4c8e79SAdrian Hunter n += 1;
578ce4c8e79SAdrian Hunter /* jmp with 4-byte displacement */
579ce4c8e79SAdrian Hunter if (p[n] == 0xff && p[n + 1] == 0x25) {
580a2410b57SAdrian Hunter u32 disp;
581a2410b57SAdrian Hunter
582ce4c8e79SAdrian Hunter n += 2;
583ce4c8e79SAdrian Hunter /* Also add offset from start of entry to end of instruction */
584a2410b57SAdrian Hunter memcpy(&disp, p + n, sizeof(disp));
585a2410b57SAdrian Hunter return n + 4 + le32toh(disp);
586ce4c8e79SAdrian Hunter }
587ce4c8e79SAdrian Hunter return 0;
588ce4c8e79SAdrian Hunter }
589ce4c8e79SAdrian Hunter
get_plt_got_name(GElf_Shdr * shdr,size_t i,struct rela_dyn_info * di,char * buf,size_t buf_sz)590ce4c8e79SAdrian Hunter static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
591ce4c8e79SAdrian Hunter struct rela_dyn_info *di,
592ce4c8e79SAdrian Hunter char *buf, size_t buf_sz)
593ce4c8e79SAdrian Hunter {
594ce4c8e79SAdrian Hunter struct rela_dyn vi, *vr;
595ce4c8e79SAdrian Hunter const char *sym_name;
596ce4c8e79SAdrian Hunter char *demangled;
597ce4c8e79SAdrian Hunter GElf_Sym sym;
598c8bb2d76SAdrian Hunter bool result;
599ce4c8e79SAdrian Hunter u32 disp;
600ce4c8e79SAdrian Hunter
601ce4c8e79SAdrian Hunter if (!di->sorted)
602ce4c8e79SAdrian Hunter return false;
603ce4c8e79SAdrian Hunter
604ce4c8e79SAdrian Hunter disp = get_x86_64_plt_disp(di->plt_got_data->d_buf + i);
605ce4c8e79SAdrian Hunter if (!disp)
606ce4c8e79SAdrian Hunter return false;
607ce4c8e79SAdrian Hunter
608ce4c8e79SAdrian Hunter /* Compute target offset of the .plt.got entry */
609ce4c8e79SAdrian Hunter vi.offset = shdr->sh_offset + di->plt_got_data->d_off + i + disp;
610ce4c8e79SAdrian Hunter
611ce4c8e79SAdrian Hunter /* Find that offset in .rela.dyn (sorted by offset) */
612ce4c8e79SAdrian Hunter vr = bsearch(&vi, di->sorted, di->nr_entries, sizeof(di->sorted[0]), cmp_offset);
613ce4c8e79SAdrian Hunter if (!vr)
614ce4c8e79SAdrian Hunter return false;
615ce4c8e79SAdrian Hunter
616ce4c8e79SAdrian Hunter /* Get the associated symbol */
617ce4c8e79SAdrian Hunter gelf_getsym(di->dynsym_data, vr->sym_idx, &sym);
618ce4c8e79SAdrian Hunter sym_name = elf_sym__name(&sym, di->dynstr_data);
619ce4c8e79SAdrian Hunter demangled = demangle_sym(di->dso, 0, sym_name);
620ce4c8e79SAdrian Hunter if (demangled != NULL)
621ce4c8e79SAdrian Hunter sym_name = demangled;
622ce4c8e79SAdrian Hunter
623ce4c8e79SAdrian Hunter snprintf(buf, buf_sz, "%s@plt", sym_name);
624ce4c8e79SAdrian Hunter
625c8bb2d76SAdrian Hunter result = *sym_name;
626c8bb2d76SAdrian Hunter
627ce4c8e79SAdrian Hunter free(demangled);
628ce4c8e79SAdrian Hunter
629c8bb2d76SAdrian Hunter return result;
630ce4c8e79SAdrian Hunter }
631ce4c8e79SAdrian Hunter
dso__synthesize_plt_got_symbols(struct dso * dso,Elf * elf,GElf_Ehdr * ehdr,char * buf,size_t buf_sz)63251a188adSAdrian Hunter static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
63351a188adSAdrian Hunter GElf_Ehdr *ehdr,
63451a188adSAdrian Hunter char *buf, size_t buf_sz)
63551a188adSAdrian Hunter {
636ce4c8e79SAdrian Hunter struct rela_dyn_info di = { .dso = dso };
63751a188adSAdrian Hunter struct symbol *sym;
63851a188adSAdrian Hunter GElf_Shdr shdr;
63951a188adSAdrian Hunter Elf_Scn *scn;
640ce4c8e79SAdrian Hunter int err = -1;
64151a188adSAdrian Hunter size_t i;
64251a188adSAdrian Hunter
64351a188adSAdrian Hunter scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL);
64451a188adSAdrian Hunter if (!scn || !shdr.sh_entsize)
64551a188adSAdrian Hunter return 0;
64651a188adSAdrian Hunter
647ce4c8e79SAdrian Hunter if (ehdr->e_machine == EM_X86_64)
648ce4c8e79SAdrian Hunter get_rela_dyn_info(elf, ehdr, &di, scn);
649ce4c8e79SAdrian Hunter
65051a188adSAdrian Hunter for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) {
651ce4c8e79SAdrian Hunter if (!get_plt_got_name(&shdr, i, &di, buf, buf_sz))
65251a188adSAdrian Hunter snprintf(buf, buf_sz, "offset_%#" PRIx64 "@plt", (u64)shdr.sh_offset + i);
65351a188adSAdrian Hunter sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf);
65451a188adSAdrian Hunter if (!sym)
655ce4c8e79SAdrian Hunter goto out;
65651a188adSAdrian Hunter symbols__insert(&dso->symbols, sym);
65751a188adSAdrian Hunter }
658ce4c8e79SAdrian Hunter err = 0;
659ce4c8e79SAdrian Hunter out:
660ce4c8e79SAdrian Hunter exit_rela_dyn(&di);
661ce4c8e79SAdrian Hunter return err;
66251a188adSAdrian Hunter }
66351a188adSAdrian Hunter
664e5a1845fSNamhyung Kim /*
665e5a1845fSNamhyung Kim * We need to check if we have a .dynsym, so that we can handle the
666e5a1845fSNamhyung Kim * .plt, synthesizing its symbols, that aren't on the symtabs (be it
667e5a1845fSNamhyung Kim * .dynsym or .symtab).
668e5a1845fSNamhyung Kim * And always look at the original dso, not at debuginfo packages, that
669e5a1845fSNamhyung Kim * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
670e5a1845fSNamhyung Kim */
dso__synthesize_plt_symbols(struct dso * dso,struct symsrc * ss)6713183f8caSArnaldo Carvalho de Melo int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss)
672e5a1845fSNamhyung Kim {
67378250284SAdrian Hunter uint32_t idx;
674e5a1845fSNamhyung Kim GElf_Sym sym;
675b2f76050SLi Bin u64 plt_offset, plt_header_size, plt_entry_size;
676b2529f82SAdrian Hunter GElf_Shdr shdr_plt, plt_sec_shdr;
677b2529f82SAdrian Hunter struct symbol *f, *plt_sym;
678e5a1845fSNamhyung Kim GElf_Shdr shdr_rel_plt, shdr_dynsym;
679375a4481SAdrian Hunter Elf_Data *syms, *symstrs;
680e5a1845fSNamhyung Kim Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
681e5a1845fSNamhyung Kim GElf_Ehdr ehdr;
682e5a1845fSNamhyung Kim char sympltname[1024];
683e5a1845fSNamhyung Kim Elf *elf;
684375a4481SAdrian Hunter int nr = 0, err = -1;
685375a4481SAdrian Hunter struct rel_info ri = { .is_rela = false };
68660fbb3e4SAdrian Hunter bool lazy_plt;
687e5a1845fSNamhyung Kim
688a44f605bSCody P Schafer elf = ss->elf;
689a44f605bSCody P Schafer ehdr = ss->ehdr;
690e5a1845fSNamhyung Kim
691698a0d1aSAdrian Hunter if (!elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL))
692698a0d1aSAdrian Hunter return 0;
693698a0d1aSAdrian Hunter
694698a0d1aSAdrian Hunter /*
695698a0d1aSAdrian Hunter * A symbol from a previous section (e.g. .init) can have been expanded
696698a0d1aSAdrian Hunter * by symbols__fixup_end() to overlap .plt. Truncate it before adding
697698a0d1aSAdrian Hunter * a symbol for .plt header.
698698a0d1aSAdrian Hunter */
699698a0d1aSAdrian Hunter f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset);
700698a0d1aSAdrian Hunter if (f && f->start < shdr_plt.sh_offset && f->end > shdr_plt.sh_offset)
701698a0d1aSAdrian Hunter f->end = shdr_plt.sh_offset;
702698a0d1aSAdrian Hunter
703698a0d1aSAdrian Hunter if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size))
704698a0d1aSAdrian Hunter return 0;
705698a0d1aSAdrian Hunter
706698a0d1aSAdrian Hunter /* Add a symbol for .plt header */
707b2529f82SAdrian Hunter plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt");
708b2529f82SAdrian Hunter if (!plt_sym)
709698a0d1aSAdrian Hunter goto out_elf_end;
710b2529f82SAdrian Hunter symbols__insert(&dso->symbols, plt_sym);
711b2529f82SAdrian Hunter
71251a188adSAdrian Hunter /* Only x86 has .plt.got */
71351a188adSAdrian Hunter if (machine_is_x86(ehdr.e_machine) &&
71451a188adSAdrian Hunter dso__synthesize_plt_got_symbols(dso, elf, &ehdr, sympltname, sizeof(sympltname)))
71551a188adSAdrian Hunter goto out_elf_end;
71651a188adSAdrian Hunter
717b2529f82SAdrian Hunter /* Only x86 has .plt.sec */
718b2529f82SAdrian Hunter if (machine_is_x86(ehdr.e_machine) &&
719b2529f82SAdrian Hunter elf_section_by_name(elf, &ehdr, &plt_sec_shdr, ".plt.sec", NULL)) {
720b2529f82SAdrian Hunter if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size))
721b2529f82SAdrian Hunter return 0;
722b2529f82SAdrian Hunter /* Extend .plt symbol to entire .plt */
723b2529f82SAdrian Hunter plt_sym->end = plt_sym->start + shdr_plt.sh_size;
724b2529f82SAdrian Hunter /* Use .plt.sec offset */
725b2529f82SAdrian Hunter plt_offset = plt_sec_shdr.sh_offset;
72660fbb3e4SAdrian Hunter lazy_plt = false;
727b2529f82SAdrian Hunter } else {
72860fbb3e4SAdrian Hunter plt_offset = shdr_plt.sh_offset;
72960fbb3e4SAdrian Hunter lazy_plt = true;
730b2529f82SAdrian Hunter }
731698a0d1aSAdrian Hunter
732e5a1845fSNamhyung Kim scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
733e5a1845fSNamhyung Kim ".rela.plt", NULL);
734e5a1845fSNamhyung Kim if (scn_plt_rel == NULL) {
735e5a1845fSNamhyung Kim scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
736e5a1845fSNamhyung Kim ".rel.plt", NULL);
737e5a1845fSNamhyung Kim if (scn_plt_rel == NULL)
738477d5e35SAdrian Hunter return 0;
739e5a1845fSNamhyung Kim }
740e5a1845fSNamhyung Kim
741df8aeaefSAdrian Hunter if (shdr_rel_plt.sh_type != SHT_RELA &&
742df8aeaefSAdrian Hunter shdr_rel_plt.sh_type != SHT_REL)
743df8aeaefSAdrian Hunter return 0;
744df8aeaefSAdrian Hunter
745a1ab1285SAdrian Hunter if (!shdr_rel_plt.sh_link)
746a1ab1285SAdrian Hunter return 0;
747a1ab1285SAdrian Hunter
748a1ab1285SAdrian Hunter if (shdr_rel_plt.sh_link == ss->dynsym_idx) {
749a1ab1285SAdrian Hunter scn_dynsym = ss->dynsym;
750a1ab1285SAdrian Hunter shdr_dynsym = ss->dynshdr;
751a1ab1285SAdrian Hunter } else if (shdr_rel_plt.sh_link == ss->symtab_idx) {
752a1ab1285SAdrian Hunter /*
753a1ab1285SAdrian Hunter * A static executable can have a .plt due to IFUNCs, in which
754a1ab1285SAdrian Hunter * case .symtab is used not .dynsym.
755a1ab1285SAdrian Hunter */
756a1ab1285SAdrian Hunter scn_dynsym = ss->symtab;
757a1ab1285SAdrian Hunter shdr_dynsym = ss->symshdr;
758a1ab1285SAdrian Hunter } else {
759e5a1845fSNamhyung Kim goto out_elf_end;
760a1ab1285SAdrian Hunter }
761a1ab1285SAdrian Hunter
762a1ab1285SAdrian Hunter if (!scn_dynsym)
763a1ab1285SAdrian Hunter return 0;
764e5a1845fSNamhyung Kim
765e5a1845fSNamhyung Kim /*
766e5a1845fSNamhyung Kim * Fetch the relocation section to find the idxes to the GOT
767e5a1845fSNamhyung Kim * and the symbols in the .dynsym they refer to.
768e5a1845fSNamhyung Kim */
769375a4481SAdrian Hunter ri.reldata = elf_getdata(scn_plt_rel, NULL);
770375a4481SAdrian Hunter if (!ri.reldata)
771e5a1845fSNamhyung Kim goto out_elf_end;
772e5a1845fSNamhyung Kim
773e5a1845fSNamhyung Kim syms = elf_getdata(scn_dynsym, NULL);
774e5a1845fSNamhyung Kim if (syms == NULL)
775e5a1845fSNamhyung Kim goto out_elf_end;
776e5a1845fSNamhyung Kim
777e5a1845fSNamhyung Kim scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
778e5a1845fSNamhyung Kim if (scn_symstrs == NULL)
779e5a1845fSNamhyung Kim goto out_elf_end;
780e5a1845fSNamhyung Kim
781e5a1845fSNamhyung Kim symstrs = elf_getdata(scn_symstrs, NULL);
782e5a1845fSNamhyung Kim if (symstrs == NULL)
783e5a1845fSNamhyung Kim goto out_elf_end;
784e5a1845fSNamhyung Kim
78552f9ddbaSCody P Schafer if (symstrs->d_size == 0)
78652f9ddbaSCody P Schafer goto out_elf_end;
78752f9ddbaSCody P Schafer
78878250284SAdrian Hunter ri.nr_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
789e5a1845fSNamhyung Kim
790375a4481SAdrian Hunter ri.is_rela = shdr_rel_plt.sh_type == SHT_RELA;
791e5a1845fSNamhyung Kim
79260fbb3e4SAdrian Hunter if (lazy_plt) {
79360fbb3e4SAdrian Hunter /*
79460fbb3e4SAdrian Hunter * Assume a .plt with the same number of entries as the number
79560fbb3e4SAdrian Hunter * of relocation entries is not lazy and does not have a header.
79660fbb3e4SAdrian Hunter */
79760fbb3e4SAdrian Hunter if (ri.nr_entries * plt_entry_size == shdr_plt.sh_size)
79860fbb3e4SAdrian Hunter dso__delete_symbol(dso, plt_sym);
79960fbb3e4SAdrian Hunter else
80060fbb3e4SAdrian Hunter plt_offset += plt_header_size;
80160fbb3e4SAdrian Hunter }
80260fbb3e4SAdrian Hunter
80378250284SAdrian Hunter /*
80478250284SAdrian Hunter * x86 doesn't insert IFUNC relocations in .plt order, so sort to get
80578250284SAdrian Hunter * back in order.
80678250284SAdrian Hunter */
80778250284SAdrian Hunter if (machine_is_x86(ehdr.e_machine) && sort_rel(&ri))
80878250284SAdrian Hunter goto out_elf_end;
80978250284SAdrian Hunter
81078250284SAdrian Hunter for (idx = 0; idx < ri.nr_entries; idx++) {
8112a8d41b4SMilian Wolff const char *elf_name = NULL;
8122a8d41b4SMilian Wolff char *demangled = NULL;
8132a8d41b4SMilian Wolff
814375a4481SAdrian Hunter gelf_getsym(syms, get_rel_symidx(&ri, idx), &sym);
8152a8d41b4SMilian Wolff
8162a8d41b4SMilian Wolff elf_name = elf_sym__name(&sym, symstrs);
8172a8d41b4SMilian Wolff demangled = demangle_sym(dso, 0, elf_name);
818df8aeaefSAdrian Hunter if (demangled)
8192a8d41b4SMilian Wolff elf_name = demangled;
82045204677SAdrian Hunter if (*elf_name)
82145204677SAdrian Hunter snprintf(sympltname, sizeof(sympltname), "%s@plt", elf_name);
822b7dbc0beSAdrian Hunter else if (!get_ifunc_name(elf, dso, &ehdr, &ri, sympltname, sizeof(sympltname)))
823e5a1845fSNamhyung Kim snprintf(sympltname, sizeof(sympltname),
82445204677SAdrian Hunter "offset_%#" PRIx64 "@plt", plt_offset);
8252a8d41b4SMilian Wolff free(demangled);
826e5a1845fSNamhyung Kim
827df8aeaefSAdrian Hunter f = symbol__new(plt_offset, plt_entry_size, STB_GLOBAL, STT_FUNC, sympltname);
828e5a1845fSNamhyung Kim if (!f)
829e5a1845fSNamhyung Kim goto out_elf_end;
830e5a1845fSNamhyung Kim
831b2f76050SLi Bin plt_offset += plt_entry_size;
8323183f8caSArnaldo Carvalho de Melo symbols__insert(&dso->symbols, f);
833e5a1845fSNamhyung Kim ++nr;
834e5a1845fSNamhyung Kim }
835e5a1845fSNamhyung Kim
836e5a1845fSNamhyung Kim err = 0;
837e5a1845fSNamhyung Kim out_elf_end:
83878250284SAdrian Hunter exit_rel(&ri);
839e5a1845fSNamhyung Kim if (err == 0)
840e5a1845fSNamhyung Kim return nr;
841e5a1845fSNamhyung Kim pr_debug("%s: problems reading %s PLT info.\n",
842e5a1845fSNamhyung Kim __func__, dso->long_name);
843e5a1845fSNamhyung Kim return 0;
844e5a1845fSNamhyung Kim }
845e5a1845fSNamhyung Kim
dso__demangle_sym(struct dso * dso,int kmodule,const char * elf_name)84680c345b2SMilian Wolff char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
847a64489c5SJin Yao {
848a64489c5SJin Yao return demangle_sym(dso, kmodule, elf_name);
849a64489c5SJin Yao }
850a64489c5SJin Yao
851e5a1845fSNamhyung Kim /*
852e5a1845fSNamhyung Kim * Align offset to 4 bytes as needed for note name and descriptor data.
853e5a1845fSNamhyung Kim */
854e5a1845fSNamhyung Kim #define NOTE_ALIGN(n) (((n) + 3) & -4U)
855e5a1845fSNamhyung Kim
elf_read_build_id(Elf * elf,void * bf,size_t size)856e5a1845fSNamhyung Kim static int elf_read_build_id(Elf *elf, void *bf, size_t size)
857e5a1845fSNamhyung Kim {
858e5a1845fSNamhyung Kim int err = -1;
859e5a1845fSNamhyung Kim GElf_Ehdr ehdr;
860e5a1845fSNamhyung Kim GElf_Shdr shdr;
861e5a1845fSNamhyung Kim Elf_Data *data;
862e5a1845fSNamhyung Kim Elf_Scn *sec;
863e5a1845fSNamhyung Kim Elf_Kind ek;
864e5a1845fSNamhyung Kim void *ptr;
865e5a1845fSNamhyung Kim
866e5a1845fSNamhyung Kim if (size < BUILD_ID_SIZE)
867e5a1845fSNamhyung Kim goto out;
868e5a1845fSNamhyung Kim
869e5a1845fSNamhyung Kim ek = elf_kind(elf);
870e5a1845fSNamhyung Kim if (ek != ELF_K_ELF)
871e5a1845fSNamhyung Kim goto out;
872e5a1845fSNamhyung Kim
873e5a1845fSNamhyung Kim if (gelf_getehdr(elf, &ehdr) == NULL) {
874e5a1845fSNamhyung Kim pr_err("%s: cannot get elf header.\n", __func__);
875e5a1845fSNamhyung Kim goto out;
876e5a1845fSNamhyung Kim }
877e5a1845fSNamhyung Kim
878e5a1845fSNamhyung Kim /*
879e5a1845fSNamhyung Kim * Check following sections for notes:
880e5a1845fSNamhyung Kim * '.note.gnu.build-id'
881e5a1845fSNamhyung Kim * '.notes'
882e5a1845fSNamhyung Kim * '.note' (VDSO specific)
883e5a1845fSNamhyung Kim */
884e5a1845fSNamhyung Kim do {
885e5a1845fSNamhyung Kim sec = elf_section_by_name(elf, &ehdr, &shdr,
886e5a1845fSNamhyung Kim ".note.gnu.build-id", NULL);
887e5a1845fSNamhyung Kim if (sec)
888e5a1845fSNamhyung Kim break;
889e5a1845fSNamhyung Kim
890e5a1845fSNamhyung Kim sec = elf_section_by_name(elf, &ehdr, &shdr,
891e5a1845fSNamhyung Kim ".notes", NULL);
892e5a1845fSNamhyung Kim if (sec)
893e5a1845fSNamhyung Kim break;
894e5a1845fSNamhyung Kim
895e5a1845fSNamhyung Kim sec = elf_section_by_name(elf, &ehdr, &shdr,
896e5a1845fSNamhyung Kim ".note", NULL);
897e5a1845fSNamhyung Kim if (sec)
898e5a1845fSNamhyung Kim break;
899e5a1845fSNamhyung Kim
900e5a1845fSNamhyung Kim return err;
901e5a1845fSNamhyung Kim
902e5a1845fSNamhyung Kim } while (0);
903e5a1845fSNamhyung Kim
904e5a1845fSNamhyung Kim data = elf_getdata(sec, NULL);
905e5a1845fSNamhyung Kim if (data == NULL)
906e5a1845fSNamhyung Kim goto out;
907e5a1845fSNamhyung Kim
908e5a1845fSNamhyung Kim ptr = data->d_buf;
909e5a1845fSNamhyung Kim while (ptr < (data->d_buf + data->d_size)) {
910e5a1845fSNamhyung Kim GElf_Nhdr *nhdr = ptr;
911e5a1845fSNamhyung Kim size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
912e5a1845fSNamhyung Kim descsz = NOTE_ALIGN(nhdr->n_descsz);
913e5a1845fSNamhyung Kim const char *name;
914e5a1845fSNamhyung Kim
915e5a1845fSNamhyung Kim ptr += sizeof(*nhdr);
916e5a1845fSNamhyung Kim name = ptr;
917e5a1845fSNamhyung Kim ptr += namesz;
918e5a1845fSNamhyung Kim if (nhdr->n_type == NT_GNU_BUILD_ID &&
919e5a1845fSNamhyung Kim nhdr->n_namesz == sizeof("GNU")) {
920e5a1845fSNamhyung Kim if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
921e5a1845fSNamhyung Kim size_t sz = min(size, descsz);
922e5a1845fSNamhyung Kim memcpy(bf, ptr, sz);
923e5a1845fSNamhyung Kim memset(bf + sz, 0, size - sz);
9241511e469SYang Jihong err = sz;
925e5a1845fSNamhyung Kim break;
926e5a1845fSNamhyung Kim }
927e5a1845fSNamhyung Kim }
928e5a1845fSNamhyung Kim ptr += descsz;
929e5a1845fSNamhyung Kim }
930e5a1845fSNamhyung Kim
931e5a1845fSNamhyung Kim out:
932e5a1845fSNamhyung Kim return err;
933e5a1845fSNamhyung Kim }
934e5a1845fSNamhyung Kim
935ba0509dcSRemi Bernon #ifdef HAVE_LIBBFD_BUILDID_SUPPORT
936ba0509dcSRemi Bernon
read_build_id(const char * filename,struct build_id * bid)93747dce51aSJiri Olsa static int read_build_id(const char *filename, struct build_id *bid)
938ba0509dcSRemi Bernon {
939f766819cSJiri Olsa size_t size = sizeof(bid->data);
940ba0509dcSRemi Bernon int err = -1;
941ba0509dcSRemi Bernon bfd *abfd;
942ba0509dcSRemi Bernon
943ba0509dcSRemi Bernon abfd = bfd_openr(filename, NULL);
944ba0509dcSRemi Bernon if (!abfd)
945ba0509dcSRemi Bernon return -1;
946ba0509dcSRemi Bernon
947ba0509dcSRemi Bernon if (!bfd_check_format(abfd, bfd_object)) {
948ba0509dcSRemi Bernon pr_debug2("%s: cannot read %s bfd file.\n", __func__, filename);
949ba0509dcSRemi Bernon goto out_close;
950ba0509dcSRemi Bernon }
951ba0509dcSRemi Bernon
952ba0509dcSRemi Bernon if (!abfd->build_id || abfd->build_id->size > size)
953ba0509dcSRemi Bernon goto out_close;
954ba0509dcSRemi Bernon
955f766819cSJiri Olsa memcpy(bid->data, abfd->build_id->data, abfd->build_id->size);
956f766819cSJiri Olsa memset(bid->data + abfd->build_id->size, 0, size - abfd->build_id->size);
957f766819cSJiri Olsa err = bid->size = abfd->build_id->size;
958ba0509dcSRemi Bernon
959ba0509dcSRemi Bernon out_close:
960ba0509dcSRemi Bernon bfd_close(abfd);
961ba0509dcSRemi Bernon return err;
962ba0509dcSRemi Bernon }
963ba0509dcSRemi Bernon
964ba0509dcSRemi Bernon #else // HAVE_LIBBFD_BUILDID_SUPPORT
965ba0509dcSRemi Bernon
read_build_id(const char * filename,struct build_id * bid)96647dce51aSJiri Olsa static int read_build_id(const char *filename, struct build_id *bid)
967e5a1845fSNamhyung Kim {
968f766819cSJiri Olsa size_t size = sizeof(bid->data);
969e5a1845fSNamhyung Kim int fd, err = -1;
970e5a1845fSNamhyung Kim Elf *elf;
971e5a1845fSNamhyung Kim
972e5a1845fSNamhyung Kim if (size < BUILD_ID_SIZE)
973e5a1845fSNamhyung Kim goto out;
974e5a1845fSNamhyung Kim
975e5a1845fSNamhyung Kim fd = open(filename, O_RDONLY);
976e5a1845fSNamhyung Kim if (fd < 0)
977e5a1845fSNamhyung Kim goto out;
978e5a1845fSNamhyung Kim
979e5a1845fSNamhyung Kim elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
980e5a1845fSNamhyung Kim if (elf == NULL) {
981e5a1845fSNamhyung Kim pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
982e5a1845fSNamhyung Kim goto out_close;
983e5a1845fSNamhyung Kim }
984e5a1845fSNamhyung Kim
985f766819cSJiri Olsa err = elf_read_build_id(elf, bid->data, size);
986f766819cSJiri Olsa if (err > 0)
987f766819cSJiri Olsa bid->size = err;
988e5a1845fSNamhyung Kim
989e5a1845fSNamhyung Kim elf_end(elf);
990e5a1845fSNamhyung Kim out_close:
991e5a1845fSNamhyung Kim close(fd);
992e5a1845fSNamhyung Kim out:
993e5a1845fSNamhyung Kim return err;
994e5a1845fSNamhyung Kim }
995e5a1845fSNamhyung Kim
996ba0509dcSRemi Bernon #endif // HAVE_LIBBFD_BUILDID_SUPPORT
997ba0509dcSRemi Bernon
filename__read_build_id(const char * filename,struct build_id * bid)99847dce51aSJiri Olsa int filename__read_build_id(const char *filename, struct build_id *bid)
99947dce51aSJiri Olsa {
100047dce51aSJiri Olsa struct kmod_path m = { .name = NULL, };
100147dce51aSJiri Olsa char path[PATH_MAX];
100247dce51aSJiri Olsa int err;
100347dce51aSJiri Olsa
100447dce51aSJiri Olsa if (!filename)
100547dce51aSJiri Olsa return -EFAULT;
100647dce51aSJiri Olsa
100747dce51aSJiri Olsa err = kmod_path__parse(&m, filename);
100847dce51aSJiri Olsa if (err)
100947dce51aSJiri Olsa return -1;
101047dce51aSJiri Olsa
101147dce51aSJiri Olsa if (m.comp) {
101247dce51aSJiri Olsa int error = 0, fd;
101347dce51aSJiri Olsa
101447dce51aSJiri Olsa fd = filename__decompress(filename, path, sizeof(path), m.comp, &error);
101547dce51aSJiri Olsa if (fd < 0) {
101647dce51aSJiri Olsa pr_debug("Failed to decompress (error %d) %s\n",
101747dce51aSJiri Olsa error, filename);
101847dce51aSJiri Olsa return -1;
101947dce51aSJiri Olsa }
102047dce51aSJiri Olsa close(fd);
102147dce51aSJiri Olsa filename = path;
102247dce51aSJiri Olsa }
102347dce51aSJiri Olsa
102447dce51aSJiri Olsa err = read_build_id(filename, bid);
102547dce51aSJiri Olsa
102647dce51aSJiri Olsa if (m.comp)
102747dce51aSJiri Olsa unlink(filename);
102847dce51aSJiri Olsa return err;
102947dce51aSJiri Olsa }
103047dce51aSJiri Olsa
sysfs__read_build_id(const char * filename,struct build_id * bid)10313ff1b8c8SJiri Olsa int sysfs__read_build_id(const char *filename, struct build_id *bid)
1032e5a1845fSNamhyung Kim {
10333ff1b8c8SJiri Olsa size_t size = sizeof(bid->data);
1034e5a1845fSNamhyung Kim int fd, err = -1;
1035e5a1845fSNamhyung Kim
1036e5a1845fSNamhyung Kim fd = open(filename, O_RDONLY);
1037e5a1845fSNamhyung Kim if (fd < 0)
1038e5a1845fSNamhyung Kim goto out;
1039e5a1845fSNamhyung Kim
1040e5a1845fSNamhyung Kim while (1) {
1041e5a1845fSNamhyung Kim char bf[BUFSIZ];
1042e5a1845fSNamhyung Kim GElf_Nhdr nhdr;
1043e5a1845fSNamhyung Kim size_t namesz, descsz;
1044e5a1845fSNamhyung Kim
1045e5a1845fSNamhyung Kim if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
1046e5a1845fSNamhyung Kim break;
1047e5a1845fSNamhyung Kim
1048e5a1845fSNamhyung Kim namesz = NOTE_ALIGN(nhdr.n_namesz);
1049e5a1845fSNamhyung Kim descsz = NOTE_ALIGN(nhdr.n_descsz);
1050e5a1845fSNamhyung Kim if (nhdr.n_type == NT_GNU_BUILD_ID &&
1051e5a1845fSNamhyung Kim nhdr.n_namesz == sizeof("GNU")) {
1052e5a1845fSNamhyung Kim if (read(fd, bf, namesz) != (ssize_t)namesz)
1053e5a1845fSNamhyung Kim break;
1054e5a1845fSNamhyung Kim if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
1055e5a1845fSNamhyung Kim size_t sz = min(descsz, size);
10563ff1b8c8SJiri Olsa if (read(fd, bid->data, sz) == (ssize_t)sz) {
10573ff1b8c8SJiri Olsa memset(bid->data + sz, 0, size - sz);
10583ff1b8c8SJiri Olsa bid->size = sz;
1059e5a1845fSNamhyung Kim err = 0;
1060e5a1845fSNamhyung Kim break;
1061e5a1845fSNamhyung Kim }
1062e5a1845fSNamhyung Kim } else if (read(fd, bf, descsz) != (ssize_t)descsz)
1063e5a1845fSNamhyung Kim break;
1064e5a1845fSNamhyung Kim } else {
1065e5a1845fSNamhyung Kim int n = namesz + descsz;
10667934c98aSArnaldo Carvalho de Melo
10677934c98aSArnaldo Carvalho de Melo if (n > (int)sizeof(bf)) {
10687934c98aSArnaldo Carvalho de Melo n = sizeof(bf);
10697934c98aSArnaldo Carvalho de Melo pr_debug("%s: truncating reading of build id in sysfs file %s: n_namesz=%u, n_descsz=%u.\n",
10707934c98aSArnaldo Carvalho de Melo __func__, filename, nhdr.n_namesz, nhdr.n_descsz);
10717934c98aSArnaldo Carvalho de Melo }
1072e5a1845fSNamhyung Kim if (read(fd, bf, n) != n)
1073e5a1845fSNamhyung Kim break;
1074e5a1845fSNamhyung Kim }
1075e5a1845fSNamhyung Kim }
1076e5a1845fSNamhyung Kim close(fd);
1077e5a1845fSNamhyung Kim out:
1078e5a1845fSNamhyung Kim return err;
1079e5a1845fSNamhyung Kim }
1080e5a1845fSNamhyung Kim
1081ba0509dcSRemi Bernon #ifdef HAVE_LIBBFD_SUPPORT
1082ba0509dcSRemi Bernon
filename__read_debuglink(const char * filename,char * debuglink,size_t size)1083ba0509dcSRemi Bernon int filename__read_debuglink(const char *filename, char *debuglink,
1084ba0509dcSRemi Bernon size_t size)
1085ba0509dcSRemi Bernon {
1086ba0509dcSRemi Bernon int err = -1;
1087ba0509dcSRemi Bernon asection *section;
1088ba0509dcSRemi Bernon bfd *abfd;
1089ba0509dcSRemi Bernon
1090ba0509dcSRemi Bernon abfd = bfd_openr(filename, NULL);
1091ba0509dcSRemi Bernon if (!abfd)
1092ba0509dcSRemi Bernon return -1;
1093ba0509dcSRemi Bernon
1094ba0509dcSRemi Bernon if (!bfd_check_format(abfd, bfd_object)) {
1095ba0509dcSRemi Bernon pr_debug2("%s: cannot read %s bfd file.\n", __func__, filename);
1096ba0509dcSRemi Bernon goto out_close;
1097ba0509dcSRemi Bernon }
1098ba0509dcSRemi Bernon
1099ba0509dcSRemi Bernon section = bfd_get_section_by_name(abfd, ".gnu_debuglink");
1100ba0509dcSRemi Bernon if (!section)
1101ba0509dcSRemi Bernon goto out_close;
1102ba0509dcSRemi Bernon
1103ba0509dcSRemi Bernon if (section->size > size)
1104ba0509dcSRemi Bernon goto out_close;
1105ba0509dcSRemi Bernon
1106ba0509dcSRemi Bernon if (!bfd_get_section_contents(abfd, section, debuglink, 0,
1107ba0509dcSRemi Bernon section->size))
1108ba0509dcSRemi Bernon goto out_close;
1109ba0509dcSRemi Bernon
1110ba0509dcSRemi Bernon err = 0;
1111ba0509dcSRemi Bernon
1112ba0509dcSRemi Bernon out_close:
1113ba0509dcSRemi Bernon bfd_close(abfd);
1114ba0509dcSRemi Bernon return err;
1115ba0509dcSRemi Bernon }
1116ba0509dcSRemi Bernon
1117ba0509dcSRemi Bernon #else
1118ba0509dcSRemi Bernon
filename__read_debuglink(const char * filename,char * debuglink,size_t size)1119e5a1845fSNamhyung Kim int filename__read_debuglink(const char *filename, char *debuglink,
1120e5a1845fSNamhyung Kim size_t size)
1121e5a1845fSNamhyung Kim {
1122e5a1845fSNamhyung Kim int fd, err = -1;
1123e5a1845fSNamhyung Kim Elf *elf;
1124e5a1845fSNamhyung Kim GElf_Ehdr ehdr;
1125e5a1845fSNamhyung Kim GElf_Shdr shdr;
1126e5a1845fSNamhyung Kim Elf_Data *data;
1127e5a1845fSNamhyung Kim Elf_Scn *sec;
1128e5a1845fSNamhyung Kim Elf_Kind ek;
1129e5a1845fSNamhyung Kim
1130e5a1845fSNamhyung Kim fd = open(filename, O_RDONLY);
1131e5a1845fSNamhyung Kim if (fd < 0)
1132e5a1845fSNamhyung Kim goto out;
1133e5a1845fSNamhyung Kim
1134e5a1845fSNamhyung Kim elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1135e5a1845fSNamhyung Kim if (elf == NULL) {
1136e5a1845fSNamhyung Kim pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
1137e5a1845fSNamhyung Kim goto out_close;
1138e5a1845fSNamhyung Kim }
1139e5a1845fSNamhyung Kim
1140e5a1845fSNamhyung Kim ek = elf_kind(elf);
1141e5a1845fSNamhyung Kim if (ek != ELF_K_ELF)
1142784f3390SChenggang Qin goto out_elf_end;
1143e5a1845fSNamhyung Kim
1144e5a1845fSNamhyung Kim if (gelf_getehdr(elf, &ehdr) == NULL) {
1145e5a1845fSNamhyung Kim pr_err("%s: cannot get elf header.\n", __func__);
1146784f3390SChenggang Qin goto out_elf_end;
1147e5a1845fSNamhyung Kim }
1148e5a1845fSNamhyung Kim
1149e5a1845fSNamhyung Kim sec = elf_section_by_name(elf, &ehdr, &shdr,
1150e5a1845fSNamhyung Kim ".gnu_debuglink", NULL);
1151e5a1845fSNamhyung Kim if (sec == NULL)
1152784f3390SChenggang Qin goto out_elf_end;
1153e5a1845fSNamhyung Kim
1154e5a1845fSNamhyung Kim data = elf_getdata(sec, NULL);
1155e5a1845fSNamhyung Kim if (data == NULL)
1156784f3390SChenggang Qin goto out_elf_end;
1157e5a1845fSNamhyung Kim
1158e5a1845fSNamhyung Kim /* the start of this section is a zero-terminated string */
1159e5a1845fSNamhyung Kim strncpy(debuglink, data->d_buf, size);
1160e5a1845fSNamhyung Kim
11610d3dc5e8SStephane Eranian err = 0;
11620d3dc5e8SStephane Eranian
1163784f3390SChenggang Qin out_elf_end:
1164e5a1845fSNamhyung Kim elf_end(elf);
1165e5a1845fSNamhyung Kim out_close:
1166e5a1845fSNamhyung Kim close(fd);
1167e5a1845fSNamhyung Kim out:
1168e5a1845fSNamhyung Kim return err;
1169e5a1845fSNamhyung Kim }
1170e5a1845fSNamhyung Kim
1171ba0509dcSRemi Bernon #endif
1172ba0509dcSRemi Bernon
dso__swap_init(struct dso * dso,unsigned char eidata)1173e5a1845fSNamhyung Kim static int dso__swap_init(struct dso *dso, unsigned char eidata)
1174e5a1845fSNamhyung Kim {
1175e5a1845fSNamhyung Kim static unsigned int const endian = 1;
1176e5a1845fSNamhyung Kim
1177e5a1845fSNamhyung Kim dso->needs_swap = DSO_SWAP__NO;
1178e5a1845fSNamhyung Kim
1179e5a1845fSNamhyung Kim switch (eidata) {
1180e5a1845fSNamhyung Kim case ELFDATA2LSB:
1181e5a1845fSNamhyung Kim /* We are big endian, DSO is little endian. */
1182e5a1845fSNamhyung Kim if (*(unsigned char const *)&endian != 1)
1183e5a1845fSNamhyung Kim dso->needs_swap = DSO_SWAP__YES;
1184e5a1845fSNamhyung Kim break;
1185e5a1845fSNamhyung Kim
1186e5a1845fSNamhyung Kim case ELFDATA2MSB:
1187e5a1845fSNamhyung Kim /* We are little endian, DSO is big endian. */
1188e5a1845fSNamhyung Kim if (*(unsigned char const *)&endian != 0)
1189e5a1845fSNamhyung Kim dso->needs_swap = DSO_SWAP__YES;
1190e5a1845fSNamhyung Kim break;
1191e5a1845fSNamhyung Kim
1192e5a1845fSNamhyung Kim default:
1193e5a1845fSNamhyung Kim pr_err("unrecognized DSO data encoding %d\n", eidata);
1194e5a1845fSNamhyung Kim return -EINVAL;
1195e5a1845fSNamhyung Kim }
1196e5a1845fSNamhyung Kim
1197e5a1845fSNamhyung Kim return 0;
1198e5a1845fSNamhyung Kim }
1199e5a1845fSNamhyung Kim
symsrc__possibly_runtime(struct symsrc * ss)12003aafe5aeSCody P Schafer bool symsrc__possibly_runtime(struct symsrc *ss)
12013aafe5aeSCody P Schafer {
12023aafe5aeSCody P Schafer return ss->dynsym || ss->opdsec;
12033aafe5aeSCody P Schafer }
12043aafe5aeSCody P Schafer
symsrc__has_symtab(struct symsrc * ss)1205d26cd12bSCody P Schafer bool symsrc__has_symtab(struct symsrc *ss)
1206d26cd12bSCody P Schafer {
1207d26cd12bSCody P Schafer return ss->symtab != NULL;
1208d26cd12bSCody P Schafer }
1209b68e2f91SCody P Schafer
symsrc__destroy(struct symsrc * ss)1210b68e2f91SCody P Schafer void symsrc__destroy(struct symsrc *ss)
1211e5a1845fSNamhyung Kim {
121274cf249dSArnaldo Carvalho de Melo zfree(&ss->name);
1213b68e2f91SCody P Schafer elf_end(ss->elf);
1214b68e2f91SCody P Schafer close(ss->fd);
1215b68e2f91SCody P Schafer }
1216b68e2f91SCody P Schafer
elf__needs_adjust_symbols(GElf_Ehdr ehdr)12177eec00a7SLeo Yan bool elf__needs_adjust_symbols(GElf_Ehdr ehdr)
1218d2332098SNaveen N. Rao {
12197eec00a7SLeo Yan /*
12207eec00a7SLeo Yan * Usually vmlinux is an ELF file with type ET_EXEC for most
12217eec00a7SLeo Yan * architectures; except Arm64 kernel is linked with option
12227eec00a7SLeo Yan * '-share', so need to check type ET_DYN.
12237eec00a7SLeo Yan */
12247eec00a7SLeo Yan return ehdr.e_type == ET_EXEC || ehdr.e_type == ET_REL ||
12257eec00a7SLeo Yan ehdr.e_type == ET_DYN;
1226d2332098SNaveen N. Rao }
1227d2332098SNaveen N. Rao
symsrc__init(struct symsrc * ss,struct dso * dso,const char * name,enum dso_binary_type type)1228b68e2f91SCody P Schafer int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
1229b68e2f91SCody P Schafer enum dso_binary_type type)
1230b68e2f91SCody P Schafer {
1231e5a1845fSNamhyung Kim GElf_Ehdr ehdr;
1232e5a1845fSNamhyung Kim Elf *elf;
1233b68e2f91SCody P Schafer int fd;
1234b68e2f91SCody P Schafer
123518425f13SArnaldo Carvalho de Melo if (dso__needs_decompress(dso)) {
123642b3fa67SNamhyung Kim fd = dso__decompress_kmodule_fd(dso, name);
1237b68e2f91SCody P Schafer if (fd < 0)
1238b68e2f91SCody P Schafer return -1;
1239c25ec42fSNamhyung Kim
1240c25ec42fSNamhyung Kim type = dso->symtab_type;
124118425f13SArnaldo Carvalho de Melo } else {
124218425f13SArnaldo Carvalho de Melo fd = open(name, O_RDONLY);
124318425f13SArnaldo Carvalho de Melo if (fd < 0) {
124418425f13SArnaldo Carvalho de Melo dso->load_errno = errno;
124518425f13SArnaldo Carvalho de Melo return -1;
124618425f13SArnaldo Carvalho de Melo }
124718425f13SArnaldo Carvalho de Melo }
1248e5a1845fSNamhyung Kim
1249e5a1845fSNamhyung Kim elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1250e5a1845fSNamhyung Kim if (elf == NULL) {
1251e5a1845fSNamhyung Kim pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
125218425f13SArnaldo Carvalho de Melo dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
1253e5a1845fSNamhyung Kim goto out_close;
1254e5a1845fSNamhyung Kim }
1255e5a1845fSNamhyung Kim
1256e5a1845fSNamhyung Kim if (gelf_getehdr(elf, &ehdr) == NULL) {
125718425f13SArnaldo Carvalho de Melo dso->load_errno = DSO_LOAD_ERRNO__INVALID_ELF;
1258e5a1845fSNamhyung Kim pr_debug("%s: cannot get elf header.\n", __func__);
1259e5a1845fSNamhyung Kim goto out_elf_end;
1260e5a1845fSNamhyung Kim }
1261e5a1845fSNamhyung Kim
126218425f13SArnaldo Carvalho de Melo if (dso__swap_init(dso, ehdr.e_ident[EI_DATA])) {
126318425f13SArnaldo Carvalho de Melo dso->load_errno = DSO_LOAD_ERRNO__INTERNAL_ERROR;
1264e5a1845fSNamhyung Kim goto out_elf_end;
126518425f13SArnaldo Carvalho de Melo }
1266e5a1845fSNamhyung Kim
1267e5a1845fSNamhyung Kim /* Always reject images with a mismatched build-id: */
1268428aff82SMasami Hiramatsu if (dso->has_build_id && !symbol_conf.ignore_vmlinux_buildid) {
1269e5a1845fSNamhyung Kim u8 build_id[BUILD_ID_SIZE];
127039be8d01SJiri Olsa struct build_id bid;
127139be8d01SJiri Olsa int size;
1272e5a1845fSNamhyung Kim
127339be8d01SJiri Olsa size = elf_read_build_id(elf, build_id, BUILD_ID_SIZE);
127439be8d01SJiri Olsa if (size <= 0) {
127518425f13SArnaldo Carvalho de Melo dso->load_errno = DSO_LOAD_ERRNO__CANNOT_READ_BUILDID;
1276e5a1845fSNamhyung Kim goto out_elf_end;
127718425f13SArnaldo Carvalho de Melo }
1278e5a1845fSNamhyung Kim
127939be8d01SJiri Olsa build_id__init(&bid, build_id, size);
128039be8d01SJiri Olsa if (!dso__build_id_equal(dso, &bid)) {
1281468f3d29SNaveen N. Rao pr_debug("%s: build id mismatch for %s.\n", __func__, name);
128218425f13SArnaldo Carvalho de Melo dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID;
1283e5a1845fSNamhyung Kim goto out_elf_end;
1284e5a1845fSNamhyung Kim }
128518425f13SArnaldo Carvalho de Melo }
1286e5a1845fSNamhyung Kim
1287c6d8f2a4SAdrian Hunter ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
1288c6d8f2a4SAdrian Hunter
1289a1ab1285SAdrian Hunter ss->symtab_idx = 0;
1290b68e2f91SCody P Schafer ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab",
1291a1ab1285SAdrian Hunter &ss->symtab_idx);
1292b68e2f91SCody P Schafer if (ss->symshdr.sh_type != SHT_SYMTAB)
1293b68e2f91SCody P Schafer ss->symtab = NULL;
1294b68e2f91SCody P Schafer
1295b68e2f91SCody P Schafer ss->dynsym_idx = 0;
1296b68e2f91SCody P Schafer ss->dynsym = elf_section_by_name(elf, &ehdr, &ss->dynshdr, ".dynsym",
1297b68e2f91SCody P Schafer &ss->dynsym_idx);
1298b68e2f91SCody P Schafer if (ss->dynshdr.sh_type != SHT_DYNSYM)
1299b68e2f91SCody P Schafer ss->dynsym = NULL;
1300b68e2f91SCody P Schafer
1301b68e2f91SCody P Schafer ss->opdidx = 0;
1302b68e2f91SCody P Schafer ss->opdsec = elf_section_by_name(elf, &ehdr, &ss->opdshdr, ".opd",
1303b68e2f91SCody P Schafer &ss->opdidx);
1304b68e2f91SCody P Schafer if (ss->opdshdr.sh_type != SHT_PROGBITS)
1305b68e2f91SCody P Schafer ss->opdsec = NULL;
1306b68e2f91SCody P Schafer
13071c695c88SJiri Olsa if (dso->kernel == DSO_SPACE__USER)
130899e87f7bSWang Nan ss->adjust_symbols = true;
130999e87f7bSWang Nan else
1310d2332098SNaveen N. Rao ss->adjust_symbols = elf__needs_adjust_symbols(ehdr);
1311b68e2f91SCody P Schafer
1312b68e2f91SCody P Schafer ss->name = strdup(name);
131318425f13SArnaldo Carvalho de Melo if (!ss->name) {
131418425f13SArnaldo Carvalho de Melo dso->load_errno = errno;
1315b68e2f91SCody P Schafer goto out_elf_end;
131618425f13SArnaldo Carvalho de Melo }
1317b68e2f91SCody P Schafer
1318b68e2f91SCody P Schafer ss->elf = elf;
1319b68e2f91SCody P Schafer ss->fd = fd;
1320b68e2f91SCody P Schafer ss->ehdr = ehdr;
1321b68e2f91SCody P Schafer ss->type = type;
1322b68e2f91SCody P Schafer
1323b68e2f91SCody P Schafer return 0;
1324b68e2f91SCody P Schafer
1325b68e2f91SCody P Schafer out_elf_end:
1326b68e2f91SCody P Schafer elf_end(elf);
1327b68e2f91SCody P Schafer out_close:
1328b68e2f91SCody P Schafer close(fd);
1329e5f177a5SLeo Yan return -1;
1330b68e2f91SCody P Schafer }
1331b68e2f91SCody P Schafer
133239b12f78SAdrian Hunter /**
133339b12f78SAdrian Hunter * ref_reloc_sym_not_found - has kernel relocation symbol been found.
133439b12f78SAdrian Hunter * @kmap: kernel maps and relocation reference symbol
133539b12f78SAdrian Hunter *
133639b12f78SAdrian Hunter * This function returns %true if we are dealing with the kernel maps and the
133739b12f78SAdrian Hunter * relocation reference symbol has not yet been found. Otherwise %false is
133839b12f78SAdrian Hunter * returned.
133939b12f78SAdrian Hunter */
ref_reloc_sym_not_found(struct kmap * kmap)134039b12f78SAdrian Hunter static bool ref_reloc_sym_not_found(struct kmap *kmap)
134139b12f78SAdrian Hunter {
134239b12f78SAdrian Hunter return kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
134339b12f78SAdrian Hunter !kmap->ref_reloc_sym->unrelocated_addr;
134439b12f78SAdrian Hunter }
134539b12f78SAdrian Hunter
134639b12f78SAdrian Hunter /**
134739b12f78SAdrian Hunter * ref_reloc - kernel relocation offset.
134839b12f78SAdrian Hunter * @kmap: kernel maps and relocation reference symbol
134939b12f78SAdrian Hunter *
135039b12f78SAdrian Hunter * This function returns the offset of kernel addresses as determined by using
135139b12f78SAdrian Hunter * the relocation reference symbol i.e. if the kernel has not been relocated
135239b12f78SAdrian Hunter * then the return value is zero.
135339b12f78SAdrian Hunter */
ref_reloc(struct kmap * kmap)135439b12f78SAdrian Hunter static u64 ref_reloc(struct kmap *kmap)
135539b12f78SAdrian Hunter {
135639b12f78SAdrian Hunter if (kmap && kmap->ref_reloc_sym &&
135739b12f78SAdrian Hunter kmap->ref_reloc_sym->unrelocated_addr)
135839b12f78SAdrian Hunter return kmap->ref_reloc_sym->addr -
135939b12f78SAdrian Hunter kmap->ref_reloc_sym->unrelocated_addr;
136039b12f78SAdrian Hunter return 0;
136139b12f78SAdrian Hunter }
136239b12f78SAdrian Hunter
arch__sym_update(struct symbol * s __maybe_unused,GElf_Sym * sym __maybe_unused)13630b3c2264SNaveen N. Rao void __weak arch__sym_update(struct symbol *s __maybe_unused,
13640b3c2264SNaveen N. Rao GElf_Sym *sym __maybe_unused) { }
1365c50fc0a4SAnanth N Mavinakayanahalli
dso__process_kernel_symbol(struct dso * dso,struct map * map,GElf_Sym * sym,GElf_Shdr * shdr,struct maps * kmaps,struct kmap * kmap,struct dso ** curr_dsop,struct map ** curr_mapp,const char * section_name,bool adjust_kernel_syms,bool kmodule,bool * remap_kernel)13664e0d1e8bSArnaldo Carvalho de Melo static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
13674e0d1e8bSArnaldo Carvalho de Melo GElf_Sym *sym, GElf_Shdr *shdr,
136879b6bb73SArnaldo Carvalho de Melo struct maps *kmaps, struct kmap *kmap,
13694e0d1e8bSArnaldo Carvalho de Melo struct dso **curr_dsop, struct map **curr_mapp,
13704e0d1e8bSArnaldo Carvalho de Melo const char *section_name,
13714e0d1e8bSArnaldo Carvalho de Melo bool adjust_kernel_syms, bool kmodule, bool *remap_kernel)
13724e0d1e8bSArnaldo Carvalho de Melo {
13734e0d1e8bSArnaldo Carvalho de Melo struct dso *curr_dso = *curr_dsop;
13744e0d1e8bSArnaldo Carvalho de Melo struct map *curr_map;
13754e0d1e8bSArnaldo Carvalho de Melo char dso_name[PATH_MAX];
13764e0d1e8bSArnaldo Carvalho de Melo
13774e0d1e8bSArnaldo Carvalho de Melo /* Adjust symbol to map to file offset */
13784e0d1e8bSArnaldo Carvalho de Melo if (adjust_kernel_syms)
13794e0d1e8bSArnaldo Carvalho de Melo sym->st_value -= shdr->sh_addr - shdr->sh_offset;
13804e0d1e8bSArnaldo Carvalho de Melo
13814e0d1e8bSArnaldo Carvalho de Melo if (strcmp(section_name, (curr_dso->short_name + dso->short_name_len)) == 0)
13824e0d1e8bSArnaldo Carvalho de Melo return 0;
13834e0d1e8bSArnaldo Carvalho de Melo
13844e0d1e8bSArnaldo Carvalho de Melo if (strcmp(section_name, ".text") == 0) {
13854e0d1e8bSArnaldo Carvalho de Melo /*
13864e0d1e8bSArnaldo Carvalho de Melo * The initial kernel mapping is based on
13874e0d1e8bSArnaldo Carvalho de Melo * kallsyms and identity maps. Overwrite it to
13884e0d1e8bSArnaldo Carvalho de Melo * map to the kernel dso.
13894e0d1e8bSArnaldo Carvalho de Melo */
1390b2fe96a3SJiri Olsa if (*remap_kernel && dso->kernel && !kmodule) {
13914e0d1e8bSArnaldo Carvalho de Melo *remap_kernel = false;
1392e6a9efceSArnaldo Carvalho de Melo map__set_start(map, shdr->sh_addr + ref_reloc(kmap));
1393e6a9efceSArnaldo Carvalho de Melo map__set_end(map, map__start(map) + shdr->sh_size);
1394e6a9efceSArnaldo Carvalho de Melo map__set_pgoff(map, shdr->sh_offset);
1395e6a9efceSArnaldo Carvalho de Melo map__set_map_ip(map, map__dso_map_ip);
1396e6a9efceSArnaldo Carvalho de Melo map__set_unmap_ip(map, map__dso_unmap_ip);
13974e0d1e8bSArnaldo Carvalho de Melo /* Ensure maps are correctly ordered */
13984e0d1e8bSArnaldo Carvalho de Melo if (kmaps) {
1399ff583dc4SIan Rogers int err;
1400fe8fec10SIan Rogers struct map *tmp = map__get(map);
1401ff583dc4SIan Rogers
140279b6bb73SArnaldo Carvalho de Melo maps__remove(kmaps, map);
1403ff583dc4SIan Rogers err = maps__insert(kmaps, map);
1404fe8fec10SIan Rogers map__put(tmp);
1405ff583dc4SIan Rogers if (err)
1406ff583dc4SIan Rogers return err;
14074e0d1e8bSArnaldo Carvalho de Melo }
14084e0d1e8bSArnaldo Carvalho de Melo }
14094e0d1e8bSArnaldo Carvalho de Melo
14104e0d1e8bSArnaldo Carvalho de Melo /*
14114e0d1e8bSArnaldo Carvalho de Melo * The initial module mapping is based on
14124e0d1e8bSArnaldo Carvalho de Melo * /proc/modules mapped to offset zero.
14134e0d1e8bSArnaldo Carvalho de Melo * Overwrite it to map to the module dso.
14144e0d1e8bSArnaldo Carvalho de Melo */
14154e0d1e8bSArnaldo Carvalho de Melo if (*remap_kernel && kmodule) {
14164e0d1e8bSArnaldo Carvalho de Melo *remap_kernel = false;
1417e6a9efceSArnaldo Carvalho de Melo map__set_pgoff(map, shdr->sh_offset);
14184e0d1e8bSArnaldo Carvalho de Melo }
14194e0d1e8bSArnaldo Carvalho de Melo
14204e0d1e8bSArnaldo Carvalho de Melo *curr_mapp = map;
14214e0d1e8bSArnaldo Carvalho de Melo *curr_dsop = dso;
14224e0d1e8bSArnaldo Carvalho de Melo return 0;
14234e0d1e8bSArnaldo Carvalho de Melo }
14244e0d1e8bSArnaldo Carvalho de Melo
14254e0d1e8bSArnaldo Carvalho de Melo if (!kmap)
14264e0d1e8bSArnaldo Carvalho de Melo return 0;
14274e0d1e8bSArnaldo Carvalho de Melo
14284e0d1e8bSArnaldo Carvalho de Melo snprintf(dso_name, sizeof(dso_name), "%s%s", dso->short_name, section_name);
14294e0d1e8bSArnaldo Carvalho de Melo
143079b6bb73SArnaldo Carvalho de Melo curr_map = maps__find_by_name(kmaps, dso_name);
14314e0d1e8bSArnaldo Carvalho de Melo if (curr_map == NULL) {
14324e0d1e8bSArnaldo Carvalho de Melo u64 start = sym->st_value;
14334e0d1e8bSArnaldo Carvalho de Melo
14344e0d1e8bSArnaldo Carvalho de Melo if (kmodule)
1435e5116f46SIan Rogers start += map__start(map) + shdr->sh_offset;
14364e0d1e8bSArnaldo Carvalho de Melo
14374e0d1e8bSArnaldo Carvalho de Melo curr_dso = dso__new(dso_name);
14384e0d1e8bSArnaldo Carvalho de Melo if (curr_dso == NULL)
14394e0d1e8bSArnaldo Carvalho de Melo return -1;
14404e0d1e8bSArnaldo Carvalho de Melo curr_dso->kernel = dso->kernel;
14414e0d1e8bSArnaldo Carvalho de Melo curr_dso->long_name = dso->long_name;
14424e0d1e8bSArnaldo Carvalho de Melo curr_dso->long_name_len = dso->long_name_len;
1443*e59fea47SAthira Rajeev curr_dso->binary_type = dso->binary_type;
1444*e59fea47SAthira Rajeev curr_dso->adjust_symbols = dso->adjust_symbols;
14454e0d1e8bSArnaldo Carvalho de Melo curr_map = map__new2(start, curr_dso);
14464e0d1e8bSArnaldo Carvalho de Melo dso__put(curr_dso);
14474e0d1e8bSArnaldo Carvalho de Melo if (curr_map == NULL)
14484e0d1e8bSArnaldo Carvalho de Melo return -1;
14494e0d1e8bSArnaldo Carvalho de Melo
1450a75af86bSArnaldo Carvalho de Melo if (curr_dso->kernel)
1451a75af86bSArnaldo Carvalho de Melo map__kmap(curr_map)->kmaps = kmaps;
1452a75af86bSArnaldo Carvalho de Melo
14534e0d1e8bSArnaldo Carvalho de Melo if (adjust_kernel_syms) {
1454e6a9efceSArnaldo Carvalho de Melo map__set_start(curr_map, shdr->sh_addr + ref_reloc(kmap));
1455e6a9efceSArnaldo Carvalho de Melo map__set_end(curr_map, map__start(curr_map) + shdr->sh_size);
1456e6a9efceSArnaldo Carvalho de Melo map__set_pgoff(curr_map, shdr->sh_offset);
14574e0d1e8bSArnaldo Carvalho de Melo } else {
1458e6a9efceSArnaldo Carvalho de Melo map__set_map_ip(curr_map, identity__map_ip);
1459e6a9efceSArnaldo Carvalho de Melo map__set_unmap_ip(curr_map, identity__map_ip);
14604e0d1e8bSArnaldo Carvalho de Melo }
14614e0d1e8bSArnaldo Carvalho de Melo curr_dso->symtab_type = dso->symtab_type;
1462ff583dc4SIan Rogers if (maps__insert(kmaps, curr_map))
1463ff583dc4SIan Rogers return -1;
14644e0d1e8bSArnaldo Carvalho de Melo /*
14654d39c89fSIngo Molnar * Add it before we drop the reference to curr_map, i.e. while
14664e0d1e8bSArnaldo Carvalho de Melo * we still are sure to have a reference to this DSO via
14674e0d1e8bSArnaldo Carvalho de Melo * *curr_map->dso.
14684e0d1e8bSArnaldo Carvalho de Melo */
14695ab6d715SIan Rogers dsos__add(&maps__machine(kmaps)->dsos, curr_dso);
14704e0d1e8bSArnaldo Carvalho de Melo /* kmaps already got it */
14714e0d1e8bSArnaldo Carvalho de Melo map__put(curr_map);
14724e0d1e8bSArnaldo Carvalho de Melo dso__set_loaded(curr_dso);
14734e0d1e8bSArnaldo Carvalho de Melo *curr_mapp = curr_map;
14744e0d1e8bSArnaldo Carvalho de Melo *curr_dsop = curr_dso;
14754e0d1e8bSArnaldo Carvalho de Melo } else
147663df0e4bSIan Rogers *curr_dsop = map__dso(curr_map);
14774e0d1e8bSArnaldo Carvalho de Melo
14784e0d1e8bSArnaldo Carvalho de Melo return 0;
14794e0d1e8bSArnaldo Carvalho de Melo }
14804e0d1e8bSArnaldo Carvalho de Melo
148187704345SMasami Hiramatsu static int
dso__load_sym_internal(struct dso * dso,struct map * map,struct symsrc * syms_ss,struct symsrc * runtime_ss,int kmodule,int dynsym)148287704345SMasami Hiramatsu dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
148387704345SMasami Hiramatsu struct symsrc *runtime_ss, int kmodule, int dynsym)
1484b68e2f91SCody P Schafer {
1485b68e2f91SCody P Schafer struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL;
148679b6bb73SArnaldo Carvalho de Melo struct maps *kmaps = kmap ? map__kmaps(map) : NULL;
1487b68e2f91SCody P Schafer struct map *curr_map = map;
1488b68e2f91SCody P Schafer struct dso *curr_dso = dso;
148983952286SRiccardo Mancini Elf_Data *symstrs, *secstrs, *secstrs_run, *secstrs_sym;
1490b68e2f91SCody P Schafer uint32_t nr_syms;
1491b68e2f91SCody P Schafer int err = -1;
1492b68e2f91SCody P Schafer uint32_t idx;
1493b68e2f91SCody P Schafer GElf_Ehdr ehdr;
1494261360b6SCody P Schafer GElf_Shdr shdr;
149573cdf0c6SWang Nan GElf_Shdr tshdr;
1496b68e2f91SCody P Schafer Elf_Data *syms, *opddata = NULL;
1497b68e2f91SCody P Schafer GElf_Sym sym;
1498261360b6SCody P Schafer Elf_Scn *sec, *sec_strndx;
1499b68e2f91SCody P Schafer Elf *elf;
1500b68e2f91SCody P Schafer int nr = 0;
150139b12f78SAdrian Hunter bool remap_kernel = false, adjust_kernel_syms = false;
1502b68e2f91SCody P Schafer
1503ba92732eSWang Nan if (kmap && !kmaps)
1504ba92732eSWang Nan return -1;
1505ba92732eSWang Nan
1506261360b6SCody P Schafer elf = syms_ss->elf;
1507261360b6SCody P Schafer ehdr = syms_ss->ehdr;
150887704345SMasami Hiramatsu if (dynsym) {
150987704345SMasami Hiramatsu sec = syms_ss->dynsym;
151087704345SMasami Hiramatsu shdr = syms_ss->dynshdr;
151187704345SMasami Hiramatsu } else {
1512261360b6SCody P Schafer sec = syms_ss->symtab;
1513261360b6SCody P Schafer shdr = syms_ss->symshdr;
151487704345SMasami Hiramatsu }
1515b68e2f91SCody P Schafer
151650de1a0cSAnton Blanchard if (elf_section_by_name(runtime_ss->elf, &runtime_ss->ehdr, &tshdr,
151750de1a0cSAnton Blanchard ".text", NULL))
151873cdf0c6SWang Nan dso->text_offset = tshdr.sh_addr - tshdr.sh_offset;
151973cdf0c6SWang Nan
1520261360b6SCody P Schafer if (runtime_ss->opdsec)
1521261360b6SCody P Schafer opddata = elf_rawdata(runtime_ss->opdsec, NULL);
1522e5a1845fSNamhyung Kim
1523e5a1845fSNamhyung Kim syms = elf_getdata(sec, NULL);
1524e5a1845fSNamhyung Kim if (syms == NULL)
1525e5a1845fSNamhyung Kim goto out_elf_end;
1526e5a1845fSNamhyung Kim
1527e5a1845fSNamhyung Kim sec = elf_getscn(elf, shdr.sh_link);
1528e5a1845fSNamhyung Kim if (sec == NULL)
1529e5a1845fSNamhyung Kim goto out_elf_end;
1530e5a1845fSNamhyung Kim
1531e5a1845fSNamhyung Kim symstrs = elf_getdata(sec, NULL);
1532e5a1845fSNamhyung Kim if (symstrs == NULL)
1533e5a1845fSNamhyung Kim goto out_elf_end;
1534e5a1845fSNamhyung Kim
1535f247fb81SAdrian Hunter sec_strndx = elf_getscn(runtime_ss->elf, runtime_ss->ehdr.e_shstrndx);
1536e5a1845fSNamhyung Kim if (sec_strndx == NULL)
1537e5a1845fSNamhyung Kim goto out_elf_end;
1538e5a1845fSNamhyung Kim
153983952286SRiccardo Mancini secstrs_run = elf_getdata(sec_strndx, NULL);
154083952286SRiccardo Mancini if (secstrs_run == NULL)
154183952286SRiccardo Mancini goto out_elf_end;
154283952286SRiccardo Mancini
154383952286SRiccardo Mancini sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
154483952286SRiccardo Mancini if (sec_strndx == NULL)
154583952286SRiccardo Mancini goto out_elf_end;
154683952286SRiccardo Mancini
154783952286SRiccardo Mancini secstrs_sym = elf_getdata(sec_strndx, NULL);
154883952286SRiccardo Mancini if (secstrs_sym == NULL)
1549e5a1845fSNamhyung Kim goto out_elf_end;
1550e5a1845fSNamhyung Kim
1551e5a1845fSNamhyung Kim nr_syms = shdr.sh_size / shdr.sh_entsize;
1552e5a1845fSNamhyung Kim
1553e5a1845fSNamhyung Kim memset(&sym, 0, sizeof(sym));
155439b12f78SAdrian Hunter
155539b12f78SAdrian Hunter /*
155639b12f78SAdrian Hunter * The kernel relocation symbol is needed in advance in order to adjust
155739b12f78SAdrian Hunter * kernel maps correctly.
155839b12f78SAdrian Hunter */
155939b12f78SAdrian Hunter if (ref_reloc_sym_not_found(kmap)) {
156039b12f78SAdrian Hunter elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
156139b12f78SAdrian Hunter const char *elf_name = elf_sym__name(&sym, symstrs);
156239b12f78SAdrian Hunter
156339b12f78SAdrian Hunter if (strcmp(elf_name, kmap->ref_reloc_sym->name))
156439b12f78SAdrian Hunter continue;
156539b12f78SAdrian Hunter kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
1566e6a9efceSArnaldo Carvalho de Melo map__set_reloc(map, kmap->ref_reloc_sym->addr - kmap->ref_reloc_sym->unrelocated_addr);
156739b12f78SAdrian Hunter break;
156839b12f78SAdrian Hunter }
156939b12f78SAdrian Hunter }
157039b12f78SAdrian Hunter
1571f0ee3b46SAdrian Hunter /*
1572f0ee3b46SAdrian Hunter * Handle any relocation of vdso necessary because older kernels
1573f0ee3b46SAdrian Hunter * attempted to prelink vdso to its virtual address.
1574f0ee3b46SAdrian Hunter */
157573cdf0c6SWang Nan if (dso__is_vdso(dso))
1576e6a9efceSArnaldo Carvalho de Melo map__set_reloc(map, map__start(map) - dso->text_offset);
1577f0ee3b46SAdrian Hunter
157839b12f78SAdrian Hunter dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap);
157939b12f78SAdrian Hunter /*
1580d1fd8d9eSArnaldo Carvalho de Melo * Initial kernel and module mappings do not map to the dso.
1581d1fd8d9eSArnaldo Carvalho de Melo * Flag the fixups.
158239b12f78SAdrian Hunter */
1583b2fe96a3SJiri Olsa if (dso->kernel) {
158439b12f78SAdrian Hunter remap_kernel = true;
158539b12f78SAdrian Hunter adjust_kernel_syms = dso->adjust_symbols;
158639b12f78SAdrian Hunter }
1587e5a1845fSNamhyung Kim elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
1588e5a1845fSNamhyung Kim struct symbol *f;
1589e5a1845fSNamhyung Kim const char *elf_name = elf_sym__name(&sym, symstrs);
1590e5a1845fSNamhyung Kim char *demangled = NULL;
1591e5a1845fSNamhyung Kim int is_label = elf_sym__is_label(&sym);
1592e5a1845fSNamhyung Kim const char *section_name;
1593261360b6SCody P Schafer bool used_opd = false;
1594e5a1845fSNamhyung Kim
15953183f8caSArnaldo Carvalho de Melo if (!is_label && !elf_sym__filter(&sym))
1596e5a1845fSNamhyung Kim continue;
1597e5a1845fSNamhyung Kim
1598e5a1845fSNamhyung Kim /* Reject ARM ELF "mapping symbols": these aren't unique and
1599e5a1845fSNamhyung Kim * don't identify functions, so will confuse the profile
1600e5a1845fSNamhyung Kim * output: */
16014886f2caSVictor Kamensky if (ehdr.e_machine == EM_ARM || ehdr.e_machine == EM_AARCH64) {
16024886f2caSVictor Kamensky if (elf_name[0] == '$' && strchr("adtx", elf_name[1])
16034886f2caSVictor Kamensky && (elf_name[2] == '\0' || elf_name[2] == '.'))
1604e5a1845fSNamhyung Kim continue;
1605e5a1845fSNamhyung Kim }
1606e5a1845fSNamhyung Kim
1607261360b6SCody P Schafer if (runtime_ss->opdsec && sym.st_shndx == runtime_ss->opdidx) {
1608261360b6SCody P Schafer u32 offset = sym.st_value - syms_ss->opdshdr.sh_addr;
1609e5a1845fSNamhyung Kim u64 *opd = opddata->d_buf + offset;
1610e5a1845fSNamhyung Kim sym.st_value = DSO__SWAP(dso, u64, *opd);
1611261360b6SCody P Schafer sym.st_shndx = elf_addr_to_index(runtime_ss->elf,
1612261360b6SCody P Schafer sym.st_value);
1613261360b6SCody P Schafer used_opd = true;
1614e5a1845fSNamhyung Kim }
16152d86612aSLeo Yan
16163843b05dSNamhyung Kim /*
16173843b05dSNamhyung Kim * When loading symbols in a data mapping, ABS symbols (which
16183843b05dSNamhyung Kim * has a value of SHN_ABS in its st_shndx) failed at
16193843b05dSNamhyung Kim * elf_getscn(). And it marks the loading as a failure so
16203843b05dSNamhyung Kim * already loaded symbols cannot be fixed up.
16213843b05dSNamhyung Kim *
16223843b05dSNamhyung Kim * I'm not sure what should be done. Just ignore them for now.
16233843b05dSNamhyung Kim * - Namhyung Kim
16243843b05dSNamhyung Kim */
16253843b05dSNamhyung Kim if (sym.st_shndx == SHN_ABS)
16263843b05dSNamhyung Kim continue;
1627e5a1845fSNamhyung Kim
16286833e0b8SJiri Slaby sec = elf_getscn(syms_ss->elf, sym.st_shndx);
16296833e0b8SJiri Slaby if (!sec)
16306833e0b8SJiri Slaby goto out_elf_end;
16316833e0b8SJiri Slaby
16326833e0b8SJiri Slaby gelf_getshdr(sec, &shdr);
16336833e0b8SJiri Slaby
1634882528d2SLeo Yan /*
1635882528d2SLeo Yan * If the attribute bit SHF_ALLOC is not set, the section
1636882528d2SLeo Yan * doesn't occupy memory during process execution.
1637882528d2SLeo Yan * E.g. ".gnu.warning.*" section is used by linker to generate
1638882528d2SLeo Yan * warnings when calling deprecated functions, the symbols in
1639882528d2SLeo Yan * the section aren't loaded to memory during process execution,
1640882528d2SLeo Yan * so skip them.
1641882528d2SLeo Yan */
1642882528d2SLeo Yan if (!(shdr.sh_flags & SHF_ALLOC))
1643882528d2SLeo Yan continue;
1644882528d2SLeo Yan
164583952286SRiccardo Mancini secstrs = secstrs_sym;
164683952286SRiccardo Mancini
16476833e0b8SJiri Slaby /*
16486833e0b8SJiri Slaby * We have to fallback to runtime when syms' section header has
16496833e0b8SJiri Slaby * NOBITS set. NOBITS results in file offset (sh_offset) not
16506833e0b8SJiri Slaby * being incremented. So sh_offset used below has different
16516833e0b8SJiri Slaby * values for syms (invalid) and runtime (valid).
16526833e0b8SJiri Slaby */
16536833e0b8SJiri Slaby if (shdr.sh_type == SHT_NOBITS) {
1654261360b6SCody P Schafer sec = elf_getscn(runtime_ss->elf, sym.st_shndx);
1655e5a1845fSNamhyung Kim if (!sec)
1656e5a1845fSNamhyung Kim goto out_elf_end;
1657e5a1845fSNamhyung Kim
1658e5a1845fSNamhyung Kim gelf_getshdr(sec, &shdr);
165983952286SRiccardo Mancini secstrs = secstrs_run;
16606833e0b8SJiri Slaby }
1661e5a1845fSNamhyung Kim
16623183f8caSArnaldo Carvalho de Melo if (is_label && !elf_sec__filter(&shdr, secstrs))
1663e5a1845fSNamhyung Kim continue;
1664e5a1845fSNamhyung Kim
1665e5a1845fSNamhyung Kim section_name = elf_sec__name(&shdr, secstrs);
1666e5a1845fSNamhyung Kim
1667e5a1845fSNamhyung Kim /* On ARM, symbols for thumb functions have 1 added to
1668e5a1845fSNamhyung Kim * the symbol address as a flag - remove it */
1669e5a1845fSNamhyung Kim if ((ehdr.e_machine == EM_ARM) &&
167018231d79SArnaldo Carvalho de Melo (GELF_ST_TYPE(sym.st_info) == STT_FUNC) &&
1671e5a1845fSNamhyung Kim (sym.st_value & 1))
1672e5a1845fSNamhyung Kim --sym.st_value;
1673e5a1845fSNamhyung Kim
1674b2fe96a3SJiri Olsa if (dso->kernel) {
16754e0d1e8bSArnaldo Carvalho de Melo if (dso__process_kernel_symbol(dso, map, &sym, &shdr, kmaps, kmap, &curr_dso, &curr_map,
16764e0d1e8bSArnaldo Carvalho de Melo section_name, adjust_kernel_syms, kmodule, &remap_kernel))
1677e5a1845fSNamhyung Kim goto out_elf_end;
1678857140e8SArnaldo Carvalho de Melo } else if ((used_opd && runtime_ss->adjust_symbols) ||
1679857140e8SArnaldo Carvalho de Melo (!used_opd && syms_ss->adjust_symbols)) {
16802d86612aSLeo Yan GElf_Phdr phdr;
16812d86612aSLeo Yan
16826f520ce1SAjay Kaher if (elf_read_program_header(runtime_ss->elf,
16832d86612aSLeo Yan (u64)sym.st_value, &phdr)) {
16846d518ac7SIan Rogers pr_debug4("%s: failed to find program header for "
16852d86612aSLeo Yan "symbol: %s st_value: %#" PRIx64 "\n",
16862d86612aSLeo Yan __func__, elf_name, (u64)sym.st_value);
16876d518ac7SIan Rogers pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
16886d518ac7SIan Rogers "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n",
16896d518ac7SIan Rogers __func__, (u64)sym.st_value, (u64)shdr.sh_addr,
16906d518ac7SIan Rogers (u64)shdr.sh_offset);
16916d518ac7SIan Rogers /*
16926d518ac7SIan Rogers * Fail to find program header, let's rollback
16936d518ac7SIan Rogers * to use shdr.sh_addr and shdr.sh_offset to
16946d518ac7SIan Rogers * calibrate symbol's file address, though this
16956d518ac7SIan Rogers * is not necessary for normal C ELF file, we
16966d518ac7SIan Rogers * still need to handle java JIT symbols in this
16976d518ac7SIan Rogers * case.
16986d518ac7SIan Rogers */
16996d518ac7SIan Rogers sym.st_value -= shdr.sh_addr - shdr.sh_offset;
17006d518ac7SIan Rogers } else {
1701e5a1845fSNamhyung Kim pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
17022d86612aSLeo Yan "p_vaddr: %#" PRIx64 " p_offset: %#" PRIx64 "\n",
17032d86612aSLeo Yan __func__, (u64)sym.st_value, (u64)phdr.p_vaddr,
17042d86612aSLeo Yan (u64)phdr.p_offset);
17052d86612aSLeo Yan sym.st_value -= phdr.p_vaddr - phdr.p_offset;
1706e5a1845fSNamhyung Kim }
17076d518ac7SIan Rogers }
17084e0d1e8bSArnaldo Carvalho de Melo
17092a8d41b4SMilian Wolff demangled = demangle_sym(dso, kmodule, elf_name);
1710e5a1845fSNamhyung Kim if (demangled != NULL)
1711e5a1845fSNamhyung Kim elf_name = demangled;
17122a8d41b4SMilian Wolff
1713e5a1845fSNamhyung Kim f = symbol__new(sym.st_value, sym.st_size,
1714af30bffaSArnaldo Carvalho de Melo GELF_ST_BIND(sym.st_info),
1715af30bffaSArnaldo Carvalho de Melo GELF_ST_TYPE(sym.st_info), elf_name);
1716e5a1845fSNamhyung Kim free(demangled);
1717e5a1845fSNamhyung Kim if (!f)
1718e5a1845fSNamhyung Kim goto out_elf_end;
1719e5a1845fSNamhyung Kim
17200b3c2264SNaveen N. Rao arch__sym_update(f, &sym);
17210b3c2264SNaveen N. Rao
17223183f8caSArnaldo Carvalho de Melo __symbols__insert(&curr_dso->symbols, f, dso->kernel);
1723e5a1845fSNamhyung Kim nr++;
1724e5a1845fSNamhyung Kim }
1725e5a1845fSNamhyung Kim
1726e5a1845fSNamhyung Kim /*
1727e5a1845fSNamhyung Kim * For misannotated, zeroed, ASM function sizes.
1728e5a1845fSNamhyung Kim */
1729e5a1845fSNamhyung Kim if (nr > 0) {
1730838425f2SNamhyung Kim symbols__fixup_end(&dso->symbols, false);
17313183f8caSArnaldo Carvalho de Melo symbols__fixup_duplicate(&dso->symbols);
1732e5a1845fSNamhyung Kim if (kmap) {
1733e5a1845fSNamhyung Kim /*
1734e5a1845fSNamhyung Kim * We need to fixup this here too because we create new
1735e5a1845fSNamhyung Kim * maps here, for things like vsyscall sections.
1736e5a1845fSNamhyung Kim */
173779b6bb73SArnaldo Carvalho de Melo maps__fixup_end(kmaps);
1738e5a1845fSNamhyung Kim }
1739e5a1845fSNamhyung Kim }
1740e5a1845fSNamhyung Kim err = nr;
1741e5a1845fSNamhyung Kim out_elf_end:
1742e5a1845fSNamhyung Kim return err;
1743e5a1845fSNamhyung Kim }
1744e5a1845fSNamhyung Kim
dso__load_sym(struct dso * dso,struct map * map,struct symsrc * syms_ss,struct symsrc * runtime_ss,int kmodule)174587704345SMasami Hiramatsu int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
174687704345SMasami Hiramatsu struct symsrc *runtime_ss, int kmodule)
174787704345SMasami Hiramatsu {
174887704345SMasami Hiramatsu int nr = 0;
174987704345SMasami Hiramatsu int err = -1;
175087704345SMasami Hiramatsu
175187704345SMasami Hiramatsu dso->symtab_type = syms_ss->type;
175287704345SMasami Hiramatsu dso->is_64_bit = syms_ss->is_64_bit;
175387704345SMasami Hiramatsu dso->rel = syms_ss->ehdr.e_type == ET_REL;
175487704345SMasami Hiramatsu
175587704345SMasami Hiramatsu /*
175687704345SMasami Hiramatsu * Modules may already have symbols from kallsyms, but those symbols
175787704345SMasami Hiramatsu * have the wrong values for the dso maps, so remove them.
175887704345SMasami Hiramatsu */
175987704345SMasami Hiramatsu if (kmodule && syms_ss->symtab)
176087704345SMasami Hiramatsu symbols__delete(&dso->symbols);
176187704345SMasami Hiramatsu
176287704345SMasami Hiramatsu if (!syms_ss->symtab) {
176387704345SMasami Hiramatsu /*
176487704345SMasami Hiramatsu * If the vmlinux is stripped, fail so we will fall back
176587704345SMasami Hiramatsu * to using kallsyms. The vmlinux runtime symbols aren't
176687704345SMasami Hiramatsu * of much use.
176787704345SMasami Hiramatsu */
176887704345SMasami Hiramatsu if (dso->kernel)
176987704345SMasami Hiramatsu return err;
177087704345SMasami Hiramatsu } else {
177187704345SMasami Hiramatsu err = dso__load_sym_internal(dso, map, syms_ss, runtime_ss,
177287704345SMasami Hiramatsu kmodule, 0);
177387704345SMasami Hiramatsu if (err < 0)
177487704345SMasami Hiramatsu return err;
177587704345SMasami Hiramatsu nr = err;
177687704345SMasami Hiramatsu }
177787704345SMasami Hiramatsu
177887704345SMasami Hiramatsu if (syms_ss->dynsym) {
177987704345SMasami Hiramatsu err = dso__load_sym_internal(dso, map, syms_ss, runtime_ss,
178087704345SMasami Hiramatsu kmodule, 1);
178187704345SMasami Hiramatsu if (err < 0)
178287704345SMasami Hiramatsu return err;
178387704345SMasami Hiramatsu err += nr;
178487704345SMasami Hiramatsu }
178587704345SMasami Hiramatsu
178687704345SMasami Hiramatsu return err;
178787704345SMasami Hiramatsu }
178887704345SMasami Hiramatsu
elf_read_maps(Elf * elf,bool exe,mapfn_t mapfn,void * data)17898e0cf965SAdrian Hunter static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data)
17908e0cf965SAdrian Hunter {
17918e0cf965SAdrian Hunter GElf_Phdr phdr;
17928e0cf965SAdrian Hunter size_t i, phdrnum;
17938e0cf965SAdrian Hunter int err;
17948e0cf965SAdrian Hunter u64 sz;
17958e0cf965SAdrian Hunter
17968e0cf965SAdrian Hunter if (elf_getphdrnum(elf, &phdrnum))
17978e0cf965SAdrian Hunter return -1;
17988e0cf965SAdrian Hunter
17998e0cf965SAdrian Hunter for (i = 0; i < phdrnum; i++) {
18008e0cf965SAdrian Hunter if (gelf_getphdr(elf, i, &phdr) == NULL)
18018e0cf965SAdrian Hunter return -1;
18028e0cf965SAdrian Hunter if (phdr.p_type != PT_LOAD)
18038e0cf965SAdrian Hunter continue;
18048e0cf965SAdrian Hunter if (exe) {
18058e0cf965SAdrian Hunter if (!(phdr.p_flags & PF_X))
18068e0cf965SAdrian Hunter continue;
18078e0cf965SAdrian Hunter } else {
18088e0cf965SAdrian Hunter if (!(phdr.p_flags & PF_R))
18098e0cf965SAdrian Hunter continue;
18108e0cf965SAdrian Hunter }
18118e0cf965SAdrian Hunter sz = min(phdr.p_memsz, phdr.p_filesz);
18128e0cf965SAdrian Hunter if (!sz)
18138e0cf965SAdrian Hunter continue;
18148e0cf965SAdrian Hunter err = mapfn(phdr.p_vaddr, sz, phdr.p_offset, data);
18158e0cf965SAdrian Hunter if (err)
18168e0cf965SAdrian Hunter return err;
18178e0cf965SAdrian Hunter }
18188e0cf965SAdrian Hunter return 0;
18198e0cf965SAdrian Hunter }
18208e0cf965SAdrian Hunter
file__read_maps(int fd,bool exe,mapfn_t mapfn,void * data,bool * is_64_bit)18218e0cf965SAdrian Hunter int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
18228e0cf965SAdrian Hunter bool *is_64_bit)
18238e0cf965SAdrian Hunter {
18248e0cf965SAdrian Hunter int err;
18258e0cf965SAdrian Hunter Elf *elf;
18268e0cf965SAdrian Hunter
18278e0cf965SAdrian Hunter elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
18288e0cf965SAdrian Hunter if (elf == NULL)
18298e0cf965SAdrian Hunter return -1;
18308e0cf965SAdrian Hunter
18318e0cf965SAdrian Hunter if (is_64_bit)
18328e0cf965SAdrian Hunter *is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
18338e0cf965SAdrian Hunter
18348e0cf965SAdrian Hunter err = elf_read_maps(elf, exe, mapfn, data);
18358e0cf965SAdrian Hunter
18368e0cf965SAdrian Hunter elf_end(elf);
18378e0cf965SAdrian Hunter return err;
18388e0cf965SAdrian Hunter }
18398e0cf965SAdrian Hunter
dso__type_fd(int fd)18402b5b8bb2SAdrian Hunter enum dso_type dso__type_fd(int fd)
18412b5b8bb2SAdrian Hunter {
18422b5b8bb2SAdrian Hunter enum dso_type dso_type = DSO__TYPE_UNKNOWN;
18432b5b8bb2SAdrian Hunter GElf_Ehdr ehdr;
18442b5b8bb2SAdrian Hunter Elf_Kind ek;
18452b5b8bb2SAdrian Hunter Elf *elf;
18462b5b8bb2SAdrian Hunter
18472b5b8bb2SAdrian Hunter elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
18482b5b8bb2SAdrian Hunter if (elf == NULL)
18492b5b8bb2SAdrian Hunter goto out;
18502b5b8bb2SAdrian Hunter
18512b5b8bb2SAdrian Hunter ek = elf_kind(elf);
18522b5b8bb2SAdrian Hunter if (ek != ELF_K_ELF)
18532b5b8bb2SAdrian Hunter goto out_end;
18542b5b8bb2SAdrian Hunter
18552b5b8bb2SAdrian Hunter if (gelf_getclass(elf) == ELFCLASS64) {
18562b5b8bb2SAdrian Hunter dso_type = DSO__TYPE_64BIT;
18572b5b8bb2SAdrian Hunter goto out_end;
18582b5b8bb2SAdrian Hunter }
18592b5b8bb2SAdrian Hunter
18602b5b8bb2SAdrian Hunter if (gelf_getehdr(elf, &ehdr) == NULL)
18612b5b8bb2SAdrian Hunter goto out_end;
18622b5b8bb2SAdrian Hunter
18632b5b8bb2SAdrian Hunter if (ehdr.e_machine == EM_X86_64)
18642b5b8bb2SAdrian Hunter dso_type = DSO__TYPE_X32BIT;
18652b5b8bb2SAdrian Hunter else
18662b5b8bb2SAdrian Hunter dso_type = DSO__TYPE_32BIT;
18672b5b8bb2SAdrian Hunter out_end:
18682b5b8bb2SAdrian Hunter elf_end(elf);
18692b5b8bb2SAdrian Hunter out:
18702b5b8bb2SAdrian Hunter return dso_type;
18712b5b8bb2SAdrian Hunter }
18722b5b8bb2SAdrian Hunter
copy_bytes(int from,off_t from_offs,int to,off_t to_offs,u64 len)1873afba19d9SAdrian Hunter static int copy_bytes(int from, off_t from_offs, int to, off_t to_offs, u64 len)
1874afba19d9SAdrian Hunter {
1875afba19d9SAdrian Hunter ssize_t r;
1876afba19d9SAdrian Hunter size_t n;
1877afba19d9SAdrian Hunter int err = -1;
1878afba19d9SAdrian Hunter char *buf = malloc(page_size);
1879afba19d9SAdrian Hunter
1880afba19d9SAdrian Hunter if (buf == NULL)
1881afba19d9SAdrian Hunter return -1;
1882afba19d9SAdrian Hunter
1883afba19d9SAdrian Hunter if (lseek(to, to_offs, SEEK_SET) != to_offs)
1884afba19d9SAdrian Hunter goto out;
1885afba19d9SAdrian Hunter
1886afba19d9SAdrian Hunter if (lseek(from, from_offs, SEEK_SET) != from_offs)
1887afba19d9SAdrian Hunter goto out;
1888afba19d9SAdrian Hunter
1889afba19d9SAdrian Hunter while (len) {
1890afba19d9SAdrian Hunter n = page_size;
1891afba19d9SAdrian Hunter if (len < n)
1892afba19d9SAdrian Hunter n = len;
1893afba19d9SAdrian Hunter /* Use read because mmap won't work on proc files */
1894afba19d9SAdrian Hunter r = read(from, buf, n);
1895afba19d9SAdrian Hunter if (r < 0)
1896afba19d9SAdrian Hunter goto out;
1897afba19d9SAdrian Hunter if (!r)
1898afba19d9SAdrian Hunter break;
1899afba19d9SAdrian Hunter n = r;
1900afba19d9SAdrian Hunter r = write(to, buf, n);
1901afba19d9SAdrian Hunter if (r < 0)
1902afba19d9SAdrian Hunter goto out;
1903afba19d9SAdrian Hunter if ((size_t)r != n)
1904afba19d9SAdrian Hunter goto out;
1905afba19d9SAdrian Hunter len -= n;
1906afba19d9SAdrian Hunter }
1907afba19d9SAdrian Hunter
1908afba19d9SAdrian Hunter err = 0;
1909afba19d9SAdrian Hunter out:
1910afba19d9SAdrian Hunter free(buf);
1911afba19d9SAdrian Hunter return err;
1912afba19d9SAdrian Hunter }
1913afba19d9SAdrian Hunter
1914afba19d9SAdrian Hunter struct kcore {
1915afba19d9SAdrian Hunter int fd;
1916afba19d9SAdrian Hunter int elfclass;
1917afba19d9SAdrian Hunter Elf *elf;
1918afba19d9SAdrian Hunter GElf_Ehdr ehdr;
1919afba19d9SAdrian Hunter };
1920afba19d9SAdrian Hunter
kcore__open(struct kcore * kcore,const char * filename)1921afba19d9SAdrian Hunter static int kcore__open(struct kcore *kcore, const char *filename)
1922afba19d9SAdrian Hunter {
1923afba19d9SAdrian Hunter GElf_Ehdr *ehdr;
1924afba19d9SAdrian Hunter
1925afba19d9SAdrian Hunter kcore->fd = open(filename, O_RDONLY);
1926afba19d9SAdrian Hunter if (kcore->fd == -1)
1927afba19d9SAdrian Hunter return -1;
1928afba19d9SAdrian Hunter
1929afba19d9SAdrian Hunter kcore->elf = elf_begin(kcore->fd, ELF_C_READ, NULL);
1930afba19d9SAdrian Hunter if (!kcore->elf)
1931afba19d9SAdrian Hunter goto out_close;
1932afba19d9SAdrian Hunter
1933afba19d9SAdrian Hunter kcore->elfclass = gelf_getclass(kcore->elf);
1934afba19d9SAdrian Hunter if (kcore->elfclass == ELFCLASSNONE)
1935afba19d9SAdrian Hunter goto out_end;
1936afba19d9SAdrian Hunter
1937afba19d9SAdrian Hunter ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr);
1938afba19d9SAdrian Hunter if (!ehdr)
1939afba19d9SAdrian Hunter goto out_end;
1940afba19d9SAdrian Hunter
1941afba19d9SAdrian Hunter return 0;
1942afba19d9SAdrian Hunter
1943afba19d9SAdrian Hunter out_end:
1944afba19d9SAdrian Hunter elf_end(kcore->elf);
1945afba19d9SAdrian Hunter out_close:
1946afba19d9SAdrian Hunter close(kcore->fd);
1947afba19d9SAdrian Hunter return -1;
1948afba19d9SAdrian Hunter }
1949afba19d9SAdrian Hunter
kcore__init(struct kcore * kcore,char * filename,int elfclass,bool temp)1950afba19d9SAdrian Hunter static int kcore__init(struct kcore *kcore, char *filename, int elfclass,
1951afba19d9SAdrian Hunter bool temp)
1952afba19d9SAdrian Hunter {
1953afba19d9SAdrian Hunter kcore->elfclass = elfclass;
1954afba19d9SAdrian Hunter
1955afba19d9SAdrian Hunter if (temp)
1956afba19d9SAdrian Hunter kcore->fd = mkstemp(filename);
1957afba19d9SAdrian Hunter else
1958afba19d9SAdrian Hunter kcore->fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400);
1959afba19d9SAdrian Hunter if (kcore->fd == -1)
1960afba19d9SAdrian Hunter return -1;
1961afba19d9SAdrian Hunter
1962afba19d9SAdrian Hunter kcore->elf = elf_begin(kcore->fd, ELF_C_WRITE, NULL);
1963afba19d9SAdrian Hunter if (!kcore->elf)
1964afba19d9SAdrian Hunter goto out_close;
1965afba19d9SAdrian Hunter
1966afba19d9SAdrian Hunter if (!gelf_newehdr(kcore->elf, elfclass))
1967afba19d9SAdrian Hunter goto out_end;
1968afba19d9SAdrian Hunter
1969b5cabbcbSAdrian Hunter memset(&kcore->ehdr, 0, sizeof(GElf_Ehdr));
1970afba19d9SAdrian Hunter
1971afba19d9SAdrian Hunter return 0;
1972afba19d9SAdrian Hunter
1973afba19d9SAdrian Hunter out_end:
1974afba19d9SAdrian Hunter elf_end(kcore->elf);
1975afba19d9SAdrian Hunter out_close:
1976afba19d9SAdrian Hunter close(kcore->fd);
1977afba19d9SAdrian Hunter unlink(filename);
1978afba19d9SAdrian Hunter return -1;
1979afba19d9SAdrian Hunter }
1980afba19d9SAdrian Hunter
kcore__close(struct kcore * kcore)1981afba19d9SAdrian Hunter static void kcore__close(struct kcore *kcore)
1982afba19d9SAdrian Hunter {
1983afba19d9SAdrian Hunter elf_end(kcore->elf);
1984afba19d9SAdrian Hunter close(kcore->fd);
1985afba19d9SAdrian Hunter }
1986afba19d9SAdrian Hunter
kcore__copy_hdr(struct kcore * from,struct kcore * to,size_t count)1987afba19d9SAdrian Hunter static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count)
1988afba19d9SAdrian Hunter {
1989afba19d9SAdrian Hunter GElf_Ehdr *ehdr = &to->ehdr;
1990afba19d9SAdrian Hunter GElf_Ehdr *kehdr = &from->ehdr;
1991afba19d9SAdrian Hunter
1992afba19d9SAdrian Hunter memcpy(ehdr->e_ident, kehdr->e_ident, EI_NIDENT);
1993afba19d9SAdrian Hunter ehdr->e_type = kehdr->e_type;
1994afba19d9SAdrian Hunter ehdr->e_machine = kehdr->e_machine;
1995afba19d9SAdrian Hunter ehdr->e_version = kehdr->e_version;
1996afba19d9SAdrian Hunter ehdr->e_entry = 0;
1997afba19d9SAdrian Hunter ehdr->e_shoff = 0;
1998afba19d9SAdrian Hunter ehdr->e_flags = kehdr->e_flags;
1999afba19d9SAdrian Hunter ehdr->e_phnum = count;
2000afba19d9SAdrian Hunter ehdr->e_shentsize = 0;
2001afba19d9SAdrian Hunter ehdr->e_shnum = 0;
2002afba19d9SAdrian Hunter ehdr->e_shstrndx = 0;
2003afba19d9SAdrian Hunter
2004afba19d9SAdrian Hunter if (from->elfclass == ELFCLASS32) {
2005afba19d9SAdrian Hunter ehdr->e_phoff = sizeof(Elf32_Ehdr);
2006afba19d9SAdrian Hunter ehdr->e_ehsize = sizeof(Elf32_Ehdr);
2007afba19d9SAdrian Hunter ehdr->e_phentsize = sizeof(Elf32_Phdr);
2008afba19d9SAdrian Hunter } else {
2009afba19d9SAdrian Hunter ehdr->e_phoff = sizeof(Elf64_Ehdr);
2010afba19d9SAdrian Hunter ehdr->e_ehsize = sizeof(Elf64_Ehdr);
2011afba19d9SAdrian Hunter ehdr->e_phentsize = sizeof(Elf64_Phdr);
2012afba19d9SAdrian Hunter }
2013afba19d9SAdrian Hunter
2014afba19d9SAdrian Hunter if (!gelf_update_ehdr(to->elf, ehdr))
2015afba19d9SAdrian Hunter return -1;
2016afba19d9SAdrian Hunter
2017afba19d9SAdrian Hunter if (!gelf_newphdr(to->elf, count))
2018afba19d9SAdrian Hunter return -1;
2019afba19d9SAdrian Hunter
2020afba19d9SAdrian Hunter return 0;
2021afba19d9SAdrian Hunter }
2022afba19d9SAdrian Hunter
kcore__add_phdr(struct kcore * kcore,int idx,off_t offset,u64 addr,u64 len)2023afba19d9SAdrian Hunter static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset,
2024afba19d9SAdrian Hunter u64 addr, u64 len)
2025afba19d9SAdrian Hunter {
2026b5cabbcbSAdrian Hunter GElf_Phdr phdr = {
2027b5cabbcbSAdrian Hunter .p_type = PT_LOAD,
2028b5cabbcbSAdrian Hunter .p_flags = PF_R | PF_W | PF_X,
2029b5cabbcbSAdrian Hunter .p_offset = offset,
2030b5cabbcbSAdrian Hunter .p_vaddr = addr,
2031b5cabbcbSAdrian Hunter .p_paddr = 0,
2032b5cabbcbSAdrian Hunter .p_filesz = len,
2033b5cabbcbSAdrian Hunter .p_memsz = len,
2034b5cabbcbSAdrian Hunter .p_align = page_size,
2035b5cabbcbSAdrian Hunter };
2036afba19d9SAdrian Hunter
2037b5cabbcbSAdrian Hunter if (!gelf_update_phdr(kcore->elf, idx, &phdr))
2038afba19d9SAdrian Hunter return -1;
2039afba19d9SAdrian Hunter
2040afba19d9SAdrian Hunter return 0;
2041afba19d9SAdrian Hunter }
2042afba19d9SAdrian Hunter
kcore__write(struct kcore * kcore)2043afba19d9SAdrian Hunter static off_t kcore__write(struct kcore *kcore)
2044afba19d9SAdrian Hunter {
2045afba19d9SAdrian Hunter return elf_update(kcore->elf, ELF_C_WRITE);
2046afba19d9SAdrian Hunter }
2047afba19d9SAdrian Hunter
2048fc1b691dSAdrian Hunter struct phdr_data {
2049fc1b691dSAdrian Hunter off_t offset;
205015acef6cSAdrian Hunter off_t rel;
2051fc1b691dSAdrian Hunter u64 addr;
2052fc1b691dSAdrian Hunter u64 len;
2053f6838209SAdrian Hunter struct list_head node;
205422916fdbSAdrian Hunter struct phdr_data *remaps;
2055fc1b691dSAdrian Hunter };
2056fc1b691dSAdrian Hunter
2057a1a3a062SAdrian Hunter struct sym_data {
2058a1a3a062SAdrian Hunter u64 addr;
2059a1a3a062SAdrian Hunter struct list_head node;
2060a1a3a062SAdrian Hunter };
2061a1a3a062SAdrian Hunter
2062fc1b691dSAdrian Hunter struct kcore_copy_info {
2063fc1b691dSAdrian Hunter u64 stext;
2064fc1b691dSAdrian Hunter u64 etext;
2065fc1b691dSAdrian Hunter u64 first_symbol;
2066fc1b691dSAdrian Hunter u64 last_symbol;
2067fc1b691dSAdrian Hunter u64 first_module;
206861f82e3fSAdrian Hunter u64 first_module_symbol;
2069fc1b691dSAdrian Hunter u64 last_module_symbol;
20706e97957dSAdrian Hunter size_t phnum;
2071f6838209SAdrian Hunter struct list_head phdrs;
2072a1a3a062SAdrian Hunter struct list_head syms;
2073fc1b691dSAdrian Hunter };
2074fc1b691dSAdrian Hunter
207515acef6cSAdrian Hunter #define kcore_copy__for_each_phdr(k, p) \
207615acef6cSAdrian Hunter list_for_each_entry((p), &(k)->phdrs, node)
207715acef6cSAdrian Hunter
phdr_data__new(u64 addr,u64 len,off_t offset)2078b4503cdbSAdrian Hunter static struct phdr_data *phdr_data__new(u64 addr, u64 len, off_t offset)
2079b4503cdbSAdrian Hunter {
2080b4503cdbSAdrian Hunter struct phdr_data *p = zalloc(sizeof(*p));
2081b4503cdbSAdrian Hunter
2082b4503cdbSAdrian Hunter if (p) {
2083b4503cdbSAdrian Hunter p->addr = addr;
2084b4503cdbSAdrian Hunter p->len = len;
2085b4503cdbSAdrian Hunter p->offset = offset;
2086b4503cdbSAdrian Hunter }
2087b4503cdbSAdrian Hunter
2088b4503cdbSAdrian Hunter return p;
2089b4503cdbSAdrian Hunter }
2090b4503cdbSAdrian Hunter
kcore_copy_info__addnew(struct kcore_copy_info * kci,u64 addr,u64 len,off_t offset)2091b4503cdbSAdrian Hunter static struct phdr_data *kcore_copy_info__addnew(struct kcore_copy_info *kci,
2092b4503cdbSAdrian Hunter u64 addr, u64 len,
2093b4503cdbSAdrian Hunter off_t offset)
2094b4503cdbSAdrian Hunter {
2095b4503cdbSAdrian Hunter struct phdr_data *p = phdr_data__new(addr, len, offset);
2096b4503cdbSAdrian Hunter
2097b4503cdbSAdrian Hunter if (p)
2098b4503cdbSAdrian Hunter list_add_tail(&p->node, &kci->phdrs);
2099b4503cdbSAdrian Hunter
2100b4503cdbSAdrian Hunter return p;
2101b4503cdbSAdrian Hunter }
2102b4503cdbSAdrian Hunter
kcore_copy__free_phdrs(struct kcore_copy_info * kci)2103b4503cdbSAdrian Hunter static void kcore_copy__free_phdrs(struct kcore_copy_info *kci)
2104b4503cdbSAdrian Hunter {
2105b4503cdbSAdrian Hunter struct phdr_data *p, *tmp;
2106b4503cdbSAdrian Hunter
2107b4503cdbSAdrian Hunter list_for_each_entry_safe(p, tmp, &kci->phdrs, node) {
2108e56fbc9dSArnaldo Carvalho de Melo list_del_init(&p->node);
2109b4503cdbSAdrian Hunter free(p);
2110b4503cdbSAdrian Hunter }
2111b4503cdbSAdrian Hunter }
2112b4503cdbSAdrian Hunter
kcore_copy__new_sym(struct kcore_copy_info * kci,u64 addr)2113a1a3a062SAdrian Hunter static struct sym_data *kcore_copy__new_sym(struct kcore_copy_info *kci,
2114a1a3a062SAdrian Hunter u64 addr)
2115a1a3a062SAdrian Hunter {
2116a1a3a062SAdrian Hunter struct sym_data *s = zalloc(sizeof(*s));
2117a1a3a062SAdrian Hunter
2118a1a3a062SAdrian Hunter if (s) {
2119a1a3a062SAdrian Hunter s->addr = addr;
2120a1a3a062SAdrian Hunter list_add_tail(&s->node, &kci->syms);
2121a1a3a062SAdrian Hunter }
2122a1a3a062SAdrian Hunter
2123a1a3a062SAdrian Hunter return s;
2124a1a3a062SAdrian Hunter }
2125a1a3a062SAdrian Hunter
kcore_copy__free_syms(struct kcore_copy_info * kci)2126a1a3a062SAdrian Hunter static void kcore_copy__free_syms(struct kcore_copy_info *kci)
2127a1a3a062SAdrian Hunter {
2128a1a3a062SAdrian Hunter struct sym_data *s, *tmp;
2129a1a3a062SAdrian Hunter
2130a1a3a062SAdrian Hunter list_for_each_entry_safe(s, tmp, &kci->syms, node) {
2131e56fbc9dSArnaldo Carvalho de Melo list_del_init(&s->node);
2132a1a3a062SAdrian Hunter free(s);
2133a1a3a062SAdrian Hunter }
2134a1a3a062SAdrian Hunter }
2135a1a3a062SAdrian Hunter
kcore_copy__process_kallsyms(void * arg,const char * name,char type,u64 start)2136fc1b691dSAdrian Hunter static int kcore_copy__process_kallsyms(void *arg, const char *name, char type,
2137fc1b691dSAdrian Hunter u64 start)
2138fc1b691dSAdrian Hunter {
2139fc1b691dSAdrian Hunter struct kcore_copy_info *kci = arg;
2140fc1b691dSAdrian Hunter
2141e85e0e0cSArnaldo Carvalho de Melo if (!kallsyms__is_function(type))
2142fc1b691dSAdrian Hunter return 0;
2143fc1b691dSAdrian Hunter
2144fc1b691dSAdrian Hunter if (strchr(name, '[')) {
214561f82e3fSAdrian Hunter if (!kci->first_module_symbol || start < kci->first_module_symbol)
214661f82e3fSAdrian Hunter kci->first_module_symbol = start;
2147fc1b691dSAdrian Hunter if (start > kci->last_module_symbol)
2148fc1b691dSAdrian Hunter kci->last_module_symbol = start;
2149fc1b691dSAdrian Hunter return 0;
2150fc1b691dSAdrian Hunter }
2151fc1b691dSAdrian Hunter
2152fc1b691dSAdrian Hunter if (!kci->first_symbol || start < kci->first_symbol)
2153fc1b691dSAdrian Hunter kci->first_symbol = start;
2154fc1b691dSAdrian Hunter
2155fc1b691dSAdrian Hunter if (!kci->last_symbol || start > kci->last_symbol)
2156fc1b691dSAdrian Hunter kci->last_symbol = start;
2157fc1b691dSAdrian Hunter
2158fc1b691dSAdrian Hunter if (!strcmp(name, "_stext")) {
2159fc1b691dSAdrian Hunter kci->stext = start;
2160fc1b691dSAdrian Hunter return 0;
2161fc1b691dSAdrian Hunter }
2162fc1b691dSAdrian Hunter
2163fc1b691dSAdrian Hunter if (!strcmp(name, "_etext")) {
2164fc1b691dSAdrian Hunter kci->etext = start;
2165fc1b691dSAdrian Hunter return 0;
2166fc1b691dSAdrian Hunter }
2167fc1b691dSAdrian Hunter
2168a1a3a062SAdrian Hunter if (is_entry_trampoline(name) && !kcore_copy__new_sym(kci, start))
2169a1a3a062SAdrian Hunter return -1;
2170a1a3a062SAdrian Hunter
2171fc1b691dSAdrian Hunter return 0;
2172fc1b691dSAdrian Hunter }
2173fc1b691dSAdrian Hunter
kcore_copy__parse_kallsyms(struct kcore_copy_info * kci,const char * dir)2174fc1b691dSAdrian Hunter static int kcore_copy__parse_kallsyms(struct kcore_copy_info *kci,
2175fc1b691dSAdrian Hunter const char *dir)
2176fc1b691dSAdrian Hunter {
2177fc1b691dSAdrian Hunter char kallsyms_filename[PATH_MAX];
2178fc1b691dSAdrian Hunter
2179fc1b691dSAdrian Hunter scnprintf(kallsyms_filename, PATH_MAX, "%s/kallsyms", dir);
2180fc1b691dSAdrian Hunter
2181fc1b691dSAdrian Hunter if (symbol__restricted_filename(kallsyms_filename, "/proc/kallsyms"))
2182fc1b691dSAdrian Hunter return -1;
2183fc1b691dSAdrian Hunter
2184fc1b691dSAdrian Hunter if (kallsyms__parse(kallsyms_filename, kci,
2185fc1b691dSAdrian Hunter kcore_copy__process_kallsyms) < 0)
2186fc1b691dSAdrian Hunter return -1;
2187fc1b691dSAdrian Hunter
2188fc1b691dSAdrian Hunter return 0;
2189fc1b691dSAdrian Hunter }
2190fc1b691dSAdrian Hunter
kcore_copy__process_modules(void * arg,const char * name __maybe_unused,u64 start,u64 size __maybe_unused)2191fc1b691dSAdrian Hunter static int kcore_copy__process_modules(void *arg,
2192fc1b691dSAdrian Hunter const char *name __maybe_unused,
21939ad4652bSThomas Richter u64 start, u64 size __maybe_unused)
2194fc1b691dSAdrian Hunter {
2195fc1b691dSAdrian Hunter struct kcore_copy_info *kci = arg;
2196fc1b691dSAdrian Hunter
2197fc1b691dSAdrian Hunter if (!kci->first_module || start < kci->first_module)
2198fc1b691dSAdrian Hunter kci->first_module = start;
2199fc1b691dSAdrian Hunter
2200fc1b691dSAdrian Hunter return 0;
2201fc1b691dSAdrian Hunter }
2202fc1b691dSAdrian Hunter
kcore_copy__parse_modules(struct kcore_copy_info * kci,const char * dir)2203fc1b691dSAdrian Hunter static int kcore_copy__parse_modules(struct kcore_copy_info *kci,
2204fc1b691dSAdrian Hunter const char *dir)
2205fc1b691dSAdrian Hunter {
2206fc1b691dSAdrian Hunter char modules_filename[PATH_MAX];
2207fc1b691dSAdrian Hunter
2208fc1b691dSAdrian Hunter scnprintf(modules_filename, PATH_MAX, "%s/modules", dir);
2209fc1b691dSAdrian Hunter
2210fc1b691dSAdrian Hunter if (symbol__restricted_filename(modules_filename, "/proc/modules"))
2211fc1b691dSAdrian Hunter return -1;
2212fc1b691dSAdrian Hunter
2213fc1b691dSAdrian Hunter if (modules__parse(modules_filename, kci,
2214fc1b691dSAdrian Hunter kcore_copy__process_modules) < 0)
2215fc1b691dSAdrian Hunter return -1;
2216fc1b691dSAdrian Hunter
2217fc1b691dSAdrian Hunter return 0;
2218fc1b691dSAdrian Hunter }
2219fc1b691dSAdrian Hunter
kcore_copy__map(struct kcore_copy_info * kci,u64 start,u64 end,u64 pgoff,u64 s,u64 e)2220b4503cdbSAdrian Hunter static int kcore_copy__map(struct kcore_copy_info *kci, u64 start, u64 end,
2221b4503cdbSAdrian Hunter u64 pgoff, u64 s, u64 e)
2222fc1b691dSAdrian Hunter {
2223b4503cdbSAdrian Hunter u64 len, offset;
2224fc1b691dSAdrian Hunter
2225b4503cdbSAdrian Hunter if (s < start || s >= end)
2226b4503cdbSAdrian Hunter return 0;
2227b4503cdbSAdrian Hunter
2228b4503cdbSAdrian Hunter offset = (s - start) + pgoff;
2229b4503cdbSAdrian Hunter len = e < end ? e - s : end - s;
2230b4503cdbSAdrian Hunter
2231b4503cdbSAdrian Hunter return kcore_copy_info__addnew(kci, s, len, offset) ? 0 : -1;
2232fc1b691dSAdrian Hunter }
2233fc1b691dSAdrian Hunter
kcore_copy__read_map(u64 start,u64 len,u64 pgoff,void * data)2234fc1b691dSAdrian Hunter static int kcore_copy__read_map(u64 start, u64 len, u64 pgoff, void *data)
2235fc1b691dSAdrian Hunter {
2236fc1b691dSAdrian Hunter struct kcore_copy_info *kci = data;
2237fc1b691dSAdrian Hunter u64 end = start + len;
2238a1a3a062SAdrian Hunter struct sym_data *sdat;
2239fc1b691dSAdrian Hunter
2240b4503cdbSAdrian Hunter if (kcore_copy__map(kci, start, end, pgoff, kci->stext, kci->etext))
2241b4503cdbSAdrian Hunter return -1;
2242fc1b691dSAdrian Hunter
2243b4503cdbSAdrian Hunter if (kcore_copy__map(kci, start, end, pgoff, kci->first_module,
2244b4503cdbSAdrian Hunter kci->last_module_symbol))
2245b4503cdbSAdrian Hunter return -1;
2246fc1b691dSAdrian Hunter
2247a1a3a062SAdrian Hunter list_for_each_entry(sdat, &kci->syms, node) {
2248a1a3a062SAdrian Hunter u64 s = round_down(sdat->addr, page_size);
2249a1a3a062SAdrian Hunter
2250a1a3a062SAdrian Hunter if (kcore_copy__map(kci, start, end, pgoff, s, s + len))
2251a1a3a062SAdrian Hunter return -1;
2252a1a3a062SAdrian Hunter }
2253a1a3a062SAdrian Hunter
2254fc1b691dSAdrian Hunter return 0;
2255fc1b691dSAdrian Hunter }
2256fc1b691dSAdrian Hunter
kcore_copy__read_maps(struct kcore_copy_info * kci,Elf * elf)2257fc1b691dSAdrian Hunter static int kcore_copy__read_maps(struct kcore_copy_info *kci, Elf *elf)
2258fc1b691dSAdrian Hunter {
2259fc1b691dSAdrian Hunter if (elf_read_maps(elf, true, kcore_copy__read_map, kci) < 0)
2260fc1b691dSAdrian Hunter return -1;
2261fc1b691dSAdrian Hunter
2262fc1b691dSAdrian Hunter return 0;
2263fc1b691dSAdrian Hunter }
2264fc1b691dSAdrian Hunter
kcore_copy__find_remaps(struct kcore_copy_info * kci)226522916fdbSAdrian Hunter static void kcore_copy__find_remaps(struct kcore_copy_info *kci)
226622916fdbSAdrian Hunter {
226722916fdbSAdrian Hunter struct phdr_data *p, *k = NULL;
226822916fdbSAdrian Hunter u64 kend;
226922916fdbSAdrian Hunter
227022916fdbSAdrian Hunter if (!kci->stext)
227122916fdbSAdrian Hunter return;
227222916fdbSAdrian Hunter
227322916fdbSAdrian Hunter /* Find phdr that corresponds to the kernel map (contains stext) */
227422916fdbSAdrian Hunter kcore_copy__for_each_phdr(kci, p) {
227522916fdbSAdrian Hunter u64 pend = p->addr + p->len - 1;
227622916fdbSAdrian Hunter
227722916fdbSAdrian Hunter if (p->addr <= kci->stext && pend >= kci->stext) {
227822916fdbSAdrian Hunter k = p;
227922916fdbSAdrian Hunter break;
228022916fdbSAdrian Hunter }
228122916fdbSAdrian Hunter }
228222916fdbSAdrian Hunter
228322916fdbSAdrian Hunter if (!k)
228422916fdbSAdrian Hunter return;
228522916fdbSAdrian Hunter
228622916fdbSAdrian Hunter kend = k->offset + k->len;
228722916fdbSAdrian Hunter
228822916fdbSAdrian Hunter /* Find phdrs that remap the kernel */
228922916fdbSAdrian Hunter kcore_copy__for_each_phdr(kci, p) {
229022916fdbSAdrian Hunter u64 pend = p->offset + p->len;
229122916fdbSAdrian Hunter
229222916fdbSAdrian Hunter if (p == k)
229322916fdbSAdrian Hunter continue;
229422916fdbSAdrian Hunter
229522916fdbSAdrian Hunter if (p->offset >= k->offset && pend <= kend)
229622916fdbSAdrian Hunter p->remaps = k;
229722916fdbSAdrian Hunter }
229822916fdbSAdrian Hunter }
229922916fdbSAdrian Hunter
kcore_copy__layout(struct kcore_copy_info * kci)230015acef6cSAdrian Hunter static void kcore_copy__layout(struct kcore_copy_info *kci)
230115acef6cSAdrian Hunter {
230215acef6cSAdrian Hunter struct phdr_data *p;
230315acef6cSAdrian Hunter off_t rel = 0;
230415acef6cSAdrian Hunter
230522916fdbSAdrian Hunter kcore_copy__find_remaps(kci);
230622916fdbSAdrian Hunter
230715acef6cSAdrian Hunter kcore_copy__for_each_phdr(kci, p) {
230822916fdbSAdrian Hunter if (!p->remaps) {
230915acef6cSAdrian Hunter p->rel = rel;
231015acef6cSAdrian Hunter rel += p->len;
231122916fdbSAdrian Hunter }
231215acef6cSAdrian Hunter kci->phnum += 1;
231315acef6cSAdrian Hunter }
231422916fdbSAdrian Hunter
231522916fdbSAdrian Hunter kcore_copy__for_each_phdr(kci, p) {
231622916fdbSAdrian Hunter struct phdr_data *k = p->remaps;
231722916fdbSAdrian Hunter
231822916fdbSAdrian Hunter if (k)
231922916fdbSAdrian Hunter p->rel = p->offset - k->offset + k->rel;
232022916fdbSAdrian Hunter }
232115acef6cSAdrian Hunter }
232215acef6cSAdrian Hunter
kcore_copy__calc_maps(struct kcore_copy_info * kci,const char * dir,Elf * elf)2323fc1b691dSAdrian Hunter static int kcore_copy__calc_maps(struct kcore_copy_info *kci, const char *dir,
2324fc1b691dSAdrian Hunter Elf *elf)
2325fc1b691dSAdrian Hunter {
2326fc1b691dSAdrian Hunter if (kcore_copy__parse_kallsyms(kci, dir))
2327fc1b691dSAdrian Hunter return -1;
2328fc1b691dSAdrian Hunter
2329fc1b691dSAdrian Hunter if (kcore_copy__parse_modules(kci, dir))
2330fc1b691dSAdrian Hunter return -1;
2331fc1b691dSAdrian Hunter
2332fc1b691dSAdrian Hunter if (kci->stext)
2333fc1b691dSAdrian Hunter kci->stext = round_down(kci->stext, page_size);
2334fc1b691dSAdrian Hunter else
2335fc1b691dSAdrian Hunter kci->stext = round_down(kci->first_symbol, page_size);
2336fc1b691dSAdrian Hunter
2337fc1b691dSAdrian Hunter if (kci->etext) {
2338fc1b691dSAdrian Hunter kci->etext = round_up(kci->etext, page_size);
2339fc1b691dSAdrian Hunter } else if (kci->last_symbol) {
2340fc1b691dSAdrian Hunter kci->etext = round_up(kci->last_symbol, page_size);
2341fc1b691dSAdrian Hunter kci->etext += page_size;
2342fc1b691dSAdrian Hunter }
2343fc1b691dSAdrian Hunter
234461f82e3fSAdrian Hunter if (kci->first_module_symbol &&
234561f82e3fSAdrian Hunter (!kci->first_module || kci->first_module_symbol < kci->first_module))
234661f82e3fSAdrian Hunter kci->first_module = kci->first_module_symbol;
234761f82e3fSAdrian Hunter
2348fc1b691dSAdrian Hunter kci->first_module = round_down(kci->first_module, page_size);
2349fc1b691dSAdrian Hunter
2350fc1b691dSAdrian Hunter if (kci->last_module_symbol) {
2351fc1b691dSAdrian Hunter kci->last_module_symbol = round_up(kci->last_module_symbol,
2352fc1b691dSAdrian Hunter page_size);
2353fc1b691dSAdrian Hunter kci->last_module_symbol += page_size;
2354fc1b691dSAdrian Hunter }
2355fc1b691dSAdrian Hunter
2356fc1b691dSAdrian Hunter if (!kci->stext || !kci->etext)
2357fc1b691dSAdrian Hunter return -1;
2358fc1b691dSAdrian Hunter
2359fc1b691dSAdrian Hunter if (kci->first_module && !kci->last_module_symbol)
2360fc1b691dSAdrian Hunter return -1;
2361fc1b691dSAdrian Hunter
236215acef6cSAdrian Hunter if (kcore_copy__read_maps(kci, elf))
236315acef6cSAdrian Hunter return -1;
236415acef6cSAdrian Hunter
236515acef6cSAdrian Hunter kcore_copy__layout(kci);
236615acef6cSAdrian Hunter
236715acef6cSAdrian Hunter return 0;
2368fc1b691dSAdrian Hunter }
2369fc1b691dSAdrian Hunter
kcore_copy__copy_file(const char * from_dir,const char * to_dir,const char * name)2370fc1b691dSAdrian Hunter static int kcore_copy__copy_file(const char *from_dir, const char *to_dir,
2371fc1b691dSAdrian Hunter const char *name)
2372fc1b691dSAdrian Hunter {
2373fc1b691dSAdrian Hunter char from_filename[PATH_MAX];
2374fc1b691dSAdrian Hunter char to_filename[PATH_MAX];
2375fc1b691dSAdrian Hunter
2376fc1b691dSAdrian Hunter scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name);
2377fc1b691dSAdrian Hunter scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name);
2378fc1b691dSAdrian Hunter
2379fc1b691dSAdrian Hunter return copyfile_mode(from_filename, to_filename, 0400);
2380fc1b691dSAdrian Hunter }
2381fc1b691dSAdrian Hunter
kcore_copy__unlink(const char * dir,const char * name)2382fc1b691dSAdrian Hunter static int kcore_copy__unlink(const char *dir, const char *name)
2383fc1b691dSAdrian Hunter {
2384fc1b691dSAdrian Hunter char filename[PATH_MAX];
2385fc1b691dSAdrian Hunter
2386fc1b691dSAdrian Hunter scnprintf(filename, PATH_MAX, "%s/%s", dir, name);
2387fc1b691dSAdrian Hunter
2388fc1b691dSAdrian Hunter return unlink(filename);
2389fc1b691dSAdrian Hunter }
2390fc1b691dSAdrian Hunter
kcore_copy__compare_fds(int from,int to)2391fc1b691dSAdrian Hunter static int kcore_copy__compare_fds(int from, int to)
2392fc1b691dSAdrian Hunter {
2393fc1b691dSAdrian Hunter char *buf_from;
2394fc1b691dSAdrian Hunter char *buf_to;
2395fc1b691dSAdrian Hunter ssize_t ret;
2396fc1b691dSAdrian Hunter size_t len;
2397fc1b691dSAdrian Hunter int err = -1;
2398fc1b691dSAdrian Hunter
2399fc1b691dSAdrian Hunter buf_from = malloc(page_size);
2400fc1b691dSAdrian Hunter buf_to = malloc(page_size);
2401fc1b691dSAdrian Hunter if (!buf_from || !buf_to)
2402fc1b691dSAdrian Hunter goto out;
2403fc1b691dSAdrian Hunter
2404fc1b691dSAdrian Hunter while (1) {
2405fc1b691dSAdrian Hunter /* Use read because mmap won't work on proc files */
2406fc1b691dSAdrian Hunter ret = read(from, buf_from, page_size);
2407fc1b691dSAdrian Hunter if (ret < 0)
2408fc1b691dSAdrian Hunter goto out;
2409fc1b691dSAdrian Hunter
2410fc1b691dSAdrian Hunter if (!ret)
2411fc1b691dSAdrian Hunter break;
2412fc1b691dSAdrian Hunter
2413fc1b691dSAdrian Hunter len = ret;
2414fc1b691dSAdrian Hunter
2415fc1b691dSAdrian Hunter if (readn(to, buf_to, len) != (int)len)
2416fc1b691dSAdrian Hunter goto out;
2417fc1b691dSAdrian Hunter
2418fc1b691dSAdrian Hunter if (memcmp(buf_from, buf_to, len))
2419fc1b691dSAdrian Hunter goto out;
2420fc1b691dSAdrian Hunter }
2421fc1b691dSAdrian Hunter
2422fc1b691dSAdrian Hunter err = 0;
2423fc1b691dSAdrian Hunter out:
2424fc1b691dSAdrian Hunter free(buf_to);
2425fc1b691dSAdrian Hunter free(buf_from);
2426fc1b691dSAdrian Hunter return err;
2427fc1b691dSAdrian Hunter }
2428fc1b691dSAdrian Hunter
kcore_copy__compare_files(const char * from_filename,const char * to_filename)2429fc1b691dSAdrian Hunter static int kcore_copy__compare_files(const char *from_filename,
2430fc1b691dSAdrian Hunter const char *to_filename)
2431fc1b691dSAdrian Hunter {
2432fc1b691dSAdrian Hunter int from, to, err = -1;
2433fc1b691dSAdrian Hunter
2434fc1b691dSAdrian Hunter from = open(from_filename, O_RDONLY);
2435fc1b691dSAdrian Hunter if (from < 0)
2436fc1b691dSAdrian Hunter return -1;
2437fc1b691dSAdrian Hunter
2438fc1b691dSAdrian Hunter to = open(to_filename, O_RDONLY);
2439fc1b691dSAdrian Hunter if (to < 0)
2440fc1b691dSAdrian Hunter goto out_close_from;
2441fc1b691dSAdrian Hunter
2442fc1b691dSAdrian Hunter err = kcore_copy__compare_fds(from, to);
2443fc1b691dSAdrian Hunter
2444fc1b691dSAdrian Hunter close(to);
2445fc1b691dSAdrian Hunter out_close_from:
2446fc1b691dSAdrian Hunter close(from);
2447fc1b691dSAdrian Hunter return err;
2448fc1b691dSAdrian Hunter }
2449fc1b691dSAdrian Hunter
kcore_copy__compare_file(const char * from_dir,const char * to_dir,const char * name)2450fc1b691dSAdrian Hunter static int kcore_copy__compare_file(const char *from_dir, const char *to_dir,
2451fc1b691dSAdrian Hunter const char *name)
2452fc1b691dSAdrian Hunter {
2453fc1b691dSAdrian Hunter char from_filename[PATH_MAX];
2454fc1b691dSAdrian Hunter char to_filename[PATH_MAX];
2455fc1b691dSAdrian Hunter
2456fc1b691dSAdrian Hunter scnprintf(from_filename, PATH_MAX, "%s/%s", from_dir, name);
2457fc1b691dSAdrian Hunter scnprintf(to_filename, PATH_MAX, "%s/%s", to_dir, name);
2458fc1b691dSAdrian Hunter
2459fc1b691dSAdrian Hunter return kcore_copy__compare_files(from_filename, to_filename);
2460fc1b691dSAdrian Hunter }
2461fc1b691dSAdrian Hunter
2462fc1b691dSAdrian Hunter /**
2463fc1b691dSAdrian Hunter * kcore_copy - copy kallsyms, modules and kcore from one directory to another.
2464fc1b691dSAdrian Hunter * @from_dir: from directory
2465fc1b691dSAdrian Hunter * @to_dir: to directory
2466fc1b691dSAdrian Hunter *
2467fc1b691dSAdrian Hunter * This function copies kallsyms, modules and kcore files from one directory to
2468fc1b691dSAdrian Hunter * another. kallsyms and modules are copied entirely. Only code segments are
2469fc1b691dSAdrian Hunter * copied from kcore. It is assumed that two segments suffice: one for the
2470fc1b691dSAdrian Hunter * kernel proper and one for all the modules. The code segments are determined
2471fc1b691dSAdrian Hunter * from kallsyms and modules files. The kernel map starts at _stext or the
2472fc1b691dSAdrian Hunter * lowest function symbol, and ends at _etext or the highest function symbol.
2473fc1b691dSAdrian Hunter * The module map starts at the lowest module address and ends at the highest
2474fc1b691dSAdrian Hunter * module symbol. Start addresses are rounded down to the nearest page. End
2475fc1b691dSAdrian Hunter * addresses are rounded up to the nearest page. An extra page is added to the
2476fc1b691dSAdrian Hunter * highest kernel symbol and highest module symbol to, hopefully, encompass that
2477fc1b691dSAdrian Hunter * symbol too. Because it contains only code sections, the resulting kcore is
2478fc1b691dSAdrian Hunter * unusual. One significant peculiarity is that the mapping (start -> pgoff)
2479fc1b691dSAdrian Hunter * is not the same for the kernel map and the modules map. That happens because
2480fc1b691dSAdrian Hunter * the data is copied adjacently whereas the original kcore has gaps. Finally,
24815b427df2SAdrian Hunter * kallsyms file is compared with its copy to check that modules have not been
24825b427df2SAdrian Hunter * loaded or unloaded while the copies were taking place.
2483fc1b691dSAdrian Hunter *
2484fc1b691dSAdrian Hunter * Return: %0 on success, %-1 on failure.
2485fc1b691dSAdrian Hunter */
kcore_copy(const char * from_dir,const char * to_dir)2486fc1b691dSAdrian Hunter int kcore_copy(const char *from_dir, const char *to_dir)
2487fc1b691dSAdrian Hunter {
2488fc1b691dSAdrian Hunter struct kcore kcore;
2489fc1b691dSAdrian Hunter struct kcore extract;
2490fc1b691dSAdrian Hunter int idx = 0, err = -1;
2491d2c95980SAdrian Hunter off_t offset, sz;
2492fc1b691dSAdrian Hunter struct kcore_copy_info kci = { .stext = 0, };
2493fc1b691dSAdrian Hunter char kcore_filename[PATH_MAX];
2494fc1b691dSAdrian Hunter char extract_filename[PATH_MAX];
2495d2c95980SAdrian Hunter struct phdr_data *p;
2496fc1b691dSAdrian Hunter
2497f6838209SAdrian Hunter INIT_LIST_HEAD(&kci.phdrs);
2498a1a3a062SAdrian Hunter INIT_LIST_HEAD(&kci.syms);
2499f6838209SAdrian Hunter
2500fc1b691dSAdrian Hunter if (kcore_copy__copy_file(from_dir, to_dir, "kallsyms"))
2501fc1b691dSAdrian Hunter return -1;
2502fc1b691dSAdrian Hunter
2503fc1b691dSAdrian Hunter if (kcore_copy__copy_file(from_dir, to_dir, "modules"))
2504fc1b691dSAdrian Hunter goto out_unlink_kallsyms;
2505fc1b691dSAdrian Hunter
2506fc1b691dSAdrian Hunter scnprintf(kcore_filename, PATH_MAX, "%s/kcore", from_dir);
2507fc1b691dSAdrian Hunter scnprintf(extract_filename, PATH_MAX, "%s/kcore", to_dir);
2508fc1b691dSAdrian Hunter
2509fc1b691dSAdrian Hunter if (kcore__open(&kcore, kcore_filename))
2510fc1b691dSAdrian Hunter goto out_unlink_modules;
2511fc1b691dSAdrian Hunter
2512fc1b691dSAdrian Hunter if (kcore_copy__calc_maps(&kci, from_dir, kcore.elf))
2513fc1b691dSAdrian Hunter goto out_kcore_close;
2514fc1b691dSAdrian Hunter
2515fc1b691dSAdrian Hunter if (kcore__init(&extract, extract_filename, kcore.elfclass, false))
2516fc1b691dSAdrian Hunter goto out_kcore_close;
2517fc1b691dSAdrian Hunter
25186e97957dSAdrian Hunter if (kcore__copy_hdr(&kcore, &extract, kci.phnum))
2519fc1b691dSAdrian Hunter goto out_extract_close;
2520fc1b691dSAdrian Hunter
2521c9dd1d89SAdrian Hunter offset = gelf_fsize(extract.elf, ELF_T_EHDR, 1, EV_CURRENT) +
2522c9dd1d89SAdrian Hunter gelf_fsize(extract.elf, ELF_T_PHDR, kci.phnum, EV_CURRENT);
2523c9dd1d89SAdrian Hunter offset = round_up(offset, page_size);
2524c9dd1d89SAdrian Hunter
2525d2c95980SAdrian Hunter kcore_copy__for_each_phdr(&kci, p) {
2526d2c95980SAdrian Hunter off_t offs = p->rel + offset;
2527fc1b691dSAdrian Hunter
2528d2c95980SAdrian Hunter if (kcore__add_phdr(&extract, idx++, offs, p->addr, p->len))
2529fc1b691dSAdrian Hunter goto out_extract_close;
2530fc1b691dSAdrian Hunter }
2531fc1b691dSAdrian Hunter
2532fc1b691dSAdrian Hunter sz = kcore__write(&extract);
2533fc1b691dSAdrian Hunter if (sz < 0 || sz > offset)
2534fc1b691dSAdrian Hunter goto out_extract_close;
2535fc1b691dSAdrian Hunter
2536d2c95980SAdrian Hunter kcore_copy__for_each_phdr(&kci, p) {
2537d2c95980SAdrian Hunter off_t offs = p->rel + offset;
2538fc1b691dSAdrian Hunter
253922916fdbSAdrian Hunter if (p->remaps)
254022916fdbSAdrian Hunter continue;
2541d2c95980SAdrian Hunter if (copy_bytes(kcore.fd, p->offset, extract.fd, offs, p->len))
2542fc1b691dSAdrian Hunter goto out_extract_close;
2543d2c95980SAdrian Hunter }
2544fc1b691dSAdrian Hunter
2545fc1b691dSAdrian Hunter if (kcore_copy__compare_file(from_dir, to_dir, "kallsyms"))
2546fc1b691dSAdrian Hunter goto out_extract_close;
2547fc1b691dSAdrian Hunter
2548fc1b691dSAdrian Hunter err = 0;
2549fc1b691dSAdrian Hunter
2550fc1b691dSAdrian Hunter out_extract_close:
2551fc1b691dSAdrian Hunter kcore__close(&extract);
2552fc1b691dSAdrian Hunter if (err)
2553fc1b691dSAdrian Hunter unlink(extract_filename);
2554fc1b691dSAdrian Hunter out_kcore_close:
2555fc1b691dSAdrian Hunter kcore__close(&kcore);
2556fc1b691dSAdrian Hunter out_unlink_modules:
2557fc1b691dSAdrian Hunter if (err)
2558fc1b691dSAdrian Hunter kcore_copy__unlink(to_dir, "modules");
2559fc1b691dSAdrian Hunter out_unlink_kallsyms:
2560fc1b691dSAdrian Hunter if (err)
2561fc1b691dSAdrian Hunter kcore_copy__unlink(to_dir, "kallsyms");
2562fc1b691dSAdrian Hunter
2563b4503cdbSAdrian Hunter kcore_copy__free_phdrs(&kci);
2564a1a3a062SAdrian Hunter kcore_copy__free_syms(&kci);
2565b4503cdbSAdrian Hunter
2566fc1b691dSAdrian Hunter return err;
2567fc1b691dSAdrian Hunter }
2568fc1b691dSAdrian Hunter
kcore_extract__create(struct kcore_extract * kce)2569afba19d9SAdrian Hunter int kcore_extract__create(struct kcore_extract *kce)
2570afba19d9SAdrian Hunter {
2571afba19d9SAdrian Hunter struct kcore kcore;
2572afba19d9SAdrian Hunter struct kcore extract;
2573afba19d9SAdrian Hunter size_t count = 1;
2574afba19d9SAdrian Hunter int idx = 0, err = -1;
2575afba19d9SAdrian Hunter off_t offset = page_size, sz;
2576afba19d9SAdrian Hunter
2577afba19d9SAdrian Hunter if (kcore__open(&kcore, kce->kcore_filename))
2578afba19d9SAdrian Hunter return -1;
2579afba19d9SAdrian Hunter
2580afba19d9SAdrian Hunter strcpy(kce->extract_filename, PERF_KCORE_EXTRACT);
2581afba19d9SAdrian Hunter if (kcore__init(&extract, kce->extract_filename, kcore.elfclass, true))
2582afba19d9SAdrian Hunter goto out_kcore_close;
2583afba19d9SAdrian Hunter
2584afba19d9SAdrian Hunter if (kcore__copy_hdr(&kcore, &extract, count))
2585afba19d9SAdrian Hunter goto out_extract_close;
2586afba19d9SAdrian Hunter
2587afba19d9SAdrian Hunter if (kcore__add_phdr(&extract, idx, offset, kce->addr, kce->len))
2588afba19d9SAdrian Hunter goto out_extract_close;
2589afba19d9SAdrian Hunter
2590afba19d9SAdrian Hunter sz = kcore__write(&extract);
2591afba19d9SAdrian Hunter if (sz < 0 || sz > offset)
2592afba19d9SAdrian Hunter goto out_extract_close;
2593afba19d9SAdrian Hunter
2594afba19d9SAdrian Hunter if (copy_bytes(kcore.fd, kce->offs, extract.fd, offset, kce->len))
2595afba19d9SAdrian Hunter goto out_extract_close;
2596afba19d9SAdrian Hunter
2597afba19d9SAdrian Hunter err = 0;
2598afba19d9SAdrian Hunter
2599afba19d9SAdrian Hunter out_extract_close:
2600afba19d9SAdrian Hunter kcore__close(&extract);
2601afba19d9SAdrian Hunter if (err)
2602afba19d9SAdrian Hunter unlink(kce->extract_filename);
2603afba19d9SAdrian Hunter out_kcore_close:
2604afba19d9SAdrian Hunter kcore__close(&kcore);
2605afba19d9SAdrian Hunter
2606afba19d9SAdrian Hunter return err;
2607afba19d9SAdrian Hunter }
2608afba19d9SAdrian Hunter
kcore_extract__delete(struct kcore_extract * kce)2609afba19d9SAdrian Hunter void kcore_extract__delete(struct kcore_extract *kce)
2610afba19d9SAdrian Hunter {
2611afba19d9SAdrian Hunter unlink(kce->extract_filename);
2612afba19d9SAdrian Hunter }
2613afba19d9SAdrian Hunter
26141c1a3a47SArnaldo Carvalho de Melo #ifdef HAVE_GELF_GETNOTE_SUPPORT
26155a5e3d3cSRavi Bangoria
sdt_adjust_loc(struct sdt_note * tmp,GElf_Addr base_off)26165a5e3d3cSRavi Bangoria static void sdt_adjust_loc(struct sdt_note *tmp, GElf_Addr base_off)
26175a5e3d3cSRavi Bangoria {
26185a5e3d3cSRavi Bangoria if (!base_off)
26195a5e3d3cSRavi Bangoria return;
26205a5e3d3cSRavi Bangoria
26215a5e3d3cSRavi Bangoria if (tmp->bit32)
26225a5e3d3cSRavi Bangoria tmp->addr.a32[SDT_NOTE_IDX_LOC] =
26235a5e3d3cSRavi Bangoria tmp->addr.a32[SDT_NOTE_IDX_LOC] + base_off -
26245a5e3d3cSRavi Bangoria tmp->addr.a32[SDT_NOTE_IDX_BASE];
26255a5e3d3cSRavi Bangoria else
26265a5e3d3cSRavi Bangoria tmp->addr.a64[SDT_NOTE_IDX_LOC] =
26275a5e3d3cSRavi Bangoria tmp->addr.a64[SDT_NOTE_IDX_LOC] + base_off -
26285a5e3d3cSRavi Bangoria tmp->addr.a64[SDT_NOTE_IDX_BASE];
26295a5e3d3cSRavi Bangoria }
26305a5e3d3cSRavi Bangoria
sdt_adjust_refctr(struct sdt_note * tmp,GElf_Addr base_addr,GElf_Addr base_off)26315a5e3d3cSRavi Bangoria static void sdt_adjust_refctr(struct sdt_note *tmp, GElf_Addr base_addr,
26325a5e3d3cSRavi Bangoria GElf_Addr base_off)
26335a5e3d3cSRavi Bangoria {
26345a5e3d3cSRavi Bangoria if (!base_off)
26355a5e3d3cSRavi Bangoria return;
26365a5e3d3cSRavi Bangoria
26375a5e3d3cSRavi Bangoria if (tmp->bit32 && tmp->addr.a32[SDT_NOTE_IDX_REFCTR])
26385a5e3d3cSRavi Bangoria tmp->addr.a32[SDT_NOTE_IDX_REFCTR] -= (base_addr - base_off);
26395a5e3d3cSRavi Bangoria else if (tmp->addr.a64[SDT_NOTE_IDX_REFCTR])
26405a5e3d3cSRavi Bangoria tmp->addr.a64[SDT_NOTE_IDX_REFCTR] -= (base_addr - base_off);
26415a5e3d3cSRavi Bangoria }
26425a5e3d3cSRavi Bangoria
2643060fa0c7SHemant Kumar /**
2644060fa0c7SHemant Kumar * populate_sdt_note : Parse raw data and identify SDT note
2645060fa0c7SHemant Kumar * @elf: elf of the opened file
2646060fa0c7SHemant Kumar * @data: raw data of a section with description offset applied
2647060fa0c7SHemant Kumar * @len: note description size
2648060fa0c7SHemant Kumar * @type: type of the note
2649060fa0c7SHemant Kumar * @sdt_notes: List to add the SDT note
2650060fa0c7SHemant Kumar *
2651060fa0c7SHemant Kumar * Responsible for parsing the @data in section .note.stapsdt in @elf and
2652060fa0c7SHemant Kumar * if its an SDT note, it appends to @sdt_notes list.
2653060fa0c7SHemant Kumar */
populate_sdt_note(Elf ** elf,const char * data,size_t len,struct list_head * sdt_notes)2654060fa0c7SHemant Kumar static int populate_sdt_note(Elf **elf, const char *data, size_t len,
2655060fa0c7SHemant Kumar struct list_head *sdt_notes)
2656060fa0c7SHemant Kumar {
2657be88184bSAlexis Berlemont const char *provider, *name, *args;
2658060fa0c7SHemant Kumar struct sdt_note *tmp = NULL;
2659060fa0c7SHemant Kumar GElf_Ehdr ehdr;
2660060fa0c7SHemant Kumar GElf_Shdr shdr;
2661060fa0c7SHemant Kumar int ret = -EINVAL;
2662060fa0c7SHemant Kumar
2663060fa0c7SHemant Kumar union {
2664060fa0c7SHemant Kumar Elf64_Addr a64[NR_ADDR];
2665060fa0c7SHemant Kumar Elf32_Addr a32[NR_ADDR];
2666060fa0c7SHemant Kumar } buf;
2667060fa0c7SHemant Kumar
2668060fa0c7SHemant Kumar Elf_Data dst = {
2669060fa0c7SHemant Kumar .d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
2670060fa0c7SHemant Kumar .d_size = gelf_fsize((*elf), ELF_T_ADDR, NR_ADDR, EV_CURRENT),
2671060fa0c7SHemant Kumar .d_off = 0, .d_align = 0
2672060fa0c7SHemant Kumar };
2673060fa0c7SHemant Kumar Elf_Data src = {
2674060fa0c7SHemant Kumar .d_buf = (void *) data, .d_type = ELF_T_ADDR,
2675060fa0c7SHemant Kumar .d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
2676060fa0c7SHemant Kumar .d_align = 0
2677060fa0c7SHemant Kumar };
2678060fa0c7SHemant Kumar
2679060fa0c7SHemant Kumar tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note));
2680060fa0c7SHemant Kumar if (!tmp) {
2681060fa0c7SHemant Kumar ret = -ENOMEM;
2682060fa0c7SHemant Kumar goto out_err;
2683060fa0c7SHemant Kumar }
2684060fa0c7SHemant Kumar
2685060fa0c7SHemant Kumar INIT_LIST_HEAD(&tmp->note_list);
2686060fa0c7SHemant Kumar
2687060fa0c7SHemant Kumar if (len < dst.d_size + 3)
2688060fa0c7SHemant Kumar goto out_free_note;
2689060fa0c7SHemant Kumar
2690060fa0c7SHemant Kumar /* Translation from file representation to memory representation */
2691060fa0c7SHemant Kumar if (gelf_xlatetom(*elf, &dst, &src,
2692060fa0c7SHemant Kumar elf_getident(*elf, NULL)[EI_DATA]) == NULL) {
2693060fa0c7SHemant Kumar pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1));
2694060fa0c7SHemant Kumar goto out_free_note;
2695060fa0c7SHemant Kumar }
2696060fa0c7SHemant Kumar
2697060fa0c7SHemant Kumar /* Populate the fields of sdt_note */
2698060fa0c7SHemant Kumar provider = data + dst.d_size;
2699060fa0c7SHemant Kumar
2700060fa0c7SHemant Kumar name = (const char *)memchr(provider, '\0', data + len - provider);
2701060fa0c7SHemant Kumar if (name++ == NULL)
2702060fa0c7SHemant Kumar goto out_free_note;
2703060fa0c7SHemant Kumar
2704060fa0c7SHemant Kumar tmp->provider = strdup(provider);
2705060fa0c7SHemant Kumar if (!tmp->provider) {
2706060fa0c7SHemant Kumar ret = -ENOMEM;
2707060fa0c7SHemant Kumar goto out_free_note;
2708060fa0c7SHemant Kumar }
2709060fa0c7SHemant Kumar tmp->name = strdup(name);
2710060fa0c7SHemant Kumar if (!tmp->name) {
2711060fa0c7SHemant Kumar ret = -ENOMEM;
2712060fa0c7SHemant Kumar goto out_free_prov;
2713060fa0c7SHemant Kumar }
2714060fa0c7SHemant Kumar
2715be88184bSAlexis Berlemont args = memchr(name, '\0', data + len - name);
2716be88184bSAlexis Berlemont
2717be88184bSAlexis Berlemont /*
2718be88184bSAlexis Berlemont * There is no argument if:
2719be88184bSAlexis Berlemont * - We reached the end of the note;
2720be88184bSAlexis Berlemont * - There is not enough room to hold a potential string;
2721be88184bSAlexis Berlemont * - The argument string is empty or just contains ':'.
2722be88184bSAlexis Berlemont */
2723be88184bSAlexis Berlemont if (args == NULL || data + len - args < 2 ||
2724be88184bSAlexis Berlemont args[1] == ':' || args[1] == '\0')
2725be88184bSAlexis Berlemont tmp->args = NULL;
2726be88184bSAlexis Berlemont else {
2727be88184bSAlexis Berlemont tmp->args = strdup(++args);
2728be88184bSAlexis Berlemont if (!tmp->args) {
2729be88184bSAlexis Berlemont ret = -ENOMEM;
2730be88184bSAlexis Berlemont goto out_free_name;
2731be88184bSAlexis Berlemont }
2732be88184bSAlexis Berlemont }
2733be88184bSAlexis Berlemont
2734060fa0c7SHemant Kumar if (gelf_getclass(*elf) == ELFCLASS32) {
2735060fa0c7SHemant Kumar memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr));
2736060fa0c7SHemant Kumar tmp->bit32 = true;
2737060fa0c7SHemant Kumar } else {
2738060fa0c7SHemant Kumar memcpy(&tmp->addr, &buf, 3 * sizeof(Elf64_Addr));
2739060fa0c7SHemant Kumar tmp->bit32 = false;
2740060fa0c7SHemant Kumar }
2741060fa0c7SHemant Kumar
2742060fa0c7SHemant Kumar if (!gelf_getehdr(*elf, &ehdr)) {
2743060fa0c7SHemant Kumar pr_debug("%s : cannot get elf header.\n", __func__);
2744060fa0c7SHemant Kumar ret = -EBADF;
2745be88184bSAlexis Berlemont goto out_free_args;
2746060fa0c7SHemant Kumar }
2747060fa0c7SHemant Kumar
2748060fa0c7SHemant Kumar /* Adjust the prelink effect :
2749060fa0c7SHemant Kumar * Find out the .stapsdt.base section.
2750060fa0c7SHemant Kumar * This scn will help us to handle prelinking (if present).
2751060fa0c7SHemant Kumar * Compare the retrieved file offset of the base section with the
2752060fa0c7SHemant Kumar * base address in the description of the SDT note. If its different,
2753060fa0c7SHemant Kumar * then accordingly, adjust the note location.
2754060fa0c7SHemant Kumar */
27555a5e3d3cSRavi Bangoria if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL))
27565a5e3d3cSRavi Bangoria sdt_adjust_loc(tmp, shdr.sh_offset);
27575a5e3d3cSRavi Bangoria
27585a5e3d3cSRavi Bangoria /* Adjust reference counter offset */
27595a5e3d3cSRavi Bangoria if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_PROBES_SCN, NULL))
27605a5e3d3cSRavi Bangoria sdt_adjust_refctr(tmp, shdr.sh_addr, shdr.sh_offset);
2761060fa0c7SHemant Kumar
2762060fa0c7SHemant Kumar list_add_tail(&tmp->note_list, sdt_notes);
2763060fa0c7SHemant Kumar return 0;
2764060fa0c7SHemant Kumar
2765be88184bSAlexis Berlemont out_free_args:
2766d8f9da24SArnaldo Carvalho de Melo zfree(&tmp->args);
2767060fa0c7SHemant Kumar out_free_name:
2768d8f9da24SArnaldo Carvalho de Melo zfree(&tmp->name);
2769060fa0c7SHemant Kumar out_free_prov:
2770d8f9da24SArnaldo Carvalho de Melo zfree(&tmp->provider);
2771060fa0c7SHemant Kumar out_free_note:
2772060fa0c7SHemant Kumar free(tmp);
2773060fa0c7SHemant Kumar out_err:
2774060fa0c7SHemant Kumar return ret;
2775060fa0c7SHemant Kumar }
2776060fa0c7SHemant Kumar
2777060fa0c7SHemant Kumar /**
2778060fa0c7SHemant Kumar * construct_sdt_notes_list : constructs a list of SDT notes
2779060fa0c7SHemant Kumar * @elf : elf to look into
2780060fa0c7SHemant Kumar * @sdt_notes : empty list_head
2781060fa0c7SHemant Kumar *
2782060fa0c7SHemant Kumar * Scans the sections in 'elf' for the section
2783060fa0c7SHemant Kumar * .note.stapsdt. It, then calls populate_sdt_note to find
2784060fa0c7SHemant Kumar * out the SDT events and populates the 'sdt_notes'.
2785060fa0c7SHemant Kumar */
construct_sdt_notes_list(Elf * elf,struct list_head * sdt_notes)2786060fa0c7SHemant Kumar static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
2787060fa0c7SHemant Kumar {
2788060fa0c7SHemant Kumar GElf_Ehdr ehdr;
2789060fa0c7SHemant Kumar Elf_Scn *scn = NULL;
2790060fa0c7SHemant Kumar Elf_Data *data;
2791060fa0c7SHemant Kumar GElf_Shdr shdr;
2792060fa0c7SHemant Kumar size_t shstrndx, next;
2793060fa0c7SHemant Kumar GElf_Nhdr nhdr;
2794060fa0c7SHemant Kumar size_t name_off, desc_off, offset;
2795060fa0c7SHemant Kumar int ret = 0;
2796060fa0c7SHemant Kumar
2797060fa0c7SHemant Kumar if (gelf_getehdr(elf, &ehdr) == NULL) {
2798060fa0c7SHemant Kumar ret = -EBADF;
2799060fa0c7SHemant Kumar goto out_ret;
2800060fa0c7SHemant Kumar }
2801060fa0c7SHemant Kumar if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
2802060fa0c7SHemant Kumar ret = -EBADF;
2803060fa0c7SHemant Kumar goto out_ret;
2804060fa0c7SHemant Kumar }
2805060fa0c7SHemant Kumar
2806060fa0c7SHemant Kumar /* Look for the required section */
2807060fa0c7SHemant Kumar scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
2808060fa0c7SHemant Kumar if (!scn) {
2809060fa0c7SHemant Kumar ret = -ENOENT;
2810060fa0c7SHemant Kumar goto out_ret;
2811060fa0c7SHemant Kumar }
2812060fa0c7SHemant Kumar
2813060fa0c7SHemant Kumar if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {
2814060fa0c7SHemant Kumar ret = -ENOENT;
2815060fa0c7SHemant Kumar goto out_ret;
2816060fa0c7SHemant Kumar }
2817060fa0c7SHemant Kumar
2818060fa0c7SHemant Kumar data = elf_getdata(scn, NULL);
2819060fa0c7SHemant Kumar
2820060fa0c7SHemant Kumar /* Get the SDT notes */
2821060fa0c7SHemant Kumar for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
2822060fa0c7SHemant Kumar &desc_off)) > 0; offset = next) {
2823060fa0c7SHemant Kumar if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
2824060fa0c7SHemant Kumar !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
2825060fa0c7SHemant Kumar sizeof(SDT_NOTE_NAME))) {
2826060fa0c7SHemant Kumar /* Check the type of the note */
2827060fa0c7SHemant Kumar if (nhdr.n_type != SDT_NOTE_TYPE)
2828060fa0c7SHemant Kumar goto out_ret;
2829060fa0c7SHemant Kumar
2830060fa0c7SHemant Kumar ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
2831060fa0c7SHemant Kumar nhdr.n_descsz, sdt_notes);
2832060fa0c7SHemant Kumar if (ret < 0)
2833060fa0c7SHemant Kumar goto out_ret;
2834060fa0c7SHemant Kumar }
2835060fa0c7SHemant Kumar }
2836060fa0c7SHemant Kumar if (list_empty(sdt_notes))
2837060fa0c7SHemant Kumar ret = -ENOENT;
2838060fa0c7SHemant Kumar
2839060fa0c7SHemant Kumar out_ret:
2840060fa0c7SHemant Kumar return ret;
2841060fa0c7SHemant Kumar }
2842060fa0c7SHemant Kumar
2843060fa0c7SHemant Kumar /**
2844060fa0c7SHemant Kumar * get_sdt_note_list : Wrapper to construct a list of sdt notes
2845060fa0c7SHemant Kumar * @head : empty list_head
2846060fa0c7SHemant Kumar * @target : file to find SDT notes from
2847060fa0c7SHemant Kumar *
2848060fa0c7SHemant Kumar * This opens the file, initializes
2849060fa0c7SHemant Kumar * the ELF and then calls construct_sdt_notes_list.
2850060fa0c7SHemant Kumar */
get_sdt_note_list(struct list_head * head,const char * target)2851060fa0c7SHemant Kumar int get_sdt_note_list(struct list_head *head, const char *target)
2852060fa0c7SHemant Kumar {
2853060fa0c7SHemant Kumar Elf *elf;
2854060fa0c7SHemant Kumar int fd, ret;
2855060fa0c7SHemant Kumar
2856060fa0c7SHemant Kumar fd = open(target, O_RDONLY);
2857060fa0c7SHemant Kumar if (fd < 0)
2858060fa0c7SHemant Kumar return -EBADF;
2859060fa0c7SHemant Kumar
2860060fa0c7SHemant Kumar elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
2861060fa0c7SHemant Kumar if (!elf) {
2862060fa0c7SHemant Kumar ret = -EBADF;
2863060fa0c7SHemant Kumar goto out_close;
2864060fa0c7SHemant Kumar }
2865060fa0c7SHemant Kumar ret = construct_sdt_notes_list(elf, head);
2866060fa0c7SHemant Kumar elf_end(elf);
2867060fa0c7SHemant Kumar out_close:
2868060fa0c7SHemant Kumar close(fd);
2869060fa0c7SHemant Kumar return ret;
2870060fa0c7SHemant Kumar }
2871060fa0c7SHemant Kumar
2872060fa0c7SHemant Kumar /**
2873060fa0c7SHemant Kumar * cleanup_sdt_note_list : free the sdt notes' list
2874060fa0c7SHemant Kumar * @sdt_notes: sdt notes' list
2875060fa0c7SHemant Kumar *
2876060fa0c7SHemant Kumar * Free up the SDT notes in @sdt_notes.
2877060fa0c7SHemant Kumar * Returns the number of SDT notes free'd.
2878060fa0c7SHemant Kumar */
cleanup_sdt_note_list(struct list_head * sdt_notes)2879060fa0c7SHemant Kumar int cleanup_sdt_note_list(struct list_head *sdt_notes)
2880060fa0c7SHemant Kumar {
2881060fa0c7SHemant Kumar struct sdt_note *tmp, *pos;
2882060fa0c7SHemant Kumar int nr_free = 0;
2883060fa0c7SHemant Kumar
2884060fa0c7SHemant Kumar list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
2885e56fbc9dSArnaldo Carvalho de Melo list_del_init(&pos->note_list);
288669c9ffedSRiccardo Mancini zfree(&pos->args);
2887d8f9da24SArnaldo Carvalho de Melo zfree(&pos->name);
2888d8f9da24SArnaldo Carvalho de Melo zfree(&pos->provider);
2889060fa0c7SHemant Kumar free(pos);
2890060fa0c7SHemant Kumar nr_free++;
2891060fa0c7SHemant Kumar }
2892060fa0c7SHemant Kumar return nr_free;
2893060fa0c7SHemant Kumar }
2894060fa0c7SHemant Kumar
2895060fa0c7SHemant Kumar /**
2896060fa0c7SHemant Kumar * sdt_notes__get_count: Counts the number of sdt events
2897060fa0c7SHemant Kumar * @start: list_head to sdt_notes list
2898060fa0c7SHemant Kumar *
2899060fa0c7SHemant Kumar * Returns the number of SDT notes in a list
2900060fa0c7SHemant Kumar */
sdt_notes__get_count(struct list_head * start)2901060fa0c7SHemant Kumar int sdt_notes__get_count(struct list_head *start)
2902060fa0c7SHemant Kumar {
2903060fa0c7SHemant Kumar struct sdt_note *sdt_ptr;
2904060fa0c7SHemant Kumar int count = 0;
2905060fa0c7SHemant Kumar
2906060fa0c7SHemant Kumar list_for_each_entry(sdt_ptr, start, note_list)
2907060fa0c7SHemant Kumar count++;
2908060fa0c7SHemant Kumar return count;
2909060fa0c7SHemant Kumar }
29101c1a3a47SArnaldo Carvalho de Melo #endif
2911060fa0c7SHemant Kumar
symbol__elf_init(void)2912e5a1845fSNamhyung Kim void symbol__elf_init(void)
2913e5a1845fSNamhyung Kim {
2914e5a1845fSNamhyung Kim elf_version(EV_CURRENT);
2915e5a1845fSNamhyung Kim }
2916