mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
6.1 KiB
246 lines
6.1 KiB
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) |
|
/* Copyright (c) 2018 Facebook */ |
|
|
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <linux/err.h> |
|
#include <linux/bpf.h> |
|
#include "libbpf.h" |
|
#include "libbpf_internal.h" |
|
|
|
struct bpf_prog_linfo { |
|
void *raw_linfo; |
|
void *raw_jited_linfo; |
|
__u32 *nr_jited_linfo_per_func; |
|
__u32 *jited_linfo_func_idx; |
|
__u32 nr_linfo; |
|
__u32 nr_jited_func; |
|
__u32 rec_size; |
|
__u32 jited_rec_size; |
|
}; |
|
|
|
static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo, |
|
const __u64 *ksym_func, const __u32 *ksym_len) |
|
{ |
|
__u32 nr_jited_func, nr_linfo; |
|
const void *raw_jited_linfo; |
|
const __u64 *jited_linfo; |
|
__u64 last_jited_linfo; |
|
/* |
|
* Index to raw_jited_linfo: |
|
* i: Index for searching the next ksym_func |
|
* prev_i: Index to the last found ksym_func |
|
*/ |
|
__u32 i, prev_i; |
|
__u32 f; /* Index to ksym_func */ |
|
|
|
raw_jited_linfo = prog_linfo->raw_jited_linfo; |
|
jited_linfo = raw_jited_linfo; |
|
if (ksym_func[0] != *jited_linfo) |
|
goto errout; |
|
|
|
prog_linfo->jited_linfo_func_idx[0] = 0; |
|
nr_jited_func = prog_linfo->nr_jited_func; |
|
nr_linfo = prog_linfo->nr_linfo; |
|
|
|
for (prev_i = 0, i = 1, f = 1; |
|
i < nr_linfo && f < nr_jited_func; |
|
i++) { |
|
raw_jited_linfo += prog_linfo->jited_rec_size; |
|
last_jited_linfo = *jited_linfo; |
|
jited_linfo = raw_jited_linfo; |
|
|
|
if (ksym_func[f] == *jited_linfo) { |
|
prog_linfo->jited_linfo_func_idx[f] = i; |
|
|
|
/* Sanity check */ |
|
if (last_jited_linfo - ksym_func[f - 1] + 1 > |
|
ksym_len[f - 1]) |
|
goto errout; |
|
|
|
prog_linfo->nr_jited_linfo_per_func[f - 1] = |
|
i - prev_i; |
|
prev_i = i; |
|
|
|
/* |
|
* The ksym_func[f] is found in jited_linfo. |
|
* Look for the next one. |
|
*/ |
|
f++; |
|
} else if (*jited_linfo <= last_jited_linfo) { |
|
/* Ensure the addr is increasing _within_ a func */ |
|
goto errout; |
|
} |
|
} |
|
|
|
if (f != nr_jited_func) |
|
goto errout; |
|
|
|
prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] = |
|
nr_linfo - prev_i; |
|
|
|
return 0; |
|
|
|
errout: |
|
return -EINVAL; |
|
} |
|
|
|
void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo) |
|
{ |
|
if (!prog_linfo) |
|
return; |
|
|
|
free(prog_linfo->raw_linfo); |
|
free(prog_linfo->raw_jited_linfo); |
|
free(prog_linfo->nr_jited_linfo_per_func); |
|
free(prog_linfo->jited_linfo_func_idx); |
|
free(prog_linfo); |
|
} |
|
|
|
struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info) |
|
{ |
|
struct bpf_prog_linfo *prog_linfo; |
|
__u32 nr_linfo, nr_jited_func; |
|
__u64 data_sz; |
|
|
|
nr_linfo = info->nr_line_info; |
|
|
|
if (!nr_linfo) |
|
return errno = EINVAL, NULL; |
|
|
|
/* |
|
* The min size that bpf_prog_linfo has to access for |
|
* searching purpose. |
|
*/ |
|
if (info->line_info_rec_size < |
|
offsetof(struct bpf_line_info, file_name_off)) |
|
return errno = EINVAL, NULL; |
|
|
|
prog_linfo = calloc(1, sizeof(*prog_linfo)); |
|
if (!prog_linfo) |
|
return errno = ENOMEM, NULL; |
|
|
|
/* Copy xlated line_info */ |
|
prog_linfo->nr_linfo = nr_linfo; |
|
prog_linfo->rec_size = info->line_info_rec_size; |
|
data_sz = (__u64)nr_linfo * prog_linfo->rec_size; |
|
prog_linfo->raw_linfo = malloc(data_sz); |
|
if (!prog_linfo->raw_linfo) |
|
goto err_free; |
|
memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info, data_sz); |
|
|
|
nr_jited_func = info->nr_jited_ksyms; |
|
if (!nr_jited_func || |
|
!info->jited_line_info || |
|
info->nr_jited_line_info != nr_linfo || |
|
info->jited_line_info_rec_size < sizeof(__u64) || |
|
info->nr_jited_func_lens != nr_jited_func || |
|
!info->jited_ksyms || |
|
!info->jited_func_lens) |
|
/* Not enough info to provide jited_line_info */ |
|
return prog_linfo; |
|
|
|
/* Copy jited_line_info */ |
|
prog_linfo->nr_jited_func = nr_jited_func; |
|
prog_linfo->jited_rec_size = info->jited_line_info_rec_size; |
|
data_sz = (__u64)nr_linfo * prog_linfo->jited_rec_size; |
|
prog_linfo->raw_jited_linfo = malloc(data_sz); |
|
if (!prog_linfo->raw_jited_linfo) |
|
goto err_free; |
|
memcpy(prog_linfo->raw_jited_linfo, |
|
(void *)(long)info->jited_line_info, data_sz); |
|
|
|
/* Number of jited_line_info per jited func */ |
|
prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func * |
|
sizeof(__u32)); |
|
if (!prog_linfo->nr_jited_linfo_per_func) |
|
goto err_free; |
|
|
|
/* |
|
* For each jited func, |
|
* the start idx to the "linfo" and "jited_linfo" array, |
|
*/ |
|
prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func * |
|
sizeof(__u32)); |
|
if (!prog_linfo->jited_linfo_func_idx) |
|
goto err_free; |
|
|
|
if (dissect_jited_func(prog_linfo, |
|
(__u64 *)(long)info->jited_ksyms, |
|
(__u32 *)(long)info->jited_func_lens)) |
|
goto err_free; |
|
|
|
return prog_linfo; |
|
|
|
err_free: |
|
bpf_prog_linfo__free(prog_linfo); |
|
return errno = EINVAL, NULL; |
|
} |
|
|
|
const struct bpf_line_info * |
|
bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo, |
|
__u64 addr, __u32 func_idx, __u32 nr_skip) |
|
{ |
|
__u32 jited_rec_size, rec_size, nr_linfo, start, i; |
|
const void *raw_jited_linfo, *raw_linfo; |
|
const __u64 *jited_linfo; |
|
|
|
if (func_idx >= prog_linfo->nr_jited_func) |
|
return errno = ENOENT, NULL; |
|
|
|
nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx]; |
|
if (nr_skip >= nr_linfo) |
|
return errno = ENOENT, NULL; |
|
|
|
start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip; |
|
jited_rec_size = prog_linfo->jited_rec_size; |
|
raw_jited_linfo = prog_linfo->raw_jited_linfo + |
|
(start * jited_rec_size); |
|
jited_linfo = raw_jited_linfo; |
|
if (addr < *jited_linfo) |
|
return errno = ENOENT, NULL; |
|
|
|
nr_linfo -= nr_skip; |
|
rec_size = prog_linfo->rec_size; |
|
raw_linfo = prog_linfo->raw_linfo + (start * rec_size); |
|
for (i = 0; i < nr_linfo; i++) { |
|
if (addr < *jited_linfo) |
|
break; |
|
|
|
raw_linfo += rec_size; |
|
raw_jited_linfo += jited_rec_size; |
|
jited_linfo = raw_jited_linfo; |
|
} |
|
|
|
return raw_linfo - rec_size; |
|
} |
|
|
|
const struct bpf_line_info * |
|
bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo, |
|
__u32 insn_off, __u32 nr_skip) |
|
{ |
|
const struct bpf_line_info *linfo; |
|
__u32 rec_size, nr_linfo, i; |
|
const void *raw_linfo; |
|
|
|
nr_linfo = prog_linfo->nr_linfo; |
|
if (nr_skip >= nr_linfo) |
|
return errno = ENOENT, NULL; |
|
|
|
rec_size = prog_linfo->rec_size; |
|
raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size); |
|
linfo = raw_linfo; |
|
if (insn_off < linfo->insn_off) |
|
return errno = ENOENT, NULL; |
|
|
|
nr_linfo -= nr_skip; |
|
for (i = 0; i < nr_linfo; i++) { |
|
if (insn_off < linfo->insn_off) |
|
break; |
|
|
|
raw_linfo += rec_size; |
|
linfo = raw_linfo; |
|
} |
|
|
|
return raw_linfo - rec_size; |
|
}
|
|
|