blob: 5c57c13eea26a405c09bc333a3795429256ca74f [file] [log] [blame]
/*
* Copyright (c) 2015 PLUMgrid, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fstream>
#include <iostream>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "kbuild_helper.h"
namespace ebpf {
using std::string;
using std::vector;
KBuildHelper::KBuildHelper(const std::string &kdir, bool has_source_dir) : kdir_(kdir),
has_source_dir_(has_source_dir) {
}
// read the flags from cache or learn
int KBuildHelper::get_flags(const char *uname_machine, vector<string> *cflags) {
//uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ -e s/sun4u/sparc64/ -e s/arm.*/arm/
// -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/
// -e s/ppc.*/powerpc/ -e s/mips.*/mips/ -e s/sh[234].*/sh/
// -e s/aarch64.*/arm64/
string arch;
const char *archenv = getenv("ARCH");
// If ARCH env is defined, use it over uname
if (archenv)
arch = string(archenv);
else
arch = string(uname_machine);
if (!arch.compare(0, 6, "x86_64")) {
arch = "x86";
} else if (arch[0] == 'i' && !arch.compare(2, 2, "86")) {
arch = "x86";
} else if (!arch.compare(0, 7, "aarch64") || !arch.compare(0, 5, "arm64")) {
arch = "arm64";
} else if (!arch.compare(0, 3, "arm")) {
arch = "arm";
} else if (!arch.compare(0, 5, "sa110")) {
arch = "arm";
} else if (!arch.compare(0, 5, "s390x")) {
arch = "s390";
} else if (!arch.compare(0, 8, "parisc64")) {
arch = "parisc";
} else if (!arch.compare(0, 3, "ppc")) {
arch = "powerpc";
} else if (!arch.compare(0, 4, "mips")) {
arch = "mips";
} else if (!arch.compare(0, 2, "sh")) {
arch = "sh";
}
cflags->push_back("-nostdinc");
cflags->push_back("-isystem");
cflags->push_back("/virtual/lib/clang/include");
// The include order from kernel top Makefile:
//
// # Use USERINCLUDE when you must reference the UAPI directories only.
// USERINCLUDE := \
// -I$(srctree)/arch/$(SRCARCH)/include/uapi \
// -I$(objtree)/arch/$(SRCARCH)/include/generated/uapi \
// -I$(srctree)/include/uapi \
// -I$(objtree)/include/generated/uapi \
// -include $(srctree)/include/linux/kconfig.h
//
// # Use LINUXINCLUDE when you must reference the include/ directory.
// # Needed to be compatible with the O= option
// LINUXINCLUDE := \
// -I$(srctree)/arch/$(SRCARCH)/include \
// -I$(objtree)/arch/$(SRCARCH)/include/generated \
// $(if $(building_out_of_srctree),-I$(srctree)/include) \
// -I$(objtree)/include \
// $(USERINCLUDE)
//
// Some distros such as openSUSE/SUSE and Debian splits the headers between
// source/ and build/. In this case, just $(srctree) is source/ and
// $(objtree) is build/.
if (has_source_dir_) {
cflags->push_back("-Iarch/"+arch+"/include/");
cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated");
cflags->push_back("-Iinclude");
cflags->push_back("-I" + kdir_ + "/build/include");
cflags->push_back("-Iarch/"+arch+"/include/uapi");
cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated/uapi");
cflags->push_back("-Iinclude/uapi");
cflags->push_back("-I" + kdir_ + "/build/include/generated/uapi");
} else {
cflags->push_back("-Iarch/"+arch+"/include/");
cflags->push_back("-Iarch/"+arch+"/include/generated");
cflags->push_back("-Iinclude");
cflags->push_back("-Iarch/"+arch+"/include/uapi");
cflags->push_back("-Iarch/"+arch+"/include/generated/uapi");
cflags->push_back("-Iinclude/uapi");
cflags->push_back("-Iinclude/generated/uapi");
}
if (arch == "mips") {
cflags->push_back("-Iarch/mips/include/asm/mach-loongson64");
cflags->push_back("-Iarch/mips/include/asm/mach-generic");
}
cflags->push_back("-include");
cflags->push_back("./include/linux/kconfig.h");
cflags->push_back("-D__KERNEL__");
cflags->push_back("-DKBUILD_MODNAME=\"bcc\"");
// If ARCH env variable is set, pass this along.
if (archenv)
cflags->push_back("-D__TARGET_ARCH_" + arch);
cflags->push_back("-Wno-unused-value");
cflags->push_back("-Wno-pointer-sign");
cflags->push_back("-fno-stack-protector");
return 0;
}
static inline int file_exists(const char *f)
{
struct stat buffer;
return (stat(f, &buffer) == 0);
}
static inline int proc_kheaders_exists(void)
{
return file_exists(PROC_KHEADERS_PATH);
}
static inline int extract_kheaders(const std::string &dirpath,
const struct utsname &uname_data)
{
char tar_cmd[256], dirpath_tmp[256];
int ret;
bool module = false;
if (!proc_kheaders_exists()) {
ret = system("modprobe kheaders");
if (ret)
return ret;
module = true;
if (!proc_kheaders_exists()) {
ret = -1;
goto cleanup;
}
}
snprintf(dirpath_tmp, sizeof(dirpath_tmp), "/tmp/kheaders-%s-XXXXXX", uname_data.release);
if (mkdtemp(dirpath_tmp) == NULL) {
ret = -1;
goto cleanup;
}
if ((size_t)snprintf(tar_cmd, sizeof(tar_cmd), "tar -xf %s -C %s", PROC_KHEADERS_PATH, dirpath_tmp) >= sizeof(tar_cmd)) {
ret = -1;
goto cleanup;
}
ret = system(tar_cmd);
if (ret) {
system(("rm -rf " + std::string(dirpath_tmp)).c_str());
goto cleanup;
}
/*
* If the new directory exists, it could have raced with a parallel
* extraction, in this case just delete the old directory and ignore.
*/
ret = rename(dirpath_tmp, dirpath.c_str());
if (ret)
ret = system(("rm -rf " + std::string(dirpath_tmp)).c_str());
cleanup:
if (module) {
int ret1 = system("rmmod kheaders");
if (ret1)
return ret1;
}
return ret;
}
int get_proc_kheaders(std::string &dirpath)
{
struct utsname uname_data;
char dirpath_tmp[256];
if (uname(&uname_data))
return -errno;
snprintf(dirpath_tmp, 256, "/tmp/kheaders-%s", uname_data.release);
dirpath = std::string(dirpath_tmp);
if (file_exists(dirpath_tmp))
return 0;
// First time so extract it
return extract_kheaders(dirpath, uname_data);
}
} // namespace ebpf