xref: /openbmc/linux/scripts/recordmcount.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
14317cf95SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
281d3858dSJohn Reiser /*
381d3858dSJohn Reiser  * recordmcount.c: construct a table of the locations of calls to 'mcount'
481d3858dSJohn Reiser  * so that ftrace can find them quickly.
581d3858dSJohn Reiser  * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
681d3858dSJohn Reiser  *
781d3858dSJohn Reiser  * Restructured to fit Linux format, as well as other updates:
881d3858dSJohn Reiser  *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
981d3858dSJohn Reiser  */
1081d3858dSJohn Reiser 
1181d3858dSJohn Reiser /*
1281d3858dSJohn Reiser  * Strategy: alter the .o file in-place.
1381d3858dSJohn Reiser  *
1481d3858dSJohn Reiser  * Append a new STRTAB that has the new section names, followed by a new array
1581d3858dSJohn Reiser  * ElfXX_Shdr[] that has the new section headers, followed by the section
1681d3858dSJohn Reiser  * contents for __mcount_loc and its relocations.  The old shstrtab strings,
1781d3858dSJohn Reiser  * and the old ElfXX_Shdr[] array, remain as "garbage" (commonly, a couple
1881d3858dSJohn Reiser  * kilobytes.)  Subsequent processing by /bin/ld (or the kernel module loader)
1981d3858dSJohn Reiser  * will ignore the garbage regions, because they are not designated by the
2081d3858dSJohn Reiser  * new .e_shoff nor the new ElfXX_Shdr[].  [In order to remove the garbage,
2181d3858dSJohn Reiser  * then use "ld -r" to create a new file that omits the garbage.]
2281d3858dSJohn Reiser  */
2381d3858dSJohn Reiser 
2481d3858dSJohn Reiser #include <sys/types.h>
2581d3858dSJohn Reiser #include <sys/mman.h>
2681d3858dSJohn Reiser #include <sys/stat.h>
27dfad3d59SSteven Rostedt #include <getopt.h>
2881d3858dSJohn Reiser #include <elf.h>
2981d3858dSJohn Reiser #include <fcntl.h>
3081d3858dSJohn Reiser #include <stdio.h>
3181d3858dSJohn Reiser #include <stdlib.h>
3281d3858dSJohn Reiser #include <string.h>
3381d3858dSJohn Reiser #include <unistd.h>
3481d3858dSJohn Reiser 
35af64d2aaSAKASHI Takahiro #ifndef EM_AARCH64
36af64d2aaSAKASHI Takahiro #define EM_AARCH64	183
372ee8a74fSLi Bin #define R_AARCH64_NONE		0
38af64d2aaSAKASHI Takahiro #define R_AARCH64_ABS64	257
39af64d2aaSAKASHI Takahiro #endif
40af64d2aaSAKASHI Takahiro 
41a0a458fbSQing Zhang #ifndef EM_LOONGARCH
42a0a458fbSQing Zhang #define EM_LOONGARCH		258
43a0a458fbSQing Zhang #define R_LARCH_32			1
44a0a458fbSQing Zhang #define R_LARCH_64			2
45a0a458fbSQing Zhang #define R_LARCH_MARK_LA			20
46a0a458fbSQing Zhang #define R_LARCH_SOP_PUSH_PLT_PCREL	29
47a0a458fbSQing Zhang #endif
48a0a458fbSQing Zhang 
49927d780eSAlex Sverdlin #define R_ARM_PC24		1
50927d780eSAlex Sverdlin #define R_ARM_THM_CALL		10
51927d780eSAlex Sverdlin #define R_ARM_CALL		28
52927d780eSAlex Sverdlin 
533df14264SChristophe Leroy #define R_AARCH64_CALL26	283
543df14264SChristophe Leroy 
5581d3858dSJohn Reiser static int fd_map;	/* File descriptor for file being modified. */
5681d3858dSJohn Reiser static int mmap_failed; /* Boolean flag. */
5781d3858dSJohn Reiser static char gpfx;	/* prefix for global symbol name (sometimes '_') */
5881d3858dSJohn Reiser static struct stat sb;	/* Remember .st_size, etc. */
59ed60453fSRabin Vincent static const char *altmcount;	/* alternate mcount symbol name */
60dfad3d59SSteven Rostedt static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
61a50bd439SSteven Rostedt (Red Hat) static void *file_map;	/* pointer of the mapped file */
62a50bd439SSteven Rostedt (Red Hat) static void *file_end;	/* pointer to the end of the mapped file */
63a50bd439SSteven Rostedt (Red Hat) static int file_updated; /* flag to state file was changed */
64a50bd439SSteven Rostedt (Red Hat) static void *file_ptr;	/* current file pointer location */
654fbcf074SMatt Helsley 
66a50bd439SSteven Rostedt (Red Hat) static void *file_append; /* added to the end of the file */
67a50bd439SSteven Rostedt (Red Hat) static size_t file_append_size; /* how much is added to end of file */
6881d3858dSJohn Reiser 
6981d3858dSJohn Reiser /* Per-file resource cleanup when multiple files. */
file_append_cleanup(void)704fbcf074SMatt Helsley static void file_append_cleanup(void)
714fbcf074SMatt Helsley {
724fbcf074SMatt Helsley 	free(file_append);
734fbcf074SMatt Helsley 	file_append = NULL;
744fbcf074SMatt Helsley 	file_append_size = 0;
754fbcf074SMatt Helsley 	file_updated = 0;
764fbcf074SMatt Helsley }
774fbcf074SMatt Helsley 
mmap_cleanup(void)784fbcf074SMatt Helsley static void mmap_cleanup(void)
7981d3858dSJohn Reiser {
8081d3858dSJohn Reiser 	if (!mmap_failed)
81a50bd439SSteven Rostedt (Red Hat) 		munmap(file_map, sb.st_size);
8281d3858dSJohn Reiser 	else
83a50bd439SSteven Rostedt (Red Hat) 		free(file_map);
84a50bd439SSteven Rostedt (Red Hat) 	file_map = NULL;
8581d3858dSJohn Reiser }
8681d3858dSJohn Reiser 
87a1462079SMatt Helsley /* ulseek, uwrite, ...:  Check return value for errors. */
8881d3858dSJohn Reiser 
ulseek(off_t const offset,int const whence)893aec8638SMatt Helsley static off_t ulseek(off_t const offset, int const whence)
9081d3858dSJohn Reiser {
91a50bd439SSteven Rostedt (Red Hat) 	switch (whence) {
92a50bd439SSteven Rostedt (Red Hat) 	case SEEK_SET:
93a50bd439SSteven Rostedt (Red Hat) 		file_ptr = file_map + offset;
94a50bd439SSteven Rostedt (Red Hat) 		break;
95a50bd439SSteven Rostedt (Red Hat) 	case SEEK_CUR:
96a50bd439SSteven Rostedt (Red Hat) 		file_ptr += offset;
97a50bd439SSteven Rostedt (Red Hat) 		break;
98a50bd439SSteven Rostedt (Red Hat) 	case SEEK_END:
99a50bd439SSteven Rostedt (Red Hat) 		file_ptr = file_map + (sb.st_size - offset);
100a50bd439SSteven Rostedt (Red Hat) 		break;
101a50bd439SSteven Rostedt (Red Hat) 	}
102a50bd439SSteven Rostedt (Red Hat) 	if (file_ptr < file_map) {
103a50bd439SSteven Rostedt (Red Hat) 		fprintf(stderr, "lseek: seek before file\n");
1043f1df120SMatt Helsley 		return -1;
10581d3858dSJohn Reiser 	}
106a50bd439SSteven Rostedt (Red Hat) 	return file_ptr - file_map;
10781d3858dSJohn Reiser }
10881d3858dSJohn Reiser 
uwrite(void const * const buf,size_t const count)1093aec8638SMatt Helsley static ssize_t uwrite(void const *const buf, size_t const count)
11081d3858dSJohn Reiser {
111a50bd439SSteven Rostedt (Red Hat) 	size_t cnt = count;
112a50bd439SSteven Rostedt (Red Hat) 	off_t idx = 0;
113*fa359d06SHao Zeng 	void *p = NULL;
114a50bd439SSteven Rostedt (Red Hat) 
115a50bd439SSteven Rostedt (Red Hat) 	file_updated = 1;
116a50bd439SSteven Rostedt (Red Hat) 
117a50bd439SSteven Rostedt (Red Hat) 	if (file_ptr + count >= file_end) {
118a50bd439SSteven Rostedt (Red Hat) 		off_t aoffset = (file_ptr + count) - file_end;
119a50bd439SSteven Rostedt (Red Hat) 
120a50bd439SSteven Rostedt (Red Hat) 		if (aoffset > file_append_size) {
121*fa359d06SHao Zeng 			p = realloc(file_append, aoffset);
122*fa359d06SHao Zeng 			if (!p)
123*fa359d06SHao Zeng 				free(file_append);
124*fa359d06SHao Zeng 			file_append = p;
125a50bd439SSteven Rostedt (Red Hat) 			file_append_size = aoffset;
126a50bd439SSteven Rostedt (Red Hat) 		}
127a50bd439SSteven Rostedt (Red Hat) 		if (!file_append) {
12881d3858dSJohn Reiser 			perror("write");
1294fbcf074SMatt Helsley 			file_append_cleanup();
1304fbcf074SMatt Helsley 			mmap_cleanup();
1313f1df120SMatt Helsley 			return -1;
13281d3858dSJohn Reiser 		}
133a50bd439SSteven Rostedt (Red Hat) 		if (file_ptr < file_end) {
134a50bd439SSteven Rostedt (Red Hat) 			cnt = file_end - file_ptr;
135a50bd439SSteven Rostedt (Red Hat) 		} else {
136a50bd439SSteven Rostedt (Red Hat) 			cnt = 0;
137a50bd439SSteven Rostedt (Red Hat) 			idx = aoffset - count;
138a50bd439SSteven Rostedt (Red Hat) 		}
139a50bd439SSteven Rostedt (Red Hat) 	}
140a50bd439SSteven Rostedt (Red Hat) 
141a50bd439SSteven Rostedt (Red Hat) 	if (cnt)
142a50bd439SSteven Rostedt (Red Hat) 		memcpy(file_ptr, buf, cnt);
143a50bd439SSteven Rostedt (Red Hat) 
144a50bd439SSteven Rostedt (Red Hat) 	if (cnt < count)
145a50bd439SSteven Rostedt (Red Hat) 		memcpy(file_append + idx, buf + cnt, count - cnt);
146a50bd439SSteven Rostedt (Red Hat) 
147a50bd439SSteven Rostedt (Red Hat) 	file_ptr += count;
148a50bd439SSteven Rostedt (Red Hat) 	return count;
14981d3858dSJohn Reiser }
15081d3858dSJohn Reiser 
umalloc(size_t size)1513aec8638SMatt Helsley static void * umalloc(size_t size)
15281d3858dSJohn Reiser {
15381d3858dSJohn Reiser 	void *const addr = malloc(size);
154dd5477ffSSteven Rostedt 	if (addr == 0) {
15581d3858dSJohn Reiser 		fprintf(stderr, "malloc failed: %zu bytes\n", size);
1564fbcf074SMatt Helsley 		file_append_cleanup();
1574fbcf074SMatt Helsley 		mmap_cleanup();
1583f1df120SMatt Helsley 		return NULL;
15981d3858dSJohn Reiser 	}
16081d3858dSJohn Reiser 	return addr;
16181d3858dSJohn Reiser }
16281d3858dSJohn Reiser 
1634fbcf074SMatt Helsley /*
1644fbcf074SMatt Helsley  * Get the whole file as a programming convenience in order to avoid
1654fbcf074SMatt Helsley  * malloc+lseek+read+free of many pieces.  If successful, then mmap
1664fbcf074SMatt Helsley  * avoids copying unused pieces; else just read the whole file.
1674fbcf074SMatt Helsley  * Open for both read and write; new info will be appended to the file.
1684fbcf074SMatt Helsley  * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr
1694fbcf074SMatt Helsley  * do not propagate to the file until an explicit overwrite at the last.
1704fbcf074SMatt Helsley  * This preserves most aspects of consistency (all except .st_size)
1714fbcf074SMatt Helsley  * for simultaneous readers of the file while we are appending to it.
1724fbcf074SMatt Helsley  * However, multiple writers still are bad.  We choose not to use
1734fbcf074SMatt Helsley  * locking because it is expensive and the use case of kernel build
1744fbcf074SMatt Helsley  * makes multiple writers unlikely.
1754fbcf074SMatt Helsley  */
mmap_file(char const * fname)1764fbcf074SMatt Helsley static void *mmap_file(char const *fname)
1774fbcf074SMatt Helsley {
1784fbcf074SMatt Helsley 	/* Avoid problems if early cleanup() */
1794fbcf074SMatt Helsley 	fd_map = -1;
1804fbcf074SMatt Helsley 	mmap_failed = 1;
1814fbcf074SMatt Helsley 	file_map = NULL;
1824fbcf074SMatt Helsley 	file_ptr = NULL;
1834fbcf074SMatt Helsley 	file_updated = 0;
1844fbcf074SMatt Helsley 	sb.st_size = 0;
1854fbcf074SMatt Helsley 
1864fbcf074SMatt Helsley 	fd_map = open(fname, O_RDONLY);
1874fbcf074SMatt Helsley 	if (fd_map < 0) {
1884fbcf074SMatt Helsley 		perror(fname);
1894fbcf074SMatt Helsley 		return NULL;
1904fbcf074SMatt Helsley 	}
1914fbcf074SMatt Helsley 	if (fstat(fd_map, &sb) < 0) {
1924fbcf074SMatt Helsley 		perror(fname);
1934fbcf074SMatt Helsley 		goto out;
1944fbcf074SMatt Helsley 	}
1954fbcf074SMatt Helsley 	if (!S_ISREG(sb.st_mode)) {
1964fbcf074SMatt Helsley 		fprintf(stderr, "not a regular file: %s\n", fname);
1974fbcf074SMatt Helsley 		goto out;
1984fbcf074SMatt Helsley 	}
1994fbcf074SMatt Helsley 	file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
2004fbcf074SMatt Helsley 			fd_map, 0);
2014fbcf074SMatt Helsley 	if (file_map == MAP_FAILED) {
2024fbcf074SMatt Helsley 		mmap_failed = 1;
2034fbcf074SMatt Helsley 		file_map = umalloc(sb.st_size);
2044fbcf074SMatt Helsley 		if (!file_map) {
2054fbcf074SMatt Helsley 			perror(fname);
2064fbcf074SMatt Helsley 			goto out;
2074fbcf074SMatt Helsley 		}
2084fbcf074SMatt Helsley 		if (read(fd_map, file_map, sb.st_size) != sb.st_size) {
2094fbcf074SMatt Helsley 			perror(fname);
2104fbcf074SMatt Helsley 			free(file_map);
2114fbcf074SMatt Helsley 			file_map = NULL;
2124fbcf074SMatt Helsley 			goto out;
2134fbcf074SMatt Helsley 		}
2144fbcf074SMatt Helsley 	} else
2154fbcf074SMatt Helsley 		mmap_failed = 0;
2164fbcf074SMatt Helsley out:
2174fbcf074SMatt Helsley 	close(fd_map);
2184fbcf074SMatt Helsley 	fd_map = -1;
2194fbcf074SMatt Helsley 
2204fbcf074SMatt Helsley 	file_end = file_map + sb.st_size;
2214fbcf074SMatt Helsley 
2224fbcf074SMatt Helsley 	return file_map;
2234fbcf074SMatt Helsley }
2244fbcf074SMatt Helsley 
2254fbcf074SMatt Helsley 
226ffd618faSSteven Rostedt static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
227ffd618faSSteven Rostedt static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
228ffd618faSSteven Rostedt static unsigned char *ideal_nop;
229ffd618faSSteven Rostedt 
230ffd618faSSteven Rostedt static char rel_type_nop;
231ffd618faSSteven Rostedt 
232ffd618faSSteven Rostedt static int (*make_nop)(void *map, size_t const offset);
233ffd618faSSteven Rostedt 
make_nop_x86(void * map,size_t const offset)234ffd618faSSteven Rostedt static int make_nop_x86(void *map, size_t const offset)
235ffd618faSSteven Rostedt {
236ffd618faSSteven Rostedt 	uint32_t *ptr;
237ffd618faSSteven Rostedt 	unsigned char *op;
238ffd618faSSteven Rostedt 
239ffd618faSSteven Rostedt 	/* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */
240ffd618faSSteven Rostedt 	ptr = map + offset;
241ffd618faSSteven Rostedt 	if (*ptr != 0)
242ffd618faSSteven Rostedt 		return -1;
243ffd618faSSteven Rostedt 
244ffd618faSSteven Rostedt 	op = map + offset - 1;
245ffd618faSSteven Rostedt 	if (*op != 0xe8)
246ffd618faSSteven Rostedt 		return -1;
247ffd618faSSteven Rostedt 
248ffd618faSSteven Rostedt 	/* convert to nop */
2493f1df120SMatt Helsley 	if (ulseek(offset - 1, SEEK_SET) < 0)
2503f1df120SMatt Helsley 		return -1;
2513f1df120SMatt Helsley 	if (uwrite(ideal_nop, 5) < 0)
2523f1df120SMatt Helsley 		return -1;
253ffd618faSSteven Rostedt 	return 0;
254ffd618faSSteven Rostedt }
255ffd618faSSteven Rostedt 
2569648dc15SStephen Boyd static unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
2579648dc15SStephen Boyd static unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
2589648dc15SStephen Boyd static unsigned char *ideal_nop4_arm;
2599648dc15SStephen Boyd 
2609648dc15SStephen Boyd static unsigned char bl_mcount_arm_le[4] = { 0xfe, 0xff, 0xff, 0xeb }; /* bl */
2619648dc15SStephen Boyd static unsigned char bl_mcount_arm_be[4] = { 0xeb, 0xff, 0xff, 0xfe }; /* bl */
2629648dc15SStephen Boyd static unsigned char *bl_mcount_arm;
2639648dc15SStephen Boyd 
2649648dc15SStephen Boyd static unsigned char push_arm_le[4] = { 0x04, 0xe0, 0x2d, 0xe5 }; /* push {lr} */
2659648dc15SStephen Boyd static unsigned char push_arm_be[4] = { 0xe5, 0x2d, 0xe0, 0x04 }; /* push {lr} */
2669648dc15SStephen Boyd static unsigned char *push_arm;
2679648dc15SStephen Boyd 
2689648dc15SStephen Boyd static unsigned char ideal_nop2_thumb_le[2] = { 0x00, 0xbf }; /* nop */
2699648dc15SStephen Boyd static unsigned char ideal_nop2_thumb_be[2] = { 0xbf, 0x00 }; /* nop */
2709648dc15SStephen Boyd static unsigned char *ideal_nop2_thumb;
2719648dc15SStephen Boyd 
2729648dc15SStephen Boyd static unsigned char push_bl_mcount_thumb_le[6] = { 0x00, 0xb5, 0xff, 0xf7, 0xfe, 0xff }; /* push {lr}, bl */
2739648dc15SStephen Boyd static unsigned char push_bl_mcount_thumb_be[6] = { 0xb5, 0x00, 0xf7, 0xff, 0xff, 0xfe }; /* push {lr}, bl */
2749648dc15SStephen Boyd static unsigned char *push_bl_mcount_thumb;
2759648dc15SStephen Boyd 
make_nop_arm(void * map,size_t const offset)2769648dc15SStephen Boyd static int make_nop_arm(void *map, size_t const offset)
2779648dc15SStephen Boyd {
2789648dc15SStephen Boyd 	char *ptr;
2799648dc15SStephen Boyd 	int cnt = 1;
2809648dc15SStephen Boyd 	int nop_size;
2819648dc15SStephen Boyd 	size_t off = offset;
2829648dc15SStephen Boyd 
2839648dc15SStephen Boyd 	ptr = map + offset;
2849648dc15SStephen Boyd 	if (memcmp(ptr, bl_mcount_arm, 4) == 0) {
2859648dc15SStephen Boyd 		if (memcmp(ptr - 4, push_arm, 4) == 0) {
2869648dc15SStephen Boyd 			off -= 4;
2879648dc15SStephen Boyd 			cnt = 2;
2889648dc15SStephen Boyd 		}
2899648dc15SStephen Boyd 		ideal_nop = ideal_nop4_arm;
2909648dc15SStephen Boyd 		nop_size = 4;
2919648dc15SStephen Boyd 	} else if (memcmp(ptr - 2, push_bl_mcount_thumb, 6) == 0) {
2929648dc15SStephen Boyd 		cnt = 3;
2939648dc15SStephen Boyd 		nop_size = 2;
2949648dc15SStephen Boyd 		off -= 2;
2959648dc15SStephen Boyd 		ideal_nop = ideal_nop2_thumb;
2969648dc15SStephen Boyd 	} else
2979648dc15SStephen Boyd 		return -1;
2989648dc15SStephen Boyd 
2999648dc15SStephen Boyd 	/* Convert to nop */
3003f1df120SMatt Helsley 	if (ulseek(off, SEEK_SET) < 0)
3013f1df120SMatt Helsley 		return -1;
3029648dc15SStephen Boyd 
3039648dc15SStephen Boyd 	do {
3043f1df120SMatt Helsley 		if (uwrite(ideal_nop, nop_size) < 0)
3053f1df120SMatt Helsley 			return -1;
3069648dc15SStephen Boyd 	} while (--cnt > 0);
3079648dc15SStephen Boyd 
3089648dc15SStephen Boyd 	return 0;
3099648dc15SStephen Boyd }
3109648dc15SStephen Boyd 
3112ee8a74fSLi Bin static unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
make_nop_arm64(void * map,size_t const offset)3122ee8a74fSLi Bin static int make_nop_arm64(void *map, size_t const offset)
3132ee8a74fSLi Bin {
3142ee8a74fSLi Bin 	uint32_t *ptr;
3152ee8a74fSLi Bin 
3162ee8a74fSLi Bin 	ptr = map + offset;
3172ee8a74fSLi Bin 	/* bl <_mcount> is 0x94000000 before relocation */
3182ee8a74fSLi Bin 	if (*ptr != 0x94000000)
3192ee8a74fSLi Bin 		return -1;
3202ee8a74fSLi Bin 
3212ee8a74fSLi Bin 	/* Convert to nop */
3223f1df120SMatt Helsley 	if (ulseek(offset, SEEK_SET) < 0)
3233f1df120SMatt Helsley 		return -1;
3243f1df120SMatt Helsley 	if (uwrite(ideal_nop, 4) < 0)
3253f1df120SMatt Helsley 		return -1;
3262ee8a74fSLi Bin 	return 0;
3272ee8a74fSLi Bin }
3282ee8a74fSLi Bin 
write_file(const char * fname)3293f1df120SMatt Helsley static int write_file(const char *fname)
330a50bd439SSteven Rostedt (Red Hat) {
331a50bd439SSteven Rostedt (Red Hat) 	char tmp_file[strlen(fname) + 4];
332a50bd439SSteven Rostedt (Red Hat) 	size_t n;
333a50bd439SSteven Rostedt (Red Hat) 
334a50bd439SSteven Rostedt (Red Hat) 	if (!file_updated)
3353f1df120SMatt Helsley 		return 0;
336a50bd439SSteven Rostedt (Red Hat) 
337a50bd439SSteven Rostedt (Red Hat) 	sprintf(tmp_file, "%s.rc", fname);
338a50bd439SSteven Rostedt (Red Hat) 
339a50bd439SSteven Rostedt (Red Hat) 	/*
340a50bd439SSteven Rostedt (Red Hat) 	 * After reading the entire file into memory, delete it
341a50bd439SSteven Rostedt (Red Hat) 	 * and write it back, to prevent weird side effects of modifying
342a50bd439SSteven Rostedt (Red Hat) 	 * an object file in place.
343a50bd439SSteven Rostedt (Red Hat) 	 */
344a50bd439SSteven Rostedt (Red Hat) 	fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
345dd39a265SRussell King 	if (fd_map < 0) {
346dd39a265SRussell King 		perror(fname);
3473f1df120SMatt Helsley 		return -1;
348dd39a265SRussell King 	}
349a50bd439SSteven Rostedt (Red Hat) 	n = write(fd_map, file_map, sb.st_size);
350a50bd439SSteven Rostedt (Red Hat) 	if (n != sb.st_size) {
351a50bd439SSteven Rostedt (Red Hat) 		perror("write");
3523f1df120SMatt Helsley 		close(fd_map);
3533f1df120SMatt Helsley 		return -1;
354dd39a265SRussell King 	}
355a50bd439SSteven Rostedt (Red Hat) 	if (file_append_size) {
356a50bd439SSteven Rostedt (Red Hat) 		n = write(fd_map, file_append, file_append_size);
357a50bd439SSteven Rostedt (Red Hat) 		if (n != file_append_size) {
358a50bd439SSteven Rostedt (Red Hat) 			perror("write");
3593f1df120SMatt Helsley 			close(fd_map);
3603f1df120SMatt Helsley 			return -1;
361a50bd439SSteven Rostedt (Red Hat) 		}
362a50bd439SSteven Rostedt (Red Hat) 	}
363a50bd439SSteven Rostedt (Red Hat) 	close(fd_map);
364a50bd439SSteven Rostedt (Red Hat) 	if (rename(tmp_file, fname) < 0) {
365a50bd439SSteven Rostedt (Red Hat) 		perror(fname);
3663f1df120SMatt Helsley 		return -1;
367a50bd439SSteven Rostedt (Red Hat) 	}
3683f1df120SMatt Helsley 	return 0;
36981d3858dSJohn Reiser }
37081d3858dSJohn Reiser 
37181d3858dSJohn Reiser /* w8rev, w8nat, ...: Handle endianness. */
37281d3858dSJohn Reiser 
w8rev(uint64_t const x)37381d3858dSJohn Reiser static uint64_t w8rev(uint64_t const x)
37481d3858dSJohn Reiser {
37581d3858dSJohn Reiser 	return   ((0xff & (x >> (0 * 8))) << (7 * 8))
37681d3858dSJohn Reiser 	       | ((0xff & (x >> (1 * 8))) << (6 * 8))
37781d3858dSJohn Reiser 	       | ((0xff & (x >> (2 * 8))) << (5 * 8))
37881d3858dSJohn Reiser 	       | ((0xff & (x >> (3 * 8))) << (4 * 8))
37981d3858dSJohn Reiser 	       | ((0xff & (x >> (4 * 8))) << (3 * 8))
38081d3858dSJohn Reiser 	       | ((0xff & (x >> (5 * 8))) << (2 * 8))
38181d3858dSJohn Reiser 	       | ((0xff & (x >> (6 * 8))) << (1 * 8))
38281d3858dSJohn Reiser 	       | ((0xff & (x >> (7 * 8))) << (0 * 8));
38381d3858dSJohn Reiser }
38481d3858dSJohn Reiser 
w4rev(uint32_t const x)38581d3858dSJohn Reiser static uint32_t w4rev(uint32_t const x)
38681d3858dSJohn Reiser {
38781d3858dSJohn Reiser 	return   ((0xff & (x >> (0 * 8))) << (3 * 8))
38881d3858dSJohn Reiser 	       | ((0xff & (x >> (1 * 8))) << (2 * 8))
38981d3858dSJohn Reiser 	       | ((0xff & (x >> (2 * 8))) << (1 * 8))
39081d3858dSJohn Reiser 	       | ((0xff & (x >> (3 * 8))) << (0 * 8));
39181d3858dSJohn Reiser }
39281d3858dSJohn Reiser 
w2rev(uint16_t const x)39381d3858dSJohn Reiser static uint32_t w2rev(uint16_t const x)
39481d3858dSJohn Reiser {
39581d3858dSJohn Reiser 	return   ((0xff & (x >> (0 * 8))) << (1 * 8))
39681d3858dSJohn Reiser 	       | ((0xff & (x >> (1 * 8))) << (0 * 8));
39781d3858dSJohn Reiser }
39881d3858dSJohn Reiser 
w8nat(uint64_t const x)39981d3858dSJohn Reiser static uint64_t w8nat(uint64_t const x)
40081d3858dSJohn Reiser {
40181d3858dSJohn Reiser 	return x;
40281d3858dSJohn Reiser }
40381d3858dSJohn Reiser 
w4nat(uint32_t const x)40481d3858dSJohn Reiser static uint32_t w4nat(uint32_t const x)
40581d3858dSJohn Reiser {
40681d3858dSJohn Reiser 	return x;
40781d3858dSJohn Reiser }
40881d3858dSJohn Reiser 
w2nat(uint16_t const x)40981d3858dSJohn Reiser static uint32_t w2nat(uint16_t const x)
41081d3858dSJohn Reiser {
41181d3858dSJohn Reiser 	return x;
41281d3858dSJohn Reiser }
41381d3858dSJohn Reiser 
41481d3858dSJohn Reiser static uint64_t (*w8)(uint64_t);
41581d3858dSJohn Reiser static uint32_t (*w)(uint32_t);
41681d3858dSJohn Reiser static uint32_t (*w2)(uint16_t);
41781d3858dSJohn Reiser 
41881d3858dSJohn Reiser /* Names of the sections that could contain calls to mcount. */
is_mcounted_section_name(char const * const txtname)4193aec8638SMatt Helsley static int is_mcounted_section_name(char const *const txtname)
42081d3858dSJohn Reiser {
4219c8e2f6dSJoe Lawrence 	return strncmp(".text",          txtname, 5) == 0 ||
42242c269c8SSteven Rostedt (VMware) 		strcmp(".init.text",     txtname) == 0 ||
423dd5477ffSSteven Rostedt 		strcmp(".ref.text",      txtname) == 0 ||
424dd5477ffSSteven Rostedt 		strcmp(".sched.text",    txtname) == 0 ||
425dd5477ffSSteven Rostedt 		strcmp(".spinlock.text", txtname) == 0 ||
426dd5477ffSSteven Rostedt 		strcmp(".irqentry.text", txtname) == 0 ||
427e436fd61SDmitry Vyukov 		strcmp(".softirqentry.text", txtname) == 0 ||
4289f087e76SSteven Rostedt 		strcmp(".kprobes.text", txtname) == 0 ||
4291bd95be2SMatt Helsley 		strcmp(".cpuidle.text", txtname) == 0;
43081d3858dSJohn Reiser }
43181d3858dSJohn Reiser 
4323f1df120SMatt Helsley static char const *already_has_rel_mcount = "success"; /* our work here is done! */
4333f1df120SMatt Helsley 
434c28d5077SSteven Rostedt /* 32 bit and 64 bit are very similar */
435c28d5077SSteven Rostedt #include "recordmcount.h"
436c28d5077SSteven Rostedt #define RECORD_MCOUNT_64
437c28d5077SSteven Rostedt #include "recordmcount.h"
43881d3858dSJohn Reiser 
arm_is_fake_mcount(Elf32_Rel const * rp)439927d780eSAlex Sverdlin static int arm_is_fake_mcount(Elf32_Rel const *rp)
440927d780eSAlex Sverdlin {
441927d780eSAlex Sverdlin 	switch (ELF32_R_TYPE(w(rp->r_info))) {
442927d780eSAlex Sverdlin 	case R_ARM_THM_CALL:
443927d780eSAlex Sverdlin 	case R_ARM_CALL:
444927d780eSAlex Sverdlin 	case R_ARM_PC24:
445927d780eSAlex Sverdlin 		return 0;
446927d780eSAlex Sverdlin 	}
447927d780eSAlex Sverdlin 
448927d780eSAlex Sverdlin 	return 1;
449927d780eSAlex Sverdlin }
450927d780eSAlex Sverdlin 
arm64_is_fake_mcount(Elf64_Rel const * rp)451ea0eada4SGregory Herrero static int arm64_is_fake_mcount(Elf64_Rel const *rp)
452ea0eada4SGregory Herrero {
453999340d5SChen Jun 	return ELF64_R_TYPE(w8(rp->r_info)) != R_AARCH64_CALL26;
454ea0eada4SGregory Herrero }
455ea0eada4SGregory Herrero 
LARCH32_is_fake_mcount(Elf32_Rel const * rp)456a0a458fbSQing Zhang static int LARCH32_is_fake_mcount(Elf32_Rel const *rp)
457a0a458fbSQing Zhang {
458a0a458fbSQing Zhang 	switch (ELF64_R_TYPE(w(rp->r_info))) {
459a0a458fbSQing Zhang 	case R_LARCH_MARK_LA:
460a0a458fbSQing Zhang 	case R_LARCH_SOP_PUSH_PLT_PCREL:
461a0a458fbSQing Zhang 		return 0;
462a0a458fbSQing Zhang 	}
463a0a458fbSQing Zhang 
464a0a458fbSQing Zhang 	return 1;
465a0a458fbSQing Zhang }
466a0a458fbSQing Zhang 
LARCH64_is_fake_mcount(Elf64_Rel const * rp)467a0a458fbSQing Zhang static int LARCH64_is_fake_mcount(Elf64_Rel const *rp)
468a0a458fbSQing Zhang {
469a0a458fbSQing Zhang 	switch (ELF64_R_TYPE(w(rp->r_info))) {
470a0a458fbSQing Zhang 	case R_LARCH_MARK_LA:
471a0a458fbSQing Zhang 	case R_LARCH_SOP_PUSH_PLT_PCREL:
472a0a458fbSQing Zhang 		return 0;
473a0a458fbSQing Zhang 	}
474a0a458fbSQing Zhang 
475a0a458fbSQing Zhang 	return 1;
476a0a458fbSQing Zhang }
477a0a458fbSQing Zhang 
478a2d49358SJohn Reiser /* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
479a2d49358SJohn Reiser  * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
480a2d49358SJohn Reiser  * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
481a2d49358SJohn Reiser  * to imply the order of the members; the spec does not say so.
482a2d49358SJohn Reiser  *	typedef unsigned char Elf64_Byte;
483a2d49358SJohn Reiser  * fails on MIPS64 because their <elf.h> already has it!
484a2d49358SJohn Reiser  */
485a2d49358SJohn Reiser 
486a2d49358SJohn Reiser typedef uint8_t myElf64_Byte;		/* Type for a 8-bit quantity.  */
487a2d49358SJohn Reiser 
488a2d49358SJohn Reiser union mips_r_info {
489a2d49358SJohn Reiser 	Elf64_Xword r_info;
490a2d49358SJohn Reiser 	struct {
491a2d49358SJohn Reiser 		Elf64_Word r_sym;		/* Symbol index.  */
492a2d49358SJohn Reiser 		myElf64_Byte r_ssym;		/* Special symbol.  */
493a2d49358SJohn Reiser 		myElf64_Byte r_type3;		/* Third relocation.  */
494a2d49358SJohn Reiser 		myElf64_Byte r_type2;		/* Second relocation.  */
495a2d49358SJohn Reiser 		myElf64_Byte r_type;		/* First relocation.  */
496a2d49358SJohn Reiser 	} r_mips;
497a2d49358SJohn Reiser };
498a2d49358SJohn Reiser 
MIPS64_r_sym(Elf64_Rel const * rp)499a2d49358SJohn Reiser static uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
500a2d49358SJohn Reiser {
501a2d49358SJohn Reiser 	return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
502a2d49358SJohn Reiser }
503a2d49358SJohn Reiser 
MIPS64_r_info(Elf64_Rel * const rp,unsigned sym,unsigned type)504a2d49358SJohn Reiser static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
505a2d49358SJohn Reiser {
506a2d49358SJohn Reiser 	rp->r_info = ((union mips_r_info){
507a2d49358SJohn Reiser 		.r_mips = { .r_sym = w(sym), .r_type = type }
508a2d49358SJohn Reiser 	}).r_info;
509a2d49358SJohn Reiser }
510a2d49358SJohn Reiser 
do_file(char const * const fname)5113aec8638SMatt Helsley static int do_file(char const *const fname)
51281d3858dSJohn Reiser {
51381d3858dSJohn Reiser 	unsigned int reltype = 0;
5144fbcf074SMatt Helsley 	Elf32_Ehdr *ehdr;
5153f1df120SMatt Helsley 	int rc = -1;
5163f1df120SMatt Helsley 
5174fbcf074SMatt Helsley 	ehdr = mmap_file(fname);
5183f1df120SMatt Helsley 	if (!ehdr)
5193f1df120SMatt Helsley 		goto out;
52081d3858dSJohn Reiser 
52181d3858dSJohn Reiser 	w = w4nat;
52281d3858dSJohn Reiser 	w2 = w2nat;
52381d3858dSJohn Reiser 	w8 = w8nat;
52481d3858dSJohn Reiser 	switch (ehdr->e_ident[EI_DATA]) {
52581d3858dSJohn Reiser 		static unsigned int const endian = 1;
526e90b0c8bSSteven Rostedt 	default:
52781d3858dSJohn Reiser 		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
52881d3858dSJohn Reiser 			ehdr->e_ident[EI_DATA], fname);
5293f1df120SMatt Helsley 		goto out;
530e90b0c8bSSteven Rostedt 	case ELFDATA2LSB:
531dd5477ffSSteven Rostedt 		if (*(unsigned char const *)&endian != 1) {
53281d3858dSJohn Reiser 			/* main() is big endian, file.o is little endian. */
53381d3858dSJohn Reiser 			w = w4rev;
53481d3858dSJohn Reiser 			w2 = w2rev;
53581d3858dSJohn Reiser 			w8 = w8rev;
53681d3858dSJohn Reiser 		}
5379648dc15SStephen Boyd 		ideal_nop4_arm = ideal_nop4_arm_le;
5389648dc15SStephen Boyd 		bl_mcount_arm = bl_mcount_arm_le;
5399648dc15SStephen Boyd 		push_arm = push_arm_le;
5409648dc15SStephen Boyd 		ideal_nop2_thumb = ideal_nop2_thumb_le;
5419648dc15SStephen Boyd 		push_bl_mcount_thumb = push_bl_mcount_thumb_le;
542e90b0c8bSSteven Rostedt 		break;
543e90b0c8bSSteven Rostedt 	case ELFDATA2MSB:
544dd5477ffSSteven Rostedt 		if (*(unsigned char const *)&endian != 0) {
54581d3858dSJohn Reiser 			/* main() is little endian, file.o is big endian. */
54681d3858dSJohn Reiser 			w = w4rev;
54781d3858dSJohn Reiser 			w2 = w2rev;
54881d3858dSJohn Reiser 			w8 = w8rev;
54981d3858dSJohn Reiser 		}
5509648dc15SStephen Boyd 		ideal_nop4_arm = ideal_nop4_arm_be;
5519648dc15SStephen Boyd 		bl_mcount_arm = bl_mcount_arm_be;
5529648dc15SStephen Boyd 		push_arm = push_arm_be;
5539648dc15SStephen Boyd 		ideal_nop2_thumb = ideal_nop2_thumb_be;
5549648dc15SStephen Boyd 		push_bl_mcount_thumb = push_bl_mcount_thumb_be;
555e90b0c8bSSteven Rostedt 		break;
55681d3858dSJohn Reiser 	}  /* end switch */
5572e63152bSMatt Helsley 	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
5582e63152bSMatt Helsley 	    w2(ehdr->e_type) != ET_REL ||
5592e63152bSMatt Helsley 	    ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
56081d3858dSJohn Reiser 		fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
5613f1df120SMatt Helsley 		goto out;
56281d3858dSJohn Reiser 	}
56381d3858dSJohn Reiser 
5642e63152bSMatt Helsley 	gpfx = '_';
56581d3858dSJohn Reiser 	switch (w2(ehdr->e_machine)) {
566e90b0c8bSSteven Rostedt 	default:
567ac5db1fcSnixiaoming 		fprintf(stderr, "unrecognized e_machine %u %s\n",
56881d3858dSJohn Reiser 			w2(ehdr->e_machine), fname);
5693f1df120SMatt Helsley 		goto out;
570ffd618faSSteven Rostedt 	case EM_386:
571ffd618faSSteven Rostedt 		reltype = R_386_32;
57246a2b61eSLi Bin 		rel_type_nop = R_386_NONE;
573ffd618faSSteven Rostedt 		make_nop = make_nop_x86;
574ffd618faSSteven Rostedt 		ideal_nop = ideal_nop5_x86_32;
575521ccb5cSMartin Schwidefsky 		mcount_adjust_32 = -1;
5762e63152bSMatt Helsley 		gpfx = 0;
577ffd618faSSteven Rostedt 		break;
5782e63152bSMatt Helsley 	case EM_ARM:
5792e63152bSMatt Helsley 		reltype = R_ARM_ABS32;
580ed60453fSRabin Vincent 		altmcount = "__gnu_mcount_nc";
5819648dc15SStephen Boyd 		make_nop = make_nop_arm;
5829648dc15SStephen Boyd 		rel_type_nop = R_ARM_NONE;
583927d780eSAlex Sverdlin 		is_fake_mcount32 = arm_is_fake_mcount;
5842e63152bSMatt Helsley 		gpfx = 0;
585ed60453fSRabin Vincent 		break;
586af64d2aaSAKASHI Takahiro 	case EM_AARCH64:
5872ee8a74fSLi Bin 		reltype = R_AARCH64_ABS64;
5882ee8a74fSLi Bin 		make_nop = make_nop_arm64;
5892ee8a74fSLi Bin 		rel_type_nop = R_AARCH64_NONE;
5902ee8a74fSLi Bin 		ideal_nop = ideal_nop4_arm64;
591ea0eada4SGregory Herrero 		is_fake_mcount64 = arm64_is_fake_mcount;
5922ee8a74fSLi Bin 		break;
5932e63152bSMatt Helsley 	case EM_IA_64:	reltype = R_IA64_IMM64; break;
5942e63152bSMatt Helsley 	case EM_MIPS:	/* reltype: e_class    */ break;
595a0a458fbSQing Zhang 	case EM_LOONGARCH:	/* reltype: e_class    */ break;
5962e63152bSMatt Helsley 	case EM_PPC:	reltype = R_PPC_ADDR32; break;
5972e63152bSMatt Helsley 	case EM_PPC64:	reltype = R_PPC64_ADDR64; break;
5982e63152bSMatt Helsley 	case EM_S390:	/* reltype: e_class    */ break;
5992e63152bSMatt Helsley 	case EM_SH:	reltype = R_SH_DIR32; gpfx = 0; break;
6002e63152bSMatt Helsley 	case EM_SPARCV9: reltype = R_SPARC_64; break;
601ffd618faSSteven Rostedt 	case EM_X86_64:
602ffd618faSSteven Rostedt 		make_nop = make_nop_x86;
603ffd618faSSteven Rostedt 		ideal_nop = ideal_nop5_x86_64;
604ffd618faSSteven Rostedt 		reltype = R_X86_64_64;
60546a2b61eSLi Bin 		rel_type_nop = R_X86_64_NONE;
606521ccb5cSMartin Schwidefsky 		mcount_adjust_64 = -1;
6072e63152bSMatt Helsley 		gpfx = 0;
608ffd618faSSteven Rostedt 		break;
60981d3858dSJohn Reiser 	}  /* end switch */
61081d3858dSJohn Reiser 
61181d3858dSJohn Reiser 	switch (ehdr->e_ident[EI_CLASS]) {
612e90b0c8bSSteven Rostedt 	default:
61381d3858dSJohn Reiser 		fprintf(stderr, "unrecognized ELF class %d %s\n",
61481d3858dSJohn Reiser 			ehdr->e_ident[EI_CLASS], fname);
6153f1df120SMatt Helsley 		goto out;
616e90b0c8bSSteven Rostedt 	case ELFCLASS32:
617dd5477ffSSteven Rostedt 		if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
618dd5477ffSSteven Rostedt 		||  w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
61981d3858dSJohn Reiser 			fprintf(stderr,
62081d3858dSJohn Reiser 				"unrecognized ET_REL file: %s\n", fname);
6213f1df120SMatt Helsley 			goto out;
62281d3858dSJohn Reiser 		}
623dd5477ffSSteven Rostedt 		if (w2(ehdr->e_machine) == EM_MIPS) {
624a2d49358SJohn Reiser 			reltype = R_MIPS_32;
625412910cdSWu Zhangjin 			is_fake_mcount32 = MIPS32_is_fake_mcount;
626412910cdSWu Zhangjin 		}
627a0a458fbSQing Zhang 		if (w2(ehdr->e_machine) == EM_LOONGARCH) {
628a0a458fbSQing Zhang 			reltype = R_LARCH_32;
629a0a458fbSQing Zhang 			is_fake_mcount32 = LARCH32_is_fake_mcount;
630a0a458fbSQing Zhang 		}
6313f1df120SMatt Helsley 		if (do32(ehdr, fname, reltype) < 0)
6323f1df120SMatt Helsley 			goto out;
633e90b0c8bSSteven Rostedt 		break;
63481d3858dSJohn Reiser 	case ELFCLASS64: {
63581d3858dSJohn Reiser 		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
636dd5477ffSSteven Rostedt 		if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
637dd5477ffSSteven Rostedt 		||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
63881d3858dSJohn Reiser 			fprintf(stderr,
63981d3858dSJohn Reiser 				"unrecognized ET_REL file: %s\n", fname);
6403f1df120SMatt Helsley 			goto out;
64181d3858dSJohn Reiser 		}
642f2963886SMartin Schwidefsky 		if (w2(ghdr->e_machine) == EM_S390) {
64381d3858dSJohn Reiser 			reltype = R_390_64;
644c933146aSHeiko Carstens 			mcount_adjust_64 = -14;
645f2963886SMartin Schwidefsky 		}
646dd5477ffSSteven Rostedt 		if (w2(ghdr->e_machine) == EM_MIPS) {
647a2d49358SJohn Reiser 			reltype = R_MIPS_64;
648a2d49358SJohn Reiser 			Elf64_r_sym = MIPS64_r_sym;
649a2d49358SJohn Reiser 			Elf64_r_info = MIPS64_r_info;
650412910cdSWu Zhangjin 			is_fake_mcount64 = MIPS64_is_fake_mcount;
651a2d49358SJohn Reiser 		}
652a0a458fbSQing Zhang 		if (w2(ghdr->e_machine) == EM_LOONGARCH) {
653a0a458fbSQing Zhang 			reltype = R_LARCH_64;
654a0a458fbSQing Zhang 			is_fake_mcount64 = LARCH64_is_fake_mcount;
655a0a458fbSQing Zhang 		}
6563f1df120SMatt Helsley 		if (do64(ghdr, fname, reltype) < 0)
6573f1df120SMatt Helsley 			goto out;
658e90b0c8bSSteven Rostedt 		break;
659e90b0c8bSSteven Rostedt 	}
66081d3858dSJohn Reiser 	}  /* end switch */
66181d3858dSJohn Reiser 
6623f1df120SMatt Helsley 	rc = write_file(fname);
6633f1df120SMatt Helsley out:
6644fbcf074SMatt Helsley 	file_append_cleanup();
6654fbcf074SMatt Helsley 	mmap_cleanup();
6663f1df120SMatt Helsley 	return rc;
66781d3858dSJohn Reiser }
66881d3858dSJohn Reiser 
main(int argc,char * argv[])6693aec8638SMatt Helsley int main(int argc, char *argv[])
67081d3858dSJohn Reiser {
671cd3478f2SRabin Vincent 	const char ftrace[] = "/ftrace.o";
67244475863SSteven Rostedt 	int ftrace_size = sizeof(ftrace) - 1;
67381d3858dSJohn Reiser 	int n_error = 0;  /* gcc-4.3.0 false positive complaint */
674dfad3d59SSteven Rostedt 	int c;
675dfad3d59SSteven Rostedt 	int i;
67644475863SSteven Rostedt 
677dfad3d59SSteven Rostedt 	while ((c = getopt(argc, argv, "w")) >= 0) {
678dfad3d59SSteven Rostedt 		switch (c) {
679dfad3d59SSteven Rostedt 		case 'w':
680dfad3d59SSteven Rostedt 			warn_on_notrace_sect = 1;
681dfad3d59SSteven Rostedt 			break;
682dfad3d59SSteven Rostedt 		default:
683dfad3d59SSteven Rostedt 			fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
684dfad3d59SSteven Rostedt 			return 0;
685dfad3d59SSteven Rostedt 		}
686dfad3d59SSteven Rostedt 	}
687dfad3d59SSteven Rostedt 
688dfad3d59SSteven Rostedt 	if ((argc - optind) < 1) {
689dfad3d59SSteven Rostedt 		fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
69044475863SSteven Rostedt 		return 0;
69144475863SSteven Rostedt 	}
69244475863SSteven Rostedt 
69344475863SSteven Rostedt 	/* Process each file in turn, allowing deep failure. */
694dfad3d59SSteven Rostedt 	for (i = optind; i < argc; i++) {
695dfad3d59SSteven Rostedt 		char *file = argv[i];
69644475863SSteven Rostedt 		int len;
69744475863SSteven Rostedt 
69844475863SSteven Rostedt 		/*
69944475863SSteven Rostedt 		 * The file kernel/trace/ftrace.o references the mcount
70044475863SSteven Rostedt 		 * function but does not call it. Since ftrace.o should
70144475863SSteven Rostedt 		 * not be traced anyway, we just skip it.
70244475863SSteven Rostedt 		 */
703dfad3d59SSteven Rostedt 		len = strlen(file);
70444475863SSteven Rostedt 		if (len >= ftrace_size &&
705dfad3d59SSteven Rostedt 		    strcmp(file + (len - ftrace_size), ftrace) == 0)
70644475863SSteven Rostedt 			continue;
70744475863SSteven Rostedt 
7083f1df120SMatt Helsley 		if (do_file(file)) {
709713a3e4dSColin Ian King 			fprintf(stderr, "%s: failed\n", file);
71081d3858dSJohn Reiser 			++n_error;
7113f1df120SMatt Helsley 		}
71281d3858dSJohn Reiser 	}
71381d3858dSJohn Reiser 	return !!n_error;
71481d3858dSJohn Reiser }
715