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