xref: /aosp_15_r20/external/elfutils/libelf/elf_getdata_rawchunk.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
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