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