summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.btf2
-rw-r--r--scripts/coccinelle/misc/newline_in_nl_msg.cocci13
-rwxr-xr-xscripts/gen-crc-consts.py291
-rwxr-xr-xscripts/link-vmlinux.sh4
-rwxr-xr-xscripts/make_fit.py6
-rwxr-xr-xscripts/selinux/install_policy.sh15
-rw-r--r--scripts/sorttable.c411
-rwxr-xr-xscripts/tracing/draw_functrace.py129
8 files changed, 717 insertions, 154 deletions
diff --git a/scripts/Makefile.btf b/scripts/Makefile.btf
index c3cbeb13de50..fbaaec2187e5 100644
--- a/scripts/Makefile.btf
+++ b/scripts/Makefile.btf
@@ -24,7 +24,7 @@ else
pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs
ifneq ($(KBUILD_EXTMOD),)
-module-pahole-flags-$(call test-ge, $(pahole-ver), 126) += --btf_features=distilled_base
+module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base
endif
endif
diff --git a/scripts/coccinelle/misc/newline_in_nl_msg.cocci b/scripts/coccinelle/misc/newline_in_nl_msg.cocci
index 9baffe55d917..2814f6b205b9 100644
--- a/scripts/coccinelle/misc/newline_in_nl_msg.cocci
+++ b/scripts/coccinelle/misc/newline_in_nl_msg.cocci
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
///
-/// Catch strings ending in newline with GENL_SET_ERR_MSG, NL_SET_ERR_MSG,
-/// NL_SET_ERR_MSG_MOD.
+/// Catch strings ending in newline with (GE)NL_SET_ERR_MSG*.
///
// Confidence: Very High
// Copyright: (C) 2020 Intel Corporation
@@ -17,7 +16,11 @@ expression e;
constant m;
position p;
@@
- \(GENL_SET_ERR_MSG\|NL_SET_ERR_MSG\|NL_SET_ERR_MSG_MOD\)(e,m@p)
+ \(GENL_SET_ERR_MSG\|GENL_SET_ERR_MSG_FMT\|NL_SET_ERR_MSG\|NL_SET_ERR_MSG_MOD\|
+ NL_SET_ERR_MSG_FMT\|NL_SET_ERR_MSG_FMT_MOD\|NL_SET_ERR_MSG_WEAK\|
+ NL_SET_ERR_MSG_WEAK_MOD\|NL_SET_ERR_MSG_ATTR_POL\|
+ NL_SET_ERR_MSG_ATTR_POL_FMT\|NL_SET_ERR_MSG_ATTR\|
+ NL_SET_ERR_MSG_ATTR_FMT\)(e,m@p,...)
@script:python@
m << r.m;
@@ -32,7 +35,7 @@ expression r.e;
constant r.m;
position r.p;
@@
- fname(e,m@p)
+ fname(e,m@p,...)
//----------------------------------------------------------
// For context mode
@@ -43,7 +46,7 @@ identifier r1.fname;
expression r.e;
constant r.m;
@@
-* fname(e,m)
+* fname(e,m,...)
//----------------------------------------------------------
// For org mode
diff --git a/scripts/gen-crc-consts.py b/scripts/gen-crc-consts.py
new file mode 100755
index 000000000000..f9b44fc3a03f
--- /dev/null
+++ b/scripts/gen-crc-consts.py
@@ -0,0 +1,291 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script that generates constants for computing the given CRC variant(s).
+#
+# Copyright 2025 Google LLC
+#
+# Author: Eric Biggers <ebiggers@google.com>
+
+import sys
+
+# XOR (add) an iterable of polynomials.
+def xor(iterable):
+ res = 0
+ for val in iterable:
+ res ^= val
+ return res
+
+# Multiply two polynomials.
+def clmul(a, b):
+ return xor(a << i for i in range(b.bit_length()) if (b & (1 << i)) != 0)
+
+# Polynomial division floor(a / b).
+def div(a, b):
+ q = 0
+ while a.bit_length() >= b.bit_length():
+ q ^= 1 << (a.bit_length() - b.bit_length())
+ a ^= b << (a.bit_length() - b.bit_length())
+ return q
+
+# Reduce the polynomial 'a' modulo the polynomial 'b'.
+def reduce(a, b):
+ return a ^ clmul(div(a, b), b)
+
+# Reflect the bits of a polynomial.
+def bitreflect(poly, num_bits):
+ assert poly.bit_length() <= num_bits
+ return xor(((poly >> i) & 1) << (num_bits - 1 - i) for i in range(num_bits))
+
+# Format a polynomial as hex. Bit-reflect it if the CRC is lsb-first.
+def fmt_poly(variant, poly, num_bits):
+ if variant.lsb:
+ poly = bitreflect(poly, num_bits)
+ return f'0x{poly:0{2*num_bits//8}x}'
+
+# Print a pair of 64-bit polynomial multipliers. They are always passed in the
+# order [HI64_TERMS, LO64_TERMS] but will be printed in the appropriate order.
+def print_mult_pair(variant, mults):
+ mults = list(mults if variant.lsb else reversed(mults))
+ terms = ['HI64_TERMS', 'LO64_TERMS'] if variant.lsb else ['LO64_TERMS', 'HI64_TERMS']
+ for i in range(2):
+ print(f'\t\t{fmt_poly(variant, mults[i]["val"], 64)},\t/* {terms[i]}: {mults[i]["desc"]} */')
+
+# Pretty-print a polynomial.
+def pprint_poly(prefix, poly):
+ terms = [f'x^{i}' for i in reversed(range(poly.bit_length()))
+ if (poly & (1 << i)) != 0]
+ j = 0
+ while j < len(terms):
+ s = prefix + terms[j] + (' +' if j < len(terms) - 1 else '')
+ j += 1
+ while j < len(terms) and len(s) < 73:
+ s += ' ' + terms[j] + (' +' if j < len(terms) - 1 else '')
+ j += 1
+ print(s)
+ prefix = ' * ' + (' ' * (len(prefix) - 3))
+
+# Print a comment describing constants generated for the given CRC variant.
+def print_header(variant, what):
+ print('/*')
+ s = f'{"least" if variant.lsb else "most"}-significant-bit-first CRC-{variant.bits}'
+ print(f' * {what} generated for {s} using')
+ pprint_poly(' * G(x) = ', variant.G)
+ print(' */')
+
+class CrcVariant:
+ def __init__(self, bits, generator_poly, bit_order):
+ self.bits = bits
+ if bit_order not in ['lsb', 'msb']:
+ raise ValueError('Invalid value for bit_order')
+ self.lsb = bit_order == 'lsb'
+ self.name = f'crc{bits}_{bit_order}_0x{generator_poly:0{(2*bits+7)//8}x}'
+ if self.lsb:
+ generator_poly = bitreflect(generator_poly, bits)
+ self.G = generator_poly ^ (1 << bits)
+
+# Generate tables for CRC computation using the "slice-by-N" method.
+# N=1 corresponds to the traditional byte-at-a-time table.
+def gen_slicebyN_tables(variants, n):
+ for v in variants:
+ print('')
+ print_header(v, f'Slice-by-{n} CRC table')
+ print(f'static const u{v.bits} __maybe_unused {v.name}_table[{256*n}] = {{')
+ s = ''
+ for i in range(256 * n):
+ # The i'th table entry is the CRC of the message consisting of byte
+ # i % 256 followed by i // 256 zero bytes.
+ poly = (bitreflect(i % 256, 8) if v.lsb else i % 256) << (v.bits + 8*(i//256))
+ next_entry = fmt_poly(v, reduce(poly, v.G), v.bits) + ','
+ if len(s + next_entry) > 71:
+ print(f'\t{s}')
+ s = ''
+ s += (' ' if s else '') + next_entry
+ if s:
+ print(f'\t{s}')
+ print('};')
+
+def print_riscv_const(v, bits_per_long, name, val, desc):
+ print(f'\t.{name} = {fmt_poly(v, val, bits_per_long)}, /* {desc} */')
+
+def do_gen_riscv_clmul_consts(v, bits_per_long):
+ (G, n, lsb) = (v.G, v.bits, v.lsb)
+
+ pow_of_x = 3 * bits_per_long - (1 if lsb else 0)
+ print_riscv_const(v, bits_per_long, 'fold_across_2_longs_const_hi',
+ reduce(1 << pow_of_x, G), f'x^{pow_of_x} mod G')
+ pow_of_x = 2 * bits_per_long - (1 if lsb else 0)
+ print_riscv_const(v, bits_per_long, 'fold_across_2_longs_const_lo',
+ reduce(1 << pow_of_x, G), f'x^{pow_of_x} mod G')
+
+ pow_of_x = bits_per_long - 1 + n
+ print_riscv_const(v, bits_per_long, 'barrett_reduction_const_1',
+ div(1 << pow_of_x, G), f'floor(x^{pow_of_x} / G)')
+
+ val = G - (1 << n)
+ desc = f'G - x^{n}'
+ if lsb:
+ val <<= bits_per_long - n
+ desc = f'({desc}) * x^{bits_per_long - n}'
+ print_riscv_const(v, bits_per_long, 'barrett_reduction_const_2', val, desc)
+
+def gen_riscv_clmul_consts(variants):
+ print('')
+ print('struct crc_clmul_consts {');
+ print('\tunsigned long fold_across_2_longs_const_hi;');
+ print('\tunsigned long fold_across_2_longs_const_lo;');
+ print('\tunsigned long barrett_reduction_const_1;');
+ print('\tunsigned long barrett_reduction_const_2;');
+ print('};');
+ for v in variants:
+ print('');
+ if v.bits > 32:
+ print_header(v, 'Constants')
+ print('#ifdef CONFIG_64BIT')
+ print(f'static const struct crc_clmul_consts {v.name}_consts __maybe_unused = {{')
+ do_gen_riscv_clmul_consts(v, 64)
+ print('};')
+ print('#endif')
+ else:
+ print_header(v, 'Constants')
+ print(f'static const struct crc_clmul_consts {v.name}_consts __maybe_unused = {{')
+ print('#ifdef CONFIG_64BIT')
+ do_gen_riscv_clmul_consts(v, 64)
+ print('#else')
+ do_gen_riscv_clmul_consts(v, 32)
+ print('#endif')
+ print('};')
+
+# Generate constants for carryless multiplication based CRC computation.
+def gen_x86_pclmul_consts(variants):
+ # These are the distances, in bits, to generate folding constants for.
+ FOLD_DISTANCES = [2048, 1024, 512, 256, 128]
+
+ for v in variants:
+ (G, n, lsb) = (v.G, v.bits, v.lsb)
+ print('')
+ print_header(v, 'CRC folding constants')
+ print('static const struct {')
+ if not lsb:
+ print('\tu8 bswap_mask[16];')
+ for i in FOLD_DISTANCES:
+ print(f'\tu64 fold_across_{i}_bits_consts[2];')
+ print('\tu8 shuf_table[48];')
+ print('\tu64 barrett_reduction_consts[2];')
+ print(f'}} {v.name}_consts ____cacheline_aligned __maybe_unused = {{')
+
+ # Byte-reflection mask, needed for msb-first CRCs
+ if not lsb:
+ print('\t.bswap_mask = {' + ', '.join(str(i) for i in reversed(range(16))) + '},')
+
+ # Fold constants for all distances down to 128 bits
+ for i in FOLD_DISTANCES:
+ print(f'\t.fold_across_{i}_bits_consts = {{')
+ # Given 64x64 => 128 bit carryless multiplication instructions, two
+ # 64-bit fold constants are needed per "fold distance" i: one for
+ # HI64_TERMS that is basically x^(i+64) mod G and one for LO64_TERMS
+ # that is basically x^i mod G. The exact values however undergo a
+ # couple adjustments, described below.
+ mults = []
+ for j in [64, 0]:
+ pow_of_x = i + j
+ if lsb:
+ # Each 64x64 => 128 bit carryless multiplication instruction
+ # actually generates a 127-bit product in physical bits 0
+ # through 126, which in the lsb-first case represent the
+ # coefficients of x^1 through x^127, not x^0 through x^126.
+ # Thus in the lsb-first case, each such instruction
+ # implicitly adds an extra factor of x. The below removes a
+ # factor of x from each constant to compensate for this.
+ # For n < 64 the x could be removed from either the reduced
+ # part or unreduced part, but for n == 64 the reduced part
+ # is the only option. Just always use the reduced part.
+ pow_of_x -= 1
+ # Make a factor of x^(64-n) be applied unreduced rather than
+ # reduced, to cause the product to use only the x^(64-n) and
+ # higher terms and always be zero in the lower terms. Usually
+ # this makes no difference as it does not affect the product's
+ # congruence class mod G and the constant remains 64-bit, but
+ # part of the final reduction from 128 bits does rely on this
+ # property when it reuses one of the constants.
+ pow_of_x -= 64 - n
+ mults.append({ 'val': reduce(1 << pow_of_x, G) << (64 - n),
+ 'desc': f'(x^{pow_of_x} mod G) * x^{64-n}' })
+ print_mult_pair(v, mults)
+ print('\t},')
+
+ # Shuffle table for handling 1..15 bytes at end
+ print('\t.shuf_table = {')
+ print('\t\t' + (16*'-1, ').rstrip())
+ print('\t\t' + ''.join(f'{i:2}, ' for i in range(16)).rstrip())
+ print('\t\t' + (16*'-1, ').rstrip())
+ print('\t},')
+
+ # Barrett reduction constants for reducing 128 bits to the final CRC
+ print('\t.barrett_reduction_consts = {')
+ mults = []
+
+ val = div(1 << (63+n), G)
+ desc = f'floor(x^{63+n} / G)'
+ if not lsb:
+ val = (val << 1) - (1 << 64)
+ desc = f'({desc} * x) - x^64'
+ mults.append({ 'val': val, 'desc': desc })
+
+ val = G - (1 << n)
+ desc = f'G - x^{n}'
+ if lsb and n == 64:
+ assert (val & 1) != 0 # The x^0 term should always be nonzero.
+ val >>= 1
+ desc = f'({desc} - x^0) / x'
+ else:
+ pow_of_x = 64 - n - (1 if lsb else 0)
+ val <<= pow_of_x
+ desc = f'({desc}) * x^{pow_of_x}'
+ mults.append({ 'val': val, 'desc': desc })
+
+ print_mult_pair(v, mults)
+ print('\t},')
+
+ print('};')
+
+def parse_crc_variants(vars_string):
+ variants = []
+ for var_string in vars_string.split(','):
+ bits, bit_order, generator_poly = var_string.split('_')
+ assert bits.startswith('crc')
+ bits = int(bits.removeprefix('crc'))
+ assert generator_poly.startswith('0x')
+ generator_poly = generator_poly.removeprefix('0x')
+ assert len(generator_poly) % 2 == 0
+ generator_poly = int(generator_poly, 16)
+ variants.append(CrcVariant(bits, generator_poly, bit_order))
+ return variants
+
+if len(sys.argv) != 3:
+ sys.stderr.write(f'Usage: {sys.argv[0]} CONSTS_TYPE[,CONSTS_TYPE]... CRC_VARIANT[,CRC_VARIANT]...\n')
+ sys.stderr.write(' CONSTS_TYPE can be sliceby[1-8], riscv_clmul, or x86_pclmul\n')
+ sys.stderr.write(' CRC_VARIANT is crc${num_bits}_${bit_order}_${generator_poly_as_hex}\n')
+ sys.stderr.write(' E.g. crc16_msb_0x8bb7 or crc32_lsb_0xedb88320\n')
+ sys.stderr.write(' Polynomial must use the given bit_order and exclude x^{num_bits}\n')
+ sys.exit(1)
+
+print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
+print('/*')
+print(' * CRC constants generated by:')
+print(' *')
+print(f' *\t{sys.argv[0]} {" ".join(sys.argv[1:])}')
+print(' *')
+print(' * Do not edit manually.')
+print(' */')
+consts_types = sys.argv[1].split(',')
+variants = parse_crc_variants(sys.argv[2])
+for consts_type in consts_types:
+ if consts_type.startswith('sliceby'):
+ gen_slicebyN_tables(variants, int(consts_type.removeprefix('sliceby')))
+ elif consts_type == 'riscv_clmul':
+ gen_riscv_clmul_consts(variants)
+ elif consts_type == 'x86_pclmul':
+ gen_x86_pclmul_consts(variants)
+ else:
+ raise ValueError(f'Unknown consts_type: {consts_type}')
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 67e66333bd2a..94c78244a2b0 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -173,12 +173,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
diff --git a/scripts/make_fit.py b/scripts/make_fit.py
index 4a1bb2f55861..1683e5ec6e67 100755
--- a/scripts/make_fit.py
+++ b/scripts/make_fit.py
@@ -279,7 +279,11 @@ def build_fit(args):
if os.path.splitext(fname)[1] != '.dtb':
continue
- (model, compat, files) = process_dtb(fname, args)
+ try:
+ (model, compat, files) = process_dtb(fname, args)
+ except Exception as e:
+ sys.stderr.write(f"Error processing {fname}:\n")
+ raise e
for fn in files:
if fn not in fdts:
diff --git a/scripts/selinux/install_policy.sh b/scripts/selinux/install_policy.sh
index 24086793b0d8..db40237e60ce 100755
--- a/scripts/selinux/install_policy.sh
+++ b/scripts/selinux/install_policy.sh
@@ -6,27 +6,24 @@ if [ `id -u` -ne 0 ]; then
exit 1
fi
-SF=`which setfiles`
-if [ $? -eq 1 ]; then
+SF=`which setfiles` || {
echo "Could not find setfiles"
echo "Do you have policycoreutils installed?"
exit 1
-fi
+}
-CP=`which checkpolicy`
-if [ $? -eq 1 ]; then
+CP=`which checkpolicy` || {
echo "Could not find checkpolicy"
echo "Do you have checkpolicy installed?"
exit 1
-fi
+}
VERS=`$CP -V | awk '{print $1}'`
-ENABLED=`which selinuxenabled`
-if [ $? -eq 1 ]; then
+ENABLED=`which selinuxenabled` || {
echo "Could not find selinuxenabled"
echo "Do you have libselinux-utils installed?"
exit 1
-fi
+}
if selinuxenabled; then
echo "SELinux is already enabled"
diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index 9f41575afd7a..7b4b3714b1af 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -28,6 +28,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -79,10 +80,16 @@ typedef union {
Elf64_Sym e64;
} Elf_Sym;
+typedef union {
+ Elf32_Rela e32;
+ Elf64_Rela e64;
+} Elf_Rela;
+
static uint32_t (*r)(const uint32_t *);
static uint16_t (*r2)(const uint16_t *);
static uint64_t (*r8)(const uint64_t *);
static void (*w)(uint32_t, uint32_t *);
+static void (*w8)(uint64_t, uint64_t *);
typedef void (*table_sort_t)(char *, int);
static struct elf_funcs {
@@ -102,6 +109,10 @@ static struct elf_funcs {
uint32_t (*sym_name)(Elf_Sym *sym);
uint64_t (*sym_value)(Elf_Sym *sym);
uint16_t (*sym_shndx)(Elf_Sym *sym);
+ uint64_t (*rela_offset)(Elf_Rela *rela);
+ uint64_t (*rela_info)(Elf_Rela *rela);
+ uint64_t (*rela_addend)(Elf_Rela *rela);
+ void (*rela_write_addend)(Elf_Rela *rela, uint64_t val);
} e;
static uint64_t ehdr64_shoff(Elf_Ehdr *ehdr)
@@ -262,6 +273,38 @@ SYM_ADDR(value)
SYM_WORD(name)
SYM_HALF(shndx)
+#define __maybe_unused __attribute__((__unused__))
+
+#define RELA_ADDR(fn_name) \
+static uint64_t rela64_##fn_name(Elf_Rela *rela) \
+{ \
+ return r8((uint64_t *)&rela->e64.r_##fn_name); \
+} \
+ \
+static uint64_t rela32_##fn_name(Elf_Rela *rela) \
+{ \
+ return r((uint32_t *)&rela->e32.r_##fn_name); \
+} \
+ \
+static uint64_t __maybe_unused rela_##fn_name(Elf_Rela *rela) \
+{ \
+ return e.rela_##fn_name(rela); \
+}
+
+RELA_ADDR(offset)
+RELA_ADDR(info)
+RELA_ADDR(addend)
+
+static void rela64_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ w8(val, (uint64_t *)&rela->e64.r_addend);
+}
+
+static void rela32_write_addend(Elf_Rela *rela, uint64_t val)
+{
+ w(val, (uint32_t *)&rela->e32.r_addend);
+}
+
/*
* Get the whole file as a programming convenience in order to avoid
* malloc+lseek+read+free of many pieces. If successful, then mmap
@@ -341,6 +384,16 @@ static void wle(uint32_t val, uint32_t *x)
put_unaligned_le32(val, x);
}
+static void w8be(uint64_t val, uint64_t *x)
+{
+ put_unaligned_be64(val, x);
+}
+
+static void w8le(uint64_t val, uint64_t *x)
+{
+ put_unaligned_le64(val, x);
+}
+
/*
* Move reserved section indices SHN_LORESERVE..SHN_HIRESERVE out of
* 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)
static int extable_ent_size;
static int long_size;
+#define ERRSTR_MAXSZ 256
#ifdef UNWINDER_ORC_ENABLED
/* ORC unwinder only support X86_64 */
#include <asm/orc_types.h>
-#define ERRSTR_MAXSZ 256
-
static char g_err[ERRSTR_MAXSZ];
static int *g_orc_ip_table;
static struct orc_entry *g_orc_table;
@@ -499,7 +551,136 @@ static void *sort_orctable(void *arg)
#endif
#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)
+{
+ e.rela_write_addend(rela, val);
+}
+
+struct func_info {
+ uint64_t addr;
+ uint64_t size;
+};
+
+/* List of functions created by: nm -S vmlinux */
+static struct func_info *function_list;
+static int function_list_size;
+
+/* Allocate functions in 1k blocks */
+#define FUNC_BLK_SIZE 1024
+#define FUNC_BLK_MASK (FUNC_BLK_SIZE - 1)
+
+static int add_field(uint64_t addr, uint64_t size)
+{
+ struct func_info *fi;
+ int fsize = function_list_size;
+
+ if (!(fsize & FUNC_BLK_MASK)) {
+ fsize += FUNC_BLK_SIZE;
+ fi = realloc(function_list, fsize * sizeof(struct func_info));
+ if (!fi)
+ return -1;
+ function_list = fi;
+ }
+ fi = &function_list[function_list_size++];
+ fi->addr = addr;
+ fi->size = 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 + before_func < a->addr)
+ return -1;
+ return key >= a->addr + a->size;
+}
+
+/* Find the function in function list that is bounded by the function size */
+static int find_func(uint64_t key)
+{
+ return bsearch(&key, function_list, function_list_size,
+ sizeof(struct func_info), cmp_func_addr) != NULL;
+}
+
+static int cmp_funcs(const void *A, const void *B)
+{
+ const struct func_info *a = A;
+ const struct func_info *b = B;
+
+ if (a->addr < b->addr)
+ return -1;
+ return a->addr > b->addr;
+}
+
+static int parse_symbols(const char *fname)
+{
+ FILE *fp;
+ char addr_str[20]; /* Only need 17, but round up to next int size */
+ char size_str[20];
+ char type;
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ perror(fname);
+ return -1;
+ }
+
+ while (fscanf(fp, "%16s %16s %c %*s\n", addr_str, size_str, &type) == 3) {
+ uint64_t addr;
+ uint64_t size;
+
+ /* Only care about functions */
+ if (type != 't' && type != 'T' && type != 'W')
+ continue;
+
+ addr = strtoull(addr_str, NULL, 16);
+ size = strtoull(size_str, NULL, 16);
+ if (add_field(addr, size) < 0)
+ return -1;
+ }
+ fclose(fp);
+
+ qsort(function_list, function_list_size, sizeof(struct func_info), cmp_funcs);
+
+ return 0;
+}
+
static pthread_t mcount_sort_thread;
+static bool sort_reloc;
+
+static long rela_type;
+
+static char m_err[ERRSTR_MAXSZ];
struct elf_mcount_loc {
Elf_Ehdr *ehdr;
@@ -508,17 +689,197 @@ struct elf_mcount_loc {
uint64_t stop_mcount_loc;
};
+/* 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 = 0;
+ int shentsize;
+ void *array_end = ptr + size;
+
+ 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);
+
+ for (int i = 0; i < shnum; i++) {
+ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
+ void *end;
+
+ if (shdr_type(shdr) != SHT_RELA)
+ continue;
+
+ rel = (void *)ehdr + shdr_offset(shdr);
+ end = (void *)rel + shdr_size(shdr);
+
+ for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) {
+ uint64_t offset = rela_offset(rel);
+
+ if (offset >= start_loc && offset < start_loc + size) {
+ if (ptr + long_size > array_end) {
+ snprintf(m_err, ERRSTR_MAXSZ,
+ "Too many relocations");
+ return -1;
+ }
+
+ /* Make sure this has the correct type */
+ if (rela_info(rel) != rela_type) {
+ snprintf(m_err, ERRSTR_MAXSZ,
+ "rela has type %lx but expected %lx\n",
+ (long)rela_info(rel), rela_type);
+ return -1;
+ }
+
+ if (long_size == 4)
+ *(uint32_t *)ptr = rela_addend(rel);
+ else
+ *(uint64_t *)ptr = rela_addend(rel);
+ ptr += long_size;
+ count++;
+ }
+ }
+ }
+ 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);
+
+ for (int i = 0; i < shnum; i++) {
+ Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
+ void *end;
+
+ if (shdr_type(shdr) != SHT_RELA)
+ continue;
+
+ rel = (void *)ehdr + shdr_offset(shdr);
+ end = (void *)rel + shdr_size(shdr);
+
+ for (; (void *)rel < end; rel = (void *)rel + shdr_entsize(shdr)) {
+ uint64_t offset = rela_offset(rel);
+
+ if (offset >= start_loc && offset < start_loc + size) {
+ if (long_size == 4)
+ rela_write_addend(rel, *(uint32_t *)ptr);
+ else
+ rela_write_addend(rel, *(uint64_t *)ptr);
+ ptr += long_size;
+ }
+ }
+ }
+}
+
+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 */
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) {
+ count = fill_relocs(vals, size, ehdr, emloc->start_mcount_loc);
+ /* 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);
- qsort(start_loc, count/long_size, long_size, compare_extable);
- return NULL;
+ 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;
+ }
+
+ /* zero out any locations not found by function list */
+ if (function_list_size) {
+ for (void *ptr = vals; ptr < vals + size; ptr += long_size) {
+ uint64_t key;
+
+ key = long_size == 4 ? r((uint32_t *)ptr) : r8((uint64_t *)ptr);
+ if (!find_func(key)) {
+ if (long_size == 4)
+ *(uint32_t *)ptr = 0;
+ else
+ *(uint64_t *)ptr = 0;
+ }
+ }
+ }
+
+ 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);
+
+ pthread_exit(e_msg);
}
/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */
@@ -555,6 +916,8 @@ static void get_mcount_loc(struct elf_mcount_loc *emloc, Elf_Shdr *symtab_sec,
return;
}
}
+#else /* MCOUNT_SORT_ENABLED */
+static inline int parse_symbols(const char *fname) { return 0; }
#endif
static int do_sort(Elf_Ehdr *ehdr,
@@ -866,12 +1229,14 @@ static int do_file(char const *const fname, void *addr)
r2 = r2le;
r8 = r8le;
w = wle;
+ w8 = w8le;
break;
case ELFDATA2MSB:
r = rbe;
r2 = r2be;
r8 = r8be;
w = wbe;
+ w8 = w8be;
break;
default:
fprintf(stderr, "unrecognized ELF data encoding %d: %s\n",
@@ -887,8 +1252,15 @@ static int do_file(char const *const fname, void *addr)
}
switch (r2(&ehdr->e32.e_machine)) {
- case EM_386:
case EM_AARCH64:
+#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:
case EM_LOONGARCH:
case EM_RISCV:
case EM_S390:
@@ -932,6 +1304,10 @@ static int do_file(char const *const fname, void *addr)
.sym_name = sym32_name,
.sym_value = sym32_value,
.sym_shndx = sym32_shndx,
+ .rela_offset = rela32_offset,
+ .rela_info = rela32_info,
+ .rela_addend = rela32_addend,
+ .rela_write_addend = rela32_write_addend,
};
e = efuncs;
@@ -965,6 +1341,10 @@ static int do_file(char const *const fname, void *addr)
.sym_name = sym64_name,
.sym_value = sym64_value,
.sym_shndx = sym64_shndx,
+ .rela_offset = rela64_offset,
+ .rela_info = rela64_info,
+ .rela_addend = rela64_addend,
+ .rela_write_addend = rela64_write_addend,
};
e = efuncs;
@@ -995,14 +1375,29 @@ int main(int argc, char *argv[])
int i, n_error = 0; /* gcc-4.3.0 false positive complaint */
size_t size = 0;
void *addr = NULL;
+ int c;
+
+ while ((c = getopt(argc, argv, "s:")) >= 0) {
+ switch (c) {
+ case 's':
+ if (parse_symbols(optarg) < 0) {
+ fprintf(stderr, "Could not parse %s\n", optarg);
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "usage: sorttable [-s nm-file] vmlinux...\n");
+ return 0;
+ }
+ }
- if (argc < 2) {
+ if ((argc - optind) < 1) {
fprintf(stderr, "usage: sorttable vmlinux...\n");
return 0;
}
/* Process each file in turn, allowing deep failure. */
- for (i = 1; i < argc; i++) {
+ for (i = optind; i < argc; i++) {
addr = mmap_file(argv[i], &size);
if (!addr) {
++n_error;
diff --git a/scripts/tracing/draw_functrace.py b/scripts/tracing/draw_functrace.py
deleted file mode 100755
index 42fa87300941..000000000000
--- a/scripts/tracing/draw_functrace.py
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/env python
-# SPDX-License-Identifier: GPL-2.0-only
-
-"""
-Copyright 2008 (c) Frederic Weisbecker <fweisbec@gmail.com>
-
-This script parses a trace provided by the function tracer in
-kernel/trace/trace_functions.c
-The resulted trace is processed into a tree to produce a more human
-view of the call stack by drawing textual but hierarchical tree of
-calls. Only the functions's names and the call time are provided.
-
-Usage:
- Be sure that you have CONFIG_FUNCTION_TRACER
- # mount -t tracefs nodev /sys/kernel/tracing
- # echo function > /sys/kernel/tracing/current_tracer
- $ cat /sys/kernel/tracing/trace_pipe > ~/raw_trace_func
- Wait some times but not too much, the script is a bit slow.
- Break the pipe (Ctrl + Z)
- $ scripts/tracing/draw_functrace.py < ~/raw_trace_func > draw_functrace
- Then you have your drawn trace in draw_functrace
-"""
-
-
-import sys, re
-
-class CallTree:
- """ This class provides a tree representation of the functions
- call stack. If a function has no parent in the kernel (interrupt,
- syscall, kernel thread...) then it is attached to a virtual parent
- called ROOT.
- """
- ROOT = None
-
- def __init__(self, func, time = None, parent = None):
- self._func = func
- self._time = time
- if parent is None:
- self._parent = CallTree.ROOT
- else:
- self._parent = parent
- self._children = []
-
- def calls(self, func, calltime):
- """ If a function calls another one, call this method to insert it
- into the tree at the appropriate place.
- @return: A reference to the newly created child node.
- """
- child = CallTree(func, calltime, self)
- self._children.append(child)
- return child
-
- def getParent(self, func):
- """ Retrieve the last parent of the current node that
- has the name given by func. If this function is not
- on a parent, then create it as new child of root
- @return: A reference to the parent.
- """
- tree = self
- while tree != CallTree.ROOT and tree._func != func:
- tree = tree._parent
- if tree == CallTree.ROOT:
- child = CallTree.ROOT.calls(func, None)
- return child
- return tree
-
- def __repr__(self):
- return self.__toString("", True)
-
- def __toString(self, branch, lastChild):
- if self._time is not None:
- s = "%s----%s (%s)\n" % (branch, self._func, self._time)
- else:
- s = "%s----%s\n" % (branch, self._func)
-
- i = 0
- if lastChild:
- branch = branch[:-1] + " "
- while i < len(self._children):
- if i != len(self._children) - 1:
- s += "%s" % self._children[i].__toString(branch +\
- " |", False)
- else:
- s += "%s" % self._children[i].__toString(branch +\
- " |", True)
- i += 1
- return s
-
-class BrokenLineException(Exception):
- """If the last line is not complete because of the pipe breakage,
- we want to stop the processing and ignore this line.
- """
- pass
-
-class CommentLineException(Exception):
- """ If the line is a comment (as in the beginning of the trace file),
- just ignore it.
- """
- pass
-
-
-def parseLine(line):
- line = line.strip()
- if line.startswith("#"):
- raise CommentLineException
- m = re.match("[^]]+?\\] +([a-z.]+) +([0-9.]+): (\\w+) <-(\\w+)", line)
- if m is None:
- raise BrokenLineException
- return (m.group(2), m.group(3), m.group(4))
-
-
-def main():
- CallTree.ROOT = CallTree("Root (Nowhere)", None, None)
- tree = CallTree.ROOT
-
- for line in sys.stdin:
- try:
- calltime, callee, caller = parseLine(line)
- except BrokenLineException:
- break
- except CommentLineException:
- continue
- tree = tree.getParent(caller)
- tree = tree.calls(callee, calltime)
-
- print(CallTree.ROOT)
-
-if __name__ == "__main__":
- main()