| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * recordmcount.h | 
|  | 3 | * | 
|  | 4 | * This code was taken out of recordmcount.c written by | 
|  | 5 | * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved. | 
|  | 6 | * | 
|  | 7 | * The original code had the same algorithms for both 32bit | 
|  | 8 | * and 64bit ELF files, but the code was duplicated to support | 
|  | 9 | * the difference in structures that were used. This | 
|  | 10 | * file creates a macro of everything that is different between | 
|  | 11 | * the 64 and 32 bit code, such that by including this header | 
|  | 12 | * twice we can create both sets of functions by including this | 
|  | 13 | * header once with RECORD_MCOUNT_64 undefined, and again with | 
|  | 14 | * it defined. | 
|  | 15 | * | 
|  | 16 | * This conversion to macros was done by: | 
|  | 17 | * Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. | 
|  | 18 | * | 
|  | 19 | * Licensed under the GNU General Public License, version 2 (GPLv2). | 
|  | 20 | */ | 
|  | 21 | #undef append_func | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 22 | #undef is_fake_mcount | 
|  | 23 | #undef fn_is_fake_mcount | 
|  | 24 | #undef MIPS_is_fake_mcount | 
| Martin Schwidefsky | 07d8b59 | 2011-05-10 10:10:40 +0200 | [diff] [blame] | 25 | #undef mcount_adjust | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 26 | #undef sift_rel_mcount | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 27 | #undef nop_mcount | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 28 | #undef find_secsym_ndx | 
|  | 29 | #undef __has_rel_mcount | 
|  | 30 | #undef has_rel_mcount | 
|  | 31 | #undef tot_relsize | 
| Steven Rostedt | 37762cb | 2011-04-20 20:47:34 -0400 | [diff] [blame] | 32 | #undef get_mcountsym | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 33 | #undef get_sym_str_and_relp | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 34 | #undef do_func | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 35 | #undef Elf_Addr | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 36 | #undef Elf_Ehdr | 
|  | 37 | #undef Elf_Shdr | 
|  | 38 | #undef Elf_Rel | 
|  | 39 | #undef Elf_Rela | 
|  | 40 | #undef Elf_Sym | 
|  | 41 | #undef ELF_R_SYM | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 42 | #undef Elf_r_sym | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 43 | #undef ELF_R_INFO | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 44 | #undef Elf_r_info | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 45 | #undef ELF_ST_BIND | 
| Rabin Vincent | 9905ce8 | 2011-05-11 22:53:51 +0530 | [diff] [blame] | 46 | #undef ELF_ST_TYPE | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 47 | #undef fn_ELF_R_SYM | 
|  | 48 | #undef fn_ELF_R_INFO | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 49 | #undef uint_t | 
|  | 50 | #undef _w | 
|  | 51 | #undef _align | 
|  | 52 | #undef _size | 
|  | 53 |  | 
|  | 54 | #ifdef RECORD_MCOUNT_64 | 
|  | 55 | # define append_func		append64 | 
|  | 56 | # define sift_rel_mcount	sift64_rel_mcount | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 57 | # define nop_mcount		nop_mcount_64 | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 58 | # define find_secsym_ndx	find64_secsym_ndx | 
|  | 59 | # define __has_rel_mcount	__has64_rel_mcount | 
|  | 60 | # define has_rel_mcount		has64_rel_mcount | 
|  | 61 | # define tot_relsize		tot64_relsize | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 62 | # define get_sym_str_and_relp	get_sym_str_and_relp_64 | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 63 | # define do_func		do64 | 
| Steven Rostedt | 37762cb | 2011-04-20 20:47:34 -0400 | [diff] [blame] | 64 | # define get_mcountsym		get_mcountsym_64 | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 65 | # define is_fake_mcount		is_fake_mcount64 | 
|  | 66 | # define fn_is_fake_mcount	fn_is_fake_mcount64 | 
|  | 67 | # define MIPS_is_fake_mcount	MIPS64_is_fake_mcount | 
| Martin Schwidefsky | 07d8b59 | 2011-05-10 10:10:40 +0200 | [diff] [blame] | 68 | # define mcount_adjust		mcount_adjust_64 | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 69 | # define Elf_Addr		Elf64_Addr | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 70 | # define Elf_Ehdr		Elf64_Ehdr | 
|  | 71 | # define Elf_Shdr		Elf64_Shdr | 
|  | 72 | # define Elf_Rel		Elf64_Rel | 
|  | 73 | # define Elf_Rela		Elf64_Rela | 
|  | 74 | # define Elf_Sym		Elf64_Sym | 
|  | 75 | # define ELF_R_SYM		ELF64_R_SYM | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 76 | # define Elf_r_sym		Elf64_r_sym | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 77 | # define ELF_R_INFO		ELF64_R_INFO | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 78 | # define Elf_r_info		Elf64_r_info | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 79 | # define ELF_ST_BIND		ELF64_ST_BIND | 
| Rabin Vincent | 9905ce8 | 2011-05-11 22:53:51 +0530 | [diff] [blame] | 80 | # define ELF_ST_TYPE		ELF64_ST_TYPE | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 81 | # define fn_ELF_R_SYM		fn_ELF64_R_SYM | 
|  | 82 | # define fn_ELF_R_INFO		fn_ELF64_R_INFO | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 83 | # define uint_t			uint64_t | 
|  | 84 | # define _w			w8 | 
|  | 85 | # define _align			7u | 
|  | 86 | # define _size			8 | 
|  | 87 | #else | 
|  | 88 | # define append_func		append32 | 
|  | 89 | # define sift_rel_mcount	sift32_rel_mcount | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 90 | # define nop_mcount		nop_mcount_32 | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 91 | # define find_secsym_ndx	find32_secsym_ndx | 
|  | 92 | # define __has_rel_mcount	__has32_rel_mcount | 
|  | 93 | # define has_rel_mcount		has32_rel_mcount | 
|  | 94 | # define tot_relsize		tot32_relsize | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 95 | # define get_sym_str_and_relp	get_sym_str_and_relp_32 | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 96 | # define do_func		do32 | 
| Steven Rostedt | 37762cb | 2011-04-20 20:47:34 -0400 | [diff] [blame] | 97 | # define get_mcountsym		get_mcountsym_32 | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 98 | # define is_fake_mcount		is_fake_mcount32 | 
|  | 99 | # define fn_is_fake_mcount	fn_is_fake_mcount32 | 
|  | 100 | # define MIPS_is_fake_mcount	MIPS32_is_fake_mcount | 
| Martin Schwidefsky | 07d8b59 | 2011-05-10 10:10:40 +0200 | [diff] [blame] | 101 | # define mcount_adjust		mcount_adjust_32 | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 102 | # define Elf_Addr		Elf32_Addr | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 103 | # define Elf_Ehdr		Elf32_Ehdr | 
|  | 104 | # define Elf_Shdr		Elf32_Shdr | 
|  | 105 | # define Elf_Rel		Elf32_Rel | 
|  | 106 | # define Elf_Rela		Elf32_Rela | 
|  | 107 | # define Elf_Sym		Elf32_Sym | 
|  | 108 | # define ELF_R_SYM		ELF32_R_SYM | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 109 | # define Elf_r_sym		Elf32_r_sym | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 110 | # define ELF_R_INFO		ELF32_R_INFO | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 111 | # define Elf_r_info		Elf32_r_info | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 112 | # define ELF_ST_BIND		ELF32_ST_BIND | 
| Rabin Vincent | 9905ce8 | 2011-05-11 22:53:51 +0530 | [diff] [blame] | 113 | # define ELF_ST_TYPE		ELF32_ST_TYPE | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 114 | # define fn_ELF_R_SYM		fn_ELF32_R_SYM | 
|  | 115 | # define fn_ELF_R_INFO		fn_ELF32_R_INFO | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 116 | # define uint_t			uint32_t | 
|  | 117 | # define _w			w | 
|  | 118 | # define _align			3u | 
|  | 119 | # define _size			4 | 
|  | 120 | #endif | 
|  | 121 |  | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 122 | /* Functions and pointers that do_file() may override for specific e_machine. */ | 
|  | 123 | static int fn_is_fake_mcount(Elf_Rel const *rp) | 
|  | 124 | { | 
|  | 125 | return 0; | 
|  | 126 | } | 
|  | 127 | static int (*is_fake_mcount)(Elf_Rel const *rp) = fn_is_fake_mcount; | 
|  | 128 |  | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 129 | static uint_t fn_ELF_R_SYM(Elf_Rel const *rp) | 
|  | 130 | { | 
|  | 131 | return ELF_R_SYM(_w(rp->r_info)); | 
|  | 132 | } | 
|  | 133 | static uint_t (*Elf_r_sym)(Elf_Rel const *rp) = fn_ELF_R_SYM; | 
|  | 134 |  | 
|  | 135 | static void fn_ELF_R_INFO(Elf_Rel *const rp, unsigned sym, unsigned type) | 
|  | 136 | { | 
| John Reiser | e63233f7 | 2010-11-22 19:41:44 -0800 | [diff] [blame] | 137 | rp->r_info = _w(ELF_R_INFO(sym, type)); | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 138 | } | 
|  | 139 | static void (*Elf_r_info)(Elf_Rel *const rp, unsigned sym, unsigned type) = fn_ELF_R_INFO; | 
|  | 140 |  | 
| Martin Schwidefsky | 07d8b59 | 2011-05-10 10:10:40 +0200 | [diff] [blame] | 141 | static int mcount_adjust = 0; | 
|  | 142 |  | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 143 | /* | 
|  | 144 | * MIPS mcount long call has 2 _mcount symbols, only the position of the 1st | 
|  | 145 | * _mcount symbol is needed for dynamic function tracer, with it, to disable | 
|  | 146 | * tracing(ftrace_make_nop), the instruction in the position is replaced with | 
|  | 147 | * the "b label" instruction, to enable tracing(ftrace_make_call), replace the | 
|  | 148 | * instruction back. So, here, we set the 2nd one as fake and filter it. | 
|  | 149 | * | 
|  | 150 | * c:	3c030000	lui	v1,0x0		<-->	b	label | 
|  | 151 | *		c: R_MIPS_HI16	_mcount | 
|  | 152 | *		c: R_MIPS_NONE	*ABS* | 
|  | 153 | *		c: R_MIPS_NONE	*ABS* | 
|  | 154 | * 10:	64630000	daddiu	v1,v1,0 | 
|  | 155 | *		10: R_MIPS_LO16	_mcount | 
|  | 156 | *		10: R_MIPS_NONE	*ABS* | 
|  | 157 | *		10: R_MIPS_NONE	*ABS* | 
|  | 158 | * 14:	03e0082d	move	at,ra | 
|  | 159 | * 18:	0060f809	jalr	v1 | 
|  | 160 | * label: | 
|  | 161 | */ | 
|  | 162 | #define MIPS_FAKEMCOUNT_OFFSET	4 | 
|  | 163 |  | 
|  | 164 | static int MIPS_is_fake_mcount(Elf_Rel const *rp) | 
|  | 165 | { | 
|  | 166 | static Elf_Addr old_r_offset; | 
|  | 167 | Elf_Addr current_r_offset = _w(rp->r_offset); | 
|  | 168 | int is_fake; | 
|  | 169 |  | 
|  | 170 | is_fake = old_r_offset && | 
|  | 171 | (current_r_offset - old_r_offset == MIPS_FAKEMCOUNT_OFFSET); | 
|  | 172 | old_r_offset = current_r_offset; | 
|  | 173 |  | 
|  | 174 | return is_fake; | 
|  | 175 | } | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 176 |  | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 177 | /* Append the new shstrtab, Elf_Shdr[], __mcount_loc and its relocations. */ | 
|  | 178 | static void append_func(Elf_Ehdr *const ehdr, | 
|  | 179 | Elf_Shdr *const shstr, | 
|  | 180 | uint_t const *const mloc0, | 
|  | 181 | uint_t const *const mlocp, | 
|  | 182 | Elf_Rel const *const mrel0, | 
|  | 183 | Elf_Rel const *const mrelp, | 
|  | 184 | unsigned int const rel_entsize, | 
|  | 185 | unsigned int const symsec_sh_link) | 
|  | 186 | { | 
|  | 187 | /* Begin constructing output file */ | 
|  | 188 | Elf_Shdr mcsec; | 
|  | 189 | char const *mc_name = (sizeof(Elf_Rela) == rel_entsize) | 
|  | 190 | ? ".rela__mcount_loc" | 
|  | 191 | :  ".rel__mcount_loc"; | 
|  | 192 | unsigned const old_shnum = w2(ehdr->e_shnum); | 
|  | 193 | uint_t const old_shoff = _w(ehdr->e_shoff); | 
|  | 194 | uint_t const old_shstr_sh_size   = _w(shstr->sh_size); | 
|  | 195 | uint_t const old_shstr_sh_offset = _w(shstr->sh_offset); | 
|  | 196 | uint_t t = 1 + strlen(mc_name) + _w(shstr->sh_size); | 
|  | 197 | uint_t new_e_shoff; | 
|  | 198 |  | 
|  | 199 | shstr->sh_size = _w(t); | 
|  | 200 | shstr->sh_offset = _w(sb.st_size); | 
|  | 201 | t += sb.st_size; | 
|  | 202 | t += (_align & -t);  /* word-byte align */ | 
|  | 203 | new_e_shoff = t; | 
|  | 204 |  | 
|  | 205 | /* body for new shstrtab */ | 
|  | 206 | ulseek(fd_map, sb.st_size, SEEK_SET); | 
|  | 207 | uwrite(fd_map, old_shstr_sh_offset + (void *)ehdr, old_shstr_sh_size); | 
|  | 208 | uwrite(fd_map, mc_name, 1 + strlen(mc_name)); | 
|  | 209 |  | 
|  | 210 | /* old(modified) Elf_Shdr table, word-byte aligned */ | 
|  | 211 | ulseek(fd_map, t, SEEK_SET); | 
|  | 212 | t += sizeof(Elf_Shdr) * old_shnum; | 
|  | 213 | uwrite(fd_map, old_shoff + (void *)ehdr, | 
|  | 214 | sizeof(Elf_Shdr) * old_shnum); | 
|  | 215 |  | 
|  | 216 | /* new sections __mcount_loc and .rel__mcount_loc */ | 
|  | 217 | t += 2*sizeof(mcsec); | 
|  | 218 | mcsec.sh_name = w((sizeof(Elf_Rela) == rel_entsize) + strlen(".rel") | 
|  | 219 | + old_shstr_sh_size); | 
|  | 220 | mcsec.sh_type = w(SHT_PROGBITS); | 
|  | 221 | mcsec.sh_flags = _w(SHF_ALLOC); | 
|  | 222 | mcsec.sh_addr = 0; | 
|  | 223 | mcsec.sh_offset = _w(t); | 
|  | 224 | mcsec.sh_size = _w((void *)mlocp - (void *)mloc0); | 
|  | 225 | mcsec.sh_link = 0; | 
|  | 226 | mcsec.sh_info = 0; | 
|  | 227 | mcsec.sh_addralign = _w(_size); | 
|  | 228 | mcsec.sh_entsize = _w(_size); | 
|  | 229 | uwrite(fd_map, &mcsec, sizeof(mcsec)); | 
|  | 230 |  | 
|  | 231 | mcsec.sh_name = w(old_shstr_sh_size); | 
|  | 232 | mcsec.sh_type = (sizeof(Elf_Rela) == rel_entsize) | 
|  | 233 | ? w(SHT_RELA) | 
|  | 234 | : w(SHT_REL); | 
|  | 235 | mcsec.sh_flags = 0; | 
|  | 236 | mcsec.sh_addr = 0; | 
|  | 237 | mcsec.sh_offset = _w((void *)mlocp - (void *)mloc0 + t); | 
|  | 238 | mcsec.sh_size   = _w((void *)mrelp - (void *)mrel0); | 
|  | 239 | mcsec.sh_link = w(symsec_sh_link); | 
|  | 240 | mcsec.sh_info = w(old_shnum); | 
|  | 241 | mcsec.sh_addralign = _w(_size); | 
|  | 242 | mcsec.sh_entsize = _w(rel_entsize); | 
|  | 243 | uwrite(fd_map, &mcsec, sizeof(mcsec)); | 
|  | 244 |  | 
|  | 245 | uwrite(fd_map, mloc0, (void *)mlocp - (void *)mloc0); | 
|  | 246 | uwrite(fd_map, mrel0, (void *)mrelp - (void *)mrel0); | 
|  | 247 |  | 
|  | 248 | ehdr->e_shoff = _w(new_e_shoff); | 
|  | 249 | ehdr->e_shnum = w2(2 + w2(ehdr->e_shnum));  /* {.rel,}__mcount_loc */ | 
|  | 250 | ulseek(fd_map, 0, SEEK_SET); | 
|  | 251 | uwrite(fd_map, ehdr, sizeof(*ehdr)); | 
|  | 252 | } | 
|  | 253 |  | 
| Steven Rostedt | 37762cb | 2011-04-20 20:47:34 -0400 | [diff] [blame] | 254 | static unsigned get_mcountsym(Elf_Sym const *const sym0, | 
|  | 255 | Elf_Rel const *relp, | 
|  | 256 | char const *const str0) | 
|  | 257 | { | 
|  | 258 | unsigned mcountsym = 0; | 
|  | 259 |  | 
|  | 260 | Elf_Sym const *const symp = | 
|  | 261 | &sym0[Elf_r_sym(relp)]; | 
|  | 262 | char const *symname = &str0[w(symp->st_name)]; | 
|  | 263 | char const *mcount = gpfx == '_' ? "_mcount" : "mcount"; | 
|  | 264 |  | 
|  | 265 | if (symname[0] == '.') | 
|  | 266 | ++symname;  /* ppc64 hack */ | 
|  | 267 | if (strcmp(mcount, symname) == 0 || | 
|  | 268 | (altmcount && strcmp(altmcount, symname) == 0)) | 
|  | 269 | mcountsym = Elf_r_sym(relp); | 
|  | 270 |  | 
|  | 271 | return mcountsym; | 
|  | 272 | } | 
|  | 273 |  | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 274 | static void get_sym_str_and_relp(Elf_Shdr const *const relhdr, | 
|  | 275 | Elf_Ehdr const *const ehdr, | 
|  | 276 | Elf_Sym const **sym0, | 
|  | 277 | char const **str0, | 
|  | 278 | Elf_Rel const **relp) | 
|  | 279 | { | 
|  | 280 | Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff) | 
|  | 281 | + (void *)ehdr); | 
|  | 282 | unsigned const symsec_sh_link = w(relhdr->sh_link); | 
|  | 283 | Elf_Shdr const *const symsec = &shdr0[symsec_sh_link]; | 
|  | 284 | Elf_Shdr const *const strsec = &shdr0[w(symsec->sh_link)]; | 
|  | 285 | Elf_Rel const *const rel0 = (Elf_Rel const *)(_w(relhdr->sh_offset) | 
|  | 286 | + (void *)ehdr); | 
|  | 287 |  | 
|  | 288 | *sym0 = (Elf_Sym const *)(_w(symsec->sh_offset) | 
|  | 289 | + (void *)ehdr); | 
|  | 290 |  | 
|  | 291 | *str0 = (char const *)(_w(strsec->sh_offset) | 
|  | 292 | + (void *)ehdr); | 
|  | 293 |  | 
|  | 294 | *relp = rel0; | 
|  | 295 | } | 
|  | 296 |  | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 297 | /* | 
|  | 298 | * Look at the relocations in order to find the calls to mcount. | 
|  | 299 | * Accumulate the section offsets that are found, and their relocation info, | 
|  | 300 | * onto the end of the existing arrays. | 
|  | 301 | */ | 
|  | 302 | static uint_t *sift_rel_mcount(uint_t *mlocp, | 
|  | 303 | unsigned const offbase, | 
|  | 304 | Elf_Rel **const mrelpp, | 
|  | 305 | Elf_Shdr const *const relhdr, | 
|  | 306 | Elf_Ehdr const *const ehdr, | 
|  | 307 | unsigned const recsym, | 
|  | 308 | uint_t const recval, | 
|  | 309 | unsigned const reltype) | 
|  | 310 | { | 
|  | 311 | uint_t *const mloc0 = mlocp; | 
|  | 312 | Elf_Rel *mrelp = *mrelpp; | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 313 | Elf_Sym const *sym0; | 
|  | 314 | char const *str0; | 
|  | 315 | Elf_Rel const *relp; | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 316 | unsigned rel_entsize = _w(relhdr->sh_entsize); | 
|  | 317 | unsigned const nrel = _w(relhdr->sh_size) / rel_entsize; | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 318 | unsigned mcountsym = 0; | 
|  | 319 | unsigned t; | 
|  | 320 |  | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 321 | get_sym_str_and_relp(relhdr, ehdr, &sym0, &str0, &relp); | 
|  | 322 |  | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 323 | for (t = nrel; t; --t) { | 
| Steven Rostedt | 37762cb | 2011-04-20 20:47:34 -0400 | [diff] [blame] | 324 | if (!mcountsym) | 
|  | 325 | mcountsym = get_mcountsym(sym0, relp, str0); | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 326 |  | 
| Wu Zhangjin | 412910c | 2010-10-27 18:59:08 +0800 | [diff] [blame] | 327 | if (mcountsym == Elf_r_sym(relp) && !is_fake_mcount(relp)) { | 
| Martin Schwidefsky | 07d8b59 | 2011-05-10 10:10:40 +0200 | [diff] [blame] | 328 | uint_t const addend = | 
|  | 329 | _w(_w(relp->r_offset) - recval + mcount_adjust); | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 330 | mrelp->r_offset = _w(offbase | 
|  | 331 | + ((void *)mlocp - (void *)mloc0)); | 
| John Reiser | a2d4935 | 2010-10-27 18:59:07 +0800 | [diff] [blame] | 332 | Elf_r_info(mrelp, recsym, reltype); | 
| Steven Rostedt | dd5477f | 2011-04-06 13:21:17 -0400 | [diff] [blame] | 333 | if (rel_entsize == sizeof(Elf_Rela)) { | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 334 | ((Elf_Rela *)mrelp)->r_addend = addend; | 
|  | 335 | *mlocp++ = 0; | 
|  | 336 | } else | 
|  | 337 | *mlocp++ = addend; | 
|  | 338 |  | 
|  | 339 | mrelp = (Elf_Rel *)(rel_entsize + (void *)mrelp); | 
|  | 340 | } | 
|  | 341 | relp = (Elf_Rel const *)(rel_entsize + (void *)relp); | 
|  | 342 | } | 
|  | 343 | *mrelpp = mrelp; | 
|  | 344 | return mlocp; | 
|  | 345 | } | 
|  | 346 |  | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 347 | /* | 
|  | 348 | * Read the relocation table again, but this time its called on sections | 
|  | 349 | * that are not going to be traced. The mcount calls here will be converted | 
|  | 350 | * into nops. | 
|  | 351 | */ | 
|  | 352 | static void nop_mcount(Elf_Shdr const *const relhdr, | 
| Steven Rostedt | dfad3d5 | 2011-04-12 18:53:25 -0400 | [diff] [blame] | 353 | Elf_Ehdr const *const ehdr, | 
|  | 354 | const char *const txtname) | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 355 | { | 
|  | 356 | Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff) | 
|  | 357 | + (void *)ehdr); | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 358 | Elf_Sym const *sym0; | 
|  | 359 | char const *str0; | 
|  | 360 | Elf_Rel const *relp; | 
|  | 361 | Elf_Shdr const *const shdr = &shdr0[w(relhdr->sh_info)]; | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 362 | unsigned rel_entsize = _w(relhdr->sh_entsize); | 
|  | 363 | unsigned const nrel = _w(relhdr->sh_size) / rel_entsize; | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 364 | unsigned mcountsym = 0; | 
|  | 365 | unsigned t; | 
| Steven Rostedt | dfad3d5 | 2011-04-12 18:53:25 -0400 | [diff] [blame] | 366 | int once = 0; | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 367 |  | 
| Steven Rostedt | 41b402a | 2011-04-20 21:13:06 -0400 | [diff] [blame] | 368 | get_sym_str_and_relp(relhdr, ehdr, &sym0, &str0, &relp); | 
|  | 369 |  | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 370 | for (t = nrel; t; --t) { | 
|  | 371 | int ret = -1; | 
|  | 372 |  | 
| Steven Rostedt | 37762cb | 2011-04-20 20:47:34 -0400 | [diff] [blame] | 373 | if (!mcountsym) | 
|  | 374 | mcountsym = get_mcountsym(sym0, relp, str0); | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 375 |  | 
| Steven Rostedt | dfad3d5 | 2011-04-12 18:53:25 -0400 | [diff] [blame] | 376 | if (mcountsym == Elf_r_sym(relp) && !is_fake_mcount(relp)) { | 
|  | 377 | if (make_nop) | 
|  | 378 | ret = make_nop((void *)ehdr, shdr->sh_offset + relp->r_offset); | 
|  | 379 | if (warn_on_notrace_sect && !once) { | 
|  | 380 | printf("Section %s has mcount callers being ignored\n", | 
|  | 381 | txtname); | 
|  | 382 | once = 1; | 
|  | 383 | /* just warn? */ | 
|  | 384 | if (!make_nop) | 
|  | 385 | return; | 
|  | 386 | } | 
|  | 387 | } | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 388 |  | 
|  | 389 | /* | 
|  | 390 | * If we successfully removed the mcount, mark the relocation | 
|  | 391 | * as a nop (don't do anything with it). | 
|  | 392 | */ | 
|  | 393 | if (!ret) { | 
|  | 394 | Elf_Rel rel; | 
|  | 395 | rel = *(Elf_Rel *)relp; | 
|  | 396 | Elf_r_info(&rel, Elf_r_sym(relp), rel_type_nop); | 
|  | 397 | ulseek(fd_map, (void *)relp - (void *)ehdr, SEEK_SET); | 
|  | 398 | uwrite(fd_map, &rel, sizeof(rel)); | 
|  | 399 | } | 
|  | 400 | relp = (Elf_Rel const *)(rel_entsize + (void *)relp); | 
|  | 401 | } | 
|  | 402 | } | 
|  | 403 |  | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 404 |  | 
|  | 405 | /* | 
|  | 406 | * Find a symbol in the given section, to be used as the base for relocating | 
|  | 407 | * the table of offsets of calls to mcount.  A local or global symbol suffices, | 
|  | 408 | * but avoid a Weak symbol because it may be overridden; the change in value | 
|  | 409 | * would invalidate the relocations of the offsets of the calls to mcount. | 
|  | 410 | * Often the found symbol will be the unnamed local symbol generated by | 
|  | 411 | * GNU 'as' for the start of each section.  For example: | 
|  | 412 | *    Num:    Value  Size Type    Bind   Vis      Ndx Name | 
|  | 413 | *      2: 00000000     0 SECTION LOCAL  DEFAULT    1 | 
|  | 414 | */ | 
|  | 415 | static unsigned find_secsym_ndx(unsigned const txtndx, | 
|  | 416 | char const *const txtname, | 
|  | 417 | uint_t *const recvalp, | 
|  | 418 | Elf_Shdr const *const symhdr, | 
|  | 419 | Elf_Ehdr const *const ehdr) | 
|  | 420 | { | 
|  | 421 | Elf_Sym const *const sym0 = (Elf_Sym const *)(_w(symhdr->sh_offset) | 
|  | 422 | + (void *)ehdr); | 
|  | 423 | unsigned const nsym = _w(symhdr->sh_size) / _w(symhdr->sh_entsize); | 
|  | 424 | Elf_Sym const *symp; | 
|  | 425 | unsigned t; | 
|  | 426 |  | 
|  | 427 | for (symp = sym0, t = nsym; t; --t, ++symp) { | 
|  | 428 | unsigned int const st_bind = ELF_ST_BIND(symp->st_info); | 
|  | 429 |  | 
|  | 430 | if (txtndx == w2(symp->st_shndx) | 
|  | 431 | /* avoid STB_WEAK */ | 
|  | 432 | && (STB_LOCAL == st_bind || STB_GLOBAL == st_bind)) { | 
| Rabin Vincent | 9905ce8 | 2011-05-11 22:53:51 +0530 | [diff] [blame] | 433 | /* function symbols on ARM have quirks, avoid them */ | 
|  | 434 | if (w2(ehdr->e_machine) == EM_ARM | 
|  | 435 | && ELF_ST_TYPE(symp->st_info) == STT_FUNC) | 
|  | 436 | continue; | 
|  | 437 |  | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 438 | *recvalp = _w(symp->st_value); | 
|  | 439 | return symp - sym0; | 
|  | 440 | } | 
|  | 441 | } | 
|  | 442 | fprintf(stderr, "Cannot find symbol for section %d: %s.\n", | 
|  | 443 | txtndx, txtname); | 
|  | 444 | fail_file(); | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 |  | 
|  | 448 | /* Evade ISO C restriction: no declaration after statement in has_rel_mcount. */ | 
|  | 449 | static char const * | 
|  | 450 | __has_rel_mcount(Elf_Shdr const *const relhdr,  /* is SHT_REL or SHT_RELA */ | 
|  | 451 | Elf_Shdr const *const shdr0, | 
|  | 452 | char const *const shstrtab, | 
|  | 453 | char const *const fname) | 
|  | 454 | { | 
|  | 455 | /* .sh_info depends on .sh_type == SHT_REL[,A] */ | 
|  | 456 | Elf_Shdr const *const txthdr = &shdr0[w(relhdr->sh_info)]; | 
|  | 457 | char const *const txtname = &shstrtab[w(txthdr->sh_name)]; | 
|  | 458 |  | 
| Steven Rostedt | dd5477f | 2011-04-06 13:21:17 -0400 | [diff] [blame] | 459 | if (strcmp("__mcount_loc", txtname) == 0) { | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 460 | fprintf(stderr, "warning: __mcount_loc already exists: %s\n", | 
|  | 461 | fname); | 
|  | 462 | succeed_file(); | 
|  | 463 | } | 
| Steven Rostedt | dd5477f | 2011-04-06 13:21:17 -0400 | [diff] [blame] | 464 | if (w(txthdr->sh_type) != SHT_PROGBITS || | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 465 | !(w(txthdr->sh_flags) & SHF_EXECINSTR)) | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 466 | return NULL; | 
|  | 467 | return txtname; | 
|  | 468 | } | 
|  | 469 |  | 
|  | 470 | static char const *has_rel_mcount(Elf_Shdr const *const relhdr, | 
|  | 471 | Elf_Shdr const *const shdr0, | 
|  | 472 | char const *const shstrtab, | 
|  | 473 | char const *const fname) | 
|  | 474 | { | 
| Steven Rostedt | dd5477f | 2011-04-06 13:21:17 -0400 | [diff] [blame] | 475 | if (w(relhdr->sh_type) != SHT_REL && w(relhdr->sh_type) != SHT_RELA) | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 476 | return NULL; | 
|  | 477 | return __has_rel_mcount(relhdr, shdr0, shstrtab, fname); | 
|  | 478 | } | 
|  | 479 |  | 
|  | 480 |  | 
|  | 481 | static unsigned tot_relsize(Elf_Shdr const *const shdr0, | 
|  | 482 | unsigned nhdr, | 
|  | 483 | const char *const shstrtab, | 
|  | 484 | const char *const fname) | 
|  | 485 | { | 
|  | 486 | unsigned totrelsz = 0; | 
|  | 487 | Elf_Shdr const *shdrp = shdr0; | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 488 | char const *txtname; | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 489 |  | 
|  | 490 | for (; nhdr; --nhdr, ++shdrp) { | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 491 | txtname = has_rel_mcount(shdrp, shdr0, shstrtab, fname); | 
|  | 492 | if (txtname && is_mcounted_section_name(txtname)) | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 493 | totrelsz += _w(shdrp->sh_size); | 
|  | 494 | } | 
|  | 495 | return totrelsz; | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 |  | 
|  | 499 | /* Overall supervision for Elf32 ET_REL file. */ | 
|  | 500 | static void | 
|  | 501 | do_func(Elf_Ehdr *const ehdr, char const *const fname, unsigned const reltype) | 
|  | 502 | { | 
|  | 503 | Elf_Shdr *const shdr0 = (Elf_Shdr *)(_w(ehdr->e_shoff) | 
|  | 504 | + (void *)ehdr); | 
|  | 505 | unsigned const nhdr = w2(ehdr->e_shnum); | 
|  | 506 | Elf_Shdr *const shstr = &shdr0[w2(ehdr->e_shstrndx)]; | 
|  | 507 | char const *const shstrtab = (char const *)(_w(shstr->sh_offset) | 
|  | 508 | + (void *)ehdr); | 
|  | 509 |  | 
|  | 510 | Elf_Shdr const *relhdr; | 
|  | 511 | unsigned k; | 
|  | 512 |  | 
|  | 513 | /* Upper bound on space: assume all relevant relocs are for mcount. */ | 
|  | 514 | unsigned const totrelsz = tot_relsize(shdr0, nhdr, shstrtab, fname); | 
|  | 515 | Elf_Rel *const mrel0 = umalloc(totrelsz); | 
|  | 516 | Elf_Rel *      mrelp = mrel0; | 
|  | 517 |  | 
|  | 518 | /* 2*sizeof(address) <= sizeof(Elf_Rel) */ | 
|  | 519 | uint_t *const mloc0 = umalloc(totrelsz>>1); | 
|  | 520 | uint_t *      mlocp = mloc0; | 
|  | 521 |  | 
|  | 522 | unsigned rel_entsize = 0; | 
|  | 523 | unsigned symsec_sh_link = 0; | 
|  | 524 |  | 
|  | 525 | for (relhdr = shdr0, k = nhdr; k; --k, ++relhdr) { | 
|  | 526 | char const *const txtname = has_rel_mcount(relhdr, shdr0, | 
|  | 527 | shstrtab, fname); | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 528 | if (txtname && is_mcounted_section_name(txtname)) { | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 529 | uint_t recval = 0; | 
|  | 530 | unsigned const recsym = find_secsym_ndx( | 
|  | 531 | w(relhdr->sh_info), txtname, &recval, | 
|  | 532 | &shdr0[symsec_sh_link = w(relhdr->sh_link)], | 
|  | 533 | ehdr); | 
|  | 534 |  | 
|  | 535 | rel_entsize = _w(relhdr->sh_entsize); | 
|  | 536 | mlocp = sift_rel_mcount(mlocp, | 
|  | 537 | (void *)mlocp - (void *)mloc0, &mrelp, | 
|  | 538 | relhdr, ehdr, recsym, recval, reltype); | 
| Steven Rostedt | dfad3d5 | 2011-04-12 18:53:25 -0400 | [diff] [blame] | 539 | } else if (txtname && (warn_on_notrace_sect || make_nop)) { | 
| Steven Rostedt | ffd618f | 2011-04-08 03:58:48 -0400 | [diff] [blame] | 540 | /* | 
|  | 541 | * This section is ignored by ftrace, but still | 
|  | 542 | * has mcount calls. Convert them to nops now. | 
|  | 543 | */ | 
| Steven Rostedt | dfad3d5 | 2011-04-12 18:53:25 -0400 | [diff] [blame] | 544 | nop_mcount(relhdr, ehdr, txtname); | 
| Steven Rostedt | c28d507 | 2010-10-13 19:06:14 -0400 | [diff] [blame] | 545 | } | 
|  | 546 | } | 
|  | 547 | if (mloc0 != mlocp) { | 
|  | 548 | append_func(ehdr, shstr, mloc0, mlocp, mrel0, mrelp, | 
|  | 549 | rel_entsize, symsec_sh_link); | 
|  | 550 | } | 
|  | 551 | free(mrel0); | 
|  | 552 | free(mloc0); | 
|  | 553 | } |