cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gcc_4_7.c (10512B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *  This code provides functions to handle gcc's profiling data format
      4 *  introduced with gcc 4.7.
      5 *
      6 *  This file is based heavily on gcc_3_4.c file.
      7 *
      8 *  For a better understanding, refer to gcc source:
      9 *  gcc/gcov-io.h
     10 *  libgcc/libgcov.c
     11 *
     12 *  Uses gcc-internal data definitions.
     13 */
     14
     15#include <linux/errno.h>
     16#include <linux/slab.h>
     17#include <linux/string.h>
     18#include <linux/mm.h>
     19#include "gcov.h"
     20
     21#if (__GNUC__ >= 10)
     22#define GCOV_COUNTERS			8
     23#elif (__GNUC__ >= 7)
     24#define GCOV_COUNTERS			9
     25#elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
     26#define GCOV_COUNTERS			10
     27#else
     28#define GCOV_COUNTERS			9
     29#endif
     30
     31#define GCOV_TAG_FUNCTION_LENGTH	3
     32
     33static struct gcov_info *gcov_info_head;
     34
     35/**
     36 * struct gcov_ctr_info - information about counters for a single function
     37 * @num: number of counter values for this type
     38 * @values: array of counter values for this type
     39 *
     40 * This data is generated by gcc during compilation and doesn't change
     41 * at run-time with the exception of the values array.
     42 */
     43struct gcov_ctr_info {
     44	unsigned int num;
     45	gcov_type *values;
     46};
     47
     48/**
     49 * struct gcov_fn_info - profiling meta data per function
     50 * @key: comdat key
     51 * @ident: unique ident of function
     52 * @lineno_checksum: function lineo_checksum
     53 * @cfg_checksum: function cfg checksum
     54 * @ctrs: instrumented counters
     55 *
     56 * This data is generated by gcc during compilation and doesn't change
     57 * at run-time.
     58 *
     59 * Information about a single function.  This uses the trailing array
     60 * idiom. The number of counters is determined from the merge pointer
     61 * array in gcov_info.  The key is used to detect which of a set of
     62 * comdat functions was selected -- it points to the gcov_info object
     63 * of the object file containing the selected comdat function.
     64 */
     65struct gcov_fn_info {
     66	const struct gcov_info *key;
     67	unsigned int ident;
     68	unsigned int lineno_checksum;
     69	unsigned int cfg_checksum;
     70	struct gcov_ctr_info ctrs[];
     71};
     72
     73/**
     74 * struct gcov_info - profiling data per object file
     75 * @version: gcov version magic indicating the gcc version used for compilation
     76 * @next: list head for a singly-linked list
     77 * @stamp: uniquifying time stamp
     78 * @filename: name of the associated gcov data file
     79 * @merge: merge functions (null for unused counter type)
     80 * @n_functions: number of instrumented functions
     81 * @functions: pointer to pointers to function information
     82 *
     83 * This data is generated by gcc during compilation and doesn't change
     84 * at run-time with the exception of the next pointer.
     85 */
     86struct gcov_info {
     87	unsigned int version;
     88	struct gcov_info *next;
     89	unsigned int stamp;
     90	const char *filename;
     91	void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
     92	unsigned int n_functions;
     93	struct gcov_fn_info **functions;
     94};
     95
     96/**
     97 * gcov_info_filename - return info filename
     98 * @info: profiling data set
     99 */
    100const char *gcov_info_filename(struct gcov_info *info)
    101{
    102	return info->filename;
    103}
    104
    105/**
    106 * gcov_info_version - return info version
    107 * @info: profiling data set
    108 */
    109unsigned int gcov_info_version(struct gcov_info *info)
    110{
    111	return info->version;
    112}
    113
    114/**
    115 * gcov_info_next - return next profiling data set
    116 * @info: profiling data set
    117 *
    118 * Returns next gcov_info following @info or first gcov_info in the chain if
    119 * @info is %NULL.
    120 */
    121struct gcov_info *gcov_info_next(struct gcov_info *info)
    122{
    123	if (!info)
    124		return gcov_info_head;
    125
    126	return info->next;
    127}
    128
    129/**
    130 * gcov_info_link - link/add profiling data set to the list
    131 * @info: profiling data set
    132 */
    133void gcov_info_link(struct gcov_info *info)
    134{
    135	info->next = gcov_info_head;
    136	gcov_info_head = info;
    137}
    138
    139/**
    140 * gcov_info_unlink - unlink/remove profiling data set from the list
    141 * @prev: previous profiling data set
    142 * @info: profiling data set
    143 */
    144void gcov_info_unlink(struct gcov_info *prev, struct gcov_info *info)
    145{
    146	if (prev)
    147		prev->next = info->next;
    148	else
    149		gcov_info_head = info->next;
    150}
    151
    152/**
    153 * gcov_info_within_module - check if a profiling data set belongs to a module
    154 * @info: profiling data set
    155 * @mod: module
    156 *
    157 * Returns true if profiling data belongs module, false otherwise.
    158 */
    159bool gcov_info_within_module(struct gcov_info *info, struct module *mod)
    160{
    161	return within_module((unsigned long)info, mod);
    162}
    163
    164/* Symbolic links to be created for each profiling data file. */
    165const struct gcov_link gcov_link[] = {
    166	{ OBJ_TREE, "gcno" },	/* Link to .gcno file in $(objtree). */
    167	{ 0, NULL},
    168};
    169
    170/*
    171 * Determine whether a counter is active. Doesn't change at run-time.
    172 */
    173static int counter_active(struct gcov_info *info, unsigned int type)
    174{
    175	return info->merge[type] ? 1 : 0;
    176}
    177
    178/* Determine number of active counters. Based on gcc magic. */
    179static unsigned int num_counter_active(struct gcov_info *info)
    180{
    181	unsigned int i;
    182	unsigned int result = 0;
    183
    184	for (i = 0; i < GCOV_COUNTERS; i++) {
    185		if (counter_active(info, i))
    186			result++;
    187	}
    188	return result;
    189}
    190
    191/**
    192 * gcov_info_reset - reset profiling data to zero
    193 * @info: profiling data set
    194 */
    195void gcov_info_reset(struct gcov_info *info)
    196{
    197	struct gcov_ctr_info *ci_ptr;
    198	unsigned int fi_idx;
    199	unsigned int ct_idx;
    200
    201	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
    202		ci_ptr = info->functions[fi_idx]->ctrs;
    203
    204		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
    205			if (!counter_active(info, ct_idx))
    206				continue;
    207
    208			memset(ci_ptr->values, 0,
    209					sizeof(gcov_type) * ci_ptr->num);
    210			ci_ptr++;
    211		}
    212	}
    213}
    214
    215/**
    216 * gcov_info_is_compatible - check if profiling data can be added
    217 * @info1: first profiling data set
    218 * @info2: second profiling data set
    219 *
    220 * Returns non-zero if profiling data can be added, zero otherwise.
    221 */
    222int gcov_info_is_compatible(struct gcov_info *info1, struct gcov_info *info2)
    223{
    224	return (info1->stamp == info2->stamp);
    225}
    226
    227/**
    228 * gcov_info_add - add up profiling data
    229 * @dst: profiling data set to which data is added
    230 * @src: profiling data set which is added
    231 *
    232 * Adds profiling counts of @src to @dst.
    233 */
    234void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
    235{
    236	struct gcov_ctr_info *dci_ptr;
    237	struct gcov_ctr_info *sci_ptr;
    238	unsigned int fi_idx;
    239	unsigned int ct_idx;
    240	unsigned int val_idx;
    241
    242	for (fi_idx = 0; fi_idx < src->n_functions; fi_idx++) {
    243		dci_ptr = dst->functions[fi_idx]->ctrs;
    244		sci_ptr = src->functions[fi_idx]->ctrs;
    245
    246		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
    247			if (!counter_active(src, ct_idx))
    248				continue;
    249
    250			for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
    251				dci_ptr->values[val_idx] +=
    252					sci_ptr->values[val_idx];
    253
    254			dci_ptr++;
    255			sci_ptr++;
    256		}
    257	}
    258}
    259
    260/**
    261 * gcov_info_dup - duplicate profiling data set
    262 * @info: profiling data set to duplicate
    263 *
    264 * Return newly allocated duplicate on success, %NULL on error.
    265 */
    266struct gcov_info *gcov_info_dup(struct gcov_info *info)
    267{
    268	struct gcov_info *dup;
    269	struct gcov_ctr_info *dci_ptr; /* dst counter info */
    270	struct gcov_ctr_info *sci_ptr; /* src counter info */
    271	unsigned int active;
    272	unsigned int fi_idx; /* function info idx */
    273	unsigned int ct_idx; /* counter type idx */
    274	size_t fi_size; /* function info size */
    275	size_t cv_size; /* counter values size */
    276
    277	dup = kmemdup(info, sizeof(*dup), GFP_KERNEL);
    278	if (!dup)
    279		return NULL;
    280
    281	dup->next = NULL;
    282	dup->filename = NULL;
    283	dup->functions = NULL;
    284
    285	dup->filename = kstrdup(info->filename, GFP_KERNEL);
    286	if (!dup->filename)
    287		goto err_free;
    288
    289	dup->functions = kcalloc(info->n_functions,
    290				 sizeof(struct gcov_fn_info *), GFP_KERNEL);
    291	if (!dup->functions)
    292		goto err_free;
    293
    294	active = num_counter_active(info);
    295	fi_size = sizeof(struct gcov_fn_info);
    296	fi_size += sizeof(struct gcov_ctr_info) * active;
    297
    298	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
    299		dup->functions[fi_idx] = kzalloc(fi_size, GFP_KERNEL);
    300		if (!dup->functions[fi_idx])
    301			goto err_free;
    302
    303		*(dup->functions[fi_idx]) = *(info->functions[fi_idx]);
    304
    305		sci_ptr = info->functions[fi_idx]->ctrs;
    306		dci_ptr = dup->functions[fi_idx]->ctrs;
    307
    308		for (ct_idx = 0; ct_idx < active; ct_idx++) {
    309
    310			cv_size = sizeof(gcov_type) * sci_ptr->num;
    311
    312			dci_ptr->values = kvmalloc(cv_size, GFP_KERNEL);
    313
    314			if (!dci_ptr->values)
    315				goto err_free;
    316
    317			dci_ptr->num = sci_ptr->num;
    318			memcpy(dci_ptr->values, sci_ptr->values, cv_size);
    319
    320			sci_ptr++;
    321			dci_ptr++;
    322		}
    323	}
    324
    325	return dup;
    326err_free:
    327	gcov_info_free(dup);
    328	return NULL;
    329}
    330
    331/**
    332 * gcov_info_free - release memory for profiling data set duplicate
    333 * @info: profiling data set duplicate to free
    334 */
    335void gcov_info_free(struct gcov_info *info)
    336{
    337	unsigned int active;
    338	unsigned int fi_idx;
    339	unsigned int ct_idx;
    340	struct gcov_ctr_info *ci_ptr;
    341
    342	if (!info->functions)
    343		goto free_info;
    344
    345	active = num_counter_active(info);
    346
    347	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
    348		if (!info->functions[fi_idx])
    349			continue;
    350
    351		ci_ptr = info->functions[fi_idx]->ctrs;
    352
    353		for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++)
    354			kvfree(ci_ptr->values);
    355
    356		kfree(info->functions[fi_idx]);
    357	}
    358
    359free_info:
    360	kfree(info->functions);
    361	kfree(info->filename);
    362	kfree(info);
    363}
    364
    365/**
    366 * convert_to_gcda - convert profiling data set to gcda file format
    367 * @buffer: the buffer to store file data or %NULL if no data should be stored
    368 * @info: profiling data set to be converted
    369 *
    370 * Returns the number of bytes that were/would have been stored into the buffer.
    371 */
    372size_t convert_to_gcda(char *buffer, struct gcov_info *info)
    373{
    374	struct gcov_fn_info *fi_ptr;
    375	struct gcov_ctr_info *ci_ptr;
    376	unsigned int fi_idx;
    377	unsigned int ct_idx;
    378	unsigned int cv_idx;
    379	size_t pos = 0;
    380
    381	/* File header. */
    382	pos += store_gcov_u32(buffer, pos, GCOV_DATA_MAGIC);
    383	pos += store_gcov_u32(buffer, pos, info->version);
    384	pos += store_gcov_u32(buffer, pos, info->stamp);
    385
    386	for (fi_idx = 0; fi_idx < info->n_functions; fi_idx++) {
    387		fi_ptr = info->functions[fi_idx];
    388
    389		/* Function record. */
    390		pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION);
    391		pos += store_gcov_u32(buffer, pos, GCOV_TAG_FUNCTION_LENGTH);
    392		pos += store_gcov_u32(buffer, pos, fi_ptr->ident);
    393		pos += store_gcov_u32(buffer, pos, fi_ptr->lineno_checksum);
    394		pos += store_gcov_u32(buffer, pos, fi_ptr->cfg_checksum);
    395
    396		ci_ptr = fi_ptr->ctrs;
    397
    398		for (ct_idx = 0; ct_idx < GCOV_COUNTERS; ct_idx++) {
    399			if (!counter_active(info, ct_idx))
    400				continue;
    401
    402			/* Counter record. */
    403			pos += store_gcov_u32(buffer, pos,
    404					      GCOV_TAG_FOR_COUNTER(ct_idx));
    405			pos += store_gcov_u32(buffer, pos, ci_ptr->num * 2);
    406
    407			for (cv_idx = 0; cv_idx < ci_ptr->num; cv_idx++) {
    408				pos += store_gcov_u64(buffer, pos,
    409						      ci_ptr->values[cv_idx]);
    410			}
    411
    412			ci_ptr++;
    413		}
    414	}
    415
    416	return pos;
    417}