1/* Return string pointer from string section.
2   Copyright (C) 1998-2002, 2004, 2008, 2009, 2015 Red Hat, Inc.
3   This file is part of elfutils.
4   Contributed by Ulrich Drepper <drepper@redhat.com>, 1998.
5
6   This file is free software; you can redistribute it and/or modify
7   it under the terms of either
8
9     * the GNU Lesser General Public License as published by the Free
10       Software Foundation; either version 3 of the License, or (at
11       your option) any later version
12
13   or
14
15     * the GNU General Public License as published by the Free
16       Software Foundation; either version 2 of the License, or (at
17       your option) any later version
18
19   or both in parallel, as here.
20
21   elfutils is distributed in the hope that it will be useful, but
22   WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24   General Public License for more details.
25
26   You should have received copies of the GNU General Public License and
27   the GNU Lesser General Public License along with this program.  If
28   not, see <http://www.gnu.org/licenses/>.  */
29
30#ifdef HAVE_CONFIG_H
31# include <config.h>
32#endif
33
34#include <libelf.h>
35#include <stddef.h>
36
37#include "libelfP.h"
38
39
40static void *
41get_zdata (Elf_Scn *strscn)
42{
43  size_t zsize, zalign;
44  void *zdata = __libelf_decompress_elf (strscn, &zsize, &zalign);
45  if (zdata == NULL)
46    return NULL;
47
48  strscn->zdata_base = zdata;
49  strscn->zdata_size = zsize;
50  strscn->zdata_align = zalign;
51
52  return zdata;
53}
54
55char *
56elf_strptr (Elf *elf, size_t idx, size_t offset)
57{
58  if (elf == NULL)
59    return NULL;
60
61  if (elf->kind != ELF_K_ELF)
62    {
63      __libelf_seterrno (ELF_E_INVALID_HANDLE);
64      return NULL;
65    }
66
67  rwlock_rdlock (elf->lock);
68
69  char *result = NULL;
70  Elf_Scn *strscn;
71
72  /* Find the section in the list.  */
73  Elf_ScnList *runp = (elf->class == ELFCLASS32
74		       || (offsetof (struct Elf, state.elf32.scns)
75			   == offsetof (struct Elf, state.elf64.scns))
76		       ? &elf->state.elf32.scns : &elf->state.elf64.scns);
77  while (1)
78    {
79      if (idx < runp->max)
80	{
81	  if (idx < runp->cnt)
82	    strscn = &runp->data[idx];
83	  else
84	    {
85	      __libelf_seterrno (ELF_E_INVALID_INDEX);
86	      goto out;
87	    }
88	  break;
89	}
90
91      idx -= runp->max;
92
93      runp = runp->next;
94      if (runp == NULL)
95	{
96	  __libelf_seterrno (ELF_E_INVALID_INDEX);
97	  goto out;
98	}
99    }
100
101  size_t sh_size = 0;
102  if (elf->class == ELFCLASS32)
103    {
104      Elf32_Shdr *shdr = strscn->shdr.e32 ?: __elf32_getshdr_rdlock (strscn);
105      if (unlikely (shdr->sh_type != SHT_STRTAB))
106	{
107	  /* This is no string section.  */
108	  __libelf_seterrno (ELF_E_INVALID_SECTION);
109	  goto out;
110	}
111
112      if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
113	sh_size = shdr->sh_size;
114      else
115	{
116	  if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
117	    goto out;
118	  sh_size = strscn->zdata_size;
119	}
120
121      if (unlikely (offset >= sh_size))
122	{
123	  /* The given offset is too big, it is beyond this section.  */
124	  __libelf_seterrno (ELF_E_OFFSET_RANGE);
125	  goto out;
126	}
127    }
128  else
129    {
130      Elf64_Shdr *shdr = strscn->shdr.e64 ?: __elf64_getshdr_rdlock (strscn);
131      if (unlikely (shdr->sh_type != SHT_STRTAB))
132	{
133	  /* This is no string section.  */
134	  __libelf_seterrno (ELF_E_INVALID_SECTION);
135	  goto out;
136	}
137
138      if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
139	sh_size = shdr->sh_size;
140      else
141	{
142	  if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
143	    goto out;
144	  sh_size = strscn->zdata_size;
145	}
146
147      if (unlikely (offset >= sh_size))
148	{
149	  /* The given offset is too big, it is beyond this section.  */
150	  __libelf_seterrno (ELF_E_OFFSET_RANGE);
151	  goto out;
152	}
153    }
154
155  if (strscn->rawdata_base == NULL && ! strscn->data_read)
156    {
157      rwlock_unlock (elf->lock);
158      rwlock_wrlock (elf->lock);
159      if (strscn->rawdata_base == NULL && ! strscn->data_read
160	/* Read the section data.  */
161	  && __libelf_set_rawdata_wrlock (strscn) != 0)
162	goto out;
163    }
164
165  if (unlikely (strscn->zdata_base != NULL))
166    {
167      /* Make sure the string is NUL terminated.  Start from the end,
168         which very likely is a NUL char.  */
169      if (likely (memrchr (&strscn->zdata_base[offset],
170			   '\0', sh_size - offset) != NULL))
171        result = &strscn->zdata_base[offset];
172      else
173        __libelf_seterrno (ELF_E_INVALID_INDEX);
174    }
175  else if (likely (strscn->data_list_rear == NULL))
176    {
177      // XXX The above is currently correct since elf_newdata will
178      // make sure to convert the rawdata into the datalist if
179      // necessary. But it would be more efficient to keep the rawdata
180      // unconverted and only then iterate over the rest of the (newly
181      // added data) list.  Note that when the ELF file is mmapped
182      // rawdata_base can be set while rawdata.d hasn't been
183      // initialized yet (when data_read is zero). So we cannot just
184      // look at the rawdata.d.d_size.
185
186      /* Make sure the string is NUL terminated.  Start from the end,
187	 which very likely is a NUL char.  */
188      if (likely (memrchr (&strscn->rawdata_base[offset],
189			  '\0', sh_size - offset) != NULL))
190	result = &strscn->rawdata_base[offset];
191      else
192	__libelf_seterrno (ELF_E_INVALID_INDEX);
193    }
194  else
195    {
196      /* This is a file which is currently created.  Use the list of
197	 data blocks.  */
198      struct Elf_Data_List *dl = &strscn->data_list;
199      while (dl != NULL)
200	{
201	  if (offset >= (size_t) dl->data.d.d_off
202	      && offset < dl->data.d.d_off + dl->data.d.d_size)
203	    {
204	      /* Make sure the string is NUL terminated.  Start from
205		 the end, which very likely is a NUL char.  */
206	      if (likely (memrchr ((char *) dl->data.d.d_buf
207				   + (offset - dl->data.d.d_off), '\0',
208				   (dl->data.d.d_size
209				    - (offset - dl->data.d.d_off))) != NULL))
210		result = ((char *) dl->data.d.d_buf
211			  + (offset - dl->data.d.d_off));
212	      else
213		__libelf_seterrno (ELF_E_INVALID_INDEX);
214	      break;
215	    }
216
217	  dl = dl->next;
218	}
219    }
220
221 out:
222  rwlock_unlock (elf->lock);
223
224  return result;
225}
226INTDEF(elf_strptr)
227