Skip to content

Commit b3d09d0

Browse files
committed
arm64: scripts/sorttable: Implement sorting mcount_loc at boot for arm64
The mcount_loc section holds the addresses of the functions that get patched by ftrace when enabling function callbacks. It can contain tens of thousands of entries. These addresses must be sorted. If they are not sorted at compile time, they are sorted at boot. Sorting at boot does take some time and does have a small impact on boot performance. x86 and arm32 have the addresses in the mcount_loc section of the ELF file. But for arm64, the section just contains zeros. The .rela.dyn Elf_Rela section holds the addresses and they get patched at boot during the relocation phase. In order to sort these addresses, the Elf_Rela needs to be updated instead of the location in the binary that holds the mcount_loc section. Have the sorttable code, allocate an array to hold the functions, load the addresses from the Elf_Rela entries, sort them, then put them back in order into the Elf_rela entries so that they will be sorted at boot up without having to sort them during boot up. Cc: bpf <[email protected]> Cc: Masami Hiramatsu <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Mathieu Desnoyers <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Masahiro Yamada <[email protected]> Cc: Nathan Chancellor <[email protected]> Cc: Nicolas Schier <[email protected]> Cc: Zheng Yejian <[email protected]> Cc: Martin Kelly <[email protected]> Cc: Christophe Leroy <[email protected]> Cc: Josh Poimboeuf <[email protected]> Cc: Heiko Carstens <[email protected]> Cc: Will Deacon <[email protected]> Cc: Vasily Gorbik <[email protected]> Cc: Alexander Gordeev <[email protected]> Link: https://lore.kernel.org/[email protected] Acked-by: Catalin Marinas <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent 0ad2507 commit b3d09d0

File tree

2 files changed

+183
-3
lines changed

2 files changed

+183
-3
lines changed

arch/arm64/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ config ARM64
217217
if DYNAMIC_FTRACE_WITH_ARGS
218218
select HAVE_SAMPLE_FTRACE_DIRECT
219219
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI
220+
select HAVE_BUILDTIME_MCOUNT_SORT
220221
select HAVE_EFFICIENT_UNALIGNED_ACCESS
221222
select HAVE_GUP_FAST
222223
select HAVE_FTRACE_GRAPH_FUNC

scripts/sorttable.c

Lines changed: 182 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <fcntl.h>
2929
#include <stdio.h>
3030
#include <stdlib.h>
31+
#include <stdbool.h>
3132
#include <string.h>
3233
#include <unistd.h>
3334
#include <errno.h>
@@ -79,10 +80,16 @@ typedef union {
7980
Elf64_Sym e64;
8081
} Elf_Sym;
8182

83+
typedef union {
84+
Elf32_Rela e32;
85+
Elf64_Rela e64;
86+
} Elf_Rela;
87+
8288
static uint32_t (*r)(const uint32_t *);
8389
static uint16_t (*r2)(const uint16_t *);
8490
static uint64_t (*r8)(const uint64_t *);
8591
static void (*w)(uint32_t, uint32_t *);
92+
static void (*w8)(uint64_t, uint64_t *);
8693
typedef void (*table_sort_t)(char *, int);
8794

8895
static struct elf_funcs {
@@ -102,6 +109,10 @@ static struct elf_funcs {
102109
uint32_t (*sym_name)(Elf_Sym *sym);
103110
uint64_t (*sym_value)(Elf_Sym *sym);
104111
uint16_t (*sym_shndx)(Elf_Sym *sym);
112+
uint64_t (*rela_offset)(Elf_Rela *rela);
113+
uint64_t (*rela_info)(Elf_Rela *rela);
114+
uint64_t (*rela_addend)(Elf_Rela *rela);
115+
void (*rela_write_addend)(Elf_Rela *rela, uint64_t val);
105116
} e;
106117

107118
static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr)
@@ -262,6 +273,38 @@ SYM_ADDR(value)
262273
SYM_WORD(name)
263274
SYM_HALF(shndx)
264275

276+
#define __maybe_unused __attribute__((__unused__))
277+
278+
#define RELA_ADDR(fn_name) \
279+
static uint64_t rela64_##fn_name(Elf_Rela *rela) \
280+
{ \
281+
return r8((uint64_t *)&rela->e64.r_##fn_name); \
282+
} \
283+
\
284+
static uint64_t rela32_##fn_name(Elf_Rela *rela) \
285+
{ \
286+
return r((uint32_t *)&rela->e32.r_##fn_name); \
287+
} \
288+
\
289+
static uint64_t __maybe_unused rela_##fn_name(Elf_Rela *rela) \
290+
{ \
291+
return e.rela_##fn_name(rela); \
292+
}
293+
294+
RELA_ADDR(offset)
295+
RELA_ADDR(info)
296+
RELA_ADDR(addend)
297+
298+
static void rela64_write_addend(Elf_Rela *rela, uint64_t val)
299+
{
300+
w8(val, (uint64_t *)&rela->e64.r_addend);
301+
}
302+
303+
static void rela32_write_addend(Elf_Rela *rela, uint64_t val)
304+
{
305+
w(val, (uint32_t *)&rela->e32.r_addend);
306+
}
307+
265308
/*
266309
* Get the whole file as a programming convenience in order to avoid
267310
* malloc+lseek+read+free of many pieces. If successful, then mmap
@@ -341,6 +384,16 @@ static void wle(uint32_t val, uint32_t *x)
341384
put_unaligned_le32(val, x);
342385
}
343386

387+
static void w8be(uint64_t val, uint64_t *x)
388+
{
389+
put_unaligned_be64(val, x);
390+
}
391+
392+
static void w8le(uint64_t val, uint64_t *x)
393+
{
394+
put_unaligned_le64(val, x);
395+
}
396+
344397
/*
345398
* Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
346399
* the way to -256..-1, to avoid conflicting with real section
@@ -398,13 +451,12 @@ static inline void *get_index(void *start, int entsize, int index)
398451
static int extable_ent_size;
399452
static int long_size;
400453

454+
#define ERRSTR_MAXSZ 256
401455

402456
#ifdef UNWINDER_ORC_ENABLED
403457
/* ORC unwinder only support X86_64 */
404458
#include <asm/orc_types.h>
405459

406-
#define ERRSTR_MAXSZ 256
407-
408460
static char g_err[ERRSTR_MAXSZ];
409461
static int *g_orc_ip_table;
410462
static struct orc_entry *g_orc_table;
@@ -499,7 +551,19 @@ static void *sort_orctable(void *arg)
499551
#endif
500552

501553
#ifdef MCOUNT_SORT_ENABLED
554+
555+
/* Only used for sorting mcount table */
556+
static void rela_write_addend(Elf_Rela *rela, uint64_t val)
557+
{
558+
e.rela_write_addend(rela, val);
559+
}
560+
502561
static pthread_t mcount_sort_thread;
562+
static bool sort_reloc;
563+
564+
static long rela_type;
565+
566+
static char m_err[ERRSTR_MAXSZ];
503567

504568
struct elf_mcount_loc {
505569
Elf_Ehdr *ehdr;
@@ -508,6 +572,103 @@ struct elf_mcount_loc {
508572
uint64_t stop_mcount_loc;
509573
};
510574

575+
/* Sort the relocations not the address itself */
576+
static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)
577+
{
578+
Elf_Shdr *shdr_start;
579+
Elf_Rela *rel;
580+
unsigned int shnum;
581+
unsigned int count;
582+
int shentsize;
583+
void *vals;
584+
void *ptr;
585+
586+
shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
587+
shentsize = ehdr_shentsize(ehdr);
588+
589+
vals = malloc(long_size * size);
590+
if (!vals) {
591+
snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array");
592+
pthread_exit(m_err);
593+
return NULL;
594+
}
595+
596+
ptr = vals;
597+
598+
shnum = ehdr_shnum(ehdr);
599+
if (shnum == SHN_UNDEF)
600+
shnum = shdr_size(shdr_start);
601+
602+
for (int i = 0; i < shnum; i++) {
603+
Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
604+
void *end;
605+
606+
if (shdr_type(shdr) != SHT_RELA)
607+
continue;
608+
609+
rel = (void *)ehdr + shdr_offset(shdr);
610+
end = (void *)rel + shdr_size(shdr);
611+
612+
for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) {
613+
uint64_t offset = rela_offset(rel);
614+
615+
if (offset >= start_loc && offset < start_loc + size) {
616+
if (ptr + long_size > vals + size) {
617+
free(vals);
618+
snprintf(m_err, ERRSTR_MAXSZ,
619+
"Too many relocations");
620+
pthread_exit(m_err);
621+
return NULL;
622+
}
623+
624+
/* Make sure this has the correct type */
625+
if (rela_info(rel) != rela_type) {
626+
free(vals);
627+
snprintf(m_err, ERRSTR_MAXSZ,
628+
"rela has type %lx but expected %lx\n",
629+
(long)rela_info(rel), rela_type);
630+
pthread_exit(m_err);
631+
return NULL;
632+
}
633+
634+
if (long_size == 4)
635+
*(uint32_t *)ptr = rela_addend(rel);
636+
else
637+
*(uint64_t *)ptr = rela_addend(rel);
638+
ptr += long_size;
639+
}
640+
}
641+
}
642+
count = ptr - vals;
643+
qsort(vals, count / long_size, long_size, compare_extable);
644+
645+
ptr = vals;
646+
for (int i = 0; i < shnum; i++) {
647+
Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
648+
void *end;
649+
650+
if (shdr_type(shdr) != SHT_RELA)
651+
continue;
652+
653+
rel = (void *)ehdr + shdr_offset(shdr);
654+
end = (void *)rel + shdr_size(shdr);
655+
656+
for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) {
657+
uint64_t offset = rela_offset(rel);
658+
659+
if (offset >= start_loc && offset < start_loc + size) {
660+
if (long_size == 4)
661+
rela_write_addend(rel, *(uint32_t *)ptr);
662+
else
663+
rela_write_addend(rel, *(uint64_t *)ptr);
664+
ptr += long_size;
665+
}
666+
}
667+
}
668+
free(vals);
669+
return NULL;
670+
}
671+
511672
/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
512673
static void *sort_mcount_loc(void *arg)
513674
{
@@ -517,6 +678,9 @@ static void *sort_mcount_loc(void *arg)
517678
uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc;
518679
unsigned char *start_loc = (void *)emloc->ehdr + offset;
519680

681+
if (sort_reloc)
682+
return sort_relocs(emloc->ehdr, emloc->start_mcount_loc, count);
683+
520684
qsort(start_loc, count/long_size, long_size, compare_extable);
521685
return NULL;
522686
}
@@ -866,12 +1030,14 @@ static int do_file(char const *const fname, void *addr)
8661030
r2 = r2le;
8671031
r8 = r8le;
8681032
w = wle;
1033+
w8 = w8le;
8691034
break;
8701035
case ELFDATA2MSB:
8711036
r = rbe;
8721037
r2 = r2be;
8731038
r8 = r8be;
8741039
w = wbe;
1040+
w8 = w8be;
8751041
break;
8761042
default:
8771043
fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
@@ -887,8 +1053,13 @@ static int do_file(char const *const fname, void *addr)
8871053
}
8881054

8891055
switch (r2(&ehdr->e32.e_machine)) {
890-
case EM_386:
8911056
case EM_AARCH64:
1057+
#ifdef MCOUNT_SORT_ENABLED
1058+
sort_reloc = true;
1059+
rela_type = 0x403;
1060+
#endif
1061+
/* fallthrough */
1062+
case EM_386:
8921063
case EM_LOONGARCH:
8931064
case EM_RISCV:
8941065
case EM_S390:
@@ -932,6 +1103,10 @@ static int do_file(char const *const fname, void *addr)
9321103
.sym_name = sym32_name,
9331104
.sym_value = sym32_value,
9341105
.sym_shndx = sym32_shndx,
1106+
.rela_offset = rela32_offset,
1107+
.rela_info = rela32_info,
1108+
.rela_addend = rela32_addend,
1109+
.rela_write_addend = rela32_write_addend,
9351110
};
9361111

9371112
e = efuncs;
@@ -965,6 +1140,10 @@ static int do_file(char const *const fname, void *addr)
9651140
.sym_name = sym64_name,
9661141
.sym_value = sym64_value,
9671142
.sym_shndx = sym64_shndx,
1143+
.rela_offset = rela64_offset,
1144+
.rela_info = rela64_info,
1145+
.rela_addend = rela64_addend,
1146+
.rela_write_addend = rela64_write_addend,
9681147
};
9691148

9701149
e = efuncs;

0 commit comments

Comments
 (0)