xref: /aosp_15_r20/external/bcc/src/cc/frontends/clang/kbuild_helper.cc (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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