1 //===-RTLs/generic-64bit/src/rtl.cpp - Target RTLs Implementation - C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is dual licensed under the MIT and the University of Illinois Open 6 // Source Licenses. See LICENSE.txt for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // RTL for generic 64-bit machine 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include <cassert> 15 #include <cstdio> 16 #include <cstring> 17 #include <cstdlib> 18 #include <dlfcn.h> 19 #include <ffi.h> 20 #include <gelf.h> 21 #include <link.h> 22 #include <list> 23 #include <string> 24 #include <vector> 25 26 #include "omptargetplugin.h" 27 28 #ifndef TARGET_NAME 29 #define TARGET_NAME Generic ELF - 64bit 30 #endif 31 32 #ifndef TARGET_ELF_ID 33 #define TARGET_ELF_ID 0 34 #endif 35 36 #ifdef OMPTARGET_DEBUG 37 static int DebugLevel = 0; 38 39 #define GETNAME2(name) #name 40 #define GETNAME(name) GETNAME2(name) 41 #define DP(...) \ 42 do { \ 43 if (DebugLevel > 0) { \ 44 DEBUGP("Target " GETNAME(TARGET_NAME) " RTL", __VA_ARGS__); \ 45 } \ 46 } while (false) 47 #else // OMPTARGET_DEBUG 48 #define DP(...) {} 49 #endif // OMPTARGET_DEBUG 50 51 #include "../../common/elf_common.c" 52 53 #define NUMBER_OF_DEVICES 4 54 #define OFFLOADSECTIONNAME ".omp_offloading.entries" 55 56 /// Array of Dynamic libraries loaded for this target. 57 struct DynLibTy { 58 char *FileName; 59 void *Handle; 60 }; 61 62 /// Keep entries table per device. 63 struct FuncOrGblEntryTy { 64 __tgt_target_table Table; 65 }; 66 67 /// Class containing all the device information. 68 class RTLDeviceInfoTy { 69 std::vector<FuncOrGblEntryTy> FuncGblEntries; 70 71 public: 72 std::list<DynLibTy> DynLibs; 73 74 // Record entry point associated with device. 75 void createOffloadTable(int32_t device_id, __tgt_offload_entry *begin, 76 __tgt_offload_entry *end) { 77 assert(device_id < (int32_t)FuncGblEntries.size() && 78 "Unexpected device id!"); 79 FuncOrGblEntryTy &E = FuncGblEntries[device_id]; 80 81 E.Table.EntriesBegin = begin; 82 E.Table.EntriesEnd = end; 83 } 84 85 // Return true if the entry is associated with device. 86 bool findOffloadEntry(int32_t device_id, void *addr) { 87 assert(device_id < (int32_t)FuncGblEntries.size() && 88 "Unexpected device id!"); 89 FuncOrGblEntryTy &E = FuncGblEntries[device_id]; 90 91 for (__tgt_offload_entry *i = E.Table.EntriesBegin, *e = E.Table.EntriesEnd; 92 i < e; ++i) { 93 if (i->addr == addr) 94 return true; 95 } 96 97 return false; 98 } 99 100 // Return the pointer to the target entries table. 101 __tgt_target_table *getOffloadEntriesTable(int32_t device_id) { 102 assert(device_id < (int32_t)FuncGblEntries.size() && 103 "Unexpected device id!"); 104 FuncOrGblEntryTy &E = FuncGblEntries[device_id]; 105 106 return &E.Table; 107 } 108 109 RTLDeviceInfoTy(int32_t num_devices) { 110 #ifdef OMPTARGET_DEBUG 111 if (char *envStr = getenv("LIBOMPTARGET_DEBUG")) { 112 DebugLevel = std::stoi(envStr); 113 } 114 #endif // OMPTARGET_DEBUG 115 116 FuncGblEntries.resize(num_devices); 117 } 118 119 ~RTLDeviceInfoTy() { 120 // Close dynamic libraries 121 for (auto &lib : DynLibs) { 122 if (lib.Handle) { 123 dlclose(lib.Handle); 124 remove(lib.FileName); 125 } 126 } 127 } 128 }; 129 130 static RTLDeviceInfoTy DeviceInfo(NUMBER_OF_DEVICES); 131 132 #ifdef __cplusplus 133 extern "C" { 134 #endif 135 136 int32_t __tgt_rtl_is_valid_binary(__tgt_device_image *image) { 137 // If we don't have a valid ELF ID we can just fail. 138 #if TARGET_ELF_ID < 1 139 return 0; 140 #else 141 return elf_check_machine(image, TARGET_ELF_ID); 142 #endif 143 } 144 145 int32_t __tgt_rtl_number_of_devices() { return NUMBER_OF_DEVICES; } 146 147 int32_t __tgt_rtl_init_device(int32_t device_id) { return OFFLOAD_SUCCESS; } 148 149 __tgt_target_table *__tgt_rtl_load_binary(int32_t device_id, 150 __tgt_device_image *image) { 151 152 DP("Dev %d: load binary from " DPxMOD " image\n", device_id, 153 DPxPTR(image->ImageStart)); 154 155 assert(device_id >= 0 && device_id < NUMBER_OF_DEVICES && "bad dev id"); 156 157 size_t ImageSize = (size_t)image->ImageEnd - (size_t)image->ImageStart; 158 size_t NumEntries = (size_t)(image->EntriesEnd - image->EntriesBegin); 159 DP("Expecting to have %zd entries defined.\n", NumEntries); 160 161 // Is the library version incompatible with the header file? 162 if (elf_version(EV_CURRENT) == EV_NONE) { 163 DP("Incompatible ELF library!\n"); 164 return NULL; 165 } 166 167 // Obtain elf handler 168 Elf *e = elf_memory((char *)image->ImageStart, ImageSize); 169 if (!e) { 170 DP("Unable to get ELF handle: %s!\n", elf_errmsg(-1)); 171 return NULL; 172 } 173 174 if (elf_kind(e) != ELF_K_ELF) { 175 DP("Invalid Elf kind!\n"); 176 elf_end(e); 177 return NULL; 178 } 179 180 // Find the entries section offset 181 Elf_Scn *section = 0; 182 Elf64_Off entries_offset = 0; 183 184 size_t shstrndx; 185 186 if (elf_getshdrstrndx(e, &shstrndx)) { 187 DP("Unable to get ELF strings index!\n"); 188 elf_end(e); 189 return NULL; 190 } 191 192 while ((section = elf_nextscn(e, section))) { 193 GElf_Shdr hdr; 194 gelf_getshdr(section, &hdr); 195 196 if (!strcmp(elf_strptr(e, shstrndx, hdr.sh_name), OFFLOADSECTIONNAME)) { 197 entries_offset = hdr.sh_addr; 198 break; 199 } 200 } 201 202 if (!entries_offset) { 203 DP("Entries Section Offset Not Found\n"); 204 elf_end(e); 205 return NULL; 206 } 207 208 DP("Offset of entries section is (" DPxMOD ").\n", DPxPTR(entries_offset)); 209 210 // load dynamic library and get the entry points. We use the dl library 211 // to do the loading of the library, but we could do it directly to avoid the 212 // dump to the temporary file. 213 // 214 // 1) Create tmp file with the library contents. 215 // 2) Use dlopen to load the file and dlsym to retrieve the symbols. 216 char tmp_name[] = "/tmp/tmpfile_XXXXXX"; 217 int tmp_fd = mkstemp(tmp_name); 218 219 if (tmp_fd == -1) { 220 elf_end(e); 221 return NULL; 222 } 223 224 FILE *ftmp = fdopen(tmp_fd, "wb"); 225 226 if (!ftmp) { 227 elf_end(e); 228 return NULL; 229 } 230 231 fwrite(image->ImageStart, ImageSize, 1, ftmp); 232 fclose(ftmp); 233 234 DynLibTy Lib = {tmp_name, dlopen(tmp_name, RTLD_LAZY)}; 235 236 if (!Lib.Handle) { 237 DP("Target library loading error: %s\n", dlerror()); 238 elf_end(e); 239 return NULL; 240 } 241 242 DeviceInfo.DynLibs.push_back(Lib); 243 244 struct link_map *libInfo = (struct link_map *)Lib.Handle; 245 246 // The place where the entries info is loaded is the library base address 247 // plus the offset determined from the ELF file. 248 Elf64_Addr entries_addr = libInfo->l_addr + entries_offset; 249 250 DP("Pointer to first entry to be loaded is (" DPxMOD ").\n", 251 DPxPTR(entries_addr)); 252 253 // Table of pointers to all the entries in the target. 254 __tgt_offload_entry *entries_table = (__tgt_offload_entry *)entries_addr; 255 256 __tgt_offload_entry *entries_begin = &entries_table[0]; 257 __tgt_offload_entry *entries_end = entries_begin + NumEntries; 258 259 if (!entries_begin) { 260 DP("Can't obtain entries begin\n"); 261 elf_end(e); 262 return NULL; 263 } 264 265 DP("Entries table range is (" DPxMOD ")->(" DPxMOD ")\n", 266 DPxPTR(entries_begin), DPxPTR(entries_end)); 267 DeviceInfo.createOffloadTable(device_id, entries_begin, entries_end); 268 269 elf_end(e); 270 271 return DeviceInfo.getOffloadEntriesTable(device_id); 272 } 273 274 void *__tgt_rtl_data_alloc(int32_t device_id, int64_t size, void *hst_ptr) { 275 void *ptr = malloc(size); 276 return ptr; 277 } 278 279 int32_t __tgt_rtl_data_submit(int32_t device_id, void *tgt_ptr, void *hst_ptr, 280 int64_t size) { 281 memcpy(tgt_ptr, hst_ptr, size); 282 return OFFLOAD_SUCCESS; 283 } 284 285 int32_t __tgt_rtl_data_retrieve(int32_t device_id, void *hst_ptr, void *tgt_ptr, 286 int64_t size) { 287 memcpy(hst_ptr, tgt_ptr, size); 288 return OFFLOAD_SUCCESS; 289 } 290 291 int32_t __tgt_rtl_data_delete(int32_t device_id, void *tgt_ptr) { 292 free(tgt_ptr); 293 return OFFLOAD_SUCCESS; 294 } 295 296 int32_t __tgt_rtl_run_target_team_region(int32_t device_id, void *tgt_entry_ptr, 297 void **tgt_args, ptrdiff_t *tgt_offsets, int32_t arg_num, int32_t team_num, 298 int32_t thread_limit, uint64_t loop_tripcount /*not used*/) { 299 // ignore team num and thread limit. 300 301 // Use libffi to launch execution. 302 ffi_cif cif; 303 304 // All args are references. 305 std::vector<ffi_type *> args_types(arg_num, &ffi_type_pointer); 306 std::vector<void *> args(arg_num); 307 std::vector<void *> ptrs(arg_num); 308 309 for (int32_t i = 0; i < arg_num; ++i) { 310 ptrs[i] = (void *)((intptr_t)tgt_args[i] + tgt_offsets[i]); 311 args[i] = &ptrs[i]; 312 } 313 314 ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_num, 315 &ffi_type_void, &args_types[0]); 316 317 assert(status == FFI_OK && "Unable to prepare target launch!"); 318 319 if (status != FFI_OK) 320 return OFFLOAD_FAIL; 321 322 DP("Running entry point at " DPxMOD "...\n", DPxPTR(tgt_entry_ptr)); 323 324 void (*entry)(void); 325 *((void**) &entry) = tgt_entry_ptr; 326 ffi_call(&cif, entry, NULL, &args[0]); 327 return OFFLOAD_SUCCESS; 328 } 329 330 int32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr, 331 void **tgt_args, ptrdiff_t *tgt_offsets, int32_t arg_num) { 332 // use one team and one thread. 333 return __tgt_rtl_run_target_team_region(device_id, tgt_entry_ptr, tgt_args, 334 tgt_offsets, arg_num, 1, 1, 0); 335 } 336 337 #ifdef __cplusplus 338 } 339 #endif 340