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