18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * recordmcount.c: construct a table of the locations of calls to 'mcount'
48c2ecf20Sopenharmony_ci * so that ftrace can find them quickly.
58c2ecf20Sopenharmony_ci * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Restructured to fit Linux format, as well as other updates:
88c2ecf20Sopenharmony_ci *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * Strategy: alter the .o file in-place.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Append a new STRTAB that has the new section names, followed by a new array
158c2ecf20Sopenharmony_ci * ElfXX_Shdr[] that has the new section headers, followed by the section
168c2ecf20Sopenharmony_ci * contents for __mcount_loc and its relocations.  The old shstrtab strings,
178c2ecf20Sopenharmony_ci * and the old ElfXX_Shdr[] array, remain as "garbage" (commonly, a couple
188c2ecf20Sopenharmony_ci * kilobytes.)  Subsequent processing by /bin/ld (or the kernel module loader)
198c2ecf20Sopenharmony_ci * will ignore the garbage regions, because they are not designated by the
208c2ecf20Sopenharmony_ci * new .e_shoff nor the new ElfXX_Shdr[].  [In order to remove the garbage,
218c2ecf20Sopenharmony_ci * then use "ld -r" to create a new file that omits the garbage.]
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <sys/types.h>
258c2ecf20Sopenharmony_ci#include <sys/mman.h>
268c2ecf20Sopenharmony_ci#include <sys/stat.h>
278c2ecf20Sopenharmony_ci#include <getopt.h>
288c2ecf20Sopenharmony_ci#include <elf.h>
298c2ecf20Sopenharmony_ci#include <fcntl.h>
308c2ecf20Sopenharmony_ci#include <stdio.h>
318c2ecf20Sopenharmony_ci#include <stdlib.h>
328c2ecf20Sopenharmony_ci#include <string.h>
338c2ecf20Sopenharmony_ci#include <unistd.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#ifndef EM_AARCH64
368c2ecf20Sopenharmony_ci#define EM_AARCH64	183
378c2ecf20Sopenharmony_ci#define R_AARCH64_NONE		0
388c2ecf20Sopenharmony_ci#define R_AARCH64_ABS64	257
398c2ecf20Sopenharmony_ci#endif
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#ifndef EM_LOONGARCH
428c2ecf20Sopenharmony_ci#define EM_LOONGARCH	258
438c2ecf20Sopenharmony_ci#define R_LARCH_32			1
448c2ecf20Sopenharmony_ci#define R_LARCH_64			2
458c2ecf20Sopenharmony_ci#define R_LARCH_MARK_LA			20
468c2ecf20Sopenharmony_ci#define R_LARCH_SOP_PUSH_PLT_PCREL	29
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define R_ARM_PC24		1
508c2ecf20Sopenharmony_ci#define R_ARM_THM_CALL		10
518c2ecf20Sopenharmony_ci#define R_ARM_CALL		28
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define R_AARCH64_CALL26	283
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int fd_map;	/* File descriptor for file being modified. */
568c2ecf20Sopenharmony_cistatic int mmap_failed; /* Boolean flag. */
578c2ecf20Sopenharmony_cistatic char gpfx;	/* prefix for global symbol name (sometimes '_') */
588c2ecf20Sopenharmony_cistatic struct stat sb;	/* Remember .st_size, etc. */
598c2ecf20Sopenharmony_cistatic const char *altmcount;	/* alternate mcount symbol name */
608c2ecf20Sopenharmony_cistatic int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
618c2ecf20Sopenharmony_cistatic void *file_map;	/* pointer of the mapped file */
628c2ecf20Sopenharmony_cistatic void *file_end;	/* pointer to the end of the mapped file */
638c2ecf20Sopenharmony_cistatic int file_updated; /* flag to state file was changed */
648c2ecf20Sopenharmony_cistatic void *file_ptr;	/* current file pointer location */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void *file_append; /* added to the end of the file */
678c2ecf20Sopenharmony_cistatic size_t file_append_size; /* how much is added to end of file */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* Per-file resource cleanup when multiple files. */
708c2ecf20Sopenharmony_cistatic void file_append_cleanup(void)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	free(file_append);
738c2ecf20Sopenharmony_ci	file_append = NULL;
748c2ecf20Sopenharmony_ci	file_append_size = 0;
758c2ecf20Sopenharmony_ci	file_updated = 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void mmap_cleanup(void)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	if (!mmap_failed)
818c2ecf20Sopenharmony_ci		munmap(file_map, sb.st_size);
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		free(file_map);
848c2ecf20Sopenharmony_ci	file_map = NULL;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* ulseek, uwrite, ...:  Check return value for errors. */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic off_t ulseek(off_t const offset, int const whence)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	switch (whence) {
928c2ecf20Sopenharmony_ci	case SEEK_SET:
938c2ecf20Sopenharmony_ci		file_ptr = file_map + offset;
948c2ecf20Sopenharmony_ci		break;
958c2ecf20Sopenharmony_ci	case SEEK_CUR:
968c2ecf20Sopenharmony_ci		file_ptr += offset;
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	case SEEK_END:
998c2ecf20Sopenharmony_ci		file_ptr = file_map + (sb.st_size - offset);
1008c2ecf20Sopenharmony_ci		break;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci	if (file_ptr < file_map) {
1038c2ecf20Sopenharmony_ci		fprintf(stderr, "lseek: seek before file\n");
1048c2ecf20Sopenharmony_ci		return -1;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	return file_ptr - file_map;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic ssize_t uwrite(void const *const buf, size_t const count)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	size_t cnt = count;
1128c2ecf20Sopenharmony_ci	off_t idx = 0;
1138c2ecf20Sopenharmony_ci	void *p = NULL;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	file_updated = 1;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (file_ptr + count >= file_end) {
1188c2ecf20Sopenharmony_ci		off_t aoffset = (file_ptr + count) - file_end;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		if (aoffset > file_append_size) {
1218c2ecf20Sopenharmony_ci			p = realloc(file_append, aoffset);
1228c2ecf20Sopenharmony_ci			if (!p)
1238c2ecf20Sopenharmony_ci				free(file_append);
1248c2ecf20Sopenharmony_ci			file_append = p;
1258c2ecf20Sopenharmony_ci			file_append_size = aoffset;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci		if (!file_append) {
1288c2ecf20Sopenharmony_ci			perror("write");
1298c2ecf20Sopenharmony_ci			file_append_cleanup();
1308c2ecf20Sopenharmony_ci			mmap_cleanup();
1318c2ecf20Sopenharmony_ci			return -1;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci		if (file_ptr < file_end) {
1348c2ecf20Sopenharmony_ci			cnt = file_end - file_ptr;
1358c2ecf20Sopenharmony_ci		} else {
1368c2ecf20Sopenharmony_ci			cnt = 0;
1378c2ecf20Sopenharmony_ci			idx = aoffset - count;
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (cnt)
1428c2ecf20Sopenharmony_ci		memcpy(file_ptr, buf, cnt);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (cnt < count)
1458c2ecf20Sopenharmony_ci		memcpy(file_append + idx, buf + cnt, count - cnt);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	file_ptr += count;
1488c2ecf20Sopenharmony_ci	return count;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic void * umalloc(size_t size)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	void *const addr = malloc(size);
1548c2ecf20Sopenharmony_ci	if (addr == 0) {
1558c2ecf20Sopenharmony_ci		fprintf(stderr, "malloc failed: %zu bytes\n", size);
1568c2ecf20Sopenharmony_ci		file_append_cleanup();
1578c2ecf20Sopenharmony_ci		mmap_cleanup();
1588c2ecf20Sopenharmony_ci		return NULL;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	return addr;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/*
1648c2ecf20Sopenharmony_ci * Get the whole file as a programming convenience in order to avoid
1658c2ecf20Sopenharmony_ci * malloc+lseek+read+free of many pieces.  If successful, then mmap
1668c2ecf20Sopenharmony_ci * avoids copying unused pieces; else just read the whole file.
1678c2ecf20Sopenharmony_ci * Open for both read and write; new info will be appended to the file.
1688c2ecf20Sopenharmony_ci * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr
1698c2ecf20Sopenharmony_ci * do not propagate to the file until an explicit overwrite at the last.
1708c2ecf20Sopenharmony_ci * This preserves most aspects of consistency (all except .st_size)
1718c2ecf20Sopenharmony_ci * for simultaneous readers of the file while we are appending to it.
1728c2ecf20Sopenharmony_ci * However, multiple writers still are bad.  We choose not to use
1738c2ecf20Sopenharmony_ci * locking because it is expensive and the use case of kernel build
1748c2ecf20Sopenharmony_ci * makes multiple writers unlikely.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic void *mmap_file(char const *fname)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	/* Avoid problems if early cleanup() */
1798c2ecf20Sopenharmony_ci	fd_map = -1;
1808c2ecf20Sopenharmony_ci	mmap_failed = 1;
1818c2ecf20Sopenharmony_ci	file_map = NULL;
1828c2ecf20Sopenharmony_ci	file_ptr = NULL;
1838c2ecf20Sopenharmony_ci	file_updated = 0;
1848c2ecf20Sopenharmony_ci	sb.st_size = 0;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	fd_map = open(fname, O_RDONLY);
1878c2ecf20Sopenharmony_ci	if (fd_map < 0) {
1888c2ecf20Sopenharmony_ci		perror(fname);
1898c2ecf20Sopenharmony_ci		return NULL;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	if (fstat(fd_map, &sb) < 0) {
1928c2ecf20Sopenharmony_ci		perror(fname);
1938c2ecf20Sopenharmony_ci		goto out;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	if (!S_ISREG(sb.st_mode)) {
1968c2ecf20Sopenharmony_ci		fprintf(stderr, "not a regular file: %s\n", fname);
1978c2ecf20Sopenharmony_ci		goto out;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
2008c2ecf20Sopenharmony_ci			fd_map, 0);
2018c2ecf20Sopenharmony_ci	if (file_map == MAP_FAILED) {
2028c2ecf20Sopenharmony_ci		mmap_failed = 1;
2038c2ecf20Sopenharmony_ci		file_map = umalloc(sb.st_size);
2048c2ecf20Sopenharmony_ci		if (!file_map) {
2058c2ecf20Sopenharmony_ci			perror(fname);
2068c2ecf20Sopenharmony_ci			goto out;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci		if (read(fd_map, file_map, sb.st_size) != sb.st_size) {
2098c2ecf20Sopenharmony_ci			perror(fname);
2108c2ecf20Sopenharmony_ci			free(file_map);
2118c2ecf20Sopenharmony_ci			file_map = NULL;
2128c2ecf20Sopenharmony_ci			goto out;
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci	} else
2158c2ecf20Sopenharmony_ci		mmap_failed = 0;
2168c2ecf20Sopenharmony_ciout:
2178c2ecf20Sopenharmony_ci	close(fd_map);
2188c2ecf20Sopenharmony_ci	fd_map = -1;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	file_end = file_map + sb.st_size;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return file_map;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
2278c2ecf20Sopenharmony_cistatic unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
2288c2ecf20Sopenharmony_cistatic unsigned char *ideal_nop;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic char rel_type_nop;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int (*make_nop)(void *map, size_t const offset);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int make_nop_x86(void *map, size_t const offset)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	uint32_t *ptr;
2378c2ecf20Sopenharmony_ci	unsigned char *op;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */
2408c2ecf20Sopenharmony_ci	ptr = map + offset;
2418c2ecf20Sopenharmony_ci	if (*ptr != 0)
2428c2ecf20Sopenharmony_ci		return -1;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	op = map + offset - 1;
2458c2ecf20Sopenharmony_ci	if (*op != 0xe8)
2468c2ecf20Sopenharmony_ci		return -1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* convert to nop */
2498c2ecf20Sopenharmony_ci	if (ulseek(offset - 1, SEEK_SET) < 0)
2508c2ecf20Sopenharmony_ci		return -1;
2518c2ecf20Sopenharmony_ci	if (uwrite(ideal_nop, 5) < 0)
2528c2ecf20Sopenharmony_ci		return -1;
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
2578c2ecf20Sopenharmony_cistatic unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
2588c2ecf20Sopenharmony_cistatic unsigned char *ideal_nop4_arm;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic unsigned char bl_mcount_arm_le[4] = { 0xfe, 0xff, 0xff, 0xeb }; /* bl */
2618c2ecf20Sopenharmony_cistatic unsigned char bl_mcount_arm_be[4] = { 0xeb, 0xff, 0xff, 0xfe }; /* bl */
2628c2ecf20Sopenharmony_cistatic unsigned char *bl_mcount_arm;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic unsigned char push_arm_le[4] = { 0x04, 0xe0, 0x2d, 0xe5 }; /* push {lr} */
2658c2ecf20Sopenharmony_cistatic unsigned char push_arm_be[4] = { 0xe5, 0x2d, 0xe0, 0x04 }; /* push {lr} */
2668c2ecf20Sopenharmony_cistatic unsigned char *push_arm;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic unsigned char ideal_nop2_thumb_le[2] = { 0x00, 0xbf }; /* nop */
2698c2ecf20Sopenharmony_cistatic unsigned char ideal_nop2_thumb_be[2] = { 0xbf, 0x00 }; /* nop */
2708c2ecf20Sopenharmony_cistatic unsigned char *ideal_nop2_thumb;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic unsigned char push_bl_mcount_thumb_le[6] = { 0x00, 0xb5, 0xff, 0xf7, 0xfe, 0xff }; /* push {lr}, bl */
2738c2ecf20Sopenharmony_cistatic unsigned char push_bl_mcount_thumb_be[6] = { 0xb5, 0x00, 0xf7, 0xff, 0xff, 0xfe }; /* push {lr}, bl */
2748c2ecf20Sopenharmony_cistatic unsigned char *push_bl_mcount_thumb;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int make_nop_arm(void *map, size_t const offset)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	char *ptr;
2798c2ecf20Sopenharmony_ci	int cnt = 1;
2808c2ecf20Sopenharmony_ci	int nop_size;
2818c2ecf20Sopenharmony_ci	size_t off = offset;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	ptr = map + offset;
2848c2ecf20Sopenharmony_ci	if (memcmp(ptr, bl_mcount_arm, 4) == 0) {
2858c2ecf20Sopenharmony_ci		if (memcmp(ptr - 4, push_arm, 4) == 0) {
2868c2ecf20Sopenharmony_ci			off -= 4;
2878c2ecf20Sopenharmony_ci			cnt = 2;
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci		ideal_nop = ideal_nop4_arm;
2908c2ecf20Sopenharmony_ci		nop_size = 4;
2918c2ecf20Sopenharmony_ci	} else if (memcmp(ptr - 2, push_bl_mcount_thumb, 6) == 0) {
2928c2ecf20Sopenharmony_ci		cnt = 3;
2938c2ecf20Sopenharmony_ci		nop_size = 2;
2948c2ecf20Sopenharmony_ci		off -= 2;
2958c2ecf20Sopenharmony_ci		ideal_nop = ideal_nop2_thumb;
2968c2ecf20Sopenharmony_ci	} else
2978c2ecf20Sopenharmony_ci		return -1;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* Convert to nop */
3008c2ecf20Sopenharmony_ci	if (ulseek(off, SEEK_SET) < 0)
3018c2ecf20Sopenharmony_ci		return -1;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	do {
3048c2ecf20Sopenharmony_ci		if (uwrite(ideal_nop, nop_size) < 0)
3058c2ecf20Sopenharmony_ci			return -1;
3068c2ecf20Sopenharmony_ci	} while (--cnt > 0);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
3128c2ecf20Sopenharmony_cistatic int make_nop_arm64(void *map, size_t const offset)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	uint32_t *ptr;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ptr = map + offset;
3178c2ecf20Sopenharmony_ci	/* bl <_mcount> is 0x94000000 before relocation */
3188c2ecf20Sopenharmony_ci	if (*ptr != 0x94000000)
3198c2ecf20Sopenharmony_ci		return -1;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* Convert to nop */
3228c2ecf20Sopenharmony_ci	if (ulseek(offset, SEEK_SET) < 0)
3238c2ecf20Sopenharmony_ci		return -1;
3248c2ecf20Sopenharmony_ci	if (uwrite(ideal_nop, 4) < 0)
3258c2ecf20Sopenharmony_ci		return -1;
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int write_file(const char *fname)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	char tmp_file[strlen(fname) + 4];
3328c2ecf20Sopenharmony_ci	size_t n;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (!file_updated)
3358c2ecf20Sopenharmony_ci		return 0;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	sprintf(tmp_file, "%s.rc", fname);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/*
3408c2ecf20Sopenharmony_ci	 * After reading the entire file into memory, delete it
3418c2ecf20Sopenharmony_ci	 * and write it back, to prevent weird side effects of modifying
3428c2ecf20Sopenharmony_ci	 * an object file in place.
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
3458c2ecf20Sopenharmony_ci	if (fd_map < 0) {
3468c2ecf20Sopenharmony_ci		perror(fname);
3478c2ecf20Sopenharmony_ci		return -1;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	n = write(fd_map, file_map, sb.st_size);
3508c2ecf20Sopenharmony_ci	if (n != sb.st_size) {
3518c2ecf20Sopenharmony_ci		perror("write");
3528c2ecf20Sopenharmony_ci		close(fd_map);
3538c2ecf20Sopenharmony_ci		return -1;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	if (file_append_size) {
3568c2ecf20Sopenharmony_ci		n = write(fd_map, file_append, file_append_size);
3578c2ecf20Sopenharmony_ci		if (n != file_append_size) {
3588c2ecf20Sopenharmony_ci			perror("write");
3598c2ecf20Sopenharmony_ci			close(fd_map);
3608c2ecf20Sopenharmony_ci			return -1;
3618c2ecf20Sopenharmony_ci		}
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	close(fd_map);
3648c2ecf20Sopenharmony_ci	if (rename(tmp_file, fname) < 0) {
3658c2ecf20Sopenharmony_ci		perror(fname);
3668c2ecf20Sopenharmony_ci		return -1;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/* w8rev, w8nat, ...: Handle endianness. */
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic uint64_t w8rev(uint64_t const x)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	return   ((0xff & (x >> (0 * 8))) << (7 * 8))
3768c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (1 * 8))) << (6 * 8))
3778c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (2 * 8))) << (5 * 8))
3788c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (3 * 8))) << (4 * 8))
3798c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (4 * 8))) << (3 * 8))
3808c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (5 * 8))) << (2 * 8))
3818c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (6 * 8))) << (1 * 8))
3828c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (7 * 8))) << (0 * 8));
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic uint32_t w4rev(uint32_t const x)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	return   ((0xff & (x >> (0 * 8))) << (3 * 8))
3888c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (1 * 8))) << (2 * 8))
3898c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (2 * 8))) << (1 * 8))
3908c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (3 * 8))) << (0 * 8));
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic uint32_t w2rev(uint16_t const x)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	return   ((0xff & (x >> (0 * 8))) << (1 * 8))
3968c2ecf20Sopenharmony_ci	       | ((0xff & (x >> (1 * 8))) << (0 * 8));
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic uint64_t w8nat(uint64_t const x)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	return x;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic uint32_t w4nat(uint32_t const x)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	return x;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic uint32_t w2nat(uint16_t const x)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	return x;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic uint64_t (*w8)(uint64_t);
4158c2ecf20Sopenharmony_cistatic uint32_t (*w)(uint32_t);
4168c2ecf20Sopenharmony_cistatic uint32_t (*w2)(uint16_t);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci/* Names of the sections that could contain calls to mcount. */
4198c2ecf20Sopenharmony_cistatic int is_mcounted_section_name(char const *const txtname)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	return strncmp(".text",          txtname, 5) == 0 ||
4228c2ecf20Sopenharmony_ci		strcmp(".init.text",     txtname) == 0 ||
4238c2ecf20Sopenharmony_ci		strcmp(".ref.text",      txtname) == 0 ||
4248c2ecf20Sopenharmony_ci		strcmp(".sched.text",    txtname) == 0 ||
4258c2ecf20Sopenharmony_ci		strcmp(".spinlock.text", txtname) == 0 ||
4268c2ecf20Sopenharmony_ci		strcmp(".irqentry.text", txtname) == 0 ||
4278c2ecf20Sopenharmony_ci		strcmp(".softirqentry.text", txtname) == 0 ||
4288c2ecf20Sopenharmony_ci		strcmp(".kprobes.text", txtname) == 0 ||
4298c2ecf20Sopenharmony_ci		strcmp(".cpuidle.text", txtname) == 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic char const *already_has_rel_mcount = "success"; /* our work here is done! */
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci/* 32 bit and 64 bit are very similar */
4358c2ecf20Sopenharmony_ci#include "recordmcount.h"
4368c2ecf20Sopenharmony_ci#define RECORD_MCOUNT_64
4378c2ecf20Sopenharmony_ci#include "recordmcount.h"
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int arm_is_fake_mcount(Elf32_Rel const *rp)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	switch (ELF32_R_TYPE(w(rp->r_info))) {
4428c2ecf20Sopenharmony_ci	case R_ARM_THM_CALL:
4438c2ecf20Sopenharmony_ci	case R_ARM_CALL:
4448c2ecf20Sopenharmony_ci	case R_ARM_PC24:
4458c2ecf20Sopenharmony_ci		return 0;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return 1;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int arm64_is_fake_mcount(Elf64_Rel const *rp)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	return ELF64_R_TYPE(w8(rp->r_info)) != R_AARCH64_CALL26;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int LARCH32_is_fake_mcount(Elf32_Rel const *rp)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	switch (ELF64_R_TYPE(w(rp->r_info))) {
4598c2ecf20Sopenharmony_ci	case R_LARCH_MARK_LA:
4608c2ecf20Sopenharmony_ci	case R_LARCH_SOP_PUSH_PLT_PCREL:
4618c2ecf20Sopenharmony_ci		return 0;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	return 1;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic int LARCH64_is_fake_mcount(Elf64_Rel const *rp)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	switch (ELF64_R_TYPE(w(rp->r_info))) {
4708c2ecf20Sopenharmony_ci	case R_LARCH_MARK_LA:
4718c2ecf20Sopenharmony_ci	case R_LARCH_SOP_PUSH_PLT_PCREL:
4728c2ecf20Sopenharmony_ci		return 0;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return 1;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci/* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
4798c2ecf20Sopenharmony_ci * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
4808c2ecf20Sopenharmony_ci * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
4818c2ecf20Sopenharmony_ci * to imply the order of the members; the spec does not say so.
4828c2ecf20Sopenharmony_ci *	typedef unsigned char Elf64_Byte;
4838c2ecf20Sopenharmony_ci * fails on MIPS64 because their <elf.h> already has it!
4848c2ecf20Sopenharmony_ci */
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_citypedef uint8_t myElf64_Byte;		/* Type for a 8-bit quantity.  */
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ciunion mips_r_info {
4898c2ecf20Sopenharmony_ci	Elf64_Xword r_info;
4908c2ecf20Sopenharmony_ci	struct {
4918c2ecf20Sopenharmony_ci		Elf64_Word r_sym;		/* Symbol index.  */
4928c2ecf20Sopenharmony_ci		myElf64_Byte r_ssym;		/* Special symbol.  */
4938c2ecf20Sopenharmony_ci		myElf64_Byte r_type3;		/* Third relocation.  */
4948c2ecf20Sopenharmony_ci		myElf64_Byte r_type2;		/* Second relocation.  */
4958c2ecf20Sopenharmony_ci		myElf64_Byte r_type;		/* First relocation.  */
4968c2ecf20Sopenharmony_ci	} r_mips;
4978c2ecf20Sopenharmony_ci};
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cistatic void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	rp->r_info = ((union mips_r_info){
5078c2ecf20Sopenharmony_ci		.r_mips = { .r_sym = w(sym), .r_type = type }
5088c2ecf20Sopenharmony_ci	}).r_info;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int do_file(char const *const fname)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	unsigned int reltype = 0;
5148c2ecf20Sopenharmony_ci	Elf32_Ehdr *ehdr;
5158c2ecf20Sopenharmony_ci	int rc = -1;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	ehdr = mmap_file(fname);
5188c2ecf20Sopenharmony_ci	if (!ehdr)
5198c2ecf20Sopenharmony_ci		goto out;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	w = w4nat;
5228c2ecf20Sopenharmony_ci	w2 = w2nat;
5238c2ecf20Sopenharmony_ci	w8 = w8nat;
5248c2ecf20Sopenharmony_ci	switch (ehdr->e_ident[EI_DATA]) {
5258c2ecf20Sopenharmony_ci		static unsigned int const endian = 1;
5268c2ecf20Sopenharmony_ci	default:
5278c2ecf20Sopenharmony_ci		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
5288c2ecf20Sopenharmony_ci			ehdr->e_ident[EI_DATA], fname);
5298c2ecf20Sopenharmony_ci		goto out;
5308c2ecf20Sopenharmony_ci	case ELFDATA2LSB:
5318c2ecf20Sopenharmony_ci		if (*(unsigned char const *)&endian != 1) {
5328c2ecf20Sopenharmony_ci			/* main() is big endian, file.o is little endian. */
5338c2ecf20Sopenharmony_ci			w = w4rev;
5348c2ecf20Sopenharmony_ci			w2 = w2rev;
5358c2ecf20Sopenharmony_ci			w8 = w8rev;
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci		ideal_nop4_arm = ideal_nop4_arm_le;
5388c2ecf20Sopenharmony_ci		bl_mcount_arm = bl_mcount_arm_le;
5398c2ecf20Sopenharmony_ci		push_arm = push_arm_le;
5408c2ecf20Sopenharmony_ci		ideal_nop2_thumb = ideal_nop2_thumb_le;
5418c2ecf20Sopenharmony_ci		push_bl_mcount_thumb = push_bl_mcount_thumb_le;
5428c2ecf20Sopenharmony_ci		break;
5438c2ecf20Sopenharmony_ci	case ELFDATA2MSB:
5448c2ecf20Sopenharmony_ci		if (*(unsigned char const *)&endian != 0) {
5458c2ecf20Sopenharmony_ci			/* main() is little endian, file.o is big endian. */
5468c2ecf20Sopenharmony_ci			w = w4rev;
5478c2ecf20Sopenharmony_ci			w2 = w2rev;
5488c2ecf20Sopenharmony_ci			w8 = w8rev;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci		ideal_nop4_arm = ideal_nop4_arm_be;
5518c2ecf20Sopenharmony_ci		bl_mcount_arm = bl_mcount_arm_be;
5528c2ecf20Sopenharmony_ci		push_arm = push_arm_be;
5538c2ecf20Sopenharmony_ci		ideal_nop2_thumb = ideal_nop2_thumb_be;
5548c2ecf20Sopenharmony_ci		push_bl_mcount_thumb = push_bl_mcount_thumb_be;
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	}  /* end switch */
5578c2ecf20Sopenharmony_ci	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
5588c2ecf20Sopenharmony_ci	    w2(ehdr->e_type) != ET_REL ||
5598c2ecf20Sopenharmony_ci	    ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
5608c2ecf20Sopenharmony_ci		fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
5618c2ecf20Sopenharmony_ci		goto out;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	gpfx = '_';
5658c2ecf20Sopenharmony_ci	switch (w2(ehdr->e_machine)) {
5668c2ecf20Sopenharmony_ci	default:
5678c2ecf20Sopenharmony_ci		fprintf(stderr, "unrecognized e_machine %u %s\n",
5688c2ecf20Sopenharmony_ci			w2(ehdr->e_machine), fname);
5698c2ecf20Sopenharmony_ci		goto out;
5708c2ecf20Sopenharmony_ci	case EM_386:
5718c2ecf20Sopenharmony_ci		reltype = R_386_32;
5728c2ecf20Sopenharmony_ci		rel_type_nop = R_386_NONE;
5738c2ecf20Sopenharmony_ci		make_nop = make_nop_x86;
5748c2ecf20Sopenharmony_ci		ideal_nop = ideal_nop5_x86_32;
5758c2ecf20Sopenharmony_ci		mcount_adjust_32 = -1;
5768c2ecf20Sopenharmony_ci		gpfx = 0;
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci	case EM_ARM:
5798c2ecf20Sopenharmony_ci		reltype = R_ARM_ABS32;
5808c2ecf20Sopenharmony_ci		altmcount = "__gnu_mcount_nc";
5818c2ecf20Sopenharmony_ci		make_nop = make_nop_arm;
5828c2ecf20Sopenharmony_ci		rel_type_nop = R_ARM_NONE;
5838c2ecf20Sopenharmony_ci		is_fake_mcount32 = arm_is_fake_mcount;
5848c2ecf20Sopenharmony_ci		gpfx = 0;
5858c2ecf20Sopenharmony_ci		break;
5868c2ecf20Sopenharmony_ci	case EM_AARCH64:
5878c2ecf20Sopenharmony_ci		reltype = R_AARCH64_ABS64;
5888c2ecf20Sopenharmony_ci		make_nop = make_nop_arm64;
5898c2ecf20Sopenharmony_ci		rel_type_nop = R_AARCH64_NONE;
5908c2ecf20Sopenharmony_ci		ideal_nop = ideal_nop4_arm64;
5918c2ecf20Sopenharmony_ci		is_fake_mcount64 = arm64_is_fake_mcount;
5928c2ecf20Sopenharmony_ci		break;
5938c2ecf20Sopenharmony_ci	case EM_IA_64:	reltype = R_IA64_IMM64; break;
5948c2ecf20Sopenharmony_ci	case EM_MIPS:	/* reltype: e_class    */ break;
5958c2ecf20Sopenharmony_ci	case EM_LOONGARCH:	/* reltype: e_class    */ break;
5968c2ecf20Sopenharmony_ci	case EM_PPC:	reltype = R_PPC_ADDR32; break;
5978c2ecf20Sopenharmony_ci	case EM_PPC64:	reltype = R_PPC64_ADDR64; break;
5988c2ecf20Sopenharmony_ci	case EM_S390:	/* reltype: e_class    */ break;
5998c2ecf20Sopenharmony_ci	case EM_SH:	reltype = R_SH_DIR32; gpfx = 0; break;
6008c2ecf20Sopenharmony_ci	case EM_SPARCV9: reltype = R_SPARC_64; break;
6018c2ecf20Sopenharmony_ci	case EM_X86_64:
6028c2ecf20Sopenharmony_ci		make_nop = make_nop_x86;
6038c2ecf20Sopenharmony_ci		ideal_nop = ideal_nop5_x86_64;
6048c2ecf20Sopenharmony_ci		reltype = R_X86_64_64;
6058c2ecf20Sopenharmony_ci		rel_type_nop = R_X86_64_NONE;
6068c2ecf20Sopenharmony_ci		mcount_adjust_64 = -1;
6078c2ecf20Sopenharmony_ci		gpfx = 0;
6088c2ecf20Sopenharmony_ci		break;
6098c2ecf20Sopenharmony_ci	}  /* end switch */
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	switch (ehdr->e_ident[EI_CLASS]) {
6128c2ecf20Sopenharmony_ci	default:
6138c2ecf20Sopenharmony_ci		fprintf(stderr, "unrecognized ELF class %d %s\n",
6148c2ecf20Sopenharmony_ci			ehdr->e_ident[EI_CLASS], fname);
6158c2ecf20Sopenharmony_ci		goto out;
6168c2ecf20Sopenharmony_ci	case ELFCLASS32:
6178c2ecf20Sopenharmony_ci		if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
6188c2ecf20Sopenharmony_ci		||  w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
6198c2ecf20Sopenharmony_ci			fprintf(stderr,
6208c2ecf20Sopenharmony_ci				"unrecognized ET_REL file: %s\n", fname);
6218c2ecf20Sopenharmony_ci			goto out;
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci		if (w2(ehdr->e_machine) == EM_MIPS) {
6248c2ecf20Sopenharmony_ci			reltype = R_MIPS_32;
6258c2ecf20Sopenharmony_ci			is_fake_mcount32 = MIPS32_is_fake_mcount;
6268c2ecf20Sopenharmony_ci		}
6278c2ecf20Sopenharmony_ci		if (w2(ehdr->e_machine) == EM_LOONGARCH) {
6288c2ecf20Sopenharmony_ci			reltype = R_LARCH_32;
6298c2ecf20Sopenharmony_ci			is_fake_mcount32 = LARCH32_is_fake_mcount;
6308c2ecf20Sopenharmony_ci		}
6318c2ecf20Sopenharmony_ci		if (do32(ehdr, fname, reltype) < 0)
6328c2ecf20Sopenharmony_ci			goto out;
6338c2ecf20Sopenharmony_ci		break;
6348c2ecf20Sopenharmony_ci	case ELFCLASS64: {
6358c2ecf20Sopenharmony_ci		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
6368c2ecf20Sopenharmony_ci		if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
6378c2ecf20Sopenharmony_ci		||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
6388c2ecf20Sopenharmony_ci			fprintf(stderr,
6398c2ecf20Sopenharmony_ci				"unrecognized ET_REL file: %s\n", fname);
6408c2ecf20Sopenharmony_ci			goto out;
6418c2ecf20Sopenharmony_ci		}
6428c2ecf20Sopenharmony_ci		if (w2(ghdr->e_machine) == EM_S390) {
6438c2ecf20Sopenharmony_ci			reltype = R_390_64;
6448c2ecf20Sopenharmony_ci			mcount_adjust_64 = -14;
6458c2ecf20Sopenharmony_ci		}
6468c2ecf20Sopenharmony_ci		if (w2(ghdr->e_machine) == EM_MIPS) {
6478c2ecf20Sopenharmony_ci			reltype = R_MIPS_64;
6488c2ecf20Sopenharmony_ci			Elf64_r_sym = MIPS64_r_sym;
6498c2ecf20Sopenharmony_ci			Elf64_r_info = MIPS64_r_info;
6508c2ecf20Sopenharmony_ci			is_fake_mcount64 = MIPS64_is_fake_mcount;
6518c2ecf20Sopenharmony_ci		}
6528c2ecf20Sopenharmony_ci		if (w2(ghdr->e_machine) == EM_LOONGARCH) {
6538c2ecf20Sopenharmony_ci			reltype = R_LARCH_64;
6548c2ecf20Sopenharmony_ci			is_fake_mcount64 = LARCH64_is_fake_mcount;
6558c2ecf20Sopenharmony_ci		}
6568c2ecf20Sopenharmony_ci		if (do64(ghdr, fname, reltype) < 0)
6578c2ecf20Sopenharmony_ci			goto out;
6588c2ecf20Sopenharmony_ci		break;
6598c2ecf20Sopenharmony_ci	}
6608c2ecf20Sopenharmony_ci	}  /* end switch */
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	rc = write_file(fname);
6638c2ecf20Sopenharmony_ciout:
6648c2ecf20Sopenharmony_ci	file_append_cleanup();
6658c2ecf20Sopenharmony_ci	mmap_cleanup();
6668c2ecf20Sopenharmony_ci	return rc;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ciint main(int argc, char *argv[])
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	const char ftrace[] = "/ftrace.o";
6728c2ecf20Sopenharmony_ci	int ftrace_size = sizeof(ftrace) - 1;
6738c2ecf20Sopenharmony_ci	int n_error = 0;  /* gcc-4.3.0 false positive complaint */
6748c2ecf20Sopenharmony_ci	int c;
6758c2ecf20Sopenharmony_ci	int i;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	while ((c = getopt(argc, argv, "w")) >= 0) {
6788c2ecf20Sopenharmony_ci		switch (c) {
6798c2ecf20Sopenharmony_ci		case 'w':
6808c2ecf20Sopenharmony_ci			warn_on_notrace_sect = 1;
6818c2ecf20Sopenharmony_ci			break;
6828c2ecf20Sopenharmony_ci		default:
6838c2ecf20Sopenharmony_ci			fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
6848c2ecf20Sopenharmony_ci			return 0;
6858c2ecf20Sopenharmony_ci		}
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if ((argc - optind) < 1) {
6898c2ecf20Sopenharmony_ci		fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
6908c2ecf20Sopenharmony_ci		return 0;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* Process each file in turn, allowing deep failure. */
6948c2ecf20Sopenharmony_ci	for (i = optind; i < argc; i++) {
6958c2ecf20Sopenharmony_ci		char *file = argv[i];
6968c2ecf20Sopenharmony_ci		int len;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		/*
6998c2ecf20Sopenharmony_ci		 * The file kernel/trace/ftrace.o references the mcount
7008c2ecf20Sopenharmony_ci		 * function but does not call it. Since ftrace.o should
7018c2ecf20Sopenharmony_ci		 * not be traced anyway, we just skip it.
7028c2ecf20Sopenharmony_ci		 */
7038c2ecf20Sopenharmony_ci		len = strlen(file);
7048c2ecf20Sopenharmony_ci		if (len >= ftrace_size &&
7058c2ecf20Sopenharmony_ci		    strcmp(file + (len - ftrace_size), ftrace) == 0)
7068c2ecf20Sopenharmony_ci			continue;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		if (do_file(file)) {
7098c2ecf20Sopenharmony_ci			fprintf(stderr, "%s: failed\n", file);
7108c2ecf20Sopenharmony_ci			++n_error;
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci	return !!n_error;
7148c2ecf20Sopenharmony_ci}
715