• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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