diff --git a/lisa/_assets/kmodules/lisa/Makefile b/lisa/_assets/kmodules/lisa/Makefile index 661a825460cc9bc3232a038bc617fca189e0875d..edbe3f953125e885120f9b71afa6a436a9c5f33c 100644 --- a/lisa/_assets/kmodules/lisa/Makefile +++ b/lisa/_assets/kmodules/lisa/Makefile @@ -68,7 +68,7 @@ ifneq ($(KERNELRELEASE),) LISA_KMOD_NAME ?= lisa obj-m := $(LISA_KMOD_NAME).o -$(LISA_KMOD_NAME)-y := main.o tp.o wq.o features.o pixel6.o introspection_data.o +$(LISA_KMOD_NAME)-y := main.o tp.o wq.o features.o pixel6.o introspection_data.o latelinker.o kallsyms_parser.o # -fno-stack-protector is needed to possibly undefined __stack_chk_guard symbol ccflags-y := "-I$(MODULE_SRC)" -std=gnu11 -fno-stack-protector -Wno-declaration-after-statement -Wno-error @@ -78,7 +78,7 @@ ccflags-y := "-I$(MODULE_SRC)" -std=gnu11 -fno-stack-protector -Wno-declaration- CFLAGS_introspection_data.o := -g3 -gno-split-dwarf -gdwarf -fno-eliminate-unused-debug-types GENERATED := $(MODULE_OBJ)/generated -FEATURES_LDS := $(MODULE_SRC)/features.lds +LINKER_LDS := $(MODULE_SRC)/linker.lds SYMBOLS_LDS := $(GENERATED)/symbols.lds clean-files := $(GENERATED) @@ -90,7 +90,7 @@ SYMBOL_NAMESPACES_H := $(GENERATED)/symbol_namespaces.h MODULE_VERSION_H := $(GENERATED)/module_version.h KALLSYMS := $(GENERATED)/kallsyms -ldflags-y += -T $(FEATURES_LDS) +ldflags-y += -T $(LINKER_LDS) ifeq ($(IN_TREE_BUILD),1) ccflags-y += -I$(srctree) -D_IN_TREE_BUILD diff --git a/lisa/_assets/kmodules/lisa/features.lds b/lisa/_assets/kmodules/lisa/features.lds deleted file mode 100644 index 1d81d944443a2cf0d94db982ed326380fee3e29f..0000000000000000000000000000000000000000 --- a/lisa/_assets/kmodules/lisa/features.lds +++ /dev/null @@ -1,9 +0,0 @@ -SECTIONS -{ - .__lisa_features : { - __lisa_features_start = . ; - KEEP(*(.__lisa_features)) - __lisa_features_stop = . ; - } -} - diff --git a/lisa/_assets/kmodules/lisa/kallsyms_parser.c b/lisa/_assets/kmodules/lisa/kallsyms_parser.c new file mode 100644 index 0000000000000000000000000000000000000000..23c9de9970d4ca9ff4a02fa41163ae885341f168 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/kallsyms_parser.c @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include "kallsyms_parser.h" + +CHAR_NOT_IN(parse_name_char, " \n\t[]"); +TAKEWHILE(u8, __parse_name, parse_name_char); +STRDUP(parse_name, __parse_name); + +PURE(str, null_mod_name, NULL); +SEQUENCE(str, __parse_mod_name, ({ + PARSE(parse_char, '['); + PARSE(consume_whitespaces); + char *mod_name = PARSE(parse_name); + PARSE(consume_whitespaces); + PARSE(parse_char, ']'); + mod_name; +})); +/* Parse an optional module name of that format, or returns NULL: + * [themodule] + */ +OR(str, parse_mod_name, __parse_mod_name, null_mod_name); + +/* Parse a line of /proc/kallsyms such as: + * 0000000000000000 A mysymbol [themodule] + */ +SEQUENCE(kallsyms_entry_t, parse_line, ({ + kallsyms_entry_t entry; + + PARSE(consume_whitespaces); + + /* Symbol value */ + entry.sym_value = (void *)PARSE(parse_u64); + + PARSE(consume_whitespaces); + + /* Symbol type, discarded */ + PARSE(parse_name); + + PARSE(consume_whitespaces); + + /* Symbol name */ + entry.sym_name = PARSE(parse_name); + + PARSE(consume_whitespaces); + + entry.mod_name = PARSE(parse_mod_name); + + PARSE(consume_whitespaces); + + entry; +})); + +struct closure { + kallsyms_callback *cb; + void *private; +}; +typedef struct closure* closure_p; +DEFINE_PARSE_RESULT_TYPE(closure_p); + +static closure_p process_entry(closure_p closure, kallsyms_entry_t entry) { + closure->cb(closure->private, &entry); + return closure; +} + +MANY(closure_p, parse_all_entries, parse_line, process_entry); + +int parse_kallsyms(char *input, kallsyms_callback *callback, void *private) { + struct closure closure = (struct closure){ + .cb = callback, + .private = private, + }; + parse_buffer buf = charp2parse_buffer(input, NULL); + return !IS_SUCCESS(parse_all_entries(&buf, &closure)); +} diff --git a/lisa/_assets/kmodules/lisa/kallsyms_parser.h b/lisa/_assets/kmodules/lisa/kallsyms_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..b98fca3b9a173caaf33594566c4d8b3ea90fd6a9 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/kallsyms_parser.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef KALLSYMS_H +#define KALLSYMS_H + +#include "linux/types.h" +#include "linux/kallsyms.h" + +#include "parsec.h" + + +typedef struct kallsyms_entry { + char *sym_name; + void *sym_value; + char *mod_name; +} kallsyms_entry_t; + +DEFINE_PARSE_RESULT_TYPE(kallsyms_entry_t); + + +typedef void kallsyms_callback(void *private, kallsyms_entry_t *entry); +int parse_kallsyms(char *input, kallsyms_callback *callback, void *private); + + +#endif /* KALLSYMS_H */ diff --git a/lisa/_assets/kmodules/lisa/latelinker.c b/lisa/_assets/kmodules/lisa/latelinker.c new file mode 100644 index 0000000000000000000000000000000000000000..1e1ecb18b8b5949b988427bf8e3b03f586818730 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/latelinker.c @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "linux/spinlock.h" + +#include "latelinker.h" +#include "main.h" + +static void __list_late_symbols(void) { + pr_info("This module depends on the following symbols:\n"); + __FOREACH_LATE_SYMBOL(sym) { + pr_info(" %s\n", sym->name); + } +} + +static int __init_late_symbol(struct late_symbol* sym) { + rwlock_init(&sym->lock); + sym->frozen = false; + sym->value = NULL; + return 0; +} + +int init_late_symbols(void) { + int ret = 0; + __FOREACH_LATE_SYMBOL(sym) { + ret |= __init_late_symbol(sym); + } + + __list_late_symbols(); + return ret; +} + +static void *__resolve_late_symbol(struct late_symbol *sym) { + // TODO: actually resolve the symbol + return NULL; +} + +void *__get_late_symbol(struct late_symbol *sym) { + void *addr = NULL; + read_lock(&sym->lock); + if (sym->frozen) { + addr = sym->value; + read_unlock(&sym->lock); + } else { + read_unlock(&sym->lock); + write_lock(&sym->lock); + + addr = __resolve_late_symbol(sym); + sym->frozen = true; + + write_unlock(&sym->lock); + } + return addr; +} + +void __put_late_symbol(struct late_symbol *sym) { +} diff --git a/lisa/_assets/kmodules/lisa/latelinker.h b/lisa/_assets/kmodules/lisa/latelinker.h new file mode 100644 index 0000000000000000000000000000000000000000..ec5efd4e2ac4a54f43810daf94639f8597f53c16 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/latelinker.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LATELINKER_H +#define LATELINKER_H + +#include "linux/spinlock.h" + + +struct late_symbol { + const char *name; + void *value; + bool frozen; + rwlock_t lock; +}; + +#define __FOREACH_LATE_SYMBOL(sym) for (struct late_symbol *sym=__lisa_late_symbols_start; sym < __lisa_late_symbols_stop; sym++) + +#define __LATE_SYMBOL_NAME(_name) __lisa_late_symbol##_name +#define DECLARE_LATE_SYMBOL(_name) \ + static struct late_symbol __LATE_SYMBOL_NAME(_name) __attribute__((section(".__lisa_late_symbols"),used,unused)) = (struct late_symbol){.name = #_name} + +#define GET_LATE_SYMBOL(_name) __get_late_symbol(&(__LATE_SYMBOL_NAME(_name))) +#define PUT_LATE_SYMBOL(_name) __put_late_symbol(&(__LATE_SYMBOL_NAME(_name))) + + +/* Start and stop address of the ELF section containing the struct late_symbol + * instances + */ +extern struct late_symbol __lisa_late_symbols_start[]; +extern struct late_symbol __lisa_late_symbols_stop[]; + +int init_late_symbols(void); + +void *__get_late_symbol(struct late_symbol *); +void __put_late_symbol(struct late_symbol *); + +void list_late_symbols(void); + +#endif /* LATELINKER_H */ diff --git a/lisa/_assets/kmodules/lisa/linker.lds b/lisa/_assets/kmodules/lisa/linker.lds new file mode 100644 index 0000000000000000000000000000000000000000..2c52a57a618b0e9f19ab78272a1cc8320d1531a9 --- /dev/null +++ b/lisa/_assets/kmodules/lisa/linker.lds @@ -0,0 +1,15 @@ +SECTIONS +{ + .__lisa_features : { + __lisa_features_start = . ; + KEEP(*(.__lisa_features)) + __lisa_features_stop = . ; + } + + .__lisa_late_symbols : { + __lisa_late_symbols_start = . ; + KEEP(*(.__lisa_late_symbols)) + __lisa_late_symbols_stop = . ; + } +} + diff --git a/lisa/_assets/kmodules/lisa/main.c b/lisa/_assets/kmodules/lisa/main.c index 05ec9b2cfcb167a5af51d3aba0a428d9746caca1..b3b92ee59fe8770cd999d4ba92c21d0c74d917a7 100644 --- a/lisa/_assets/kmodules/lisa/main.c +++ b/lisa/_assets/kmodules/lisa/main.c @@ -10,6 +10,12 @@ */ #include "generated/symbol_namespaces.h" + +#include "latelinker.h" +DECLARE_LATE_SYMBOL(foobarsymbol); +DECLARE_LATE_SYMBOL(foobarsymbol2); + + static char* version = LISA_MODULE_VERSION; module_param(version, charp, 0); MODULE_PARM_DESC(version, "Module version defined as sha1sum of the module sources"); @@ -24,8 +30,30 @@ static void modexit(void) { pr_err("Some errors happened while unloading LISA kernel module\n"); } +// TODO: remove that +#include "kallsyms_parser.h" +static void cb(void *private, kallsyms_entry_t *entry) { + pr_info("PARSED ENTRY: %s [%s] %llu", entry->sym_name, entry->mod_name, (unsigned long long)entry->sym_value); +} + static int __init modinit(void) { - int ret; + int ret = 0; + + ret |= init_late_symbols(); + if (ret) { + pr_err("Some errors happened while linking late symbols\n"); + return ret; + } + + // TODO: remove that + void *ptr = GET_LATE_SYMBOL(foobarsymbol); + pr_info("MY SYM: %llu\n", (unsigned long long)ptr); + char kallsyms[] = "0000000000000001 A\tfoobarsymbol\n 43 t \t hello [ lll\t] \n0000000000000002 A foobarsymbol2 [mymod]\n0000000000000000 r __func__.6250 [tcp_diag]\nthisisnotanentry\n123 r mysym\n"; + ret |= parse_kallsyms(kallsyms, cb, NULL); + if (ret) { + pr_err("Some errors happened while parsing kallsyms\n"); + return ret; + } pr_info("Loading Lisa module version %s\n", LISA_MODULE_VERSION); if (strcmp(version, LISA_MODULE_VERSION)) { @@ -40,7 +68,7 @@ static int __init modinit(void) { pr_info(" %s: %s\n", kernel_feature_names[i], kernel_feature_values[i] ? "enabled" : "disabled"); } - ret = init_features(features_len ? features : NULL , features_len); + ret |= init_features(features_len ? features : NULL , features_len); if (ret) { pr_err("Some errors happened while loading LISA kernel module\n"); diff --git a/lisa/_assets/kmodules/lisa/parsec.h b/lisa/_assets/kmodules/lisa/parsec.h index 19f1807f0e7448b7730baa4861c7f88ed5697202..26ff4ea9d5215ccac9c717a7c6d0d163acd4a338 100644 --- a/lisa/_assets/kmodules/lisa/parsec.h +++ b/lisa/_assets/kmodules/lisa/parsec.h @@ -69,7 +69,7 @@ static inline parse_buffer charp2parse_buffer(char *s, void *private) } /** - * charp2parse_buffer() - Copy a struct parse_buffer into a null-terminated string. + * parse_buffer2charp() - Copy a struct parse_buffer into a null-terminated string. * @src: Buffer to copy from. * @dst: Null terminated string to copy into. * @max: Maximum number of bytes to copy. @@ -79,8 +79,13 @@ static inline parse_buffer charp2parse_buffer(char *s, void *private) static inline size_t parse_buffer2charp(parse_buffer *src, char *dst, size_t max) { + // Ensure we always provide a null-terminated string, even if the + // buffer is empty. + if (max) + dst[0] = '\0'; + size_t to_copy = min(max, src->size); - size_t to_zero = src->size < max ? src->size : max - 1; + size_t to_zero = min(to_copy, max - 1); if (to_copy) { memcpy(dst, src->data, to_copy); dst[to_zero] = '\0'; @@ -88,6 +93,24 @@ static inline size_t parse_buffer2charp(parse_buffer *src, char *dst, return to_copy; } +/** + * parse_buffer_strdup() - Copy the parse_buffer into a newly allocated string. + * @src: Buffer to copy from. + * + * The string string is guaranteed to be null-terminated. + */ +static inline char *parse_buffer_strdup(parse_buffer *src) { + size_t size = src->size + 1; + char *s = NULL; + if (size) { + s = kmalloc(size, GFP_KERNEL); + if (s) { + parse_buffer2charp(src, s, size); + } + } + return s; +} + /** * enum parse_result_tag - Tag indicating the result of a parser. * @PARSE_SUCCESS: The parse was successful and the value parsed is therefore @@ -126,8 +149,11 @@ enum parse_result_tag { */ DEFINE_PARSE_RESULT_TYPE(parse_buffer); DEFINE_PARSE_RESULT_TYPE(u8); +DEFINE_PARSE_RESULT_TYPE(u64); DEFINE_PARSE_RESULT_TYPE(int); DEFINE_PARSE_RESULT_TYPE(ulong); +typedef char *str; +DEFINE_PARSE_RESULT_TYPE(str); /** * IS_SUCCESS() - Evaluates to true if the parse result is successful. @@ -179,54 +205,69 @@ static inline PARSE_RESULT(parse_buffer) } static inline PARSE_RESULT(u8) - __parse_u8_in(parse_buffer *input, const char *allowed, bool revert) + __parse_u8_in(parse_buffer *input, const bool lookup[256], bool revert) { + if (input->size) { u8 input_char = *input->data; - u8 c; - for (c = *allowed; c != '\0'; c = *allowed++) { - if (revert ? input_char != c : input_char == c) { - return (PARSE_RESULT(u8)){ - .tag = PARSE_SUCCESS, - .remainder = - (parse_buffer){ - .private = input->private, - .data = input->data + 1, - .size = input->size - 1, - .capacity = - input->capacity - - 1 }, - .value = c, - }; - } + bool success = lookup[(size_t)input_char]; + success = revert ? !success : success; + success = input_char == '\0' ? false : success; + + if (success) { + return (PARSE_RESULT(u8)){ + .tag = PARSE_SUCCESS, + .remainder = + (parse_buffer){ + .private = input->private, + .data = input->data + 1, + .size = input->size - 1, + .capacity = + input->capacity - + 1 }, + .value = input_char, + }; } } return (PARSE_RESULT(u8)){ .tag = PARSE_FAILURE, .remainder = *input }; } + +// Build the lookup table in a way that allows the compiler to optimize-out the +// code so the table is initialized only once, given that the lookup table is +// actually stored in a static variable. Clang is very good at optimizing this +// out from experiments. +static inline __attribute__((always_inline)) void __init_char_lookup(const char *allowed, bool lookup[256]) { + // Using strlen makes the pattern obvious to the compiler that + // is then able to optimize-out the loop. + size_t allowed_len = strlen(allowed); + for (size_t i=0; i < allowed_len; i++) { + lookup[(size_t)allowed[i]] = true; + } +} + + +#define __CHAR_IN(name, allowed, revert) \ + static inline PARSE_RESULT(u8) name(parse_buffer *input) \ + { \ + static bool lookup[256] = {0}; \ + __init_char_lookup((allowed), lookup); \ + return __parse_u8_in(input, lookup, (revert)); \ + } + + /** - * parse_char_in() - Recognize one character of the set passed in @allowed. - * @input: parse_buffer input to the parser. + * CHAR_IN() - Recognize one character in the set passed in @allowed. * @allowed: Null-terminated string containing the characters to recognize. */ -static inline PARSE_RESULT(u8) - parse_char_in(parse_buffer *input, const char *allowed) -{ - return __parse_u8_in(input, allowed, false); -} +#define CHAR_IN(name, allowed) __CHAR_IN(name, allowed, false) /** - * parse_char_not_in() - Recognize one character not part of the set passed in - * @disallowed. - * @input: parse_buffer input to the parser. - * @disallowed: Null-terminated string containing the characters to not - * recognize. + * CHAR_NOT_IN() - Recognize one character not in the set passed in @allowed. + * @allowed: Null-terminated string containing the characters to not recognize. */ -static inline PARSE_RESULT(u8) - parse_char_not_in(parse_buffer *input, const char *disallowed) -{ - return __parse_u8_in(input, disallowed, true); -} +#define CHAR_NOT_IN(name, allowed) __CHAR_IN(name, allowed, true) + static inline PARSE_RESULT(u8) __parse_u8(parse_buffer *input, u8 c, bool revert) @@ -355,6 +396,25 @@ static inline PARSE_RESULT(u8) parse_not_char(parse_buffer *input, u8 c) } \ MAP(f_type, parser_type, name, parser, __map_parse_buffer_##f) + +static inline char *__strdup(const char *src) { + if (src) { + size_t size = strlen(src) + 1; + char *dst = kmalloc(size, GFP_KERNEL); + if (dst) + memcpy(dst, src, size); + return dst; + } else { + return NULL; + } +} + +/** + * STRDUP() - Transforms a parser returning a parse_buffer to a parser + * returning an char * allocated with kmalloc. + */ +#define STRDUP(name, parser) MAP_PARSE_BUFFER(str, parse_buffer, name, parser, __strdup) + /** * PEEK() - Combinator that applies the parser but does not consume any input. * @type: Return type of the parser. @@ -408,7 +468,7 @@ static inline PARSE_RESULT(u8) parse_not_char(parse_buffer *input, u8 c) /** * MANY() - Same as AT_LEAST() with n=0 */ -#define MANY(type, name, parser, f) AT_LEAST(type, name, parser, f, 0) +#define MANY(type, name, parser, f, ...) AT_LEAST(type, name, parser, f, 0, ##__VA_ARGS__) /** * TAKEWHILE_AT_LEAST() - Combinator that applies @parser at least @n times and @@ -449,7 +509,7 @@ static inline PARSE_RESULT(u8) parse_not_char(parse_buffer *input, u8 c) } \ } /** - * TAKEWHILE() - Same as TAKEWHILE_AT_LEAST() with n=1 + * TAKEWHILE() - Same as TAKEWHILE_AT_LEAST() with n=0 */ #define TAKEWHILE(type, name, parser) TAKEWHILE_AT_LEAST(type, name, parser, 0) @@ -576,12 +636,26 @@ static inline PARSE_RESULT(u8) parse_not_char(parse_buffer *input, u8 c) }; \ } +/** + * DISCARD() - Discard the output of the given parser + * @parser_type: Return type of @parser. + * @name: Name of the new parser to create. + * @parser: Parser to wrap. + */ +#define DISCARD(parser_type, name, parser) RIGHT(parser_type, void_t, name, parser, parse_void) + /* * Collection of common basic parsers. */ +typedef struct { + u8 __internal; +} void_t; +DEFINE_PARSE_RESULT_TYPE(void_t); +PURE(void_t, parse_void, (void_t){0}); -APPLY(u8, parse_whitespace, parse_char_in, " \n\t"); -COUNT_MANY(u8, count_whitespaces, parse_whitespace); +CHAR_IN(parse_whitespace, " \n\t"); +COUNT_MANY(u8, __count_whitespaces, parse_whitespace); +DISCARD(int, consume_whitespaces, __count_whitespaces); static inline unsigned long __to_ulong(const char *s) { @@ -591,8 +665,20 @@ static inline unsigned long __to_ulong(const char *s) else return res; } -APPLY(u8, parse_digit, parse_char_in, "0123456789"); + +static inline unsigned long __to_u64(const char *s) +{ + u64 res; + if (kstrtou64(s, 10, &res)) + return 0; + else + return res; +} + +CHAR_IN(parse_digit, "0123456789"); TAKEWHILE_AT_LEAST(u8, parse_number_string, parse_digit, 1); MAP_PARSE_BUFFER(ulong, parse_buffer, parse_ulong, parse_number_string, __to_ulong); +MAP_PARSE_BUFFER(u64, parse_buffer, parse_u64, parse_number_string, + __to_u64); #endif diff --git a/lisa/_assets/kmodules/lisa/pixel6.c b/lisa/_assets/kmodules/lisa/pixel6.c index cf9715fb7266b16dc9ce16a508f97f14db7ea507..f7f898ed64ed465493abce223557519483d19086 100644 --- a/lisa/_assets/kmodules/lisa/pixel6.c +++ b/lisa/_assets/kmodules/lisa/pixel6.c @@ -225,7 +225,7 @@ SEQUENCE(sample_t, parse_sample, ({ value; })) -LEFT(sample_t, int, parse_sample_line, parse_sample, count_whitespaces) +LEFT(sample_t, void_t, parse_sample_line, parse_sample, consume_whitespaces); static int process_sample(int nr, sample_t sample) { @@ -244,7 +244,7 @@ SEQUENCE(int, parse_content, ({ /* t=12345 */ PARSE(parse_string, "t="); PARSE(parse_number_string); - PARSE(count_whitespaces); + PARSE(consume_whitespaces); /* Parse all the following sample lines */ PARSE(parse_all_samples, 0);