1/* Relocate debug information.
2   Copyright (C) 2005-2010 Red Hat, Inc.
3   This file is part of Red Hat elfutils.
4
5   Red Hat elfutils is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by the
7   Free Software Foundation; version 2 of the License.
8
9   Red Hat elfutils is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13
14   You should have received a copy of the GNU General Public License along
15   with Red Hat elfutils; if not, write to the Free Software Foundation,
16   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18   In addition, as a special exception, Red Hat, Inc. gives You the
19   additional right to link the code of Red Hat elfutils with code licensed
20   under any Open Source Initiative certified open source license
21   (http://www.opensource.org/licenses/index.php) which requires the
22   distribution of source code with any binary distribution and to
23   distribute linked combinations of the two.  Non-GPL Code permitted under
24   this exception must only link to the code of Red Hat elfutils through
25   those well defined interfaces identified in the file named EXCEPTION
26   found in the source code files (the "Approved Interfaces").  The files
27   of Non-GPL Code may instantiate templates or use macros or inline
28   functions from the Approved Interfaces without causing the resulting
29   work to be covered by the GNU General Public License.  Only Red Hat,
30   Inc. may make changes or additions to the list of Approved Interfaces.
31   Red Hat's grant of this exception is conditioned upon your not adding
32   any new exceptions.  If you wish to add a new Approved Interface or
33   exception, please contact Red Hat.  You must obey the GNU General Public
34   License in all respects for all of the Red Hat elfutils code and other
35   code used in conjunction with Red Hat elfutils except the Non-GPL Code
36   covered by this exception.  If you modify this file, you may extend this
37   exception to your version of the file, but you are not obligated to do
38   so.  If you do not wish to provide this exception without modification,
39   you must delete this exception statement from your version and license
40   this file solely under the GPL without exception.
41
42   Red Hat elfutils is an included package of the Open Invention Network.
43   An included package of the Open Invention Network is a package for which
44   Open Invention Network licensees cross-license their patents.  No patent
45   license is granted, either expressly or impliedly, by designation as an
46   included package.  Should you wish to participate in the Open Invention
47   Network licensing program, please visit www.openinventionnetwork.com
48   <http://www.openinventionnetwork.com>.  */
49
50#include "libdwflP.h"
51
52typedef uint8_t GElf_Byte;
53
54/* Adjust *VALUE to add the load address of the SHNDX section.
55   We update the section header in place to cache the result.  */
56
57Dwfl_Error
58internal_function
59__libdwfl_relocate_value (Dwfl_Module *mod, Elf *elf, size_t *shstrndx,
60			  Elf32_Word shndx, GElf_Addr *value)
61{
62  assert (mod->e_type == ET_REL);
63
64  Elf_Scn *refscn = elf_getscn (elf, shndx);
65  GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
66  if (refshdr == NULL)
67    return DWFL_E_LIBELF;
68
69  if (refshdr->sh_addr == 0 && (refshdr->sh_flags & SHF_ALLOC))
70    {
71      /* This is a loaded section.  Find its actual
72	 address and update the section header.  */
73
74      if (*shstrndx == SHN_UNDEF
75	  && unlikely (elf_getshdrstrndx (elf, shstrndx) < 0))
76	return DWFL_E_LIBELF;
77
78      const char *name = elf_strptr (elf, *shstrndx, refshdr->sh_name);
79      if (unlikely (name == NULL))
80	return DWFL_E_LIBELF;
81
82      if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod),
83						    name, shndx, refshdr,
84						    &refshdr->sh_addr))
85	return CBFAIL;
86
87      if (refshdr->sh_addr == (Dwarf_Addr) -1l)
88	/* The callback indicated this section wasn't really loaded but we
89	   don't really care.  */
90	refshdr->sh_addr = 0;	/* Make no adjustment below.  */
91
92      /* Update the in-core file's section header to show the final
93	 load address (or unloadedness).  This serves as a cache,
94	 so we won't get here again for the same section.  */
95      if (likely (refshdr->sh_addr != 0)
96	  && unlikely (! gelf_update_shdr (refscn, refshdr)))
97	return DWFL_E_LIBELF;
98    }
99
100  if (refshdr->sh_flags & SHF_ALLOC)
101    /* Apply the adjustment.  */
102    *value += dwfl_adjusted_address (mod, refshdr->sh_addr);
103
104  return DWFL_E_NOERROR;
105}
106
107
108/* Cache used by relocate_getsym.  */
109struct reloc_symtab_cache
110{
111  Elf *symelf;
112  Elf_Data *symdata;
113  Elf_Data *symxndxdata;
114  Elf_Data *symstrdata;
115  size_t symshstrndx;
116  size_t strtabndx;
117};
118#define RELOC_SYMTAB_CACHE(cache)	\
119  struct reloc_symtab_cache cache =	\
120    { NULL, NULL, NULL, NULL, SHN_UNDEF, SHN_UNDEF }
121
122/* This is just doing dwfl_module_getsym, except that we must always use
123   the symbol table in RELOCATED itself when it has one, not MOD->symfile.  */
124static Dwfl_Error
125relocate_getsym (Dwfl_Module *mod,
126		 Elf *relocated, struct reloc_symtab_cache *cache,
127		 int symndx, GElf_Sym *sym, GElf_Word *shndx)
128{
129  if (cache->symdata == NULL)
130    {
131      if (mod->symfile == NULL || mod->symfile->elf != relocated)
132	{
133	  /* We have to look up the symbol table in the file we are
134	     relocating, if it has its own.  These reloc sections refer to
135	     the symbol table in this file, and a symbol table in the main
136	     file might not match.  However, some tools did produce ET_REL
137	     .debug files with relocs but no symtab of their own.  */
138	  Elf_Scn *scn = NULL;
139	  while ((scn = elf_nextscn (relocated, scn)) != NULL)
140	    {
141	      GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
142	      if (shdr != NULL)
143		switch (shdr->sh_type)
144		  {
145		  default:
146		    continue;
147		  case SHT_SYMTAB:
148		    cache->symelf = relocated;
149		    cache->symdata = elf_getdata (scn, NULL);
150		    cache->strtabndx = shdr->sh_link;
151		    if (unlikely (cache->symdata == NULL))
152		      return DWFL_E_LIBELF;
153		    break;
154		  case SHT_SYMTAB_SHNDX:
155		    cache->symxndxdata = elf_getdata (scn, NULL);
156		    if (unlikely (cache->symxndxdata == NULL))
157		      return DWFL_E_LIBELF;
158		    break;
159		  }
160	      if (cache->symdata != NULL && cache->symxndxdata != NULL)
161		break;
162	    }
163	}
164      if (cache->symdata == NULL)
165	{
166	  /* We might not have looked for a symbol table file yet,
167	     when coming from __libdwfl_relocate_section.  */
168	  if (unlikely (mod->symfile == NULL)
169	      && unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0))
170	    return dwfl_errno ();
171
172	  /* The symbol table we have already cached is the one from
173	     the file being relocated, so it's what we need.  Or else
174	     this is an ET_REL .debug file with no .symtab of its own;
175	     the symbols refer to the section indices in the main file.  */
176	  cache->symelf = mod->symfile->elf;
177	  cache->symdata = mod->symdata;
178	  cache->symxndxdata = mod->symxndxdata;
179	  cache->symstrdata = mod->symstrdata;
180	}
181    }
182
183  if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata,
184				  symndx, sym, shndx) == NULL))
185    return DWFL_E_LIBELF;
186
187  if (sym->st_shndx != SHN_XINDEX)
188    *shndx = sym->st_shndx;
189
190  switch (sym->st_shndx)
191    {
192    case SHN_ABS:
193    case SHN_UNDEF:
194      return DWFL_E_NOERROR;
195
196    case SHN_COMMON:
197      sym->st_value = 0;	/* Value is size, not helpful. */
198      return DWFL_E_NOERROR;
199    }
200
201  return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx,
202				   *shndx, &sym->st_value);
203}
204
205/* Handle an undefined symbol.  We really only support ET_REL for Linux
206   kernel modules, and offline archives.  The behavior of the Linux module
207   loader is very simple and easy to mimic.  It only matches magically
208   exported symbols, and we match any defined symbols.  But we get the same
209   answer except when the module's symbols are undefined and would prevent
210   it from being loaded.  */
211static Dwfl_Error
212resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab,
213		GElf_Sym *sym, GElf_Word shndx)
214{
215  /* First we need its name.  */
216  if (sym->st_name != 0)
217    {
218      if (symtab->symstrdata == NULL)
219	{
220	  /* Cache the strtab for this symtab.  */
221	  assert (referer->symfile == NULL
222		  || referer->symfile->elf != symtab->symelf);
223	  symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf,
224							symtab->strtabndx),
225					    NULL);
226	  if (unlikely (symtab->symstrdata == NULL))
227	    return DWFL_E_LIBELF;
228	}
229      if (unlikely (sym->st_name >= symtab->symstrdata->d_size))
230	return DWFL_E_BADSTROFF;
231
232      const char *name = symtab->symstrdata->d_buf;
233      name += sym->st_name;
234
235      for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next)
236	if (m != referer)
237	  {
238	    /* Get this module's symtab.
239	       If we got a fresh error reading the table, report it.
240	       If we just have no symbols in this module, no harm done.  */
241	    if (m->symdata == NULL
242		&& m->symerr == DWFL_E_NOERROR
243		&& INTUSE(dwfl_module_getsymtab) (m) < 0
244		&& m->symerr != DWFL_E_NO_SYMTAB)
245	      return m->symerr;
246
247	    for (size_t ndx = 1; ndx < m->syments; ++ndx)
248	      {
249		sym = gelf_getsymshndx (m->symdata, m->symxndxdata,
250					ndx, sym, &shndx);
251		if (unlikely (sym == NULL))
252		  return DWFL_E_LIBELF;
253		if (sym->st_shndx != SHN_XINDEX)
254		  shndx = sym->st_shndx;
255
256		/* We are looking for a defined global symbol with a name.  */
257		if (shndx == SHN_UNDEF || shndx == SHN_COMMON
258		    || GELF_ST_BIND (sym->st_info) == STB_LOCAL
259		    || sym->st_name == 0)
260		  continue;
261
262		/* Get this candidate symbol's name.  */
263		if (unlikely (sym->st_name >= m->symstrdata->d_size))
264		  return DWFL_E_BADSTROFF;
265		const char *n = m->symstrdata->d_buf;
266		n += sym->st_name;
267
268		/* Does the name match?  */
269		if (strcmp (name, n))
270		  continue;
271
272		/* We found it!  */
273		if (shndx == SHN_ABS) /* XXX maybe should apply bias? */
274		  return DWFL_E_NOERROR;
275
276		if (m->e_type != ET_REL)
277		  {
278		    sym->st_value = dwfl_adjusted_st_value (m, sym->st_value);
279		    return DWFL_E_NOERROR;
280		  }
281
282		/* In an ET_REL file, the symbol table values are relative
283		   to the section, not to the module's load base.  */
284		size_t symshstrndx = SHN_UNDEF;
285		return __libdwfl_relocate_value (m, m->symfile->elf,
286						 &symshstrndx,
287						 shndx, &sym->st_value);
288	      }
289	  }
290    }
291
292  return DWFL_E_RELUNDEF;
293}
294
295static Dwfl_Error
296relocate_section (Dwfl_Module *mod, Elf *relocated, const GElf_Ehdr *ehdr,
297		  size_t shstrndx, struct reloc_symtab_cache *reloc_symtab,
298		  Elf_Scn *scn, GElf_Shdr *shdr,
299		  Elf_Scn *tscn, bool debugscn, bool partial)
300{
301  /* First, fetch the name of the section these relocations apply to.  */
302  GElf_Shdr tshdr_mem;
303  GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
304  const char *tname = elf_strptr (relocated, shstrndx, tshdr->sh_name);
305  if (tname == NULL)
306    return DWFL_E_LIBELF;
307
308  if (unlikely (tshdr->sh_type == SHT_NOBITS) || unlikely (tshdr->sh_size == 0))
309    /* No contents to relocate.  */
310    return DWFL_E_NOERROR;
311
312  if (debugscn && ! ebl_debugscn_p (mod->ebl, tname))
313    /* This relocation section is not for a debugging section.
314       Nothing to do here.  */
315    return DWFL_E_NOERROR;
316
317  /* Fetch the section data that needs the relocations applied.  */
318  Elf_Data *tdata = elf_rawdata (tscn, NULL);
319  if (tdata == NULL)
320    return DWFL_E_LIBELF;
321
322  /* Apply one relocation.  Returns true for any invalid data.  */
323  Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
324		       int rtype, int symndx)
325  {
326    /* First see if this is a reloc we can handle.
327       If we are skipping it, don't bother resolving the symbol.  */
328
329    if (unlikely (rtype == 0))
330      /* In some odd situations, the linker can leave R_*_NONE relocs
331	 behind.  This is probably bogus ld -r behavior, but the only
332	 cases it's known to appear in are harmless: DWARF data
333	 referring to addresses in a section that has been discarded.
334	 So we just pretend it's OK without further relocation.  */
335      return DWFL_E_NOERROR;
336
337    Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
338    if (unlikely (type == ELF_T_NUM))
339      return DWFL_E_BADRELTYPE;
340
341    /* First, resolve the symbol to an absolute value.  */
342    GElf_Addr value;
343
344    if (symndx == STN_UNDEF)
345      /* When strip removes a section symbol referring to a
346	 section moved into the debuginfo file, it replaces
347	 that symbol index in relocs with STN_UNDEF.  We
348	 don't actually need the symbol, because those relocs
349	 are always references relative to the nonallocated
350	 debugging sections, which start at zero.  */
351      value = 0;
352    else
353      {
354	GElf_Sym sym;
355	GElf_Word shndx;
356	Dwfl_Error error = relocate_getsym (mod, relocated, reloc_symtab,
357					    symndx, &sym, &shndx);
358	if (unlikely (error != DWFL_E_NOERROR))
359	  return error;
360
361	if (shndx == SHN_UNDEF || shndx == SHN_COMMON)
362	  {
363	    /* Maybe we can figure it out anyway.  */
364	    error = resolve_symbol (mod, reloc_symtab, &sym, shndx);
365	    if (error != DWFL_E_NOERROR
366		&& !(error == DWFL_E_RELUNDEF && shndx == SHN_COMMON))
367	      return error;
368	  }
369
370	value = sym.st_value;
371      }
372
373    /* These are the types we can relocate.  */
374#define TYPES		DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half);	\
375    DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword);			\
376    DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
377    size_t size;
378    switch (type)
379      {
380#define DO_TYPE(NAME, Name)			\
381	case ELF_T_##NAME:			\
382	  size = sizeof (GElf_##Name);		\
383	break
384	TYPES;
385#undef DO_TYPE
386      default:
387	return DWFL_E_BADRELTYPE;
388      }
389
390    if (offset + size > tdata->d_size)
391      return DWFL_E_BADRELOFF;
392
393#define DO_TYPE(NAME, Name) GElf_##Name Name;
394    union { TYPES; } tmpbuf;
395#undef DO_TYPE
396    Elf_Data tmpdata =
397      {
398	.d_type = type,
399	.d_buf = &tmpbuf,
400	.d_size = size,
401	.d_version = EV_CURRENT,
402      };
403    Elf_Data rdata =
404      {
405	.d_type = type,
406	.d_buf = tdata->d_buf + offset,
407	.d_size = size,
408	.d_version = EV_CURRENT,
409      };
410
411    /* XXX check for overflow? */
412    if (addend)
413      {
414	/* For the addend form, we have the value already.  */
415	value += *addend;
416	switch (type)
417	  {
418#define DO_TYPE(NAME, Name)			\
419	    case ELF_T_##NAME:			\
420	      tmpbuf.Name = value;		\
421	    break
422	    TYPES;
423#undef DO_TYPE
424	  default:
425	    abort ();
426	  }
427      }
428    else
429      {
430	/* Extract the original value and apply the reloc.  */
431	Elf_Data *d = gelf_xlatetom (relocated, &tmpdata, &rdata,
432				     ehdr->e_ident[EI_DATA]);
433	if (d == NULL)
434	  return DWFL_E_LIBELF;
435	assert (d == &tmpdata);
436	switch (type)
437	  {
438#define DO_TYPE(NAME, Name)				\
439	    case ELF_T_##NAME:				\
440	      tmpbuf.Name += (GElf_##Name) value;	\
441	    break
442	    TYPES;
443#undef DO_TYPE
444	  default:
445	    abort ();
446	  }
447      }
448
449    /* Now convert the relocated datum back to the target
450       format.  This will write into rdata.d_buf, which
451       points into the raw section data being relocated.  */
452    Elf_Data *s = gelf_xlatetof (relocated, &rdata, &tmpdata,
453				 ehdr->e_ident[EI_DATA]);
454    if (s == NULL)
455      return DWFL_E_LIBELF;
456    assert (s == &rdata);
457
458    /* We have applied this relocation!  */
459    return DWFL_E_NOERROR;
460  }
461
462  /* Fetch the relocation section and apply each reloc in it.  */
463  Elf_Data *reldata = elf_getdata (scn, NULL);
464  if (reldata == NULL)
465    return DWFL_E_LIBELF;
466
467  Dwfl_Error result = DWFL_E_NOERROR;
468  bool first_badreltype = true;
469  inline void check_badreltype (void)
470  {
471    if (first_badreltype)
472      {
473	first_badreltype = false;
474	if (ebl_get_elfmachine (mod->ebl) == EM_NONE)
475	  /* This might be because ebl_openbackend failed to find
476	     any libebl_CPU.so library.  Diagnose that clearly.  */
477	  result = DWFL_E_UNKNOWN_MACHINE;
478      }
479  }
480
481  size_t nrels = shdr->sh_size / shdr->sh_entsize;
482  size_t complete = 0;
483  if (shdr->sh_type == SHT_REL)
484    for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
485      {
486	GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
487	if (r == NULL)
488	  return DWFL_E_LIBELF;
489	result = relocate (r->r_offset, NULL,
490			   GELF_R_TYPE (r->r_info),
491			   GELF_R_SYM (r->r_info));
492	check_badreltype ();
493	if (partial)
494	  switch (result)
495	    {
496	    case DWFL_E_NOERROR:
497	      /* We applied the relocation.  Elide it.  */
498	      memset (&rel_mem, 0, sizeof rel_mem);
499	      gelf_update_rel (reldata, relidx, &rel_mem);
500	      ++complete;
501	      break;
502	    case DWFL_E_BADRELTYPE:
503	    case DWFL_E_RELUNDEF:
504	      /* We couldn't handle this relocation.  Skip it.  */
505	      result = DWFL_E_NOERROR;
506	      break;
507	    default:
508	      break;
509	    }
510      }
511  else
512    for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
513      {
514	GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
515					       &rela_mem);
516	if (r == NULL)
517	  return DWFL_E_LIBELF;
518	result = relocate (r->r_offset, &r->r_addend,
519			   GELF_R_TYPE (r->r_info),
520			   GELF_R_SYM (r->r_info));
521	check_badreltype ();
522	if (partial)
523	  switch (result)
524	    {
525	    case DWFL_E_NOERROR:
526	      /* We applied the relocation.  Elide it.  */
527	      memset (&rela_mem, 0, sizeof rela_mem);
528	      gelf_update_rela (reldata, relidx, &rela_mem);
529	      ++complete;
530	      break;
531	    case DWFL_E_BADRELTYPE:
532	    case DWFL_E_RELUNDEF:
533	      /* We couldn't handle this relocation.  Skip it.  */
534	      result = DWFL_E_NOERROR;
535	      break;
536	    default:
537	      break;
538	    }
539      }
540
541  if (likely (result == DWFL_E_NOERROR))
542    {
543      if (!partial || complete == nrels)
544	/* Mark this relocation section as being empty now that we have
545	   done its work.  This affects unstrip -R, so e.g. it emits an
546	   empty .rela.debug_info along with a .debug_info that has
547	   already been fully relocated.  */
548	nrels = 0;
549      else if (complete != 0)
550	{
551	  /* We handled some of the relocations but not all.
552	     We've zeroed out the ones we processed.
553	     Now remove them from the section.  */
554
555	  size_t next = 0;
556	  if (shdr->sh_type == SHT_REL)
557	    for (size_t relidx = 0; relidx < nrels; ++relidx)
558	      {
559		GElf_Rel rel_mem;
560		GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
561		if (r->r_info != 0 || r->r_offset != 0)
562		  {
563		    if (next != relidx)
564		      gelf_update_rel (reldata, next, r);
565		    ++next;
566		  }
567	      }
568	  else
569	    for (size_t relidx = 0; relidx < nrels; ++relidx)
570	      {
571		GElf_Rela rela_mem;
572		GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
573		if (r->r_info != 0 || r->r_offset != 0 || r->r_addend != 0)
574		  {
575		    if (next != relidx)
576		      gelf_update_rela (reldata, next, r);
577		    ++next;
578		  }
579	      }
580	  nrels = next;
581	}
582
583      shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize;
584      gelf_update_shdr (scn, shdr);
585    }
586
587  return result;
588}
589
590Dwfl_Error
591internal_function
592__libdwfl_relocate (Dwfl_Module *mod, Elf *debugfile, bool debug)
593{
594  assert (mod->e_type == ET_REL);
595
596  GElf_Ehdr ehdr_mem;
597  const GElf_Ehdr *ehdr = gelf_getehdr (debugfile, &ehdr_mem);
598  if (ehdr == NULL)
599    return DWFL_E_LIBELF;
600
601  size_t d_shstrndx;
602  if (elf_getshdrstrndx (debugfile, &d_shstrndx) < 0)
603    return DWFL_E_LIBELF;
604
605  RELOC_SYMTAB_CACHE (reloc_symtab);
606
607  /* Look at each section in the debuginfo file, and process the
608     relocation sections for debugging sections.  */
609  Dwfl_Error result = DWFL_E_NOERROR;
610  Elf_Scn *scn = NULL;
611  while (result == DWFL_E_NOERROR
612	 && (scn = elf_nextscn (debugfile, scn)) != NULL)
613    {
614      GElf_Shdr shdr_mem;
615      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
616
617      if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
618	  && shdr->sh_size != 0)
619	{
620	  /* It's a relocation section.  */
621
622	  Elf_Scn *tscn = elf_getscn (debugfile, shdr->sh_info);
623	  if (unlikely (tscn == NULL))
624	    result = DWFL_E_LIBELF;
625	  else
626	    result = relocate_section (mod, debugfile, ehdr, d_shstrndx,
627				       &reloc_symtab, scn, shdr, tscn,
628				       debug, !debug);
629	}
630    }
631
632  return result;
633}
634
635Dwfl_Error
636internal_function
637__libdwfl_relocate_section (Dwfl_Module *mod, Elf *relocated,
638			    Elf_Scn *relocscn, Elf_Scn *tscn, bool partial)
639{
640  GElf_Ehdr ehdr_mem;
641  GElf_Shdr shdr_mem;
642
643  RELOC_SYMTAB_CACHE (reloc_symtab);
644
645  size_t shstrndx;
646  if (elf_getshdrstrndx (relocated, &shstrndx) < 0)
647    return DWFL_E_LIBELF;
648
649  return (__libdwfl_module_getebl (mod)
650	  ?: relocate_section (mod, relocated,
651			       gelf_getehdr (relocated, &ehdr_mem), shstrndx,
652			       &reloc_symtab,
653			       relocscn, gelf_getshdr (relocscn, &shdr_mem),
654			       tscn, false, partial));
655}
656