• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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