Commit d1f6efda authored by Kirill Smelkov's avatar Kirill Smelkov

Detect kernel headers even if they are splitted into source/ and build/ directories

Currently bpftrace uses only /lib/modules/`uname -r`/source/ if it
exists. This however leads to failures Debian where generated kernel
headers, such as include/generated/autoconf.h are put into a separate
directory, for example:

	---- 8< ---- (fuse_vs_mmapsem.bt)
	#include <linux/path.h>
	#include <linux/dcache.h>
	#include <linux/sched.h>
	#include <linux/mm_types.h>

	kprobe:fuse_readpage,kprobe:fuse_readpages {
	        printf("%s (%d) %s\n", probe, ((task_struct *)curtask)->mm->mmap_sem.count.counter, kstack);
	}
	---- 8< ----

	# ./bpftrace fuse_vs_mmapsem.bt
	/lib/modules/4.19.0-2-amd64/source/include/linux/kconfig.h:5:10: fatal error: 'generated/autoconf.h' file not found
	Unknown struct/union: 'task_struct'

Fix it by using both /lib/modules/`uname -r`/source/ and
/lib/modules/`uname -r`/build/ if both are present.

/cc @vincentbernat

P.S. I was not using C++ for ages, so please forgive me in advance if I
missed something.
parent 5976edec
......@@ -122,21 +122,54 @@ static bool is_dir(const std::string& path)
return S_ISDIR(buf.st_mode);
}
static std::string get_kernel_root_dir(const struct utsname& utsname)
// get_kernel_dirs returns {ksrc, kobj} - directories for pristine and
// generated kernel sources.
//
// When the kernel was built in its source tree ksrc == kobj, however when
// the kernel was build in a different directory than its source, ksrc != kobj.
//
// A notable example is Debian, which places pristine kernel headers in
//
// /lib/modules/`uname -r`/source/
//
// and generated kernel headers in
//
// /lib/modules/`uname -r`/build/
//
// {"", ""} is returned if no trace of kernel headers was found at all.
// Both ksrc and kobj are guaranteed to be != "", if at least some trace of kernel sources was found.
static std::tuple<std::string, std::string> get_kernel_dirs(const struct utsname& utsname)
{
#ifdef KERNEL_HEADERS_DIR
return KERNEL_HEADERS_DIR;
return {KERNEL_HEADERS_DIR, KERNEL_HEADERS_DIR};
#endif
const char *kpath_env = ::getenv("BPFTRACE_KERNEL_SOURCE");
if (kpath_env)
return kpath_env;
return {kpath_env, kpath_env};
std::string kdir = std::string("/lib/modules/") + utsname.release;
if (is_dir(kdir + "/build") && is_dir(kdir + "/source"))
return kdir + "/source";
else
return kdir + "/build";
auto ksrc = kdir + "/source";
auto kobj = kdir + "/build";
// if one of source/ or build/ is not present - try to use the other one for both.
if (!is_dir(ksrc)) {
ksrc = "";
}
if (!is_dir(kobj)) {
kobj = "";
}
if (ksrc == "" && kobj == "") {
return {"", ""};
}
if (ksrc == "") {
ksrc = kobj;
}
else if (kobj == "") {
kobj = ksrc;
}
return {ksrc, kobj};
}
void ClangParser::parse(ast::Program *program, StructMap &structs)
......@@ -187,9 +220,9 @@ void ClangParser::parse(ast::Program *program, StructMap &structs)
std::vector<std::string> kflags;
struct utsname utsname;
uname(&utsname);
auto kpath = get_kernel_root_dir(utsname);
if (is_dir(kpath))
kflags = get_kernel_cflags(utsname.machine, kpath);
auto [ksrc, kobj] = get_kernel_dirs(utsname);
if (ksrc != "")
kflags = get_kernel_cflags(utsname.machine, ksrc, kobj);
std::vector<const char *> args =
{
......
......@@ -53,7 +53,8 @@ std::vector<int> get_possible_cpus()
std::vector<std::string> get_kernel_cflags(
const char* uname_machine,
const std::string& kdir)
const std::string& ksrc,
const std::string& kobj)
{
std::vector<std::string> cflags;
std::string arch = uname_machine;
......@@ -90,18 +91,18 @@ std::vector<std::string> get_kernel_cflags(
cflags.push_back("-isystem");
cflags.push_back("/virtual/lib/clang/include");
cflags.push_back("-I" + kdir + "/arch/"+arch+"/include");
cflags.push_back("-I" + kdir + "/arch/"+arch+"/include/generated/uapi");
cflags.push_back("-I" + kdir + "/arch/"+arch+"/include/generated");
cflags.push_back("-I" + kdir + "/include");
cflags.push_back("-I" + kdir + "/./arch/"+arch+"/include/uapi");
cflags.push_back("-I" + kdir + "/arch/"+arch+"/include/generated/uapi");
cflags.push_back("-I" + kdir + "/include/uapi");
cflags.push_back("-I" + kdir + "/include/generated");
cflags.push_back("-I" + kdir + "/include/generated/uapi");
// see linux/Makefile for $(LINUXINCLUDE) + $(USERINCLUDE)
cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include");
cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated");
cflags.push_back("-I" + ksrc + "/include");
cflags.push_back("-I" + kobj + "/include");
cflags.push_back("-I" + ksrc + "/arch/"+arch+"/include/uapi");
cflags.push_back("-I" + kobj + "/arch/"+arch+"/include/generated/uapi");
cflags.push_back("-I" + ksrc + "/include/uapi");
cflags.push_back("-I" + kobj + "/include/generated/uapi");
cflags.push_back("-include");
cflags.push_back(kdir + "/include/linux/kconfig.h");
cflags.push_back(ksrc + "/include/linux/kconfig.h");
cflags.push_back("-D__KERNEL__");
cflags.push_back("-D__BPF_TRACING__");
cflags.push_back("-D__HAVE_BUILTIN_BSWAP16__");
......
......@@ -26,7 +26,8 @@ std::vector<int> get_online_cpus();
std::vector<int> get_possible_cpus();
std::vector<std::string> get_kernel_cflags(
const char* uname_machine,
const std::string& kdir);
const std::string& ksrc,
const std::string& kobj);
std::string is_deprecated(std::string &str);
} // namespace bpftrace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment