mirror of https://github.com/torvalds/linux.git
262 lines
5.8 KiB
C
262 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <getopt.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
|
|
#include "elf-parse.h"
|
|
|
|
static Elf_Shdr *check_data_sec;
|
|
static Elf_Shdr *tracepoint_data_sec;
|
|
|
|
static inline void *get_index(void *start, int entsize, int index)
|
|
{
|
|
return start + (entsize * index);
|
|
}
|
|
|
|
static int compare_strings(const void *a, const void *b)
|
|
{
|
|
const char *av = *(const char **)a;
|
|
const char *bv = *(const char **)b;
|
|
|
|
return strcmp(av, bv);
|
|
}
|
|
|
|
struct elf_tracepoint {
|
|
Elf_Ehdr *ehdr;
|
|
const char **array;
|
|
int count;
|
|
};
|
|
|
|
#define REALLOC_SIZE (1 << 10)
|
|
#define REALLOC_MASK (REALLOC_SIZE - 1)
|
|
|
|
static int add_string(const char *str, const char ***vals, int *count)
|
|
{
|
|
const char **array = *vals;
|
|
|
|
if (!(*count & REALLOC_MASK)) {
|
|
int size = (*count) + REALLOC_SIZE;
|
|
|
|
array = realloc(array, sizeof(char *) * size);
|
|
if (!array) {
|
|
fprintf(stderr, "Failed memory allocation\n");
|
|
return -1;
|
|
}
|
|
*vals = array;
|
|
}
|
|
|
|
array[(*count)++] = str;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* for_each_shdr_str - iterator that reads strings that are in an ELF section.
|
|
* @len: "int" to hold the length of the current string
|
|
* @ehdr: A pointer to the ehdr of the ELF file
|
|
* @sec: The section that has the strings to iterate on
|
|
*
|
|
* This is a for loop that iterates over all the nul terminated strings
|
|
* that are in a given ELF section. The variable "str" will hold
|
|
* the current string for each iteration and the passed in @len will
|
|
* contain the strlen() of that string.
|
|
*/
|
|
#define for_each_shdr_str(len, ehdr, sec) \
|
|
for (const char *str = (void *)(ehdr) + shdr_offset(sec), \
|
|
*end = str + shdr_size(sec); \
|
|
len = strlen(str), str < end; \
|
|
str += (len) + 1)
|
|
|
|
|
|
static void make_trace_array(struct elf_tracepoint *etrace)
|
|
{
|
|
Elf_Ehdr *ehdr = etrace->ehdr;
|
|
const char **vals = NULL;
|
|
int count = 0;
|
|
int len;
|
|
|
|
etrace->array = NULL;
|
|
|
|
/*
|
|
* The __tracepoint_check section is filled with strings of the
|
|
* names of tracepoints (in tracepoint_strings). Create an array
|
|
* that points to each string and then sort the array.
|
|
*/
|
|
for_each_shdr_str(len, ehdr, check_data_sec) {
|
|
if (!len)
|
|
continue;
|
|
if (add_string(str, &vals, &count) < 0)
|
|
return;
|
|
}
|
|
|
|
/* If CONFIG_TRACEPOINT_VERIFY_USED is not set, there's nothing to do */
|
|
if (!count)
|
|
return;
|
|
|
|
qsort(vals, count, sizeof(char *), compare_strings);
|
|
|
|
etrace->array = vals;
|
|
etrace->count = count;
|
|
}
|
|
|
|
static int find_event(const char *str, void *array, size_t size)
|
|
{
|
|
return bsearch(&str, array, size, sizeof(char *), compare_strings) != NULL;
|
|
}
|
|
|
|
static void check_tracepoints(struct elf_tracepoint *etrace, const char *fname)
|
|
{
|
|
Elf_Ehdr *ehdr = etrace->ehdr;
|
|
int len;
|
|
|
|
if (!etrace->array)
|
|
return;
|
|
|
|
/*
|
|
* The __tracepoints_strings section holds all the names of the
|
|
* defined tracepoints. If any of them are not in the
|
|
* __tracepoint_check_section it means they are not used.
|
|
*/
|
|
for_each_shdr_str(len, ehdr, tracepoint_data_sec) {
|
|
if (!len)
|
|
continue;
|
|
if (!find_event(str, etrace->array, etrace->count)) {
|
|
fprintf(stderr, "warning: tracepoint '%s' is unused", str);
|
|
if (fname)
|
|
fprintf(stderr, " in module %s\n", fname);
|
|
else
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
free(etrace->array);
|
|
}
|
|
|
|
static void *tracepoint_check(struct elf_tracepoint *etrace, const char *fname)
|
|
{
|
|
make_trace_array(etrace);
|
|
check_tracepoints(etrace, fname);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int process_tracepoints(bool mod, void *addr, const char *fname)
|
|
{
|
|
struct elf_tracepoint etrace = {0};
|
|
Elf_Ehdr *ehdr = addr;
|
|
Elf_Shdr *shdr_start;
|
|
Elf_Shdr *string_sec;
|
|
const char *secstrings;
|
|
unsigned int shnum;
|
|
unsigned int shstrndx;
|
|
int shentsize;
|
|
int idx;
|
|
int done = 2;
|
|
|
|
shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
|
|
shentsize = ehdr_shentsize(ehdr);
|
|
|
|
shstrndx = ehdr_shstrndx(ehdr);
|
|
if (shstrndx == SHN_XINDEX)
|
|
shstrndx = shdr_link(shdr_start);
|
|
string_sec = get_index(shdr_start, shentsize, shstrndx);
|
|
secstrings = (const char *)ehdr + shdr_offset(string_sec);
|
|
|
|
shnum = ehdr_shnum(ehdr);
|
|
if (shnum == SHN_UNDEF)
|
|
shnum = shdr_size(shdr_start);
|
|
|
|
for (int i = 0; done && i < shnum; i++) {
|
|
Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
|
|
|
|
idx = shdr_name(shdr);
|
|
|
|
/* locate the __tracepoint_check in vmlinux */
|
|
if (!strcmp(secstrings + idx, "__tracepoint_check")) {
|
|
check_data_sec = shdr;
|
|
done--;
|
|
}
|
|
|
|
/* locate the __tracepoints_ptrs section in vmlinux */
|
|
if (!strcmp(secstrings + idx, "__tracepoints_strings")) {
|
|
tracepoint_data_sec = shdr;
|
|
done--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Modules may not have either section. But if it has one section,
|
|
* it should have both of them.
|
|
*/
|
|
if (mod && !check_data_sec && !tracepoint_data_sec)
|
|
return 0;
|
|
|
|
if (!check_data_sec) {
|
|
if (mod) {
|
|
fprintf(stderr, "warning: Module %s has only unused tracepoints\n", fname);
|
|
/* Do not fail build */
|
|
return 0;
|
|
}
|
|
fprintf(stderr, "no __tracepoint_check in file: %s\n", fname);
|
|
return -1;
|
|
}
|
|
|
|
if (!tracepoint_data_sec) {
|
|
fprintf(stderr, "no __tracepoint_strings in file: %s\n", fname);
|
|
return -1;
|
|
}
|
|
|
|
if (!mod)
|
|
fname = NULL;
|
|
|
|
etrace.ehdr = ehdr;
|
|
tracepoint_check(&etrace, fname);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int n_error = 0;
|
|
size_t size = 0;
|
|
void *addr = NULL;
|
|
bool mod = false;
|
|
|
|
if (argc > 1 && strcmp(argv[1], "--module") == 0) {
|
|
mod = true;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
if (argc < 2) {
|
|
if (mod)
|
|
fprintf(stderr, "usage: tracepoint-update --module module...\n");
|
|
else
|
|
fprintf(stderr, "usage: tracepoint-update vmlinux...\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Process each file in turn, allowing deep failure. */
|
|
for (int i = 1; i < argc; i++) {
|
|
addr = elf_map(argv[i], &size, 1 << ET_REL);
|
|
if (!addr) {
|
|
++n_error;
|
|
continue;
|
|
}
|
|
|
|
if (process_tracepoints(mod, addr, argv[i]))
|
|
++n_error;
|
|
|
|
elf_unmap(addr, size);
|
|
}
|
|
|
|
return !!n_error;
|
|
}
|