17c2aad20Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
27c2aad20Sopenharmony_ci/* Copyright (c) 2018 Facebook */
37c2aad20Sopenharmony_ci
47c2aad20Sopenharmony_ci#include <string.h>
57c2aad20Sopenharmony_ci#include <stdlib.h>
67c2aad20Sopenharmony_ci#include <linux/err.h>
77c2aad20Sopenharmony_ci#include <linux/bpf.h>
87c2aad20Sopenharmony_ci#include "libbpf.h"
97c2aad20Sopenharmony_ci#include "libbpf_internal.h"
107c2aad20Sopenharmony_ci
117c2aad20Sopenharmony_cistruct bpf_prog_linfo {
127c2aad20Sopenharmony_ci	void *raw_linfo;
137c2aad20Sopenharmony_ci	void *raw_jited_linfo;
147c2aad20Sopenharmony_ci	__u32 *nr_jited_linfo_per_func;
157c2aad20Sopenharmony_ci	__u32 *jited_linfo_func_idx;
167c2aad20Sopenharmony_ci	__u32 nr_linfo;
177c2aad20Sopenharmony_ci	__u32 nr_jited_func;
187c2aad20Sopenharmony_ci	__u32 rec_size;
197c2aad20Sopenharmony_ci	__u32 jited_rec_size;
207c2aad20Sopenharmony_ci};
217c2aad20Sopenharmony_ci
227c2aad20Sopenharmony_cistatic int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
237c2aad20Sopenharmony_ci			      const __u64 *ksym_func, const __u32 *ksym_len)
247c2aad20Sopenharmony_ci{
257c2aad20Sopenharmony_ci	__u32 nr_jited_func, nr_linfo;
267c2aad20Sopenharmony_ci	const void *raw_jited_linfo;
277c2aad20Sopenharmony_ci	const __u64 *jited_linfo;
287c2aad20Sopenharmony_ci	__u64 last_jited_linfo;
297c2aad20Sopenharmony_ci	/*
307c2aad20Sopenharmony_ci	 * Index to raw_jited_linfo:
317c2aad20Sopenharmony_ci	 *      i: Index for searching the next ksym_func
327c2aad20Sopenharmony_ci	 * prev_i: Index to the last found ksym_func
337c2aad20Sopenharmony_ci	 */
347c2aad20Sopenharmony_ci	__u32 i, prev_i;
357c2aad20Sopenharmony_ci	__u32 f; /* Index to ksym_func */
367c2aad20Sopenharmony_ci
377c2aad20Sopenharmony_ci	raw_jited_linfo = prog_linfo->raw_jited_linfo;
387c2aad20Sopenharmony_ci	jited_linfo = raw_jited_linfo;
397c2aad20Sopenharmony_ci	if (ksym_func[0] != *jited_linfo)
407c2aad20Sopenharmony_ci		goto errout;
417c2aad20Sopenharmony_ci
427c2aad20Sopenharmony_ci	prog_linfo->jited_linfo_func_idx[0] = 0;
437c2aad20Sopenharmony_ci	nr_jited_func = prog_linfo->nr_jited_func;
447c2aad20Sopenharmony_ci	nr_linfo = prog_linfo->nr_linfo;
457c2aad20Sopenharmony_ci
467c2aad20Sopenharmony_ci	for (prev_i = 0, i = 1, f = 1;
477c2aad20Sopenharmony_ci	     i < nr_linfo && f < nr_jited_func;
487c2aad20Sopenharmony_ci	     i++) {
497c2aad20Sopenharmony_ci		raw_jited_linfo += prog_linfo->jited_rec_size;
507c2aad20Sopenharmony_ci		last_jited_linfo = *jited_linfo;
517c2aad20Sopenharmony_ci		jited_linfo = raw_jited_linfo;
527c2aad20Sopenharmony_ci
537c2aad20Sopenharmony_ci		if (ksym_func[f] == *jited_linfo) {
547c2aad20Sopenharmony_ci			prog_linfo->jited_linfo_func_idx[f] = i;
557c2aad20Sopenharmony_ci
567c2aad20Sopenharmony_ci			/* Sanity check */
577c2aad20Sopenharmony_ci			if (last_jited_linfo - ksym_func[f - 1] + 1 >
587c2aad20Sopenharmony_ci			    ksym_len[f - 1])
597c2aad20Sopenharmony_ci				goto errout;
607c2aad20Sopenharmony_ci
617c2aad20Sopenharmony_ci			prog_linfo->nr_jited_linfo_per_func[f - 1] =
627c2aad20Sopenharmony_ci				i - prev_i;
637c2aad20Sopenharmony_ci			prev_i = i;
647c2aad20Sopenharmony_ci
657c2aad20Sopenharmony_ci			/*
667c2aad20Sopenharmony_ci			 * The ksym_func[f] is found in jited_linfo.
677c2aad20Sopenharmony_ci			 * Look for the next one.
687c2aad20Sopenharmony_ci			 */
697c2aad20Sopenharmony_ci			f++;
707c2aad20Sopenharmony_ci		} else if (*jited_linfo <= last_jited_linfo) {
717c2aad20Sopenharmony_ci			/* Ensure the addr is increasing _within_ a func */
727c2aad20Sopenharmony_ci			goto errout;
737c2aad20Sopenharmony_ci		}
747c2aad20Sopenharmony_ci	}
757c2aad20Sopenharmony_ci
767c2aad20Sopenharmony_ci	if (f != nr_jited_func)
777c2aad20Sopenharmony_ci		goto errout;
787c2aad20Sopenharmony_ci
797c2aad20Sopenharmony_ci	prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
807c2aad20Sopenharmony_ci		nr_linfo - prev_i;
817c2aad20Sopenharmony_ci
827c2aad20Sopenharmony_ci	return 0;
837c2aad20Sopenharmony_ci
847c2aad20Sopenharmony_cierrout:
857c2aad20Sopenharmony_ci	return -EINVAL;
867c2aad20Sopenharmony_ci}
877c2aad20Sopenharmony_ci
887c2aad20Sopenharmony_civoid bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
897c2aad20Sopenharmony_ci{
907c2aad20Sopenharmony_ci	if (!prog_linfo)
917c2aad20Sopenharmony_ci		return;
927c2aad20Sopenharmony_ci
937c2aad20Sopenharmony_ci	free(prog_linfo->raw_linfo);
947c2aad20Sopenharmony_ci	free(prog_linfo->raw_jited_linfo);
957c2aad20Sopenharmony_ci	free(prog_linfo->nr_jited_linfo_per_func);
967c2aad20Sopenharmony_ci	free(prog_linfo->jited_linfo_func_idx);
977c2aad20Sopenharmony_ci	free(prog_linfo);
987c2aad20Sopenharmony_ci}
997c2aad20Sopenharmony_ci
1007c2aad20Sopenharmony_cistruct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
1017c2aad20Sopenharmony_ci{
1027c2aad20Sopenharmony_ci	struct bpf_prog_linfo *prog_linfo;
1037c2aad20Sopenharmony_ci	__u32 nr_linfo, nr_jited_func;
1047c2aad20Sopenharmony_ci	__u64 data_sz;
1057c2aad20Sopenharmony_ci
1067c2aad20Sopenharmony_ci	nr_linfo = info->nr_line_info;
1077c2aad20Sopenharmony_ci
1087c2aad20Sopenharmony_ci	if (!nr_linfo)
1097c2aad20Sopenharmony_ci		return errno = EINVAL, NULL;
1107c2aad20Sopenharmony_ci
1117c2aad20Sopenharmony_ci	/*
1127c2aad20Sopenharmony_ci	 * The min size that bpf_prog_linfo has to access for
1137c2aad20Sopenharmony_ci	 * searching purpose.
1147c2aad20Sopenharmony_ci	 */
1157c2aad20Sopenharmony_ci	if (info->line_info_rec_size <
1167c2aad20Sopenharmony_ci	    offsetof(struct bpf_line_info, file_name_off))
1177c2aad20Sopenharmony_ci		return errno = EINVAL, NULL;
1187c2aad20Sopenharmony_ci
1197c2aad20Sopenharmony_ci	prog_linfo = calloc(1, sizeof(*prog_linfo));
1207c2aad20Sopenharmony_ci	if (!prog_linfo)
1217c2aad20Sopenharmony_ci		return errno = ENOMEM, NULL;
1227c2aad20Sopenharmony_ci
1237c2aad20Sopenharmony_ci	/* Copy xlated line_info */
1247c2aad20Sopenharmony_ci	prog_linfo->nr_linfo = nr_linfo;
1257c2aad20Sopenharmony_ci	prog_linfo->rec_size = info->line_info_rec_size;
1267c2aad20Sopenharmony_ci	data_sz = (__u64)nr_linfo * prog_linfo->rec_size;
1277c2aad20Sopenharmony_ci	prog_linfo->raw_linfo = malloc(data_sz);
1287c2aad20Sopenharmony_ci	if (!prog_linfo->raw_linfo)
1297c2aad20Sopenharmony_ci		goto err_free;
1307c2aad20Sopenharmony_ci	memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info, data_sz);
1317c2aad20Sopenharmony_ci
1327c2aad20Sopenharmony_ci	nr_jited_func = info->nr_jited_ksyms;
1337c2aad20Sopenharmony_ci	if (!nr_jited_func ||
1347c2aad20Sopenharmony_ci	    !info->jited_line_info ||
1357c2aad20Sopenharmony_ci	    info->nr_jited_line_info != nr_linfo ||
1367c2aad20Sopenharmony_ci	    info->jited_line_info_rec_size < sizeof(__u64) ||
1377c2aad20Sopenharmony_ci	    info->nr_jited_func_lens != nr_jited_func ||
1387c2aad20Sopenharmony_ci	    !info->jited_ksyms ||
1397c2aad20Sopenharmony_ci	    !info->jited_func_lens)
1407c2aad20Sopenharmony_ci		/* Not enough info to provide jited_line_info */
1417c2aad20Sopenharmony_ci		return prog_linfo;
1427c2aad20Sopenharmony_ci
1437c2aad20Sopenharmony_ci	/* Copy jited_line_info */
1447c2aad20Sopenharmony_ci	prog_linfo->nr_jited_func = nr_jited_func;
1457c2aad20Sopenharmony_ci	prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
1467c2aad20Sopenharmony_ci	data_sz = (__u64)nr_linfo * prog_linfo->jited_rec_size;
1477c2aad20Sopenharmony_ci	prog_linfo->raw_jited_linfo = malloc(data_sz);
1487c2aad20Sopenharmony_ci	if (!prog_linfo->raw_jited_linfo)
1497c2aad20Sopenharmony_ci		goto err_free;
1507c2aad20Sopenharmony_ci	memcpy(prog_linfo->raw_jited_linfo,
1517c2aad20Sopenharmony_ci	       (void *)(long)info->jited_line_info, data_sz);
1527c2aad20Sopenharmony_ci
1537c2aad20Sopenharmony_ci	/* Number of jited_line_info per jited func */
1547c2aad20Sopenharmony_ci	prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
1557c2aad20Sopenharmony_ci						     sizeof(__u32));
1567c2aad20Sopenharmony_ci	if (!prog_linfo->nr_jited_linfo_per_func)
1577c2aad20Sopenharmony_ci		goto err_free;
1587c2aad20Sopenharmony_ci
1597c2aad20Sopenharmony_ci	/*
1607c2aad20Sopenharmony_ci	 * For each jited func,
1617c2aad20Sopenharmony_ci	 * the start idx to the "linfo" and "jited_linfo" array,
1627c2aad20Sopenharmony_ci	 */
1637c2aad20Sopenharmony_ci	prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
1647c2aad20Sopenharmony_ci						  sizeof(__u32));
1657c2aad20Sopenharmony_ci	if (!prog_linfo->jited_linfo_func_idx)
1667c2aad20Sopenharmony_ci		goto err_free;
1677c2aad20Sopenharmony_ci
1687c2aad20Sopenharmony_ci	if (dissect_jited_func(prog_linfo,
1697c2aad20Sopenharmony_ci			       (__u64 *)(long)info->jited_ksyms,
1707c2aad20Sopenharmony_ci			       (__u32 *)(long)info->jited_func_lens))
1717c2aad20Sopenharmony_ci		goto err_free;
1727c2aad20Sopenharmony_ci
1737c2aad20Sopenharmony_ci	return prog_linfo;
1747c2aad20Sopenharmony_ci
1757c2aad20Sopenharmony_cierr_free:
1767c2aad20Sopenharmony_ci	bpf_prog_linfo__free(prog_linfo);
1777c2aad20Sopenharmony_ci	return errno = EINVAL, NULL;
1787c2aad20Sopenharmony_ci}
1797c2aad20Sopenharmony_ci
1807c2aad20Sopenharmony_ciconst struct bpf_line_info *
1817c2aad20Sopenharmony_cibpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
1827c2aad20Sopenharmony_ci				__u64 addr, __u32 func_idx, __u32 nr_skip)
1837c2aad20Sopenharmony_ci{
1847c2aad20Sopenharmony_ci	__u32 jited_rec_size, rec_size, nr_linfo, start, i;
1857c2aad20Sopenharmony_ci	const void *raw_jited_linfo, *raw_linfo;
1867c2aad20Sopenharmony_ci	const __u64 *jited_linfo;
1877c2aad20Sopenharmony_ci
1887c2aad20Sopenharmony_ci	if (func_idx >= prog_linfo->nr_jited_func)
1897c2aad20Sopenharmony_ci		return errno = ENOENT, NULL;
1907c2aad20Sopenharmony_ci
1917c2aad20Sopenharmony_ci	nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
1927c2aad20Sopenharmony_ci	if (nr_skip >= nr_linfo)
1937c2aad20Sopenharmony_ci		return errno = ENOENT, NULL;
1947c2aad20Sopenharmony_ci
1957c2aad20Sopenharmony_ci	start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
1967c2aad20Sopenharmony_ci	jited_rec_size = prog_linfo->jited_rec_size;
1977c2aad20Sopenharmony_ci	raw_jited_linfo = prog_linfo->raw_jited_linfo +
1987c2aad20Sopenharmony_ci		(start * jited_rec_size);
1997c2aad20Sopenharmony_ci	jited_linfo = raw_jited_linfo;
2007c2aad20Sopenharmony_ci	if (addr < *jited_linfo)
2017c2aad20Sopenharmony_ci		return errno = ENOENT, NULL;
2027c2aad20Sopenharmony_ci
2037c2aad20Sopenharmony_ci	nr_linfo -= nr_skip;
2047c2aad20Sopenharmony_ci	rec_size = prog_linfo->rec_size;
2057c2aad20Sopenharmony_ci	raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
2067c2aad20Sopenharmony_ci	for (i = 0; i < nr_linfo; i++) {
2077c2aad20Sopenharmony_ci		if (addr < *jited_linfo)
2087c2aad20Sopenharmony_ci			break;
2097c2aad20Sopenharmony_ci
2107c2aad20Sopenharmony_ci		raw_linfo += rec_size;
2117c2aad20Sopenharmony_ci		raw_jited_linfo += jited_rec_size;
2127c2aad20Sopenharmony_ci		jited_linfo = raw_jited_linfo;
2137c2aad20Sopenharmony_ci	}
2147c2aad20Sopenharmony_ci
2157c2aad20Sopenharmony_ci	return raw_linfo - rec_size;
2167c2aad20Sopenharmony_ci}
2177c2aad20Sopenharmony_ci
2187c2aad20Sopenharmony_ciconst struct bpf_line_info *
2197c2aad20Sopenharmony_cibpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
2207c2aad20Sopenharmony_ci		      __u32 insn_off, __u32 nr_skip)
2217c2aad20Sopenharmony_ci{
2227c2aad20Sopenharmony_ci	const struct bpf_line_info *linfo;
2237c2aad20Sopenharmony_ci	__u32 rec_size, nr_linfo, i;
2247c2aad20Sopenharmony_ci	const void *raw_linfo;
2257c2aad20Sopenharmony_ci
2267c2aad20Sopenharmony_ci	nr_linfo = prog_linfo->nr_linfo;
2277c2aad20Sopenharmony_ci	if (nr_skip >= nr_linfo)
2287c2aad20Sopenharmony_ci		return errno = ENOENT, NULL;
2297c2aad20Sopenharmony_ci
2307c2aad20Sopenharmony_ci	rec_size = prog_linfo->rec_size;
2317c2aad20Sopenharmony_ci	raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
2327c2aad20Sopenharmony_ci	linfo = raw_linfo;
2337c2aad20Sopenharmony_ci	if (insn_off < linfo->insn_off)
2347c2aad20Sopenharmony_ci		return errno = ENOENT, NULL;
2357c2aad20Sopenharmony_ci
2367c2aad20Sopenharmony_ci	nr_linfo -= nr_skip;
2377c2aad20Sopenharmony_ci	for (i = 0; i < nr_linfo; i++) {
2387c2aad20Sopenharmony_ci		if (insn_off < linfo->insn_off)
2397c2aad20Sopenharmony_ci			break;
2407c2aad20Sopenharmony_ci
2417c2aad20Sopenharmony_ci		raw_linfo += rec_size;
2427c2aad20Sopenharmony_ci		linfo = raw_linfo;
2437c2aad20Sopenharmony_ci	}
2447c2aad20Sopenharmony_ci
2457c2aad20Sopenharmony_ci	return raw_linfo - rec_size;
2467c2aad20Sopenharmony_ci}
247