HPCToolkit
RelocateCubin.cpp
Go to the documentation of this file.
1 // * BeginRiceCopyright *****************************************************
2 //
3 // $HeadURL$
4 // $Id$
5 //
6 // --------------------------------------------------------------------------
7 // Part of HPCToolkit (hpctoolkit.org)
8 //
9 // Information about sources of support for research and development of
10 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
11 // --------------------------------------------------------------------------
12 //
13 // Copyright ((c)) 2002-2019, Rice University
14 // All rights reserved.
15 //
16 // Redistribution and use in source and binary forms, with or without
17 // modification, are permitted provided that the following conditions are
18 // met:
19 //
20 // * Redistributions of source code must retain the above copyright
21 // notice, this list of conditions and the following disclaimer.
22 //
23 // * Redistributions in binary form must reproduce the above copyright
24 // notice, this list of conditions and the following disclaimer in the
25 // documentation and/or other materials provided with the distribution.
26 //
27 // * Neither the name of Rice University (RICE) nor the names of its
28 // contributors may be used to endorse or promote products derived from
29 // this software without specific prior written permission.
30 //
31 // This software is provided by RICE and contributors "as is" and any
32 // express or implied warranties, including, but not limited to, the
33 // implied warranties of merchantability and fitness for a particular
34 // purpose are disclaimed. In no event shall RICE or contributors be
35 // liable for any direct, indirect, incidental, special, exemplary, or
36 // consequential damages (including, but not limited to, procurement of
37 // substitute goods or services; loss of use, data, or profits; or
38 // business interruption) however caused and on any theory of liability,
39 // whether in contract, strict liability, or tort (including negligence
40 // or otherwise) arising in any way out of the use of this software, even
41 // if advised of the possibility of such damage.
42 //
43 // ******************************************************* EndRiceCopyright *
44 
45 
46 //***************************************************************************
47 //
48 // File: RelocateCubin.cpp
49 //
50 // Purpose:
51 // Implementation of in-memory cubin relocation.
52 //
53 // Description:
54 // The associated implementation performs in-memory relocation of a cubin so
55 // that all text segments and functions are non-overlapping. Following
56 // relocation
57 // - each text segment begins at its offset in the segment
58 // - each function, which is in a unique text segment, has its symbol
59 // adjusted to point to the new position of the function in its relocated
60 // text segment
61 // - addresses in line map entries are adjusted to account for the new
62 // offsets of the instructions to which they refer
63 //
64 //***************************************************************************
65 
66 //******************************************************************************
67 // system includes
68 //******************************************************************************
69 
70 #include <assert.h>
71 #include <string.h>
72 
73 
74 
75 //******************************************************************************
76 // local includes
77 //******************************************************************************
78 
79 #include "ElfHelper.hpp"
80 #include "RelocateCubin.hpp"
81 
82 
83 
84 //******************************************************************************
85 // macros
86 //******************************************************************************
87 
88 #define DEBUG_LINE_SECTION_NAME ".debug_line"
89 
90 #define DEBUG_INFO_SECTION_NAME ".debug_info"
91 
92 #define CASESTR(n) case n: return #n
93 #define section_index(n) (n-1)
94 
95 #define DEBUG_CUBIN_RELOCATION 0
96 
97 
98 //---------------------------------------------------------
99 // NVIDIA CUDA line map relocation types
100 // type name gleaned using cuobjdump
101 // value gleaned by examining binaries
102 //---------------------------------------------------------
103 #define R_NV_32 0x01
104 #define R_NV_64 0x02
105 
106 
107 
108 //******************************************************************************
109 // type definitions
110 //******************************************************************************
111 
112 typedef std::vector<Elf_Scn *> Elf_SectionVector;
113 typedef std::vector<Elf64_Addr> Elf_SymbolVector;
114 
115 
116 
117 //******************************************************************************
118 // private functions
119 //******************************************************************************
120 
121 static size_t
123 (
124  Elf_SectionVector *sections,
125  unsigned sindex
126 )
127 {
128  // if section index is out of range, return 0
129  if (sections->size() < sindex) return 0;
130 
131  GElf_Shdr shdr;
132  if (!gelf_getshdr((*sections)[sindex], &shdr)) return 0;
133 
134  return shdr.sh_offset;
135 }
136 
137 
138 #if DEBUG_CUBIN_RELOCATION
139 static const char *
140 binding_name
141 (
142  GElf_Sym *s
143 )
144 {
145  switch(GELF_ST_BIND(s->st_info)){
146  CASESTR(STB_LOCAL);
147  CASESTR(STB_GLOBAL);
148  CASESTR(STB_WEAK);
149  default: return "UNKNOWN";
150  }
151 }
152 #endif
153 
154 
155 // properly size the relocation update to an address based on the
156 // relocation type
157 static void
158 applyRelocation(void *addr, unsigned rel_type, uint64_t rel_value)
159 {
160  if (rel_type == R_NV_64) {
161  uint64_t *addr64 = (uint64_t *) addr;
162  *addr64 = rel_value;
163  } else if (rel_type == R_NV_32) {
164  uint32_t *addr32 = (uint32_t *) addr;
165  *addr32 = rel_value;
166  } else {
167  assert(0);
168  }
169 }
170 
171 
172 static void
174 (
175  char *line_map,
176  Elf_SymbolVector *symbol_values,
177  GElf_Rel *rel
178 )
179 {
180  // get the symbol that is the basis for the relocation
181  unsigned sym_index = GELF_R_SYM(rel->r_info);
182 
183  // determine the type of relocation
184  unsigned rel_type = GELF_R_TYPE(rel->r_info);
185 
186  // get the new offset of the aforementioned symbol
187  unsigned sym_value = (*symbol_values)[sym_index];
188 
189  // compute address where a relocation needs to be applied
190  void *addr = (void *) (line_map + rel->r_offset);
191 
192  // update the address based on the symbol value associated with the
193  // relocation entry.
194  applyRelocation(addr, rel_type, sym_value);
195 }
196 
197 
198 static void
200 (
201  char *debug_info,
202  Elf_SymbolVector *symbol_values,
203  GElf_Rela *rela
204 )
205 {
206  // get the symbol that is the basis for the relocation
207  unsigned sym_index = GELF_R_SYM(rela->r_info);
208 
209  // determine the type of relocation
210  unsigned rel_type = GELF_R_TYPE(rela->r_info);
211 
212  // get the new offset of the aforementioned symbol
213  unsigned sym_value = (*symbol_values)[sym_index];
214 
215  // compute the address where a relocation needs to be applied
216  void *addr = (unsigned long *) (debug_info + rela->r_offset);
217 
218  // update the address in based on the symbol value and addend in the
219  // relocation entry
220  applyRelocation(addr, rel_type, sym_value + rela->r_addend);
221 }
222 
223 
224 static void
226 (
227  Elf_SymbolVector *symbol_values,
228  char *line_map,
229  int n_relocations,
230  Elf_Data *relocations_data
231 )
232 {
233  //----------------------------------------------------------------
234  // apply each line map relocation
235  //----------------------------------------------------------------
236  for (int i = 0; i < n_relocations; i++) {
237  GElf_Rel rel_v;
238  GElf_Rel *rel = gelf_getrel(relocations_data, i, &rel_v);
239  applyRELrelocation(line_map, symbol_values, rel);
240  }
241 }
242 
243 
244 // if the cubin contains a line map section and a matching line map relocations
245 // section, apply the relocations to the line map
246 static void
248 (
249  char *cubin_ptr,
250  Elf *elf,
251  Elf_SectionVector *sections,
252  Elf_SymbolVector *symbol_values
253 )
254 {
255  GElf_Ehdr ehdr_v;
256  GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_v);
257  if (ehdr) {
258  unsigned line_map_scn_index;
259  char *line_map = NULL;
260 
261  //-------------------------------------------------------------
262  // scan through the sections to locate a line map, if any
263  //-------------------------------------------------------------
264  int index = 0;
265  for (auto si = sections->begin(); si != sections->end(); si++, index++) {
266  Elf_Scn *scn = *si;
267  GElf_Shdr shdr;
268  if (!gelf_getshdr(scn, &shdr)) continue;
269  if (shdr.sh_type == SHT_PROGBITS) {
270  const char *section_name = elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name);
271  if (strcmp(section_name, DEBUG_LINE_SECTION_NAME) == 0) {
272  // remember the index of line map section. we need this index to find
273  // the corresponding relocation section.
274  line_map_scn_index = index;
275 
276  // compute line map position from start of cubin and the offset
277  // of the line map section in the cubin
278  line_map = cubin_ptr + shdr.sh_offset;
279 
280  // found the line map, so we are done with the linear scan of sections
281  break;
282  }
283  }
284  }
285 
286  //-------------------------------------------------------------
287  // if a line map was found, look for its relocations section
288  //-------------------------------------------------------------
289  if (line_map) {
290  int index = 0;
291  for (auto si = sections->begin(); si != sections->end(); si++, index++) {
292  Elf_Scn *scn = *si;
293  GElf_Shdr shdr;
294  if (!gelf_getshdr(scn, &shdr)) continue;
295  if (shdr.sh_type == SHT_REL) {
296  if (section_index(shdr.sh_info) == line_map_scn_index) {
297  // the relocation section refers to the line map section
298 #if DEBUG_CUBIN_RELOCATION
299  std::cout << "line map relocations section name: "
300  << elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)
301  << std::endl;
302 #endif
303  //-----------------------------------------------------
304  // invariant: have a line map and its relocations.
305  // use new symbol values to relocate the line map entries.
306  //-----------------------------------------------------
307 
308  // if the relocation entry size is not positive, give up
309  if (shdr.sh_entsize > 0) {
310  int n_relocations = shdr.sh_size / shdr.sh_entsize;
311  if (n_relocations > 0) {
312  Elf_Data *relocations_data = elf_getdata(scn, NULL);
313  applyLineMapRelocations(symbol_values, line_map,
314  n_relocations, relocations_data);
315  }
316  }
317  return;
318  }
319  }
320  }
321  }
322  }
323 }
324 
325 
326 static void
328 (
329  Elf_SymbolVector *symbol_values,
330  char *debug_info,
331  int n_relocations,
332  Elf_Data *relocations_data
333 )
334 {
335  //----------------------------------------------------------------
336  // apply each debug info relocation
337  //----------------------------------------------------------------
338  for (int i = 0; i < n_relocations; i++) {
339  GElf_Rela rela_v;
340  GElf_Rela *rela = gelf_getrela(relocations_data, i, &rela_v);
341  applyRELArelocation(debug_info, symbol_values, rela);
342  }
343 }
344 
345 
346 // if the cubin contains a line map section and a matching line map relocations
347 // section, apply the relocations to the line map
348 static void
350 (
351  char *cubin_ptr,
352  Elf *elf,
353  Elf_SectionVector *sections,
354  Elf_SymbolVector *symbol_values
355 )
356 {
357  GElf_Ehdr ehdr_v;
358  GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_v);
359  if (ehdr) {
360  unsigned debug_info_scn_index;
361  char *debug_info = NULL;
362 
363  //-------------------------------------------------------------
364  // scan through the sections to locate debug info, if any
365  //-------------------------------------------------------------
366  int index = 0;
367  for (auto si = sections->begin(); si != sections->end(); si++, index++) {
368  Elf_Scn *scn = *si;
369  GElf_Shdr shdr;
370  if (!gelf_getshdr(scn, &shdr)) continue;
371  if (shdr.sh_type == SHT_PROGBITS) {
372  const char *section_name = elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name);
373  if (strcmp(section_name, DEBUG_INFO_SECTION_NAME) == 0) {
374  // remember the index of line map section. we need this index to find
375  // the corresponding relocation section.
376  debug_info_scn_index = index;
377 
378  // compute debug info position from start of cubin and the offset
379  // of the debug info section in the cubin
380  debug_info = cubin_ptr + shdr.sh_offset;
381 
382  // found the debug info, so we are done with the linear scan of sections
383  break;
384  }
385  }
386  }
387 
388  //-------------------------------------------------------------
389  // if debug info was found, look for its relocations section
390  //-------------------------------------------------------------
391  if (debug_info) {
392  int index = 0;
393  for (auto si = sections->begin(); si != sections->end(); si++, index++) {
394  Elf_Scn *scn = *si;
395  GElf_Shdr shdr;
396  if (!gelf_getshdr(scn, &shdr)) continue;
397  if (shdr.sh_type == SHT_RELA) {
398  if (section_index(shdr.sh_info) == debug_info_scn_index) {
399  // the relocation section refers to the line map section
400 #if DEBUG_CUBIN_RELOCATION
401  std::cout << "debug info relocations section name: "
402  << elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)
403  << std::endl;
404 #endif
405  //-----------------------------------------------------
406  // invariant: have debug info and its relocations.
407  // use new symbol values to relocate debug info entries.
408  //-----------------------------------------------------
409 
410  // if the relocation entry size is not positive, give up
411  if (shdr.sh_entsize > 0) {
412  int n_relocations = shdr.sh_size / shdr.sh_entsize;
413  if (n_relocations > 0) {
414  Elf_Data *relocations_data = elf_getdata(scn, NULL);
415  applyDebugInfoRelocations(symbol_values, debug_info,
416  n_relocations, relocations_data);
417  }
418  }
419  return;
420  }
421  }
422  }
423  }
424  }
425 }
426 
427 
428 static Elf_SymbolVector *
430 (
431  Elf *elf,
432  GElf_Ehdr *ehdr,
433  GElf_Shdr *shdr,
434  Elf_SectionVector *sections,
435  Elf_Scn *scn
436 )
437 {
438  Elf_SymbolVector *symbol_values = NULL;
439  int nsymbols = 0;
440  assert (shdr->sh_type == SHT_SYMTAB);
441  if (shdr->sh_entsize > 0) { // avoid divide by 0
442  nsymbols = shdr->sh_size / shdr->sh_entsize;
443  }
444  if (nsymbols <= 0) return NULL;
445 
446  Elf_Data *datap = elf_getdata(scn, NULL);
447  if (datap) {
448  symbol_values = new Elf_SymbolVector(nsymbols);
449  for (int i = 0; i < nsymbols; i++) {
450  GElf_Sym sym;
451  GElf_Sym *symp = gelf_getsym(datap, i, &sym);
452  if (symp) { // symbol properly read
453  int symtype = GELF_ST_TYPE(sym.st_info);
454  if (sym.st_shndx == SHN_UNDEF) continue;
455  switch(symtype) {
456  case STT_FUNC:
457  {
458  int64_t s_offset = sectionOffset(sections, section_index(sym.st_shndx));
459 #if DEBUG_CUBIN_RELOCATION
460  Elf64_Addr addr_signed = sym.st_value;
461  std::cout << "elf symbol " << elf_strptr(elf, shdr->sh_link, sym.st_name)
462  << " value=0x" << std::hex << addr_signed
463  << " binding=" << binding_name(&sym)
464  << " section=" << std::dec << section_index(sym.st_shndx)
465  << " section offset=0x" << std::hex << s_offset
466  << std::endl;
467 #endif
468  // update each function symbol's offset to match the new offset of the
469  // text section that contains it.
470  sym.st_value = (Elf64_Addr) s_offset;
471  gelf_update_sym(datap, i, &sym);
472  (*symbol_values)[i] = s_offset;
473  }
474  default: break;
475  }
476  }
477  }
478  }
479  return symbol_values;
480 }
481 
482 
483 static Elf_SymbolVector *
485 (
486  Elf *elf,
487  Elf_SectionVector *sections
488 )
489 {
490  Elf_SymbolVector *symbol_values = NULL;
491  GElf_Ehdr ehdr_v;
492  GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_v);
493  if (ehdr) {
494  for (auto si = sections->begin(); si != sections->end(); si++) {
495  Elf_Scn *scn = *si;
496  GElf_Shdr shdr;
497  if (!gelf_getshdr(scn, &shdr)) continue;
498  if (shdr.sh_type == SHT_SYMTAB) {
499 #if DEBUG_CUBIN_RELOCATION
500  std::cout << "relocating symbols in section " << elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)
501  << std::endl;
502 #endif
503  symbol_values = relocateSymbolsHelper(elf, ehdr, &shdr, sections, scn);
504  break; // AFAIK, there can only be one symbol table
505  }
506  }
507  }
508  return symbol_values;
509 }
510 
511 
512 // cubin text segments all start at offset 0 and are thus overlapping.
513 // relocate each segment of type SHT_PROGBITS (a program text
514 // or data segment) so that it begins at its offset in the
515 // cubin. when this function returns, text segments no longer overlap.
516 static void
518 (
519  Elf *elf,
520  Elf_SectionVector *sections
521 )
522 {
523  GElf_Ehdr ehdr_v;
524  GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_v);
525  if (ehdr) {
526  for (auto si = sections->begin(); si != sections->end(); si++) {
527  Elf_Scn *scn = *si;
528  GElf_Shdr shdr;
529  if (!gelf_getshdr(scn, &shdr)) continue;
530  if (shdr.sh_type == SHT_PROGBITS) {
531 #if DEBUG_CUBIN_RELOCATION
532  std::cout << "relocating section " << elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)
533  << std::endl;
534 #endif
535  // update a segment so it's starting address matches its offset in the
536  // cubin.
537  shdr.sh_addr = shdr.sh_offset;
538  gelf_update_shdr(scn, &shdr);
539  }
540  }
541  }
542 }
543 
544 
545 
546 //******************************************************************************
547 // interface functions
548 //******************************************************************************
549 
550 bool
552 (
553  char *cubin_ptr,
554  Elf *cubin_elf
555 )
556 {
557  bool success = false;
558 
559  Elf_SectionVector *sections = elfGetSectionVector(cubin_elf);
560  if (sections) {
561  relocateProgramDataSegments(cubin_elf, sections);
562  Elf_SymbolVector *symbol_values = relocateSymbols(cubin_elf, sections);
563  if (symbol_values) {
564  relocateLineMap(cubin_ptr, cubin_elf, sections, symbol_values);
565  relocateDebugInfo(cubin_ptr, cubin_elf, sections, symbol_values);
566  delete symbol_values;
567  success = true;
568  }
569  delete sections;
570  }
571 
572  return success;
573 }
static void relocateDebugInfo(char *cubin_ptr, Elf *elf, Elf_SectionVector *sections, Elf_SymbolVector *symbol_values)
#define R_NV_64
#define CASESTR(n)
#define DEBUG_LINE_SECTION_NAME
bool relocateCubin(char *cubin_ptr, Elf *cubin_elf)
static void relocateProgramDataSegments(Elf *elf, Elf_SectionVector *sections)
static void applyRelocation(void *addr, unsigned rel_type, uint64_t rel_value)
static void applyDebugInfoRelocations(Elf_SymbolVector *symbol_values, char *debug_info, int n_relocations, Elf_Data *relocations_data)
#define section_index(n)
#define DEBUG_INFO_SECTION_NAME
std::vector< Elf64_Addr > Elf_SymbolVector
static Elf_SymbolVector * relocateSymbolsHelper(Elf *elf, GElf_Ehdr *ehdr, GElf_Shdr *shdr, Elf_SectionVector *sections, Elf_Scn *scn)
static void applyLineMapRelocations(Elf_SymbolVector *symbol_values, char *line_map, int n_relocations, Elf_Data *relocations_data)
static void applyRELrelocation(char *line_map, Elf_SymbolVector *symbol_values, GElf_Rel *rel)
static Elf_SymbolVector * relocateSymbols(Elf *elf, Elf_SectionVector *sections)
#define R_NV_32
static size_t sectionOffset(Elf_SectionVector *sections, unsigned sindex)
ElfSectionVector * elfGetSectionVector(Elf *elf)
Definition: ElfHelper.cpp:142
#define NULL
Definition: ElfHelper.cpp:85
static void applyRELArelocation(char *debug_info, Elf_SymbolVector *symbol_values, GElf_Rela *rela)
static void relocateLineMap(char *cubin_ptr, Elf *elf, Elf_SectionVector *sections, Elf_SymbolVector *symbol_values)
cct_addr_t * addr
Definition: cct.c:130
std::vector< Elf_Scn * > Elf_SectionVector