我想从用户空间程序的命令行将用户指定的变量值传递给ebpf程序。我知道如何使用bpf映射,但我听说有一种更有效的方法可以使用bpf全局数据来实现这一点。
有人能提供一些代码示例吗?(我使用的是libbpf(
Clang将获取全局数据并将其放入.data
、.rodata
或.bss
部分。.data
(如果您的值已初始化,也可以更改值(,.rodata
(对于const
值(和.bss
(对于未初始化的值((将初始化为0(。
在正常程序中,这些部分会加载到堆中,但由于eBPF没有堆,所以这些部分会作为特殊映射加载。libbpf调用这些内部映射,它们本质上是具有1个键/值的数组映射,值是elf部分的大小。生成的eBPF然后在此数据blob中以适当的偏移量读取数据(使用特殊指令来提高正常映射加载的性能(。
Libbpf允许您访问和更改这些映射。除了.rodata
,因为libbpf在加载过程中冻结了它,所以无法修改它。
更改.bss
秒的值仍然可以完成,我相信您可以在调用bpf_object__load
之前用bpf_map__set_initial_value
完成。没有走那条路作为例子。
由于一个数据段可以包含多个值,我们必须弄清楚clang是如何布置内存的。为此,我们可以使用BTF,它对这些数据进行编码。
免责声明,这可能是不起作用的代码,从示例和头文件中将其拼凑在一起(我不经常使用libbpf,也没有测试/编译它(。但这应该会让你朝着正确的方向开始:
int main(int ac, char **argv)
{
struct bpf_object *obj;
struct bpf_program *prog;
struct bpf_map *map;
struct btf *btf;
const struct btf_type *datasec;
struct btf_var_secinfo *infos;
int map_fd, prog_fd;
__s32 datasec_id;
char filename[256];
int i, err;
int zero = 0;
FILE *f;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj))
return 1;
map = bpf_object__find_map_by_name(obj, ".data");
if (libbpf_get_error(map))
return 1;
err = bpf_object__load(obj);
if (err)
return 1;
map_fd = bpf_map__fd(map);
if (libbpf_get_error(map_fd))
return 1;
// Create buffer the size of .data
buff = malloc(bpf_map__def(map)->value_size);
if (!buff)
return 1;
// Read .data into the buffer
err = bpf_map_lookup_elem(map_fd, &zero, buff, 0);
if (err)
return 1;
// Get BTF, we need it do find out the memory layout of .data
btf = bpf_object__btf(obj);
if (libbpf_get_error(btf))
return 1;
// Get the type ID of the datasection of .data
datasec_id = btf__find_by_name(btf, ".data");
if (libbpf_get_error(datasec_id))
return 1;
// Get the actual BTF type from the ID
datasec = btf__type_by_id(btf, datasec_id)
if (libbpf_get_error(datasec))
return 1;
// Get all secinfos, each of which will be a global variable
infos = btf_var_secinfos(datasec);
// Loop over all sections
for(i = 0; i < btf_vlen(datasec); i++) {
// Get the BTF type of the current var
const struct btf_type *t = btf__type_by_id(btf, infos[i]->type);
// Get the name of the global variable
const char *name = btf__name_by_offset(btf, t->name_off);
// If it matches the name of the var we want to change at runtime
if (!strcmp(name, "global_var")) {
// Overwrite its value (this code assumes just a single byte)
// for multibyte values you will obviusly have to edit more bytes.
// the total size of the var can be gotten from infos[i]->size
buff[infos[i]->offset] = 0x12;
}
}
// Write the updated datasection to the map
err = bpf_map_update_elem(map_fd, &zero, buff, 0);
if (err)
return 1;
free(buff);
// TODO attach program somewhere
}