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 <mark@klomp.org>
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 }
180 else
181 {
182 if (flags)
183 buffer = rawchunk;
184 else
185 {
186 buffer = malloc (size);
187 if (unlikely (buffer == NULL))
188 goto nomem;
189 flags = ELF_F_MALLOCED;
190 }
191
192 /* Call the conversion function. */
193 (*__elf_xfctstom[elf->class - 1][type])(buffer, rawchunk, size, 0);
194 }
195
196 /* Allocate the dummy container to point at this buffer. */
197 Elf_Data_Chunk *chunk = calloc (1, sizeof *chunk);
198 if (chunk == NULL)
199 {
200 if (flags)
201 free (buffer);
202 goto nomem;
203 }
204
205 chunk->dummy_scn.elf = elf;
206 chunk->dummy_scn.flags = flags;
207 chunk->data.s = &chunk->dummy_scn;
208 chunk->data.d.d_buf = buffer;
209 chunk->data.d.d_size = size;
210 chunk->data.d.d_type = type;
211 chunk->data.d.d_align = align;
212 chunk->data.d.d_version = EV_CURRENT;
213 chunk->offset = offset;
214
215 rwlock_unlock (elf->lock);
216 rwlock_wrlock (elf->lock);
217
218 *found = chunk;
219 result = &chunk->data.d;
220
221 out:
222 rwlock_unlock (elf->lock);
223 return result;
224 }
225