]>
Commit | Line | Data |
---|---|---|
59ea7f33 | 1 | /* Return converted data from raw chunk of ELF file. |
1ccdfb68 | 2 | Copyright (C) 2007, 2014, 2015 Red Hat, Inc. |
35e059b6 | 3 | Copyright (C) 2022, 2023 Mark J. Wielaard <mark@klomp.org> |
de2ed97f | 4 | This file is part of elfutils. |
b08d5a8f | 5 | |
de2ed97f MW |
6 | This file is free software; you can redistribute it and/or modify |
7 | it under the terms of either | |
b08d5a8f | 8 | |
de2ed97f MW |
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 | |
361df7da UD |
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. | |
b08d5a8f | 25 | |
de2ed97f MW |
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/>. */ | |
b08d5a8f UD |
29 | |
30 | #ifdef HAVE_CONFIG_H | |
31 | # include <config.h> | |
32 | #endif | |
33 | ||
59ea7f33 | 34 | #include <assert.h> |
fbe998a0 | 35 | #include <errno.h> |
35e059b6 | 36 | #include <search.h> |
b08d5a8f | 37 | #include <stdlib.h> |
59ea7f33 | 38 | #include <string.h> |
b08d5a8f UD |
39 | |
40 | #include "libelfP.h" | |
59ea7f33 | 41 | #include "common.h" |
b08d5a8f | 42 | |
35e059b6 MW |
43 | static int |
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 | ||
59ea7f33 | 58 | Elf_Data * |
a46200f6 | 59 | elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type) |
b08d5a8f | 60 | { |
59ea7f33 RM |
61 | if (unlikely (elf == NULL)) |
62 | return NULL; | |
63 | ||
64 | if (unlikely (elf->kind != ELF_K_ELF)) | |
b08d5a8f UD |
65 | { |
66 | /* No valid descriptor. */ | |
67 | __libelf_seterrno (ELF_E_INVALID_HANDLE); | |
68 | return NULL; | |
69 | } | |
70 | ||
f62658f7 MW |
71 | if (unlikely (offset < 0 || (uint64_t) offset > elf->maximum_size |
72 | || elf->maximum_size - (uint64_t) offset < size)) | |
73 | ||
b08d5a8f UD |
74 | { |
75 | /* Invalid request. */ | |
76 | __libelf_seterrno (ELF_E_INVALID_OP); | |
77 | return NULL; | |
78 | } | |
79 | ||
59ea7f33 RM |
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; | |
02f66452 UD |
89 | Elf_Data *result = NULL; |
90 | ||
91 | rwlock_rdlock (elf->lock); | |
59ea7f33 | 92 | |
8db84997 | 93 | /* Maybe we already got this chunk? */ |
35e059b6 MW |
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) | |
8db84997 | 105 | { |
35e059b6 MW |
106 | result = &(*found)->data.d; |
107 | goto out; | |
8db84997 MW |
108 | } |
109 | ||
d0525deb MW |
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. */ | |
35e059b6 | 114 | |
ede1d9d8 | 115 | size_t align = __libelf_type_align (elf->class, type); |
b08d5a8f | 116 | if (elf->map_address != NULL) |
ede1d9d8 MW |
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; | |
dd813335 | 120 | if (((uintptr_t) rawdata & (align - 1)) == 0) |
ede1d9d8 MW |
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 | } | |
59ea7f33 RM |
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: | |
d0525deb | 139 | tdelete (&key, &elf->state.elf.rawchunks, &chunk_compare); |
59ea7f33 | 140 | __libelf_seterrno (ELF_E_NOMEM); |
02f66452 | 141 | goto out; |
59ea7f33 RM |
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. */ | |
d0525deb | 150 | tdelete (&key, &elf->state.elf.rawchunks, &chunk_compare); |
59ea7f33 RM |
151 | free (rawchunk); |
152 | __libelf_seterrno (ELF_E_READ_ERROR); | |
02f66452 | 153 | goto out; |
59ea7f33 | 154 | } |
b08d5a8f | 155 | |
59ea7f33 RM |
156 | flags = ELF_F_MALLOCED; |
157 | } | |
158 | ||
159 | /* Copy and/or convert the data as needed for aligned native-order access. */ | |
59ea7f33 RM |
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); | |
b21b4dfc MW |
178 | |
179 | free (rawchunk); | |
59ea7f33 RM |
180 | } |
181 | } | |
b08d5a8f | 182 | else |
59ea7f33 RM |
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. */ | |
b9d16789 | 195 | (*__elf_xfctstom[elf->class - 1][type])(buffer, rawchunk, size, 0); |
b21b4dfc MW |
196 | |
197 | if (!flags) | |
198 | free (rawchunk); | |
59ea7f33 RM |
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; | |
b9d16789 | 217 | chunk->data.d.d_version = EV_CURRENT; |
8db84997 | 218 | chunk->offset = offset; |
59ea7f33 | 219 | |
02f66452 UD |
220 | rwlock_unlock (elf->lock); |
221 | rwlock_wrlock (elf->lock); | |
222 | ||
35e059b6 | 223 | *found = chunk; |
02f66452 | 224 | result = &chunk->data.d; |
59ea7f33 | 225 | |
02f66452 UD |
226 | out: |
227 | rwlock_unlock (elf->lock); | |
228 | return result; | |
b08d5a8f | 229 | } |