text: /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <stdbool.h>
  #include "trace_helpers.h"
  
  struct ksyms {
  	struct ksym *syms;
  	int syms_sz;
  	int syms_cap;
  	char *strs;
  	int strs_sz;
  	int strs_cap;
  };
  
  static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, unsigned long addr)
  {
  	size_t new_cap, name_len = strlen(name) + 1;
  	struct ksym *ksym;
  	void *tmp;
  
  	if (ksyms->strs_sz + name_len > ksyms->strs_cap) {
  		new_cap = ksyms->strs_cap * 4 / 3;
  		if (new_cap < ksyms->strs_sz + name_len)
  			new_cap = ksyms->strs_sz + name_len;
  		if (new_cap < 1024)
  			new_cap = 1024;
  		tmp = realloc(ksyms->strs, new_cap);
  		if (!tmp)
  			return -1;
  		ksyms->strs = tmp;
  		ksyms->strs_cap = new_cap;
  	}
  	if (ksyms->syms_sz + 1 > ksyms->syms_cap) {
  		new_cap = ksyms->syms_cap * 4 / 3;
  		if (new_cap < 1024)
  			new_cap = 1024;
  		tmp = realloc(ksyms->syms, sizeof(*ksyms->syms) * new_cap);
  		if (!tmp)
  			return -1;
  		ksyms->syms = tmp;
  		ksyms->syms_cap = new_cap;
  	}
  
  	ksym = &ksyms->syms[ksyms->syms_sz];
  	/* while constructing, re-use pointer as just a plain offset */
  	ksym->name = (void *)(unsigned long)ksyms->strs_sz;
  	ksym->addr = addr;
  
  	memcpy(ksyms->strs + ksyms->strs_sz, name, name_len);
  	ksyms->strs_sz += name_len;
  	ksyms->syms_sz++;
  
  	return 0;
  }
  
  static int ksym_cmp(const void *p1, const void *p2)
  {
  	const struct ksym *s1 = p1, *s2 = p2;
  
  	if (s1->addr == s2->addr)
  		return strcmp(s1->name, s2->name);
  	return s1->addr < s2->addr ? -1 : 1;
  }
  
  struct ksyms *ksyms__load(void)
  {
  	char sym_type, sym_name[256];
  	struct ksyms *ksyms;
  	unsigned long sym_addr;
  	int i, ret;
  	FILE *f;
  
  	f = fopen("/proc/kallsyms", "r");
  	if (!f)
  		return NULL;
  
  	ksyms = calloc(1, sizeof(*ksyms));
  	if (!ksyms)
  		goto err_out;
  
  	while (true) {
  		ret = fscanf(f, "%lx %c %s%*[^\n]\n",
  			     &sym_addr, &sym_type, sym_name);
  		if (ret == EOF && feof(f))
  			break;
  		if (ret != 3)
  			goto err_out;
  		if (ksyms__add_symbol(ksyms, sym_name, sym_addr))
  			goto err_out;
  	}
  
  	/* now when strings are finalized, adjust pointers properly */
  	for (i = 0; i < ksyms->syms_sz; i++)
  		ksyms->syms[i].name += (unsigned long)ksyms->strs;
  
  	qsort(ksyms->syms, ksyms->syms_sz, sizeof(*ksyms->syms), ksym_cmp);
  
  	fclose(f);
  	return ksyms;
  
  err_out:
  	ksyms__free(ksyms);
  	fclose(f);
  	return NULL;
  }
  
  void ksyms__free(struct ksyms *ksyms)
  {
  	if (!ksyms)
  		return;
  
  	free(ksyms->syms);
  	free(ksyms->strs);
  	free(ksyms);
  }
  
  const struct ksym *ksyms__map_addr(const struct ksyms *ksyms,
  				   unsigned long addr)
  {
  	int start = 0, end = ksyms->syms_sz - 1, mid;
  	unsigned long sym_addr;
  
  	/* find largest sym_addr <= addr using binary search */
  	while (start < end) {
  		mid = start + (end - start + 1) / 2;
  		sym_addr = ksyms->syms[mid].addr;
  
  		if (sym_addr <= addr)
  			start = mid;
  		else
  			end = mid - 1;
  	}
  
  	if (start == end && ksyms->syms[start].addr <= addr)
  		return &ksyms->syms[start];
  	return NULL;
  }
  
  const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms,
  				     const char *name)
  {
  	int i;
  
  	for (i = 0; i < ksyms->syms_sz; i++) {
  		if (strcmp(ksyms->syms[i].name, name) == 0)
  			return &ksyms->syms[i];
  	}
  
  	return NULL;
  }
  