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