1 /* Return converted data from raw chunk of ELF file.
2 Copyright (C) 2007, 2014, 2015 Red Hat, Inc.
3 Copyright (C) 2022, 2023 Mark J. Wielaard <[email protected]>
4 This file is part of elfutils.
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 <assert.h>
35 #include <errno.h>
36 #include <search.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "libelfP.h"
41 #include "common.h"
42
43 static int
chunk_compare(const void * a,const void * b)44 chunk_compare (const void *a, const void *b)
45 {
46 Elf_Data_Chunk *da = (Elf_Data_Chunk *)a;
47 Elf_Data_Chunk *db = (Elf_Data_Chunk *)b;
48
49 if (da->offset != db->offset)
50 return da->offset - db->offset;
51
52 if (da->data.d.d_size != db->data.d.d_size)
53 return da->data.d.d_size - db->data.d.d_size;
54
55 return da->data.d.d_type - db->data.d.d_type;
56 }
57
58 Elf_Data *
elf_getdata_rawchunk(Elf * elf,int64_t offset,size_t size,Elf_Type type)59 elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
60 {
61 if (unlikely (elf == NULL))
62 return NULL;
63
64 if (unlikely (elf->kind != ELF_K_ELF))
65 {
66 /* No valid descriptor. */
67 __libelf_seterrno (ELF_E_INVALID_HANDLE);
68 return NULL;
69 }
70
71 if (unlikely (offset < 0 || (uint64_t) offset > elf->maximum_size
72 || elf->maximum_size - (uint64_t) offset < size))
73
74 {
75 /* Invalid request. */
76 __libelf_seterrno (ELF_E_INVALID_OP);
77 return NULL;
78 }
79
80 if (type >= ELF_T_NUM)
81 {
82 __libelf_seterrno (ELF_E_UNKNOWN_TYPE);
83 return NULL;
84 }
85
86 /* Get the raw bytes from the file. */
87 void *rawchunk;
88 int flags = 0;
89 Elf_Data *result = NULL;
90
91 rwlock_rdlock (elf->lock);
92
93 /* Maybe we already got this chunk? */
94 Elf_Data_Chunk key;
95 key.offset = offset;
96 key.data.d.d_size = size;
97 key.data.d.d_type = type;
98 Elf_Data_Chunk **found = tsearch (&key, &elf->state.elf.rawchunks,
99 &chunk_compare);
100 if (found == NULL)
101 goto nomem;
102
103 /* Existing entry. */
104 if (*found != &key && *found != NULL)
105 {
106 result = &(*found)->data.d;
107 goto out;
108 }
109
110 /* New entry. Note that *found will point to the newly inserted
111 (dummy) key. We'll replace it with a real rawchunk when that is
112 setup. Make sure to tdelete the dummy key if anything goes
113 wrong. */
114
115 size_t align = __libelf_type_align (elf->class, type);
116 if (elf->map_address != NULL)
117 {
118 /* If the file is mmap'ed we can use it directly, if aligned for type. */
119 char *rawdata = elf->map_address + elf->start_offset + offset;
120 if (((uintptr_t) rawdata & (align - 1)) == 0)
121 rawchunk = rawdata;
122 else
123 {
124 /* We allocate the memory and memcpy it to get aligned data. */
125 rawchunk = malloc (size);
126 if (rawchunk == NULL)
127 goto nomem;
128 memcpy (rawchunk, rawdata, size);
129 flags = ELF_F_MALLOCED;
130 }
131 }
132 else
133 {
134 /* We allocate the memory and read the data from the file. */
135 rawchunk = malloc (size);
136 if (rawchunk == NULL)
137 {
138 nomem:
139 tdelete (&key, &elf->state.elf.rawchunks, &chunk_compare);
140 __libelf_seterrno (ELF_E_NOMEM);
141 goto out;
142 }
143
144 /* Read the file content. */
145 if (unlikely ((size_t) pread_retry (elf->fildes, rawchunk, size,
146 elf->start_offset + offset)
147 != size))
148 {
149 /* Something went wrong. */
150 tdelete (&key, &elf->state.elf.rawchunks, &chunk_compare);
151 free (rawchunk);
152 __libelf_seterrno (ELF_E_READ_ERROR);
153 goto out;
154 }
155
156 flags = ELF_F_MALLOCED;
157 }
158
159 /* Copy and/or convert the data as needed for aligned native-order access. */
160 void *buffer;
161 if (elf->state.elf32.ehdr->e_ident[EI_DATA] == MY_ELFDATA)
162 {
163 if (((uintptr_t) rawchunk & (align - 1)) == 0)
164 /* No need to copy, we can use the raw data. */
165 buffer = rawchunk;
166 else
167 {
168 /* A malloc'd block is always sufficiently aligned. */
169 assert (flags == 0);
170
171 buffer = malloc (size);
172 if (unlikely (buffer == NULL))
173 goto nomem;
174 flags = ELF_F_MALLOCED;
175
176 /* The copy will be appropriately aligned for direct access. */
177 memcpy (buffer, rawchunk, size);
178
179 free (rawchunk);
180 }
181 }
182 else
183 {
184 if (flags)
185 buffer = rawchunk;
186 else
187 {
188 buffer = malloc (size);
189 if (unlikely (buffer == NULL))
190 goto nomem;
191 flags = ELF_F_MALLOCED;
192 }
193
194 /* Call the conversion function. */
195 (*__elf_xfctstom[elf->class - 1][type])(buffer, rawchunk, size, 0);
196
197 if (!flags)
198 free (rawchunk);
199 }
200
201 /* Allocate the dummy container to point at this buffer. */
202 Elf_Data_Chunk *chunk = calloc (1, sizeof *chunk);
203 if (chunk == NULL)
204 {
205 if (flags)
206 free (buffer);
207 goto nomem;
208 }
209
210 chunk->dummy_scn.elf = elf;
211 chunk->dummy_scn.flags = flags;
212 chunk->data.s = &chunk->dummy_scn;
213 chunk->data.d.d_buf = buffer;
214 chunk->data.d.d_size = size;
215 chunk->data.d.d_type = type;
216 chunk->data.d.d_align = align;
217 chunk->data.d.d_version = EV_CURRENT;
218 chunk->offset = offset;
219
220 rwlock_unlock (elf->lock);
221 rwlock_wrlock (elf->lock);
222
223 *found = chunk;
224 result = &chunk->data.d;
225
226 out:
227 rwlock_unlock (elf->lock);
228 return result;
229 }
230