ftrace: scripts/sorttable: Add arm64 and remove weak functions
Steven Rostedt
maintainercover ftrace: scripts/sorttable: Add arm64 and remove weak functions
git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
sorttable/for-next
Head SHA1: 264143c4e54412095f4b615e65bf736fc3c60af0
Steven Rostedt (6):
arm64: scripts/sorttable: Implement sorting mcount_loc at boot for arm64 scripts/sorttable: Have mcount rela sort use direct values scripts/sorttable: Always use an array for the mcount_loc sorting scripts/sorttable: Zero out weak functions in mcount_loc table
ftrace: Update the mcount_loc check of skipped entries
ftrace: Have ftrace pages output reflect freed pages
----
arch/arm64/Kconfig | 1 + kernel/trace/ftrace.c | 44 +++++- scripts/link-vmlinux.sh | 4 +- scripts/sorttable.c | 401 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 436 insertions(+), 14 deletions(-)
Steven Rostedt
maintainerPATCH 1/6 arm64: scripts/sorttable: Implement sorting mcount_loc at boot for arm64
From: Steven Rostedt <[email protected]>
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.
--- arch/arm64/Kconfig | 1 + scripts/sorttable.c | 185 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 183 insertions(+), 3 deletions(-)
arch/arm64/Kconfig +1 -0
@@ -217,6 +217,7 @@ config ARM64 if DYNAMIC_FTRACE_WITH_ARGS select HAVE_SAMPLE_FTRACE_DIRECT select HAVE_SAMPLE_FTRACE_DIRECT_MULTI + select HAVE_BUILDTIME_MCOUNT_SORT select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC
scripts/sorttable.c +182 -3
280 lines not on this page · raw message
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 2/6 scripts/sorttable: Have mcount rela sort use direct values
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The mcount_loc sorting for when the values are stored in the Elf_Rela entries uses the compare_extable() function to do the compares in the qsort(). That function does handle byte swapping if the machine being compiled for is a different endian than the host machine. But the sort_relocs() function sorts an array that pulled in the values from the Elf_Rela section and has already done the swapping.
Create two new compare functions that will sort the direct values. One will sort 32 bit values and the other will sort the 64 bit value. One of these will be assigned to a compare_values function pointer and that will be used for sorting the Elf_Rela mcount values.
--- scripts/sorttable.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
scripts/sorttable.c +25 -1
@@ -552,6 +552,28 @@ static void *sort_orctable(void *arg)#ifdef MCOUNT_SORT_ENABLED +static int compare_values_64 (const void *a,const void *b) +{ +uint64_t av = *(uint64_t *)a; +uint64_t bv = *(uint64_t *)b; + +if (av < bv) +return -1 ; +return av > bv; +} + +static int compare_values_32 (const void *a,const void *b) +{ +uint32_t av = *(uint32_t *)a; +uint32_t bv = *(uint32_t *)b; + +if (av < bv) +return -1 ; +return av > bv; +} + +static int (*compare_values)(const void *a,const void *b); +/* Only used for sorting mcount table */ static void rela_write_addend (Elf_Rela *rela,uint64_t val) {
@@ -583,6 +605,8 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)void *vals;void *ptr; + compare_values = long_size ==4 ? compare_values_32 : compare_values_64; + shdr_start = (Elf_Shdr *)((char *)ehdr +ehdr_shoff (ehdr)); shentsize =ehdr_shentsize (ehdr);
@@ -640,7 +664,7 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) } } count = ptr - vals; -qsort (vals, count / long_size, long_size, compare_extable); +qsort (vals, count / long_size, long_size, compare_values); ptr = vals;for (int i =0 ; i < shnum; i++) {
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 3/6 scripts/sorttable: Always use an array for the mcount_loc sorting
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The sorting of the mcount_loc section is done directly to the section for x86 and arm32 but it uses a separate array for arm64 as arm64 has the values for the mcount_loc stored in the rela sections of the vmlinux ELF file.
In order to use the same code to remove weak functions, always use a separate array to do the sorting. This requires splitting up the filling of the array into one function and the placing the contents of the array back into the rela sections or into the mcount_loc section into a separate file.
--- scripts/sorttable.c | 122 ++++++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 32 deletions(-)
scripts/sorttable.c +90 -32
@@ -594,31 +594,19 @@ struct elf_mcount_loc {uint64_t stop_mcount_loc; }; -/* Sort the relocations not the address itself */ -static void *sort_relocs (Elf_Ehdr *ehdr,uint64_t start_loc,uint64_t size) +/* Fill the array with the content of the relocs */ +static int fill_relocs (void *ptr,uint64_t size,Elf_Ehdr *ehdr,uint64_t start_loc) {Elf_Shdr *shdr_start;Elf_Rela *rel;unsigned int shnum; -unsigned int count; +unsigned int count =0 ;int shentsize; -void *vals; -void *ptr; - - compare_values = long_size ==4 ? compare_values_32 : compare_values_64; +void *array_end = ptr + size; shdr_start = (Elf_Shdr *)((char *)ehdr +ehdr_shoff (ehdr)); shentsize =ehdr_shentsize (ehdr); - vals =malloc (long_size * size); -if (!vals) { -snprintf (m_err,ERRSTR_MAXSZ ,"Failed to allocate sort array" ); -pthread_exit (m_err); -return NULL ; - } - - ptr = vals; - shnum =ehdr_shnum (ehdr);if (shnum ==SHN_UNDEF ) shnum =shdr_size (shdr_start);
@@ -637,22 +625,18 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)uint64_t offset =rela_offset (rel);if (offset >= start_loc && offset < start_loc + size) { -if (ptr + long_size > vals + size) { -free (vals); +if (ptr + long_size > array_end) {snprintf (m_err,ERRSTR_MAXSZ ,"Too many relocations" ); -pthread_exit (m_err); -return NULL ; +return -1 ; }/* Make sure this has the correct type */ if (rela_info (rel) != rela_type) { -free (vals);snprintf (m_err,ERRSTR_MAXSZ ,"rela has type %lx but expected %lx\n" , (long )rela_info (rel), rela_type); -pthread_exit (m_err); -return NULL ; +return -1 ; }if (long_size ==4 )
@@ -660,13 +644,28 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)else *(uint64_t *)ptr =rela_addend (rel); ptr += long_size; + count++; } } } - count = ptr - vals; -qsort (vals, count / long_size, long_size, compare_values); +return count; +} + +/* Put the sorted vals back into the relocation elements */ +static void replace_relocs (void *ptr,uint64_t size,Elf_Ehdr *ehdr,uint64_t start_loc) +{ +Elf_Shdr *shdr_start; +Elf_Rela *rel; +unsigned int shnum; +int shentsize; + + shdr_start = (Elf_Shdr *)((char *)ehdr +ehdr_shoff (ehdr)); + shentsize =ehdr_shentsize (ehdr); + + shnum =ehdr_shnum (ehdr); +if (shnum ==SHN_UNDEF ) + shnum =shdr_size (shdr_start); - ptr = vals;for (int i =0 ; i < shnum; i++) {Elf_Shdr *shdr =get_index (shdr_start, shentsize, i);void *end;
@@ -689,8 +688,32 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size) } } } -free (vals); -return NULL ; +} + +static int fill_addrs (void *ptr,uint64_t size,void *addrs) +{ +void *end = ptr + size; +int count =0 ; + +for (; ptr < end; ptr += long_size, addrs += long_size, count++) { +if (long_size ==4 ) + *(uint32_t *)ptr =r (addrs); +else + *(uint64_t *)ptr =r8 (addrs); + } +return count; +} + +static void replace_addrs (void *ptr,uint64_t size,void *addrs) +{ +void *end = ptr + size; + +for (; ptr < end; ptr += long_size, addrs += long_size) { +if (long_size ==4 ) +w (*(uint32_t *)ptr, addrs); +else +w8 (*(uint64_t *)ptr, addrs); + } }/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
@@ -699,14 +722,49 @@ static void *sort_mcount_loc(void *arg)struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg;uint64_t offset = emloc->start_mcount_loc -shdr_addr (emloc->init_data_sec) +shdr_offset (emloc->init_data_sec); -uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; +uint64_t size = emloc->stop_mcount_loc - emloc->start_mcount_loc;unsigned char *start_loc = (void *)emloc->ehdr + offset; +Elf_Ehdr *ehdr = emloc->ehdr; +void *e_msg =NULL ; +void *vals; +int count; + + vals =malloc (long_size * size); +if (!vals) { +snprintf (m_err,ERRSTR_MAXSZ ,"Failed to allocate sort array" ); +pthread_exit (m_err); + }if (sort_reloc) -return sort_relocs (emloc->ehdr, emloc->start_mcount_loc, count); + count =fill_relocs (vals, size, ehdr, emloc->start_mcount_loc); +else + count =fill_addrs (vals, size, start_loc); + +if (count <0 ) { + e_msg = m_err; + goto out; + } + +if (count != size / long_size) { +snprintf (m_err,ERRSTR_MAXSZ ,"Expected %u mcount elements but found %u\n" , + (int )(size / long_size), count); + e_msg = m_err; + goto out; + } + + compare_values = long_size ==4 ? compare_values_32 : compare_values_64; + +qsort (vals, count, long_size, compare_values); + +if (sort_reloc) +replace_relocs (vals, size, ehdr, emloc->start_mcount_loc); +else +replace_addrs (vals, size, start_loc); + +out: +free (vals); -qsort (start_loc, count/long_size, long_size, compare_extable); -return NULL ; +pthread_exit (e_msg); }/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 4/6 scripts/sorttable: Zero out weak functions in mcount_loc table
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
When a function is annotated as "weak" and is overridden, the code is not removed. If it is traced, the fentry/mcount location in the weak function will be referenced by the "__mcount_loc" section. This will then be added to the available_filter_functions list. Since only the address of the functions are listed, to find the name to show, a search of kallsyms is used.
Since kallsyms will return the function by simply finding the function that the address is after but before the next function, an address of a weak function will show up as the function before it. This is because kallsyms does not save names of weak functions. This has caused issues in the past, as now the traced weak function will be listed in available_filter_functions with the name of the function before it.
At best, this will cause the previous function's name to be listed twice. At worse, if the previous function was marked notrace, it will now show up as a function that can be traced. Note that it only shows up that it can be traced but will not be if enabled, which causes confusion.
https://lore.kernel.org/all/[email protected]/
The commit b39181f7c6907 ("ftrace: Add FTRACE_MCOUNT_MAX_OFFSET to avoid adding weak function") was a workaround to this by checking the function address before printing its name. If the address was too far from the function given by the name then instead of printing the name it would
print: __ftrace_invalid_address___<invalid-offset>
The real issue is that these invalid addresses are listed in the ftrace table look up which available_filter_functions is derived from. A place holder must be listed in that file because set_ftrace_filter may take a series of indexes into that file instead of names to be able to do O(1) lookups to enable filtering (many tools use this method).
Even if kallsyms saved the size of the function, it does not remove the need of having these place holders. The real solution is to not add a weak function into the ftrace table in the first place.
To solve this, the sorttable.c code that sorts the mcount regions during the build is modified to take a "nm -S vmlinux" input, sort it, and any function listed in the mcount_loc section that is not within a boundary of the function list given by nm is considered a weak function and is zeroed out.
Note, this does not mean they will remain zero when booting as KASLR will still shift those addresses. To handle this, the entries in the mcount_loc section will be ignored if they are zero or match the kaslr_offset() value.
Before:
~# grep __ftrace_invalid_address___ /sys/kernel/tracing/available_filter_functions | wc -l 551
After:
~# grep __ftrace_invalid_address___ /sys/kernel/tracing/available_filter_functions | wc -l 0
--- kernel/trace/ftrace.c | 6 +- scripts/link-vmlinux.sh | 4 +- scripts/sorttable.c | 128 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 4 deletions(-)
kernel/trace/ftrace.c +5 -1
@@ -7004,6 +7004,7 @@ static int ftrace_process_locs(struct module *mod,unsigned long count;unsigned long *p;unsigned long addr; +unsigned long kaslr;unsigned long flags =0 ;/* Shut up gcc */ int ret = -ENOMEM ;
@@ -7052,6 +7053,9 @@ static int ftrace_process_locs(struct module *mod, ftrace_pages->next = start_pg; } +/* For zeroed locations that were shifted for core kernel */ + kaslr = !mod ?kaslr_offset () :0 ; + p = start; pg = start_pg;while (p < end) {
@@ -7063,7 +7067,7 @@ static int ftrace_process_locs(struct module *mod,* object files to satisfy alignments. * Skip any NULL pointers. */ -if (!addr) { +if (!addr || addr == kaslr) { skipped++;continue ; }
scripts/link-vmlinux.sh +3 -1
@@ -177,12 +177,14 @@ mksysmap() sorttable() { - ${objtree}/scripts/sorttable ${1} + ${NM} -S ${1} > .tmp_vmlinux.nm-sort + ${objtree}/scripts/sorttable -s .tmp_vmlinux.nm-sort ${1} } cleanup() { rm -f .btf.* + rm -f .tmp_vmlinux.nm-sort rm -f System.map rm -f vmlinux rm -f vmlinux.map
scripts/sorttable.c +126 -2
158 lines not on this page · raw message
Signature · 1 line
2.47.2
Nathan Chancellor
maintainerRe: [for-next][PATCH 4/6] scripts/sorttable: Zero out weak functions in mcount_loc table
Hi Steve,
Quoted 23 lines · Steven Rostedt
kernel/trace/ftrace.c +4
-0@@ -7004,6 +7004,7 @@ static int ftrace_process_locs(struct module *mod,unsigned long count;unsigned long *p;unsigned long addr; +unsigned long kaslr;unsigned long flags =0 ;/* Shut up gcc */ int ret = -ENOMEM ;@@ -7052,6 +7053,9 @@ static int ftrace_process_locs(struct module *mod, ftrace_pages->next = start_pg; } +/* For zeroed locations that were shifted for core kernel */ + kaslr = !mod ?kaslr_offset () :0 ; + p = start; pg = start_pg;while (p < end) {
kernel/trace/ftrace.c static int ftrace_process_locs(struct module *mod, · line 7073
Quoted 8 lines
kernel/trace/ftrace.c +1
-1@@ -7063,7 +7067,7 @@ static int ftrace_process_locs(struct module *mod,* object files to satisfy alignments. * Skip any NULL pointers. */ -if (!addr) { +if (!addr || addr == kaslr) { skipped++;continue ; }
Our CI and KernelCI reports that this change as commit ef378c3b8233 ("scripts/sorttable: Zero out weak functions in mcount_loc table") in next-20250224 breaks when an architecture does not have kaslr_offset() defined:
$ make -skj"$(nproc)" ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- mrproper allmodconfig kernel/trace/ftrace.o
kernel/trace/ftrace.c: In function 'ftrace_process_locs':
kernel/trace/ftrace.c:7074:24: error: implicit declaration of function 'kaslr_offset' [-Wimplicit-function-declaration]
7074 | kaslr = !mod ? kaslr_offset() : 0;
| ^~~~~~~~~~~~https://lore.kernel.org/CACo-S-0GeJjWWcrGvos_Avg2FwGU2tj2QZpgoHOvPT+YbyknSg@mail.gmail.com/
Cheers,
Nathan
Steven Rostedt
maintainerRe: [for-next][PATCH 4/6] scripts/sorttable: Zero out weak functions in mcount_loc table
Quoted 5 lines · On Mon, 24 Feb 2025 10:08:05 -0800 Nathan Chancellor <[email protected]> wrote:
Our CI and KernelCI reports that this change as commit
ef378c3b8233("scripts/sorttable: Zero out weak functions inmcount_loctable") in next-20250224 breaks when an architecture does not havekaslr_offset()defined:
Quoted 7 lines
$ make -skj"$(nproc)" ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- mrproper allmodconfig kernel/trace/ftrace.o kernel/trace/ftrace.c: In function 'ftrace_process_locs': kernel/trace/ftrace.c:7074:24: error: implicit declaration of function 'kaslr_offset' [-Wimplicit-function-declaration] 7074 | kaslr = !mod ? kaslr_offset() : 0; | ^~~~~~~~~~~~https://lore.kernel.org/CACo-S-0GeJjWWcrGvos_Avg2FwGU2tj2QZpgoHOvPT+YbyknSg@mail.gmail.com/
Thanks, I'll add a patch to put an #ifdef around it.
Now the question is, can it still change the address of it with out kaslr_offset()?
-- Steve
Nathan Chancellor
maintainerRe: [for-next][PATCH 4/6] scripts/sorttable: Zero out weak functions in mcount_loc table
In reply to Steven RostedtHi Steve,
Quoted 50 lines · Steven Rostedt
When a function is annotated as "weak" and is overridden, the code is not removed. If it is traced, the fentry/mcount location in the weak function will be referenced by the "
__mcount_loc" section. This will then be added to theavailable_filter_functionslist. Since only the address of the functions are listed, to find the name to show, a search of kallsyms is used.Since kallsyms will return the function by simply finding the function that the address is after but before the next function, an address of a weak function will show up as the function before it. This is because kallsyms does not save names of weak functions. This has caused issues in the past, as now the traced weak function will be listed in
available_filter_functionswith the name of the function before it.At best, this will cause the previous function's name to be listed twice. At worse, if the previous function was marked notrace, it will now show up as a function that can be traced. Note that it only shows up that it can be traced but will not be if enabled, which causes confusion.
https://lore.kernel.org/all/[email protected]/
The commit
b39181f7c6907("ftrace: AddFTRACE_MCOUNT_MAX_OFFSETto avoid adding weak function") was a workaround to this by checking the function address before printing its name. If the address was too far from the function given by the name then instead of printing the name it would
print: __ftrace_invalid_address___<invalid-offset>The real issue is that these invalid addresses are listed in the ftrace table look up which
available_filter_functionsis derived from. A place holder must be listed in that file becauseset_ftrace_filtermay take a series of indexes into that file instead of names to be able to do O(1) lookups to enable filtering (many tools use this method).Even if kallsyms saved the size of the function, it does not remove the need of having these place holders. The real solution is to not add a weak function into the ftrace table in the first place.
To solve this, the
sorttable.ccode that sorts the mcount regions during the build is modified to take a "nm -S vmlinux" input, sort it, and any function listed in themcount_locsection that is not within a boundary of the function list given by nm is considered a weak function and is zeroed out.Note, this does not mean they will remain zero when booting as KASLR will still shift those addresses. To handle this, the entries in the
mcount_locsection will be ignored if they are zero or match thekaslr_offset()value.Before:
Quoted 7 lines
~# grep
__ftrace_invalid_address___/sys/kernel/tracing/available_filter_functions| wc -l 551After:
~# grep
__ftrace_invalid_address___/sys/kernel/tracing/available_filter_functions| wc -l 0
I am also seeing a crash when booting arm64 with certain configurations that I don't see at the parent change.
$ printf 'CONFIG_%s=y\n' FTRACE FUNCTION_TRACER >kernel/configs/repro.config
$ make -skj"$(nproc)" ARCH=arm64 CROSS_COMPILE=aarch64-linux- mrproper virtconfig repro.config Image.gz
$ qemu-system-aarch64 \
-display none \
-nodefaults \
-cpu max,pauth-impdef=true \
-machine virt,gic-version=max,virtualization=true \
-append 'console=ttyAMA0 earlycon' \
-kernel arch/arm64/boot/Image.gz \
-initrd rootfs.cpio \
-m 512m \
-serial mon:stdio
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510]
[ 0.000000] Linux version 6.14.0-rc4-next-20250224-dirty (nathan@ax162) (aarch64-linux-gcc (GCC) 14.2.0, GNU ld (GNU Binutils) 2.42) #1 SMP PREEMPT Mon Feb 24 18:47:59 PST 2025
...
[ 0.000000] ------------[ cut here ]------------
[ 0.000000] kernel BUG at arch/arm64/kernel/patching.c:39!
[ 0.000000] Internal error: Oops - BUG: 00000000f2000800 [#1] PREEMPT SMP
[ 0.000000] Modules linked in:
[ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.14.0-rc4-next-20250224-dirty #1
[ 0.000000] Hardware name: linux,dummy-virt (DT)
[ 0.000000] pstate: 000000c9 (nzcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 0.000000] pc : patch_map.constprop.0+0xfc/0x108
[ 0.000000] lr : patch_map.constprop.0+0x3c/0x108
[ 0.000000] sp : ffff96c0b6fa3ce0
[ 0.000000] x29: ffff96c0b6fa3ce0 x28: ffff96c0b6faafd0 x27: 00000000000000ff
[ 0.000000] x26: fff9f3a0c2408080 x25: 0000000000000001 x24: fff9f3a0c2408000
[ 0.000000] x23: 0000000000000000 x22: ffff96c0b72391d8 x21: 00000000000000c0
[ 0.000000] x20: 000016c035400000 x19: 000016c035400000 x18: 00000000f0000000
[ 0.000000] x17: 0000000000000068 x16: 0000000000000100 x15: ffff96c0b6fa39c4
[ 0.000000] x14: 0000000000000008 x13: 0000000000000000 x12: ffffe9ce43090280
[ 0.000000] x11: fff9f3a0dfef80c8 x10: ffffe9ce43090288 x9 : 0000000000000000
[ 0.000000] x8 : fff9f3a0dfef80b8 x7 : fffa5ce02929a000 x6 : ffff96c0b6fa39d0
[ 0.000000] x5 : 0000000000000030 x4 : 0000000000000000 x3 : ffff96c0b69b4000
[ 0.000000] x2 : ffff96c0b69b4000 x1 : 0000000000000000 x0 : 0000000000000000
[ 0.000000] Call trace:
[ 0.000000] patch_map.constprop.0+0xfc/0x108 (P)
[ 0.000000] aarch64_insn_write_literal_u64+0x38/0x80
[ 0.000000] ftrace_init_nop+0x40/0xe0
[ 0.000000] ftrace_process_locs+0x2a8/0x530
[ 0.000000] ftrace_init+0x60/0x130
[ 0.000000] start_kernel+0x4ac/0x708
[ 0.000000] __primary_switched+0x88/0x98
[ 0.000000] Code: d1681000 a8c27bfd d50323bf d65f03c0 (d4210000)
[ 0.000000] ---[ end trace 0000000000000000 ]---
[ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
[ 0.000000] ---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---I see the same crash with clang (after applying your suggested fix for the issue that Arnd brought up).
[ 0.000000] Unable to handle kernel paging request at virtual address 00001cb7f7800008 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x000000009600002b [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x2b: level -1 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x0000002b, ISS2 = 0x00000000 [ 0.000000] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 0.000000] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 0.000000] [00001cb7f7800008] user address but active_mm is swapper [ 0.000000] Internal error: Oops: 000000009600002b [#1] PREEMPT SMP [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.14.0-rc4-next-20250224-dirty #1 [ 0.000000] Hardware name: linux,dummy-virt (DT) [ 0.000000] pstate: 400000c9 (nZcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.000000] pc : ftrace_call_adjust+0x44/0xd0 [ 0.000000] lr : ftrace_process_locs+0x1e0/0x560 [ 0.000000] sp : ffff9cb878f93da0 [ 0.000000] x29: ffff9cb878f93da0 x28: ffff9cb879234000 x27: ffff9cb879234000 [ 0.000000] x26: 00001cb7f7800000 x25: ffff9cb878ed8578 x24: fffac24642008000 [ 0.000000] x23: ffff9cb878f3cf90 x22: fffac24642008000 x21: 0000000000000000 [ 0.000000] x20: 0000000000001000 x19: 00001cb7f7800000 x18: 0000000000000068 [ 0.000000] x17: 0000000000000002 x16: 00000000fffffffe x15: ffff9cb878fa58c0 [ 0.000000] x14: 0000000000000000 x13: 0000000000000001 x12: 0000000000000000 [ 0.000000] x11: 0000000000000000 x10: 0000000000000000 x9 : 00007fff80000000 [ 0.000000] x8 : 000000000000201f x7 : 0000000000000000 x6 : 6d6067666871ff73 [ 0.000000] x5 : 0000000000000001 x4 : 0000000000000000 x3 : 0000000000000001 [ 0.000000] x2 : 0000000000000004 x1 : 0000000000000040 x0 : 00001cb7f7800000 [ 0.000000] Call trace: [ 0.000000] ftrace_call_adjust+0x44/0xd0 (P) [ 0.000000] ftrace_process_locs+0x1e0/0x560 [ 0.000000] ftrace_init+0x7c/0xc8 [ 0.000000] start_kernel+0x160/0x3b8 [ 0.000000] __primary_switched+0x88/0x98 [ 0.000000] Code: aa1f03e0 14000014 aa0003f3 528403e8 (b8408e74)
If there is any other information I can provide or patches I can test, I am more than happy to do so.
Cheers,
Nathan
Steven Rostedt
maintainerRe: [for-next][PATCH 4/6] scripts/sorttable: Zero out weak functions in mcount_loc table
Quoted 2 lines · On Mon, 24 Feb 2025 18:56:31 -0800 Nathan Chancellor <[email protected]> wrote:
I am also seeing a crash when booting arm64 with certain configurations that I don't see at the parent change.
Thanks, I also just bisected it down to this. But I didn't have early printk on so I didn't see what was crashing. So this is helpful.
Quoted 92 lines
$ printf 'CONFIG_%s=y\n' FTRACE FUNCTION_TRACER >kernel/configs/repro.config$ make -skj"$(nproc)" ARCH=arm64 CROSS_COMPILE=aarch64-linux- mrproper virtconfig repro.config Image.gz$ qemu-system-aarch64 \ -display none \ -nodefaults \ -cpu max,pauth-impdef=true \ -machine virt,gic-version=max,virtualization=true \ -append 'console=ttyAMA0 earlycon' \ -kernel arch/arm64/boot/Image.gz \ -initrd rootfs.cpio \ -m 512m \ -serial mon:stdio [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x000f0510] [ 0.000000] Linux version 6.14.0-rc4-next-20250224-dirty (nathan@ax162) (aarch64-linux-gcc (GCC) 14.2.0, GNU ld (GNU Binutils) 2.42) #1 SMP PREEMPT Mon Feb 24 18:47:59 PST 2025 ... [ 0.000000] ------------[ cut here ]------------ [ 0.000000] kernel BUG at arch/arm64/kernel/patching.c:39! [ 0.000000] Internal error: Oops - BUG: 00000000f2000800 [#1] PREEMPT SMP [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.14.0-rc4-next-20250224-dirty #1 [ 0.000000] Hardware name: linux,dummy-virt (DT) [ 0.000000] pstate: 000000c9 (nzcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.000000] pc : patch_map.constprop.0+0xfc/0x108 [ 0.000000] lr : patch_map.constprop.0+0x3c/0x108 [ 0.000000] sp : ffff96c0b6fa3ce0 [ 0.000000] x29: ffff96c0b6fa3ce0 x28: ffff96c0b6faafd0 x27: 00000000000000ff [ 0.000000] x26: fff9f3a0c2408080 x25: 0000000000000001 x24: fff9f3a0c2408000 [ 0.000000] x23: 0000000000000000 x22: ffff96c0b72391d8 x21: 00000000000000c0 [ 0.000000] x20: 000016c035400000 x19: 000016c035400000 x18: 00000000f0000000 [ 0.000000] x17: 0000000000000068 x16: 0000000000000100 x15: ffff96c0b6fa39c4 [ 0.000000] x14: 0000000000000008 x13: 0000000000000000 x12: ffffe9ce43090280 [ 0.000000] x11: fff9f3a0dfef80c8 x10: ffffe9ce43090288 x9 : 0000000000000000 [ 0.000000] x8 : fff9f3a0dfef80b8 x7 : fffa5ce02929a000 x6 : ffff96c0b6fa39d0 [ 0.000000] x5 : 0000000000000030 x4 : 0000000000000000 x3 : ffff96c0b69b4000 [ 0.000000] x2 : ffff96c0b69b4000 x1 : 0000000000000000 x0 : 0000000000000000 [ 0.000000] Call trace: [ 0.000000] patch_map.constprop.0+0xfc/0x108 (P) [ 0.000000] aarch64_insn_write_literal_u64+0x38/0x80 [ 0.000000] ftrace_init_nop+0x40/0xe0 [ 0.000000] ftrace_process_locs+0x2a8/0x530 [ 0.000000] ftrace_init+0x60/0x130 [ 0.000000] start_kernel+0x4ac/0x708 [ 0.000000] __primary_switched+0x88/0x98 [ 0.000000] Code: d1681000 a8c27bfd d50323bf d65f03c0 (d4210000) [ 0.000000] ---[ end trace 0000000000000000 ]--- [ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task! [ 0.000000] ---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---I see the same crash with clang (after applying your suggested fix for the issue that Arnd brought up).
[ 0.000000] Unable to handle kernel paging request at virtual address 00001cb7f7800008 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x000000009600002b [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x2b: level -1 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x0000002b, ISS2 = 0x00000000 [ 0.000000] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 0.000000] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 0.000000] [00001cb7f7800008] user address but active_mm is swapper [ 0.000000] Internal error: Oops: 000000009600002b [#1] PREEMPT SMP [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.14.0-rc4-next-20250224-dirty #1 [ 0.000000] Hardware name: linux,dummy-virt (DT) [ 0.000000] pstate: 400000c9 (nZcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.000000] pc : ftrace_call_adjust+0x44/0xd0 [ 0.000000] lr : ftrace_process_locs+0x1e0/0x560 [ 0.000000] sp : ffff9cb878f93da0 [ 0.000000] x29: ffff9cb878f93da0 x28: ffff9cb879234000 x27: ffff9cb879234000 [ 0.000000] x26: 00001cb7f7800000 x25: ffff9cb878ed8578 x24: fffac24642008000 [ 0.000000] x23: ffff9cb878f3cf90 x22: fffac24642008000 x21: 0000000000000000 [ 0.000000] x20: 0000000000001000 x19: 00001cb7f7800000 x18: 0000000000000068 [ 0.000000] x17: 0000000000000002 x16: 00000000fffffffe x15: ffff9cb878fa58c0 [ 0.000000] x14: 0000000000000000 x13: 0000000000000001 x12: 0000000000000000 [ 0.000000] x11: 0000000000000000 x10: 0000000000000000 x9 : 00007fff80000000 [ 0.000000] x8 : 000000000000201f x7 : 0000000000000000 x6 : 6d6067666871ff73 [ 0.000000] x5 : 0000000000000001 x4 : 0000000000000000 x3 : 0000000000000001 [ 0.000000] x2 : 0000000000000004 x1 : 0000000000000040 x0 : 00001cb7f7800000 [ 0.000000] Call trace: [ 0.000000] ftrace_call_adjust+0x44/0xd0 (P) [ 0.000000] ftrace_process_locs+0x1e0/0x560 [ 0.000000] ftrace_init+0x7c/0xc8 [ 0.000000] start_kernel+0x160/0x3b8 [ 0.000000] __primary_switched+0x88/0x98 [ 0.000000] Code: aa1f03e0 14000014 aa0003f3 528403e8 (b8408e74)
Quoted 2 lines
If there is any other information I can provide or patches I can test, I am more than happy to do so.
Thanks, I'm about to go to bed soon and I'll take a look more into it tomorrow.
-- Steve
Steven Rostedt
maintainerRe: [for-next][PATCH 4/6] scripts/sorttable: Zero out weak functions in mcount_loc table
Quoted 1 line · On Mon, 24 Feb 2025 22:28:33 -0500 Steven Rostedt <[email protected]> wrote:
Thanks, I'm about to go to bed soon and I'll take a look more into it tomorrow.
Can you try this patch (it has the clang fix too).
-- Steve
kernel/trace/ftrace.c +17 -6
@@ -7004,7 +7004,6 @@ static int ftrace_process_locs(struct module *mod,unsigned long count;unsigned long *p;unsigned long addr; -unsigned long kaslr;unsigned long flags =0 ;/* Shut up gcc */ unsigned long pages;int ret = -ENOMEM ;
@@ -7056,25 +7055,37 @@ static int ftrace_process_locs(struct module *mod, ftrace_pages->next = start_pg; } -/* For zeroed locations that were shifted for core kernel */ - kaslr = !mod ?kaslr_offset () :0 ; - p = start; pg = start_pg;while (p < end) {unsigned long end_offset; - addr =ftrace_call_adjust (*p++); + + addr = *p++; +/* * Some architecture linkers will pad between * the different mcount_loc sections of different * object files to satisfy alignments. * Skip any NULL pointers. */ -if (!addr || addr == kaslr) { +if (!addr) { + skipped++; +continue ; + } + +/* +* If this is core kernel, make sure the address is in core +* or inittext, as weak functions get zeroed and KASLR can +* move them to something other than zero. It just will not +* move it to an area where kernel text is. +*/ +if (!mod && !(is_kernel_text (addr) ||is_kernel_inittext (addr))) { skipped++;continue ; } + addr =ftrace_call_adjust (addr); + end_offset = (pg->index+1 ) *sizeof (pg->records[0 ]);if (end_offset >PAGE_SIZE << pg->order) {/* We should have allocated enough */
scripts/sorttable.c +13 -3
@@ -611,13 +611,16 @@ static int add_field(uint64_t addr, uint64_t size)return 0 ; } +/* Used for when mcount/fentry is before the function entry */ +static int before_func; +/* Only return match if the address lies inside the function size */ static int cmp_func_addr (const void *K ,const void *A ) {uint64_t key = *(const uint64_t *)K ;const struct func_info *a =A ; -if (key < a->addr) +if (key + before_func < a->addr)return -1 ;return key >= a->addr + a->size; }
@@ -827,9 +830,14 @@ static void *sort_mcount_loc(void *arg)pthread_exit (m_err); } -if (sort_reloc) +if (sort_reloc) { count =fill_relocs (vals, size, ehdr, emloc->start_mcount_loc); -else +/* gcc may use relocs to save the addresses, but clang does not. */ +if (!count) { + count =fill_addrs (vals, size, start_loc); + sort_reloc =0 ; + } + }else count =fill_addrs (vals, size, start_loc);if (count <0 ) {
@@ -1248,6 +1256,8 @@ static int do_file(char const *const fname, void *addr)#ifdef MCOUNT_SORT_ENABLED sort_reloc = true; rela_type =0x403 ; +/* arm64 uses patchable function entry placing before function */ + before_func =8 ;#endif /* fallthrough */ case EM_386 :
Re: [for-next][PATCH 4/6] scripts/sorttable: Zero out weak functions in mcount_loc table
Quoted 6 lines · Steven Rostedt
Quoted 1 line · On Mon, 24 Feb 2025 22:28:33 -0500 Steven Rostedt <[email protected]> wrote:
Thanks, I'm about to go to bed soon and I'll take a look more into it tomorrow.
Can you try this patch (it has the clang fix too).
Yup, that appears to fix all my issues with my initial tests.
Quoted 103 lines
kernel/trace/ftrace.c +17
-6@@ -7004,7 +7004,6 @@ static int ftrace_process_locs(struct module *mod,unsigned long count;unsigned long *p;unsigned long addr; -unsigned long kaslr;unsigned long flags =0 ;/* Shut up gcc */ unsigned long pages;int ret = -ENOMEM ;@@ -7056,25 +7055,37 @@ static int ftrace_process_locs(struct module *mod, ftrace_pages->next = start_pg; } -/* For zeroed locations that were shifted for core kernel */ - kaslr = !mod ?kaslr_offset () :0 ; - p = start; pg = start_pg;while (p < end) {unsigned long end_offset; - addr =ftrace_call_adjust (*p++); + + addr = *p++; +/* * Some architecture linkers will pad between * the different mcount_loc sections of different * object files to satisfy alignments. * Skip any NULL pointers. */ -if (!addr || addr == kaslr) { +if (!addr) { + skipped++; +continue ; + } + +/* +* If this is core kernel, make sure the address is in core +* or inittext, as weak functions get zeroed and KASLR can +* move them to something other than zero. It just will not +* move it to an area where kernel text is. +*/ +if (!mod && !(is_kernel_text (addr) ||is_kernel_inittext (addr))) { skipped++;continue ; } + addr =ftrace_call_adjust (addr); + end_offset = (pg->index+1 ) *sizeof (pg->records[0 ]);if (end_offset >PAGE_SIZE << pg->order) {/* We should have allocated enough */ scripts/sorttable.c +13
-3@@ -611,13 +611,16 @@ static int add_field(uint64_t addr, uint64_t size)return 0 ; } +/* Used for when mcount/fentry is before the function entry */ +static int before_func; +/* Only return match if the address lies inside the function size */ static int cmp_func_addr (const void *K ,const void *A ) {uint64_t key = *(const uint64_t *)K ;const struct func_info *a =A ; -if (key < a->addr) +if (key + before_func < a->addr)return -1 ;return key >= a->addr + a->size; }@@ -827,9 +830,14 @@ static void *sort_mcount_loc(void *arg)pthread_exit (m_err); } -if (sort_reloc) +if (sort_reloc) { count =fill_relocs (vals, size, ehdr, emloc->start_mcount_loc); -else +/* gcc may use relocs to save the addresses, but clang does not. */ +if (!count) { + count =fill_addrs (vals, size, start_loc); + sort_reloc =0 ; + } + }else count =fill_addrs (vals, size, start_loc);if (count <0 ) {@@ -1248,6 +1256,8 @@ static int do_file(char const *const fname, void *addr)#ifdef MCOUNT_SORT_ENABLED sort_reloc = true; rela_type =0x403 ; +/* arm64 uses patchable function entry placing before function */ + before_func =8 ;#endif /* fallthrough */ case EM_386 :
Steven Rostedt
maintainerPATCH 5/6 ftrace: Update the mcount_loc check of skipped entries
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
Now that weak functions turn into skipped entries, update the check to make sure the amount that was allocated would fit both the entries that were allocated as well as those that were skipped.
--- kernel/trace/ftrace.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
kernel/trace/ftrace.c +22 -1
@@ -7111,7 +7111,28 @@ static int ftrace_process_locs(struct module *mod,/* We should have used all pages unless we skipped some */ if (pg_unuse) { -WARN_ON (!skipped); +unsigned long pg_remaining, remaining =0 ; +unsigned long skip; + +/* Count the number of entries unused and compare it to skipped. */ + pg_remaining = (ENTRIES_PER_PAGE << pg->order) - pg->index; + +if (!WARN (skipped < pg_remaining,"Extra allocated pages for ftrace" )) { + + skip = skipped - pg_remaining; + +for (pg = pg_unuse; pg; pg = pg->next) + remaining +=1 << pg->order; + + skip =DIV_ROUND_UP (skip,ENTRIES_PER_PAGE ); + +/* +* Check to see if the number of pages remaining would +* just fit the number of entries skipped. +*/ +WARN (skip != remaining,"Extra allocated pages for ftrace: %lu with %lu skipped" , + remaining, skipped); + }/* Need to synchronize with ftrace_location_range() */ synchronize_rcu ();ftrace_free_pages (pg_unuse);
Signature · 1 line
2.47.2
Steven Rostedt
maintainerPATCH 6/6 ftrace: Have ftrace pages output reflect freed pages
In reply to Steven RostedtFrom: Steven Rostedt <[email protected]>
The amount of memory that ftrace uses to save the descriptors to manage the functions it can trace is shown at output. But if there are a lot of functions that are skipped because they were weak or the architecture added holes into the tables, then the extra pages that were allocated are freed. But these freed pages are not reflected in the numbers shown, and they can even be inconsistent with what is reported:
ftrace: allocating 57482 entries in 225 pages
ftrace: allocated 224 pages with 3 groups
The above shows the number of original entries that are in the mcount_loc section and the pages needed to save them (225), but the second output reflects the number of pages that were actually used. The two should be consistent as:
ftrace: allocating 56739 entries in 224 pages
ftrace: allocated 224 pages with 3 groups
The above also shows the accurate number of entires that were actually stored and does not include the entries that were removed.
--- kernel/trace/ftrace.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
kernel/trace/ftrace.c +12 -3
@@ -7006,6 +7006,7 @@ static int ftrace_process_locs(struct module *mod,unsigned long addr;unsigned long kaslr;unsigned long flags =0 ;/* Shut up gcc */ +unsigned long pages;int ret = -ENOMEM ; count = end - start;
@@ -7013,6 +7014,8 @@ static int ftrace_process_locs(struct module *mod,if (!count)return 0 ; + pages =DIV_ROUND_UP (count,ENTRIES_PER_PAGE ); +/* * Sorting mcount in vmlinux at build time depend on * CONFIG_BUILDTIME_MCOUNT_SORT, while mcount loc in
@@ -7124,6 +7127,8 @@ static int ftrace_process_locs(struct module *mod,for (pg = pg_unuse; pg; pg = pg->next) remaining +=1 << pg->order; + pages -= remaining; + skip =DIV_ROUND_UP (skip,ENTRIES_PER_PAGE );/*
@@ -7137,6 +7142,13 @@ static int ftrace_process_locs(struct module *mod,synchronize_rcu ();ftrace_free_pages (pg_unuse); } + +if (!mod) { + count -= skipped; +pr_info ("ftrace: allocating %ld entries in %ld pages\n" , + count, pages); + } +return ret; }
@@ -7782,9 +7794,6 @@ void __init ftrace_init(void) goto failed; } -pr_info ("ftrace: allocating %ld entries in %ld pages\n" , - count,DIV_ROUND_UP (count,ENTRIES_PER_PAGE )); - ret =ftrace_process_locs (NULL , __start_mcount_loc, __stop_mcount_loc);
Signature · 1 line
2.47.2