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/ -e s/loongarch.*/loongarch/
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, 5, "riscv")) {
70 arch = "riscv";
71 } else if (!arch.compare(0, 9, "loongarch")) {
72 arch = "loongarch";
73 } else if (!arch.compare(0, 2, "sh")) {
74 arch = "sh";
75 }
76
77 cflags->push_back("-nostdinc");
78 cflags->push_back("-isystem");
79 cflags->push_back("/virtual/lib/clang/include");
80
81 // The include order from kernel top Makefile:
82 //
83 // # Use USERINCLUDE when you must reference the UAPI directories only.
84 // USERINCLUDE := \
85 // -I$(srctree)/arch/$(SRCARCH)/include/uapi \
86 // -I$(objtree)/arch/$(SRCARCH)/include/generated/uapi \
87 // -I$(srctree)/include/uapi \
88 // -I$(objtree)/include/generated/uapi \
89 // -include $(srctree)/include/linux/kconfig.h
90 //
91 // # Use LINUXINCLUDE when you must reference the include/ directory.
92 // # Needed to be compatible with the O= option
93 // LINUXINCLUDE := \
94 // -I$(srctree)/arch/$(SRCARCH)/include \
95 // -I$(objtree)/arch/$(SRCARCH)/include/generated \
96 // $(if $(building_out_of_srctree),-I$(srctree)/include) \
97 // -I$(objtree)/include \
98 // $(USERINCLUDE)
99 //
100 // Some distros such as openSUSE/SUSE and Debian splits the headers between
101 // source/ and build/. In this case, just $(srctree) is source/ and
102 // $(objtree) is build/.
103 if (has_source_dir_) {
104 cflags->push_back("-Iarch/"+arch+"/include/");
105 cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated");
106 cflags->push_back("-Iinclude");
107 cflags->push_back("-I" + kdir_ + "/build/include");
108 cflags->push_back("-Iarch/"+arch+"/include/uapi");
109 cflags->push_back("-I" + kdir_ + "/build/arch/"+arch+"/include/generated/uapi");
110 cflags->push_back("-Iinclude/uapi");
111 cflags->push_back("-I" + kdir_ + "/build/include/generated/uapi");
112 } else {
113 cflags->push_back("-Iarch/"+arch+"/include/");
114 cflags->push_back("-Iarch/"+arch+"/include/generated");
115 cflags->push_back("-Iinclude");
116 cflags->push_back("-Iarch/"+arch+"/include/uapi");
117 cflags->push_back("-Iarch/"+arch+"/include/generated/uapi");
118 cflags->push_back("-Iinclude/uapi");
119 cflags->push_back("-Iinclude/generated/uapi");
120 }
121
122 if (arch == "mips") {
123 cflags->push_back("-Iarch/mips/include/asm/mach-loongson64");
124 cflags->push_back("-Iarch/mips/include/asm/mach-generic");
125 }
126
127 cflags->push_back("-include");
128 cflags->push_back("./include/linux/kconfig.h");
129 cflags->push_back("-D__KERNEL__");
130 cflags->push_back("-DKBUILD_MODNAME=\"bcc\"");
131
132 // If ARCH env variable is set, pass this along.
133 if (archenv)
134 cflags->push_back("-D__TARGET_ARCH_" + arch);
135
136 cflags->push_back("-Wno-unused-value");
137 cflags->push_back("-Wno-pointer-sign");
138 cflags->push_back("-fno-stack-protector");
139
140 return 0;
141 }
142
file_exists(const char * f)143 static inline int file_exists(const char *f)
144 {
145 struct stat buffer;
146 return (stat(f, &buffer) == 0);
147 }
148
proc_kheaders_exists(void)149 static inline int proc_kheaders_exists(void)
150 {
151 return file_exists(PROC_KHEADERS_PATH);
152 }
153
get_tmp_dir()154 static inline const char *get_tmp_dir() {
155 const char *tmpdir = getenv("TMPDIR");
156 if (tmpdir) {
157 return tmpdir;
158 }
159 return "/tmp";
160 }
161
extract_kheaders(const std::string & dirpath,const struct utsname & uname_data)162 static inline int extract_kheaders(const std::string &dirpath,
163 const struct utsname &uname_data)
164 {
165 char tar_cmd[256], dirpath_tmp[256];
166 int ret;
167 bool module = false;
168
169 if (!proc_kheaders_exists()) {
170 ret = system("modprobe kheaders");
171 if (ret)
172 return ret;
173 module = true;
174 if (!proc_kheaders_exists()) {
175 ret = -1;
176 goto cleanup;
177 }
178 }
179
180 snprintf(dirpath_tmp, sizeof(dirpath_tmp), "%s/kheaders-%s-XXXXXX",
181 get_tmp_dir(), uname_data.release);
182 if (mkdtemp(dirpath_tmp) == NULL) {
183 ret = -1;
184 goto cleanup;
185 }
186
187 if ((size_t)snprintf(tar_cmd, sizeof(tar_cmd), "tar -xf %s -C %s", PROC_KHEADERS_PATH, dirpath_tmp) >= sizeof(tar_cmd)) {
188 ret = -1;
189 goto cleanup;
190 }
191 ret = system(tar_cmd);
192 if (ret) {
193 system(("rm -rf " + std::string(dirpath_tmp)).c_str());
194 goto cleanup;
195 }
196
197 /*
198 * If the new directory exists, it could have raced with a parallel
199 * extraction, in this case just delete the old directory and ignore.
200 */
201 ret = rename(dirpath_tmp, dirpath.c_str());
202 if (ret)
203 ret = system(("rm -rf " + std::string(dirpath_tmp)).c_str());
204
205 cleanup:
206 if (module) {
207 int ret1 = system("rmmod kheaders");
208 if (ret1)
209 return ret1;
210 }
211
212 return ret;
213 }
214
get_proc_kheaders(std::string & dirpath)215 int get_proc_kheaders(std::string &dirpath)
216 {
217 struct utsname uname_data;
218 char dirpath_tmp[256];
219
220 if (uname(&uname_data))
221 return -errno;
222
223 snprintf(dirpath_tmp, 256, "%s/kheaders-%s", get_tmp_dir(),
224 uname_data.release);
225 dirpath = std::string(dirpath_tmp);
226
227 if (file_exists(dirpath_tmp))
228 return 0;
229
230 // First time so extract it
231 return extract_kheaders(dirpath, uname_data);
232 }
233
234 } // namespace ebpf
235