1 /* Generic GDB-side plugin 2 Copyright (C) 2020-2022 Free Software Foundation, Inc. 3 4 This file is part of GCC. 5 6 GCC is free software; you can redistribute it and/or modify it under 7 the terms of the GNU General Public License as published by the Free 8 Software Foundation; either version 3, or (at your option) any later 9 version. 10 11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GCC; see the file COPYING3. If not see 18 <http://www.gnu.org/licenses/>. */ 19 20 #ifndef CC1_PLUGIN_GDBCTX_HH 21 #define CC1_PLUGIN_GDBCTX_HH 22 23 namespace cc1_plugin 24 { 25 // The compiler context that we hand back to our caller. 26 // Due to this, the entire implementation is in this header. 27 template<typename T> 28 struct base_gdb_plugin : public T 29 { base_gdb_plugincc1_plugin::base_gdb_plugin30 base_gdb_plugin (const char *plugin_name_, const char *base_name, 31 int version) 32 : verbose (false), 33 plugin_name (plugin_name_), 34 fe_version (version), 35 compiler_name (base_name), 36 compilerp (new compiler (verbose)) 37 { 38 vtable = 39 { 40 GCC_FE_VERSION_1, 41 do_set_arguments_v0, 42 do_set_source_file, 43 do_set_print_callback, 44 do_compile_v0, 45 do_destroy, 46 do_set_verbose, 47 do_compile, 48 do_set_arguments, 49 do_set_triplet_regexp, 50 do_set_driver_filename, 51 }; 52 53 this->base.ops = &vtable; 54 } 55 56 virtual ~base_gdb_plugin () = default; 57 58 // A convenience function to print something. printcc1_plugin::base_gdb_plugin59 void print (const char *str) 60 { 61 this->print_function (this->print_datum, str); 62 } 63 64 // Set the verbose flag. set_verbosecc1_plugin::base_gdb_plugin65 void set_verbose (bool v) 66 { 67 verbose = v; 68 if (compilerp != nullptr) 69 compilerp->set_verbose (v); 70 } 71 72 // Make a new connection. set_connectioncc1_plugin::base_gdb_plugin73 void set_connection (int fd, int aux_fd) 74 { 75 connection.reset (new local_connection (fd, aux_fd, this)); 76 } 77 78 // This is called just before compilation begins. It should set 79 // any needed callbacks on the connection. 80 virtual void add_callbacks () = 0; 81 82 // A local subclass of connection that holds a back-pointer to the 83 // context object that we provide to our caller. 84 class local_connection : public cc1_plugin::connection 85 { 86 public: 87 local_connection(int fd,int aux_fd,base_gdb_plugin<T> * b)88 local_connection (int fd, int aux_fd, base_gdb_plugin<T> *b) 89 : connection (fd, aux_fd), 90 back_ptr (b) 91 { 92 } 93 print(const char * buf)94 void print (const char *buf) override 95 { 96 back_ptr->print (buf); 97 } 98 99 base_gdb_plugin<T> *back_ptr; 100 }; 101 102 std::unique_ptr<local_connection> connection; 103 104 void (*print_function) (void *datum, const char *message) = nullptr; 105 void *print_datum = nullptr; 106 107 std::vector<std::string> args; 108 std::string source_file; 109 110 /* Non-zero as an equivalent to gcc driver option "-v". */ 111 bool verbose; 112 113 const char *plugin_name; 114 int fe_version; 115 116 const char *compiler_name; 117 std::unique_ptr<cc1_plugin::compiler> compilerp; 118 119 private: 120 121 struct gcc_base_vtable vtable; 122 123 static inline base_gdb_plugin<T> * get_selfcc1_plugin::base_gdb_plugin124 get_self (gcc_base_context *s) 125 { 126 T *sub = (T *) s; 127 return static_cast<base_gdb_plugin<T> *> (sub); 128 } 129 130 static void do_set_verbosecc1_plugin::base_gdb_plugin131 do_set_verbose (struct gcc_base_context *s, int /* bool */ verbose) 132 { 133 base_gdb_plugin<T> *self = get_self (s); 134 135 self->set_verbose (verbose != 0); 136 } 137 138 static char * do_set_argumentscc1_plugin::base_gdb_plugin139 do_set_arguments (struct gcc_base_context *s, 140 int argc, char **argv) 141 { 142 base_gdb_plugin<T> *self = get_self (s); 143 144 std::string compiler; 145 char *errmsg = self->compilerp->find (self->compiler_name, compiler); 146 if (errmsg != NULL) 147 return errmsg; 148 149 self->args.push_back (compiler); 150 151 for (int i = 0; i < argc; ++i) 152 self->args.push_back (argv[i]); 153 154 return NULL; 155 } 156 157 static char * do_set_triplet_regexpcc1_plugin::base_gdb_plugin158 do_set_triplet_regexp (struct gcc_base_context *s, 159 const char *triplet_regexp) 160 { 161 base_gdb_plugin<T> *self = get_self (s); 162 163 self->compilerp.reset 164 (new cc1_plugin::compiler_triplet_regexp (self->verbose, 165 triplet_regexp)); 166 return NULL; 167 } 168 169 static char * do_set_driver_filenamecc1_plugin::base_gdb_plugin170 do_set_driver_filename (struct gcc_base_context *s, 171 const char *driver_filename) 172 { 173 base_gdb_plugin<T> *self = get_self (s); 174 175 self->compilerp.reset 176 (new cc1_plugin::compiler_driver_filename (self->verbose, 177 driver_filename)); 178 return NULL; 179 } 180 181 static char * do_set_arguments_v0cc1_plugin::base_gdb_plugin182 do_set_arguments_v0 (struct gcc_base_context *s, 183 const char *triplet_regexp, 184 int argc, char **argv) 185 { 186 char *errmsg = do_set_triplet_regexp (s, triplet_regexp); 187 if (errmsg != NULL) 188 return errmsg; 189 190 return do_set_arguments (s, argc, argv); 191 } 192 193 static void do_set_source_filecc1_plugin::base_gdb_plugin194 do_set_source_file (struct gcc_base_context *s, 195 const char *file) 196 { 197 base_gdb_plugin<T> *self = get_self (s); 198 199 self->source_file = file; 200 } 201 202 static void do_set_print_callbackcc1_plugin::base_gdb_plugin203 do_set_print_callback (struct gcc_base_context *s, 204 void (*print_function) (void *datum, 205 const char *message), 206 void *datum) 207 { 208 base_gdb_plugin<T> *self = get_self (s); 209 210 self->print_function = print_function; 211 self->print_datum = datum; 212 } 213 fork_execcc1_plugin::base_gdb_plugin214 int fork_exec (char **argv, int spair_fds[2], int stderr_fds[2]) 215 { 216 pid_t child_pid = fork (); 217 218 if (child_pid == -1) 219 { 220 close (spair_fds[0]); 221 close (spair_fds[1]); 222 close (stderr_fds[0]); 223 close (stderr_fds[1]); 224 return 0; 225 } 226 227 if (child_pid == 0) 228 { 229 // Child. 230 dup2 (stderr_fds[1], 1); 231 dup2 (stderr_fds[1], 2); 232 close (stderr_fds[0]); 233 close (stderr_fds[1]); 234 close (spair_fds[0]); 235 236 execvp (argv[0], argv); 237 _exit (127); 238 } 239 else 240 { 241 // Parent. 242 close (spair_fds[1]); 243 close (stderr_fds[1]); 244 245 cc1_plugin::status result = cc1_plugin::FAIL; 246 if (connection->send ('H') 247 && ::cc1_plugin::marshall (connection.get (), fe_version)) 248 result = connection->wait_for_query (); 249 250 close (spair_fds[0]); 251 close (stderr_fds[0]); 252 253 while (true) 254 { 255 int status; 256 257 if (waitpid (child_pid, &status, 0) == -1) 258 { 259 if (errno != EINTR) 260 return 0; 261 } 262 263 if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) 264 return 0; 265 break; 266 } 267 268 if (!result) 269 return 0; 270 return 1; 271 } 272 } 273 274 static int do_compilecc1_plugin::base_gdb_plugin275 do_compile (struct gcc_base_context *s, 276 const char *filename) 277 { 278 base_gdb_plugin<T> *self = get_self (s); 279 280 int fds[2]; 281 if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) != 0) 282 { 283 self->print ("could not create socketpair\n"); 284 return 0; 285 } 286 287 int stderr_fds[2]; 288 if (pipe (stderr_fds) != 0) 289 { 290 self->print ("could not create pipe\n"); 291 close (fds[0]); 292 close (fds[1]); 293 return 0; 294 } 295 296 self->args.push_back (std::string ("-fplugin=") + self->plugin_name); 297 self->args.push_back (std::string ("-fplugin-arg-") + self->plugin_name 298 + "-fd=" + std::to_string (fds[1])); 299 300 self->args.push_back (self->source_file); 301 self->args.push_back ("-c"); 302 self->args.push_back ("-o"); 303 self->args.push_back (filename); 304 if (self->verbose) 305 self->args.push_back ("-v"); 306 307 self->set_connection (fds[0], stderr_fds[0]); 308 309 self->add_callbacks (); 310 311 std::vector<char *> argv (self->args.size () + 1); 312 for (unsigned int i = 0; i < self->args.size (); ++i) 313 argv[i] = const_cast<char *> (self->args[i].c_str ()); 314 315 return self->fork_exec (argv.data (), fds, stderr_fds); 316 } 317 318 static int do_compile_v0cc1_plugin::base_gdb_plugin319 do_compile_v0 (struct gcc_base_context *s, const char *filename, 320 int verbose) 321 { 322 do_set_verbose (s, verbose); 323 return do_compile (s, filename); 324 } 325 326 static void do_destroycc1_plugin::base_gdb_plugin327 do_destroy (struct gcc_base_context *s) 328 { 329 base_gdb_plugin<T> *self = get_self (s); 330 331 delete self; 332 } 333 }; 334 335 // Instances of this rpc<> template function are installed into the 336 // "vtable"s. These functions are parameterized by type and method 337 // name and forward the call via the connection. 338 template<typename CTX, typename R, const char *&NAME, typename... Arg> rpc(CTX * s,Arg...rest)339 R rpc (CTX *s, Arg... rest) 340 { 341 base_gdb_plugin<CTX> *self = (base_gdb_plugin<CTX> *) s; 342 R result; 343 344 if (!cc1_plugin::call (self->connection.get (), NAME, &result, rest...)) 345 return 0; 346 return result; 347 } 348 } 349 350 #endif // CC1_PLUGIN_GDBCTX_HH 351