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