xref: /openbmc/linux/tools/perf/util/symbol-elf.c (revision e59fea47)
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