1 /*
2 * Copyright (c) 2015 PLUMgrid, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <fstream>
17 #include <iostream>
18
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <sys/utsname.h>
23 #include <unistd.h>
24
25 #include "kbuild_helper.h"
26
27 namespace ebpf {
28
29 using std::string;
30 using std::vector;
31
KBuildHelper(const std::string & kdir,bool has_source_dir)32 KBuildHelper::KBuildHelper(const std::string &kdir, bool has_source_dir) : kdir_(kdir),
33 has_source_dir_(has_source_dir) {
34 }
35
36 // read the flags from cache or learn
get_flags(const char * uname_machine,vector<string> * cflags)37 int KBuildHelper::get_flags(const char *uname_machine, vector<string> *cflags) {
38 //uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ -e s/sun4u/sparc64/ -e s/arm.*/arm/
39 // -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/
40 // -e s/ppc.*/powerpc/ -e s/mips.*/mips/ -e s/sh[234].*/sh/
41 // -e s/aarch64.*/arm64/
42
43 string arch;
44 const char *archenv = getenv("ARCH");
45 // If ARCH env is defined, use it over uname
46 if (archenv)
47 arch = string(archenv);
48 else
49 arch = string(uname_machine);
50
51 if (!arch.compare(0, 6, "x86_64")) {
52 arch = "x86";
53 } else if (arch[0] == 'i' && !arch.compare(2, 2, "86")) {
54 arch = "x86";
55 } else if (!arch.compare(0, 7, "aarch64") || !arch.compare(0, 5, "arm64")) {
56 arch = "arm64";
57 } else if (!arch.compare(0, 3, "arm")) {
58 arch = "arm";
59 } else if (!arch.compare(0, 5, "sa110")) {
60 arch = "arm";
61 } else if (!arch.compare(0, 5, "s390x")) {
62 arch = "s390";
63 } else if (!arch.compare(0, 8, "parisc64")) {
64 arch = "parisc";
65 } else if (!arch.compare(0, 3, "ppc")) {
66 arch = "powerpc";
67 } else if (!arch.compare(0, 4, "mips")) {
68 arch = "mips";
69 } else if (!arch.compare(0, 2, "sh")) {
70 arch = "sh";
71 }
72
73 cflags->push_back("-nostdinc");
74 cflags->push_back("-isystem");
75 cflags->push_back("/virtual/lib/clang/include");
76
77 // The include order from kernel top Makefile:
78 //
79 // # Use USERINCLUDE when you must reference the UAPI directories only.
80 // USERINCLUDE := \
81 // -I$(srctree)/arch/$(SRCARCH)/include/uapi \
82 // -I$(objtree)/arch/$(SRCARCH)/include/generated/uapi \
83 // -I$(srctree)/include/uapi \
84 // -I$(objtree)/include/generated/uapi \
85 // -include $(srctree)/include/linux/kconfig.h
86 //
87 // # Use LINUXINCLUDE when you must reference the include/ directory.
88 // # Needed to be compatible with the O= option
89 // LINUXINCLUDE := \
90 // -I$(srctree)/arch/$(SRCARCH)/include \
91 // -I$(objtree)/arch/$(SRCARCH)/include/generated \
92 // $(if $(building_out_of_srctree),-I$(srctree)/include) \
93 // -I$(objtree)/include \
94 // $(USERINCLUDE)
95 //
96 // Some distros such as openSUSE/SUSE and Debian splits the headers between
97 // source/ and build/. In this case, just $(srctree) is source/ and
98 // $(objtree) is build/.
99 if (has_source_dir_) {
100 cflags->push_back("-Iarch/"+arch+"/include/");
101 cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated");
102 cflags->push_back("-Iinclude");
103 cflags->push_back("-I" + kdir_ + "/build/include");
104 cflags->push_back("-Iarch/"+arch+"/include/uapi");
105 cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated/uapi");
106 cflags->push_back("-Iinclude/uapi");
107 cflags->push_back("-I" + kdir_ + "/build/include/generated/uapi");
108 } else {
109 cflags->push_back("-Iarch/"+arch+"/include/");
110 cflags->push_back("-Iarch/"+arch+"/include/generated");
111 cflags->push_back("-Iinclude");
112 cflags->push_back("-Iarch/"+arch+"/include/uapi");
113 cflags->push_back("-Iarch/"+arch+"/include/generated/uapi");
114 cflags->push_back("-Iinclude/uapi");
115 cflags->push_back("-Iinclude/generated/uapi");
116 }
117
118 if (arch == "mips") {
119 cflags->push_back("-Iarch/mips/include/asm/mach-loongson64");
120 cflags->push_back("-Iarch/mips/include/asm/mach-generic");
121 }
122
123 cflags->push_back("-include");
124 cflags->push_back("./include/linux/kconfig.h");
125 cflags->push_back("-D__KERNEL__");
126 cflags->push_back("-DKBUILD_MODNAME=\"bcc\"");
127
128 // If ARCH env variable is set, pass this along.
129 if (archenv)
130 cflags->push_back("-D__TARGET_ARCH_" + arch);
131
132 cflags->push_back("-Wno-unused-value");
133 cflags->push_back("-Wno-pointer-sign");
134 cflags->push_back("-fno-stack-protector");
135
136 return 0;
137 }
138
file_exists(const char * f)139 static inline int file_exists(const char *f)
140 {
141 struct stat buffer;
142 return (stat(f, &buffer) == 0);
143 }
144
proc_kheaders_exists(void)145 static inline int proc_kheaders_exists(void)
146 {
147 return file_exists(PROC_KHEADERS_PATH);
148 }
149
extract_kheaders(const std::string & dirpath,const struct utsname & uname_data)150 static inline int extract_kheaders(const std::string &dirpath,
151 const struct utsname &uname_data)
152 {
153 char tar_cmd[256], dirpath_tmp[256];
154 int ret;
155 bool module = false;
156
157 if (!proc_kheaders_exists()) {
158 ret = system("modprobe kheaders");
159 if (ret)
160 return ret;
161 module = true;
162 if (!proc_kheaders_exists()) {
163 ret = -1;
164 goto cleanup;
165 }
166 }
167
168 snprintf(dirpath_tmp, sizeof(dirpath_tmp), "/tmp/kheaders-%s-XXXXXX", uname_data.release);
169 if (mkdtemp(dirpath_tmp) == NULL) {
170 ret = -1;
171 goto cleanup;
172 }
173
174 if ((size_t)snprintf(tar_cmd, sizeof(tar_cmd), "tar -xf %s -C %s", PROC_KHEADERS_PATH, dirpath_tmp) >= sizeof(tar_cmd)) {
175 ret = -1;
176 goto cleanup;
177 }
178 ret = system(tar_cmd);
179 if (ret) {
180 system(("rm -rf " + std::string(dirpath_tmp)).c_str());
181 goto cleanup;
182 }
183
184 /*
185 * If the new directory exists, it could have raced with a parallel
186 * extraction, in this case just delete the old directory and ignore.
187 */
188 ret = rename(dirpath_tmp, dirpath.c_str());
189 if (ret)
190 ret = system(("rm -rf " + std::string(dirpath_tmp)).c_str());
191
192 cleanup:
193 if (module) {
194 int ret1 = system("rmmod kheaders");
195 if (ret1)
196 return ret1;
197 }
198
199 return ret;
200 }
201
get_proc_kheaders(std::string & dirpath)202 int get_proc_kheaders(std::string &dirpath)
203 {
204 struct utsname uname_data;
205 char dirpath_tmp[256];
206
207 if (uname(&uname_data))
208 return -errno;
209
210 snprintf(dirpath_tmp, 256, "/tmp/kheaders-%s", uname_data.release);
211 dirpath = std::string(dirpath_tmp);
212
213 if (file_exists(dirpath_tmp))
214 return 0;
215
216 // First time so extract it
217 return extract_kheaders(dirpath, uname_data);
218 }
219
220 } // namespace ebpf
221