1/* C++ modules. Experimental! -*- c++ -*- 2 Copyright (C) 2017-2022 Free Software Foundation, Inc. 3 Written by Nathan Sidwell <nathan@acm.org> while at FaceBook 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it 8 under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3, or (at your option) 10 any later version. 11 12 GCC is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with GCC; see the file COPYING3. If not see 19<http://www.gnu.org/licenses/>. */ 20 21#include "config.h" 22 23#include "resolver.h" 24// C++ 25#include <algorithm> 26#include <memory> 27// C 28#include <cstring> 29// OS 30#include <fcntl.h> 31#include <unistd.h> 32#if 0 // 1 for testing no mmap 33#define MAPPED_READING 0 34#else 35#ifdef IN_GCC 36#if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0 37#define MAPPED_READING 1 38#else 39#define MAPPED_READING 0 40#endif 41#else 42#ifdef HAVE_SYS_MMAN_H 43#include <sys/mman.h> 44#define MAPPED_READING 1 45#else 46#define MAPPED_READING 0 47#endif 48#endif 49#endif 50 51#include <sys/types.h> 52#include <sys/stat.h> 53 54#if !defined (IN_GCC) && !MAPPED_READING 55#define xmalloc(X) malloc(X) 56#endif 57 58#if !HOST_HAS_O_CLOEXEC 59#define O_CLOEXEC 0 60#endif 61 62#ifndef DIR_SEPARATOR 63#define DIR_SEPARATOR '/' 64#endif 65 66module_resolver::module_resolver (bool map, bool xlate) 67 : default_map (map), default_translate (xlate) 68{ 69} 70 71module_resolver::~module_resolver () 72{ 73 if (fd_repo >= 0) 74 close (fd_repo); 75} 76 77bool 78module_resolver::set_repo (std::string &&r, bool force) 79{ 80 if (force || repo.empty ()) 81 { 82 repo = std::move (r); 83 force = true; 84 } 85 return force; 86} 87 88bool 89module_resolver::add_mapping (std::string &&module, std::string &&file, 90 bool force) 91{ 92 auto res = map.emplace (std::move (module), std::move (file)); 93 if (res.second) 94 force = true; 95 else if (force) 96 res.first->second = std::move (file); 97 98 return force; 99} 100 101int 102module_resolver::read_tuple_file (int fd, char const *prefix, bool force) 103{ 104 struct stat stat; 105 if (fstat (fd, &stat) < 0) 106 return -errno; 107 108 if (!stat.st_size) 109 return 0; 110 111 void *buffer = nullptr; 112#if MAPPED_READING 113 // Just map the file, we're gonna read all of it, so no need for 114 // line buffering 115 buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 116 if (buffer == MAP_FAILED) 117 return -errno; 118 struct Deleter { 119 void operator()(void* p) const { munmap(p, size); } 120 size_t size; 121 }; 122 std::unique_ptr<void, Deleter> guard(buffer, Deleter{(size_t)stat.st_size}); 123#else 124 buffer = xmalloc (stat.st_size); 125 if (!buffer) 126 return -errno; 127 struct Deleter { void operator()(void* p) const { free(p); } }; 128 std::unique_ptr<void, Deleter> guard(buffer); 129 if (read (fd, buffer, stat.st_size) != stat.st_size) 130 return -errno; 131#endif 132 133 size_t prefix_len = prefix ? strlen (prefix) : 0; 134 unsigned lineno = 0; 135 136 for (char const *begin = reinterpret_cast <char const *> (buffer), 137 *end = begin + stat.st_size, *eol; 138 begin != end; begin = eol + 1) 139 { 140 lineno++; 141 eol = std::find (begin, end, '\n'); 142 if (eol == end) 143 // last line has no \n, ignore the line, you lose 144 break; 145 146 auto *pos = begin; 147 bool pfx_search = prefix_len != 0; 148 149 pfx_search: 150 while (*pos == ' ' || *pos == '\t') 151 pos++; 152 153 auto *space = pos; 154 while (*space != '\n' && *space != ' ' && *space != '\t') 155 space++; 156 157 if (pos == space) 158 // at end of line, nothing here 159 continue; 160 161 if (pfx_search) 162 { 163 if (size_t (space - pos) == prefix_len 164 && std::equal (pos, space, prefix)) 165 pfx_search = false; 166 pos = space; 167 goto pfx_search; 168 } 169 170 std::string module (pos, space); 171 while (*space == ' ' || *space == '\t') 172 space++; 173 std::string file (space, eol); 174 175 if (module[0] == '$') 176 { 177 if (module == "$root") 178 set_repo (std::move (file)); 179 else 180 return lineno; 181 } 182 else 183 { 184 if (file.empty ()) 185 file = GetCMIName (module); 186 add_mapping (std::move (module), std::move (file), force); 187 } 188 } 189 190 return 0; 191} 192 193char const * 194module_resolver::GetCMISuffix () 195{ 196 return "gcm"; 197} 198 199module_resolver * 200module_resolver::ConnectRequest (Cody::Server *s, unsigned version, 201 std::string &a, std::string &i) 202{ 203 if (!version || version > Cody::Version) 204 s->ErrorResponse ("version mismatch"); 205 else if (a != "GCC") 206 // Refuse anything but GCC 207 ErrorResponse (s, std::string ("only GCC supported")); 208 else if (!ident.empty () && ident != i) 209 // Failed ident check 210 ErrorResponse (s, std::string ("bad ident")); 211 else 212 // Success! 213 s->ConnectResponse ("gcc"); 214 215 return this; 216} 217 218int 219module_resolver::ModuleRepoRequest (Cody::Server *s) 220{ 221 s->PathnameResponse (repo); 222 return 0; 223} 224 225int 226module_resolver::cmi_response (Cody::Server *s, std::string &module) 227{ 228 auto iter = map.find (module); 229 if (iter == map.end ()) 230 { 231 std::string file = default_map ? GetCMIName (module) : std::string (); 232 auto res = map.emplace (module, file); 233 iter = res.first; 234 } 235 236 if (iter->second.empty ()) 237 s->ErrorResponse ("no such module"); 238 else 239 s->PathnameResponse (iter->second); 240 241 return 0; 242} 243 244int 245module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags, 246 std::string &module) 247{ 248 return cmi_response (s, module); 249} 250 251int 252module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags, 253 std::string &module) 254{ 255 return cmi_response (s, module); 256} 257 258int 259module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags, 260 std::string &include) 261{ 262 auto iter = map.find (include); 263 if (iter == map.end () && default_translate) 264 { 265 // Not found, look for it 266 auto file = GetCMIName (include); 267 struct stat statbuf; 268 bool ok = true; 269 270#if HAVE_FSTATAT 271 int fd_dir = AT_FDCWD; 272 if (!repo.empty ()) 273 { 274 if (fd_repo == -1) 275 { 276 fd_repo = open (repo.c_str (), 277 O_RDONLY | O_CLOEXEC | O_DIRECTORY); 278 if (fd_repo < 0) 279 fd_repo = -2; 280 } 281 fd_dir = fd_repo; 282 } 283 284 if (!repo.empty () && fd_repo < 0) 285 ok = false; 286 else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0 287 || !S_ISREG (statbuf.st_mode)) 288 ok = false; 289#else 290 auto append = repo; 291 append.push_back (DIR_SEPARATOR); 292 append.append (file); 293 if (stat (append.c_str (), &statbuf) < 0 294 || !S_ISREG (statbuf.st_mode)) 295 ok = false; 296#endif 297 if (!ok) 298 // Mark as not present 299 file.clear (); 300 auto res = map.emplace (include, file); 301 iter = res.first; 302 } 303 304 if (iter == map.end () || iter->second.empty ()) 305 s->BoolResponse (false); 306 else 307 s->PathnameResponse (iter->second); 308 309 return 0; 310} 311 312/* This handles a client notification to the server that a CMI has been 313 produced for a module. For this simplified server, we just accept 314 the transaction and respond with "OK". */ 315 316int 317module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags, 318 std::string &) 319{ 320 s->OKResponse(); 321 return 0; 322} 323