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