1 //===-RTLs/generic-64bit/src/rtl.cpp - Target RTLs Implementation - C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // RTL for generic 64-bit machine 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include <cassert> 14 #include <cstdio> 15 #include <cstring> 16 #include <cstdlib> 17 #include <dlfcn.h> 18 #include <ffi.h> 19 #include <gelf.h> 20 #include <link.h> 21 #include <list> 22 #include <string> 23 #include <vector> 24 25 #include "omptargetplugin.h" 26 27 #ifndef TARGET_NAME 28 #define TARGET_NAME Generic ELF - 64bit 29 #endif 30 31 #ifndef TARGET_ELF_ID 32 #define TARGET_ELF_ID 0 33 #endif 34 35 #ifdef OMPTARGET_DEBUG 36 static int DebugLevel = 0; 37 38 #define GETNAME2(name) #name 39 #define GETNAME(name) GETNAME2(name) 40 #define DP(...) \ 41 do { \ 42 if (DebugLevel > 0) { \ 43 DEBUGP("Target " GETNAME(TARGET_NAME) " RTL", __VA_ARGS__); \ 44 } \ 45 } while (false) 46 #else // OMPTARGET_DEBUG 47 #define DP(...) {} 48 #endif // OMPTARGET_DEBUG 49 50 #include "../../common/elf_common.c" 51 52 #define NUMBER_OF_DEVICES 4 53 #define OFFLOADSECTIONNAME "omp_offloading_entries" 54 55 /// Array of Dynamic libraries loaded for this target. 56 struct DynLibTy { 57 char *FileName; 58 void *Handle; 59 }; 60 61 /// Keep entries table per device. 62 struct FuncOrGblEntryTy { 63 __tgt_target_table Table; 64 }; 65 66 /// Class containing all the device information. 67 class RTLDeviceInfoTy { 68 std::vector<std::list<FuncOrGblEntryTy>> FuncGblEntries; 69 70 public: 71 std::list<DynLibTy> DynLibs; 72 73 // Record entry point associated with device. 74 void createOffloadTable(int32_t device_id, __tgt_offload_entry *begin, 75 __tgt_offload_entry *end) { 76 assert(device_id < (int32_t)FuncGblEntries.size() && 77 "Unexpected device id!"); 78 FuncGblEntries[device_id].emplace_back(); 79 FuncOrGblEntryTy &E = FuncGblEntries[device_id].back(); 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].back(); 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].back(); 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, 298 ptrdiff_t *tgt_offsets, 299 int32_t arg_num, int32_t team_num, 300 int32_t thread_limit, 301 uint64_t loop_tripcount /*not used*/) { 302 // ignore team num and thread limit. 303 304 // Use libffi to launch execution. 305 ffi_cif cif; 306 307 // All args are references. 308 std::vector<ffi_type *> args_types(arg_num, &ffi_type_pointer); 309 std::vector<void *> args(arg_num); 310 std::vector<void *> ptrs(arg_num); 311 312 for (int32_t i = 0; i < arg_num; ++i) { 313 ptrs[i] = (void *)((intptr_t)tgt_args[i] + tgt_offsets[i]); 314 args[i] = &ptrs[i]; 315 } 316 317 ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_num, 318 &ffi_type_void, &args_types[0]); 319 320 assert(status == FFI_OK && "Unable to prepare target launch!"); 321 322 if (status != FFI_OK) 323 return OFFLOAD_FAIL; 324 325 DP("Running entry point at " DPxMOD "...\n", DPxPTR(tgt_entry_ptr)); 326 327 void (*entry)(void); 328 *((void**) &entry) = tgt_entry_ptr; 329 ffi_call(&cif, entry, NULL, &args[0]); 330 return OFFLOAD_SUCCESS; 331 } 332 333 int32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr, 334 void **tgt_args, ptrdiff_t *tgt_offsets, 335 int32_t arg_num) { 336 // use one team and one thread. 337 return __tgt_rtl_run_target_team_region(device_id, tgt_entry_ptr, tgt_args, 338 tgt_offsets, arg_num, 1, 1, 0); 339 } 340 341 #ifdef __cplusplus 342 } 343 #endif 344