162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * recordmcount.c: construct a table of the locations of calls to 'mcount'
462306a36Sopenharmony_ci * so that ftrace can find them quickly.
562306a36Sopenharmony_ci * Copyright 2009 John F. Reiser <jreiser@BitWagon.com>.  All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Restructured to fit Linux format, as well as other updates:
862306a36Sopenharmony_ci *  Copyright 2010 Steven Rostedt <srostedt@redhat.com>, Red Hat Inc.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Strategy: alter the .o file in-place.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Append a new STRTAB that has the new section names, followed by a new array
1562306a36Sopenharmony_ci * ElfXX_Shdr[] that has the new section headers, followed by the section
1662306a36Sopenharmony_ci * contents for __mcount_loc and its relocations.  The old shstrtab strings,
1762306a36Sopenharmony_ci * and the old ElfXX_Shdr[] array, remain as "garbage" (commonly, a couple
1862306a36Sopenharmony_ci * kilobytes.)  Subsequent processing by /bin/ld (or the kernel module loader)
1962306a36Sopenharmony_ci * will ignore the garbage regions, because they are not designated by the
2062306a36Sopenharmony_ci * new .e_shoff nor the new ElfXX_Shdr[].  [In order to remove the garbage,
2162306a36Sopenharmony_ci * then use "ld -r" to create a new file that omits the garbage.]
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <sys/types.h>
2562306a36Sopenharmony_ci#include <sys/mman.h>
2662306a36Sopenharmony_ci#include <sys/stat.h>
2762306a36Sopenharmony_ci#include <getopt.h>
2862306a36Sopenharmony_ci#include <elf.h>
2962306a36Sopenharmony_ci#include <fcntl.h>
3062306a36Sopenharmony_ci#include <stdio.h>
3162306a36Sopenharmony_ci#include <stdlib.h>
3262306a36Sopenharmony_ci#include <string.h>
3362306a36Sopenharmony_ci#include <unistd.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifndef EM_AARCH64
3662306a36Sopenharmony_ci#define EM_AARCH64	183
3762306a36Sopenharmony_ci#define R_AARCH64_NONE		0
3862306a36Sopenharmony_ci#define R_AARCH64_ABS64	257
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#ifndef EM_LOONGARCH
4262306a36Sopenharmony_ci#define EM_LOONGARCH		258
4362306a36Sopenharmony_ci#define R_LARCH_32			1
4462306a36Sopenharmony_ci#define R_LARCH_64			2
4562306a36Sopenharmony_ci#define R_LARCH_MARK_LA			20
4662306a36Sopenharmony_ci#define R_LARCH_SOP_PUSH_PLT_PCREL	29
4762306a36Sopenharmony_ci#endif
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define R_ARM_PC24		1
5062306a36Sopenharmony_ci#define R_ARM_THM_CALL		10
5162306a36Sopenharmony_ci#define R_ARM_CALL		28
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define R_AARCH64_CALL26	283
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int fd_map;	/* File descriptor for file being modified. */
5662306a36Sopenharmony_cistatic int mmap_failed; /* Boolean flag. */
5762306a36Sopenharmony_cistatic char gpfx;	/* prefix for global symbol name (sometimes '_') */
5862306a36Sopenharmony_cistatic struct stat sb;	/* Remember .st_size, etc. */
5962306a36Sopenharmony_cistatic const char *altmcount;	/* alternate mcount symbol name */
6062306a36Sopenharmony_cistatic int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
6162306a36Sopenharmony_cistatic void *file_map;	/* pointer of the mapped file */
6262306a36Sopenharmony_cistatic void *file_end;	/* pointer to the end of the mapped file */
6362306a36Sopenharmony_cistatic int file_updated; /* flag to state file was changed */
6462306a36Sopenharmony_cistatic void *file_ptr;	/* current file pointer location */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void *file_append; /* added to the end of the file */
6762306a36Sopenharmony_cistatic size_t file_append_size; /* how much is added to end of file */
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Per-file resource cleanup when multiple files. */
7062306a36Sopenharmony_cistatic void file_append_cleanup(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	free(file_append);
7362306a36Sopenharmony_ci	file_append = NULL;
7462306a36Sopenharmony_ci	file_append_size = 0;
7562306a36Sopenharmony_ci	file_updated = 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void mmap_cleanup(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	if (!mmap_failed)
8162306a36Sopenharmony_ci		munmap(file_map, sb.st_size);
8262306a36Sopenharmony_ci	else
8362306a36Sopenharmony_ci		free(file_map);
8462306a36Sopenharmony_ci	file_map = NULL;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* ulseek, uwrite, ...:  Check return value for errors. */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic off_t ulseek(off_t const offset, int const whence)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	switch (whence) {
9262306a36Sopenharmony_ci	case SEEK_SET:
9362306a36Sopenharmony_ci		file_ptr = file_map + offset;
9462306a36Sopenharmony_ci		break;
9562306a36Sopenharmony_ci	case SEEK_CUR:
9662306a36Sopenharmony_ci		file_ptr += offset;
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci	case SEEK_END:
9962306a36Sopenharmony_ci		file_ptr = file_map + (sb.st_size - offset);
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	if (file_ptr < file_map) {
10362306a36Sopenharmony_ci		fprintf(stderr, "lseek: seek before file\n");
10462306a36Sopenharmony_ci		return -1;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	return file_ptr - file_map;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic ssize_t uwrite(void const *const buf, size_t const count)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	size_t cnt = count;
11262306a36Sopenharmony_ci	off_t idx = 0;
11362306a36Sopenharmony_ci	void *p = NULL;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	file_updated = 1;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (file_ptr + count >= file_end) {
11862306a36Sopenharmony_ci		off_t aoffset = (file_ptr + count) - file_end;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (aoffset > file_append_size) {
12162306a36Sopenharmony_ci			p = realloc(file_append, aoffset);
12262306a36Sopenharmony_ci			if (!p)
12362306a36Sopenharmony_ci				free(file_append);
12462306a36Sopenharmony_ci			file_append = p;
12562306a36Sopenharmony_ci			file_append_size = aoffset;
12662306a36Sopenharmony_ci		}
12762306a36Sopenharmony_ci		if (!file_append) {
12862306a36Sopenharmony_ci			perror("write");
12962306a36Sopenharmony_ci			file_append_cleanup();
13062306a36Sopenharmony_ci			mmap_cleanup();
13162306a36Sopenharmony_ci			return -1;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci		if (file_ptr < file_end) {
13462306a36Sopenharmony_ci			cnt = file_end - file_ptr;
13562306a36Sopenharmony_ci		} else {
13662306a36Sopenharmony_ci			cnt = 0;
13762306a36Sopenharmony_ci			idx = aoffset - count;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (cnt)
14262306a36Sopenharmony_ci		memcpy(file_ptr, buf, cnt);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (cnt < count)
14562306a36Sopenharmony_ci		memcpy(file_append + idx, buf + cnt, count - cnt);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	file_ptr += count;
14862306a36Sopenharmony_ci	return count;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void * umalloc(size_t size)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	void *const addr = malloc(size);
15462306a36Sopenharmony_ci	if (addr == 0) {
15562306a36Sopenharmony_ci		fprintf(stderr, "malloc failed: %zu bytes\n", size);
15662306a36Sopenharmony_ci		file_append_cleanup();
15762306a36Sopenharmony_ci		mmap_cleanup();
15862306a36Sopenharmony_ci		return NULL;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	return addr;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/*
16462306a36Sopenharmony_ci * Get the whole file as a programming convenience in order to avoid
16562306a36Sopenharmony_ci * malloc+lseek+read+free of many pieces.  If successful, then mmap
16662306a36Sopenharmony_ci * avoids copying unused pieces; else just read the whole file.
16762306a36Sopenharmony_ci * Open for both read and write; new info will be appended to the file.
16862306a36Sopenharmony_ci * Use MAP_PRIVATE so that a few changes to the in-memory ElfXX_Ehdr
16962306a36Sopenharmony_ci * do not propagate to the file until an explicit overwrite at the last.
17062306a36Sopenharmony_ci * This preserves most aspects of consistency (all except .st_size)
17162306a36Sopenharmony_ci * for simultaneous readers of the file while we are appending to it.
17262306a36Sopenharmony_ci * However, multiple writers still are bad.  We choose not to use
17362306a36Sopenharmony_ci * locking because it is expensive and the use case of kernel build
17462306a36Sopenharmony_ci * makes multiple writers unlikely.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_cistatic void *mmap_file(char const *fname)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	/* Avoid problems if early cleanup() */
17962306a36Sopenharmony_ci	fd_map = -1;
18062306a36Sopenharmony_ci	mmap_failed = 1;
18162306a36Sopenharmony_ci	file_map = NULL;
18262306a36Sopenharmony_ci	file_ptr = NULL;
18362306a36Sopenharmony_ci	file_updated = 0;
18462306a36Sopenharmony_ci	sb.st_size = 0;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	fd_map = open(fname, O_RDONLY);
18762306a36Sopenharmony_ci	if (fd_map < 0) {
18862306a36Sopenharmony_ci		perror(fname);
18962306a36Sopenharmony_ci		return NULL;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	if (fstat(fd_map, &sb) < 0) {
19262306a36Sopenharmony_ci		perror(fname);
19362306a36Sopenharmony_ci		goto out;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci	if (!S_ISREG(sb.st_mode)) {
19662306a36Sopenharmony_ci		fprintf(stderr, "not a regular file: %s\n", fname);
19762306a36Sopenharmony_ci		goto out;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
20062306a36Sopenharmony_ci			fd_map, 0);
20162306a36Sopenharmony_ci	if (file_map == MAP_FAILED) {
20262306a36Sopenharmony_ci		mmap_failed = 1;
20362306a36Sopenharmony_ci		file_map = umalloc(sb.st_size);
20462306a36Sopenharmony_ci		if (!file_map) {
20562306a36Sopenharmony_ci			perror(fname);
20662306a36Sopenharmony_ci			goto out;
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci		if (read(fd_map, file_map, sb.st_size) != sb.st_size) {
20962306a36Sopenharmony_ci			perror(fname);
21062306a36Sopenharmony_ci			free(file_map);
21162306a36Sopenharmony_ci			file_map = NULL;
21262306a36Sopenharmony_ci			goto out;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	} else
21562306a36Sopenharmony_ci		mmap_failed = 0;
21662306a36Sopenharmony_ciout:
21762306a36Sopenharmony_ci	close(fd_map);
21862306a36Sopenharmony_ci	fd_map = -1;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	file_end = file_map + sb.st_size;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return file_map;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 };
22762306a36Sopenharmony_cistatic unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 };
22862306a36Sopenharmony_cistatic unsigned char *ideal_nop;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic char rel_type_nop;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int (*make_nop)(void *map, size_t const offset);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int make_nop_x86(void *map, size_t const offset)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	uint32_t *ptr;
23762306a36Sopenharmony_ci	unsigned char *op;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */
24062306a36Sopenharmony_ci	ptr = map + offset;
24162306a36Sopenharmony_ci	if (*ptr != 0)
24262306a36Sopenharmony_ci		return -1;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	op = map + offset - 1;
24562306a36Sopenharmony_ci	if (*op != 0xe8)
24662306a36Sopenharmony_ci		return -1;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* convert to nop */
24962306a36Sopenharmony_ci	if (ulseek(offset - 1, SEEK_SET) < 0)
25062306a36Sopenharmony_ci		return -1;
25162306a36Sopenharmony_ci	if (uwrite(ideal_nop, 5) < 0)
25262306a36Sopenharmony_ci		return -1;
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */
25762306a36Sopenharmony_cistatic unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */
25862306a36Sopenharmony_cistatic unsigned char *ideal_nop4_arm;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic unsigned char bl_mcount_arm_le[4] = { 0xfe, 0xff, 0xff, 0xeb }; /* bl */
26162306a36Sopenharmony_cistatic unsigned char bl_mcount_arm_be[4] = { 0xeb, 0xff, 0xff, 0xfe }; /* bl */
26262306a36Sopenharmony_cistatic unsigned char *bl_mcount_arm;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic unsigned char push_arm_le[4] = { 0x04, 0xe0, 0x2d, 0xe5 }; /* push {lr} */
26562306a36Sopenharmony_cistatic unsigned char push_arm_be[4] = { 0xe5, 0x2d, 0xe0, 0x04 }; /* push {lr} */
26662306a36Sopenharmony_cistatic unsigned char *push_arm;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic unsigned char ideal_nop2_thumb_le[2] = { 0x00, 0xbf }; /* nop */
26962306a36Sopenharmony_cistatic unsigned char ideal_nop2_thumb_be[2] = { 0xbf, 0x00 }; /* nop */
27062306a36Sopenharmony_cistatic unsigned char *ideal_nop2_thumb;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic unsigned char push_bl_mcount_thumb_le[6] = { 0x00, 0xb5, 0xff, 0xf7, 0xfe, 0xff }; /* push {lr}, bl */
27362306a36Sopenharmony_cistatic unsigned char push_bl_mcount_thumb_be[6] = { 0xb5, 0x00, 0xf7, 0xff, 0xff, 0xfe }; /* push {lr}, bl */
27462306a36Sopenharmony_cistatic unsigned char *push_bl_mcount_thumb;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int make_nop_arm(void *map, size_t const offset)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	char *ptr;
27962306a36Sopenharmony_ci	int cnt = 1;
28062306a36Sopenharmony_ci	int nop_size;
28162306a36Sopenharmony_ci	size_t off = offset;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ptr = map + offset;
28462306a36Sopenharmony_ci	if (memcmp(ptr, bl_mcount_arm, 4) == 0) {
28562306a36Sopenharmony_ci		if (memcmp(ptr - 4, push_arm, 4) == 0) {
28662306a36Sopenharmony_ci			off -= 4;
28762306a36Sopenharmony_ci			cnt = 2;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		ideal_nop = ideal_nop4_arm;
29062306a36Sopenharmony_ci		nop_size = 4;
29162306a36Sopenharmony_ci	} else if (memcmp(ptr - 2, push_bl_mcount_thumb, 6) == 0) {
29262306a36Sopenharmony_ci		cnt = 3;
29362306a36Sopenharmony_ci		nop_size = 2;
29462306a36Sopenharmony_ci		off -= 2;
29562306a36Sopenharmony_ci		ideal_nop = ideal_nop2_thumb;
29662306a36Sopenharmony_ci	} else
29762306a36Sopenharmony_ci		return -1;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Convert to nop */
30062306a36Sopenharmony_ci	if (ulseek(off, SEEK_SET) < 0)
30162306a36Sopenharmony_ci		return -1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	do {
30462306a36Sopenharmony_ci		if (uwrite(ideal_nop, nop_size) < 0)
30562306a36Sopenharmony_ci			return -1;
30662306a36Sopenharmony_ci	} while (--cnt > 0);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic unsigned char ideal_nop4_arm64[4] = {0x1f, 0x20, 0x03, 0xd5};
31262306a36Sopenharmony_cistatic int make_nop_arm64(void *map, size_t const offset)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	uint32_t *ptr;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ptr = map + offset;
31762306a36Sopenharmony_ci	/* bl <_mcount> is 0x94000000 before relocation */
31862306a36Sopenharmony_ci	if (*ptr != 0x94000000)
31962306a36Sopenharmony_ci		return -1;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Convert to nop */
32262306a36Sopenharmony_ci	if (ulseek(offset, SEEK_SET) < 0)
32362306a36Sopenharmony_ci		return -1;
32462306a36Sopenharmony_ci	if (uwrite(ideal_nop, 4) < 0)
32562306a36Sopenharmony_ci		return -1;
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int write_file(const char *fname)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	char tmp_file[strlen(fname) + 4];
33262306a36Sopenharmony_ci	size_t n;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (!file_updated)
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	sprintf(tmp_file, "%s.rc", fname);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/*
34062306a36Sopenharmony_ci	 * After reading the entire file into memory, delete it
34162306a36Sopenharmony_ci	 * and write it back, to prevent weird side effects of modifying
34262306a36Sopenharmony_ci	 * an object file in place.
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
34562306a36Sopenharmony_ci	if (fd_map < 0) {
34662306a36Sopenharmony_ci		perror(fname);
34762306a36Sopenharmony_ci		return -1;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	n = write(fd_map, file_map, sb.st_size);
35062306a36Sopenharmony_ci	if (n != sb.st_size) {
35162306a36Sopenharmony_ci		perror("write");
35262306a36Sopenharmony_ci		close(fd_map);
35362306a36Sopenharmony_ci		return -1;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci	if (file_append_size) {
35662306a36Sopenharmony_ci		n = write(fd_map, file_append, file_append_size);
35762306a36Sopenharmony_ci		if (n != file_append_size) {
35862306a36Sopenharmony_ci			perror("write");
35962306a36Sopenharmony_ci			close(fd_map);
36062306a36Sopenharmony_ci			return -1;
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	close(fd_map);
36462306a36Sopenharmony_ci	if (rename(tmp_file, fname) < 0) {
36562306a36Sopenharmony_ci		perror(fname);
36662306a36Sopenharmony_ci		return -1;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/* w8rev, w8nat, ...: Handle endianness. */
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic uint64_t w8rev(uint64_t const x)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	return   ((0xff & (x >> (0 * 8))) << (7 * 8))
37662306a36Sopenharmony_ci	       | ((0xff & (x >> (1 * 8))) << (6 * 8))
37762306a36Sopenharmony_ci	       | ((0xff & (x >> (2 * 8))) << (5 * 8))
37862306a36Sopenharmony_ci	       | ((0xff & (x >> (3 * 8))) << (4 * 8))
37962306a36Sopenharmony_ci	       | ((0xff & (x >> (4 * 8))) << (3 * 8))
38062306a36Sopenharmony_ci	       | ((0xff & (x >> (5 * 8))) << (2 * 8))
38162306a36Sopenharmony_ci	       | ((0xff & (x >> (6 * 8))) << (1 * 8))
38262306a36Sopenharmony_ci	       | ((0xff & (x >> (7 * 8))) << (0 * 8));
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic uint32_t w4rev(uint32_t const x)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	return   ((0xff & (x >> (0 * 8))) << (3 * 8))
38862306a36Sopenharmony_ci	       | ((0xff & (x >> (1 * 8))) << (2 * 8))
38962306a36Sopenharmony_ci	       | ((0xff & (x >> (2 * 8))) << (1 * 8))
39062306a36Sopenharmony_ci	       | ((0xff & (x >> (3 * 8))) << (0 * 8));
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic uint32_t w2rev(uint16_t const x)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	return   ((0xff & (x >> (0 * 8))) << (1 * 8))
39662306a36Sopenharmony_ci	       | ((0xff & (x >> (1 * 8))) << (0 * 8));
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic uint64_t w8nat(uint64_t const x)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	return x;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic uint32_t w4nat(uint32_t const x)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	return x;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic uint32_t w2nat(uint16_t const x)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return x;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic uint64_t (*w8)(uint64_t);
41562306a36Sopenharmony_cistatic uint32_t (*w)(uint32_t);
41662306a36Sopenharmony_cistatic uint32_t (*w2)(uint16_t);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/* Names of the sections that could contain calls to mcount. */
41962306a36Sopenharmony_cistatic int is_mcounted_section_name(char const *const txtname)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	return strncmp(".text",          txtname, 5) == 0 ||
42262306a36Sopenharmony_ci		strcmp(".init.text",     txtname) == 0 ||
42362306a36Sopenharmony_ci		strcmp(".ref.text",      txtname) == 0 ||
42462306a36Sopenharmony_ci		strcmp(".sched.text",    txtname) == 0 ||
42562306a36Sopenharmony_ci		strcmp(".spinlock.text", txtname) == 0 ||
42662306a36Sopenharmony_ci		strcmp(".irqentry.text", txtname) == 0 ||
42762306a36Sopenharmony_ci		strcmp(".softirqentry.text", txtname) == 0 ||
42862306a36Sopenharmony_ci		strcmp(".kprobes.text", txtname) == 0 ||
42962306a36Sopenharmony_ci		strcmp(".cpuidle.text", txtname) == 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic char const *already_has_rel_mcount = "success"; /* our work here is done! */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* 32 bit and 64 bit are very similar */
43562306a36Sopenharmony_ci#include "recordmcount.h"
43662306a36Sopenharmony_ci#define RECORD_MCOUNT_64
43762306a36Sopenharmony_ci#include "recordmcount.h"
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int arm_is_fake_mcount(Elf32_Rel const *rp)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	switch (ELF32_R_TYPE(w(rp->r_info))) {
44262306a36Sopenharmony_ci	case R_ARM_THM_CALL:
44362306a36Sopenharmony_ci	case R_ARM_CALL:
44462306a36Sopenharmony_ci	case R_ARM_PC24:
44562306a36Sopenharmony_ci		return 0;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return 1;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int arm64_is_fake_mcount(Elf64_Rel const *rp)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	return ELF64_R_TYPE(w8(rp->r_info)) != R_AARCH64_CALL26;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic int LARCH32_is_fake_mcount(Elf32_Rel const *rp)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	switch (ELF64_R_TYPE(w(rp->r_info))) {
45962306a36Sopenharmony_ci	case R_LARCH_MARK_LA:
46062306a36Sopenharmony_ci	case R_LARCH_SOP_PUSH_PLT_PCREL:
46162306a36Sopenharmony_ci		return 0;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return 1;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int LARCH64_is_fake_mcount(Elf64_Rel const *rp)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	switch (ELF64_R_TYPE(w(rp->r_info))) {
47062306a36Sopenharmony_ci	case R_LARCH_MARK_LA:
47162306a36Sopenharmony_ci	case R_LARCH_SOP_PUSH_PLT_PCREL:
47262306a36Sopenharmony_ci		return 0;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return 1;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
47962306a36Sopenharmony_ci * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
48062306a36Sopenharmony_ci * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
48162306a36Sopenharmony_ci * to imply the order of the members; the spec does not say so.
48262306a36Sopenharmony_ci *	typedef unsigned char Elf64_Byte;
48362306a36Sopenharmony_ci * fails on MIPS64 because their <elf.h> already has it!
48462306a36Sopenharmony_ci */
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_citypedef uint8_t myElf64_Byte;		/* Type for a 8-bit quantity.  */
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciunion mips_r_info {
48962306a36Sopenharmony_ci	Elf64_Xword r_info;
49062306a36Sopenharmony_ci	struct {
49162306a36Sopenharmony_ci		Elf64_Word r_sym;		/* Symbol index.  */
49262306a36Sopenharmony_ci		myElf64_Byte r_ssym;		/* Special symbol.  */
49362306a36Sopenharmony_ci		myElf64_Byte r_type3;		/* Third relocation.  */
49462306a36Sopenharmony_ci		myElf64_Byte r_type2;		/* Second relocation.  */
49562306a36Sopenharmony_ci		myElf64_Byte r_type;		/* First relocation.  */
49662306a36Sopenharmony_ci	} r_mips;
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic uint64_t MIPS64_r_sym(Elf64_Rel const *rp)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	return w(((union mips_r_info){ .r_info = rp->r_info }).r_mips.r_sym);
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	rp->r_info = ((union mips_r_info){
50762306a36Sopenharmony_ci		.r_mips = { .r_sym = w(sym), .r_type = type }
50862306a36Sopenharmony_ci	}).r_info;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int do_file(char const *const fname)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	unsigned int reltype = 0;
51462306a36Sopenharmony_ci	Elf32_Ehdr *ehdr;
51562306a36Sopenharmony_ci	int rc = -1;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ehdr = mmap_file(fname);
51862306a36Sopenharmony_ci	if (!ehdr)
51962306a36Sopenharmony_ci		goto out;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	w = w4nat;
52262306a36Sopenharmony_ci	w2 = w2nat;
52362306a36Sopenharmony_ci	w8 = w8nat;
52462306a36Sopenharmony_ci	switch (ehdr->e_ident[EI_DATA]) {
52562306a36Sopenharmony_ci		static unsigned int const endian = 1;
52662306a36Sopenharmony_ci	default:
52762306a36Sopenharmony_ci		fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
52862306a36Sopenharmony_ci			ehdr->e_ident[EI_DATA], fname);
52962306a36Sopenharmony_ci		goto out;
53062306a36Sopenharmony_ci	case ELFDATA2LSB:
53162306a36Sopenharmony_ci		if (*(unsigned char const *)&endian != 1) {
53262306a36Sopenharmony_ci			/* main() is big endian, file.o is little endian. */
53362306a36Sopenharmony_ci			w = w4rev;
53462306a36Sopenharmony_ci			w2 = w2rev;
53562306a36Sopenharmony_ci			w8 = w8rev;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci		ideal_nop4_arm = ideal_nop4_arm_le;
53862306a36Sopenharmony_ci		bl_mcount_arm = bl_mcount_arm_le;
53962306a36Sopenharmony_ci		push_arm = push_arm_le;
54062306a36Sopenharmony_ci		ideal_nop2_thumb = ideal_nop2_thumb_le;
54162306a36Sopenharmony_ci		push_bl_mcount_thumb = push_bl_mcount_thumb_le;
54262306a36Sopenharmony_ci		break;
54362306a36Sopenharmony_ci	case ELFDATA2MSB:
54462306a36Sopenharmony_ci		if (*(unsigned char const *)&endian != 0) {
54562306a36Sopenharmony_ci			/* main() is little endian, file.o is big endian. */
54662306a36Sopenharmony_ci			w = w4rev;
54762306a36Sopenharmony_ci			w2 = w2rev;
54862306a36Sopenharmony_ci			w8 = w8rev;
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci		ideal_nop4_arm = ideal_nop4_arm_be;
55162306a36Sopenharmony_ci		bl_mcount_arm = bl_mcount_arm_be;
55262306a36Sopenharmony_ci		push_arm = push_arm_be;
55362306a36Sopenharmony_ci		ideal_nop2_thumb = ideal_nop2_thumb_be;
55462306a36Sopenharmony_ci		push_bl_mcount_thumb = push_bl_mcount_thumb_be;
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	}  /* end switch */
55762306a36Sopenharmony_ci	if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 ||
55862306a36Sopenharmony_ci	    w2(ehdr->e_type) != ET_REL ||
55962306a36Sopenharmony_ci	    ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
56062306a36Sopenharmony_ci		fprintf(stderr, "unrecognized ET_REL file %s\n", fname);
56162306a36Sopenharmony_ci		goto out;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	gpfx = '_';
56562306a36Sopenharmony_ci	switch (w2(ehdr->e_machine)) {
56662306a36Sopenharmony_ci	default:
56762306a36Sopenharmony_ci		fprintf(stderr, "unrecognized e_machine %u %s\n",
56862306a36Sopenharmony_ci			w2(ehdr->e_machine), fname);
56962306a36Sopenharmony_ci		goto out;
57062306a36Sopenharmony_ci	case EM_386:
57162306a36Sopenharmony_ci		reltype = R_386_32;
57262306a36Sopenharmony_ci		rel_type_nop = R_386_NONE;
57362306a36Sopenharmony_ci		make_nop = make_nop_x86;
57462306a36Sopenharmony_ci		ideal_nop = ideal_nop5_x86_32;
57562306a36Sopenharmony_ci		mcount_adjust_32 = -1;
57662306a36Sopenharmony_ci		gpfx = 0;
57762306a36Sopenharmony_ci		break;
57862306a36Sopenharmony_ci	case EM_ARM:
57962306a36Sopenharmony_ci		reltype = R_ARM_ABS32;
58062306a36Sopenharmony_ci		altmcount = "__gnu_mcount_nc";
58162306a36Sopenharmony_ci		make_nop = make_nop_arm;
58262306a36Sopenharmony_ci		rel_type_nop = R_ARM_NONE;
58362306a36Sopenharmony_ci		is_fake_mcount32 = arm_is_fake_mcount;
58462306a36Sopenharmony_ci		gpfx = 0;
58562306a36Sopenharmony_ci		break;
58662306a36Sopenharmony_ci	case EM_AARCH64:
58762306a36Sopenharmony_ci		reltype = R_AARCH64_ABS64;
58862306a36Sopenharmony_ci		make_nop = make_nop_arm64;
58962306a36Sopenharmony_ci		rel_type_nop = R_AARCH64_NONE;
59062306a36Sopenharmony_ci		ideal_nop = ideal_nop4_arm64;
59162306a36Sopenharmony_ci		is_fake_mcount64 = arm64_is_fake_mcount;
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	case EM_IA_64:	reltype = R_IA64_IMM64; break;
59462306a36Sopenharmony_ci	case EM_MIPS:	/* reltype: e_class    */ break;
59562306a36Sopenharmony_ci	case EM_LOONGARCH:	/* reltype: e_class    */ break;
59662306a36Sopenharmony_ci	case EM_PPC:	reltype = R_PPC_ADDR32; break;
59762306a36Sopenharmony_ci	case EM_PPC64:	reltype = R_PPC64_ADDR64; break;
59862306a36Sopenharmony_ci	case EM_S390:	/* reltype: e_class    */ break;
59962306a36Sopenharmony_ci	case EM_SH:	reltype = R_SH_DIR32; gpfx = 0; break;
60062306a36Sopenharmony_ci	case EM_SPARCV9: reltype = R_SPARC_64; break;
60162306a36Sopenharmony_ci	case EM_X86_64:
60262306a36Sopenharmony_ci		make_nop = make_nop_x86;
60362306a36Sopenharmony_ci		ideal_nop = ideal_nop5_x86_64;
60462306a36Sopenharmony_ci		reltype = R_X86_64_64;
60562306a36Sopenharmony_ci		rel_type_nop = R_X86_64_NONE;
60662306a36Sopenharmony_ci		mcount_adjust_64 = -1;
60762306a36Sopenharmony_ci		gpfx = 0;
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci	}  /* end switch */
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	switch (ehdr->e_ident[EI_CLASS]) {
61262306a36Sopenharmony_ci	default:
61362306a36Sopenharmony_ci		fprintf(stderr, "unrecognized ELF class %d %s\n",
61462306a36Sopenharmony_ci			ehdr->e_ident[EI_CLASS], fname);
61562306a36Sopenharmony_ci		goto out;
61662306a36Sopenharmony_ci	case ELFCLASS32:
61762306a36Sopenharmony_ci		if (w2(ehdr->e_ehsize) != sizeof(Elf32_Ehdr)
61862306a36Sopenharmony_ci		||  w2(ehdr->e_shentsize) != sizeof(Elf32_Shdr)) {
61962306a36Sopenharmony_ci			fprintf(stderr,
62062306a36Sopenharmony_ci				"unrecognized ET_REL file: %s\n", fname);
62162306a36Sopenharmony_ci			goto out;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci		if (w2(ehdr->e_machine) == EM_MIPS) {
62462306a36Sopenharmony_ci			reltype = R_MIPS_32;
62562306a36Sopenharmony_ci			is_fake_mcount32 = MIPS32_is_fake_mcount;
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci		if (w2(ehdr->e_machine) == EM_LOONGARCH) {
62862306a36Sopenharmony_ci			reltype = R_LARCH_32;
62962306a36Sopenharmony_ci			is_fake_mcount32 = LARCH32_is_fake_mcount;
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci		if (do32(ehdr, fname, reltype) < 0)
63262306a36Sopenharmony_ci			goto out;
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	case ELFCLASS64: {
63562306a36Sopenharmony_ci		Elf64_Ehdr *const ghdr = (Elf64_Ehdr *)ehdr;
63662306a36Sopenharmony_ci		if (w2(ghdr->e_ehsize) != sizeof(Elf64_Ehdr)
63762306a36Sopenharmony_ci		||  w2(ghdr->e_shentsize) != sizeof(Elf64_Shdr)) {
63862306a36Sopenharmony_ci			fprintf(stderr,
63962306a36Sopenharmony_ci				"unrecognized ET_REL file: %s\n", fname);
64062306a36Sopenharmony_ci			goto out;
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci		if (w2(ghdr->e_machine) == EM_S390) {
64362306a36Sopenharmony_ci			reltype = R_390_64;
64462306a36Sopenharmony_ci			mcount_adjust_64 = -14;
64562306a36Sopenharmony_ci		}
64662306a36Sopenharmony_ci		if (w2(ghdr->e_machine) == EM_MIPS) {
64762306a36Sopenharmony_ci			reltype = R_MIPS_64;
64862306a36Sopenharmony_ci			Elf64_r_sym = MIPS64_r_sym;
64962306a36Sopenharmony_ci			Elf64_r_info = MIPS64_r_info;
65062306a36Sopenharmony_ci			is_fake_mcount64 = MIPS64_is_fake_mcount;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci		if (w2(ghdr->e_machine) == EM_LOONGARCH) {
65362306a36Sopenharmony_ci			reltype = R_LARCH_64;
65462306a36Sopenharmony_ci			is_fake_mcount64 = LARCH64_is_fake_mcount;
65562306a36Sopenharmony_ci		}
65662306a36Sopenharmony_ci		if (do64(ghdr, fname, reltype) < 0)
65762306a36Sopenharmony_ci			goto out;
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci	}  /* end switch */
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	rc = write_file(fname);
66362306a36Sopenharmony_ciout:
66462306a36Sopenharmony_ci	file_append_cleanup();
66562306a36Sopenharmony_ci	mmap_cleanup();
66662306a36Sopenharmony_ci	return rc;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ciint main(int argc, char *argv[])
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	const char ftrace[] = "/ftrace.o";
67262306a36Sopenharmony_ci	int ftrace_size = sizeof(ftrace) - 1;
67362306a36Sopenharmony_ci	int n_error = 0;  /* gcc-4.3.0 false positive complaint */
67462306a36Sopenharmony_ci	int c;
67562306a36Sopenharmony_ci	int i;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	while ((c = getopt(argc, argv, "w")) >= 0) {
67862306a36Sopenharmony_ci		switch (c) {
67962306a36Sopenharmony_ci		case 'w':
68062306a36Sopenharmony_ci			warn_on_notrace_sect = 1;
68162306a36Sopenharmony_ci			break;
68262306a36Sopenharmony_ci		default:
68362306a36Sopenharmony_ci			fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
68462306a36Sopenharmony_ci			return 0;
68562306a36Sopenharmony_ci		}
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if ((argc - optind) < 1) {
68962306a36Sopenharmony_ci		fprintf(stderr, "usage: recordmcount [-w] file.o...\n");
69062306a36Sopenharmony_ci		return 0;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/* Process each file in turn, allowing deep failure. */
69462306a36Sopenharmony_ci	for (i = optind; i < argc; i++) {
69562306a36Sopenharmony_ci		char *file = argv[i];
69662306a36Sopenharmony_ci		int len;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci		/*
69962306a36Sopenharmony_ci		 * The file kernel/trace/ftrace.o references the mcount
70062306a36Sopenharmony_ci		 * function but does not call it. Since ftrace.o should
70162306a36Sopenharmony_ci		 * not be traced anyway, we just skip it.
70262306a36Sopenharmony_ci		 */
70362306a36Sopenharmony_ci		len = strlen(file);
70462306a36Sopenharmony_ci		if (len >= ftrace_size &&
70562306a36Sopenharmony_ci		    strcmp(file + (len - ftrace_size), ftrace) == 0)
70662306a36Sopenharmony_ci			continue;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci		if (do_file(file)) {
70962306a36Sopenharmony_ci			fprintf(stderr, "%s: failed\n", file);
71062306a36Sopenharmony_ci			++n_error;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	return !!n_error;
71462306a36Sopenharmony_ci}
715