Add patch against GNU Binutils-2.20 to support "meminfo".
[libmeminfo.git] / meminfo_read_elf.c
blobaa9e1126f73cc2f8b4ba9c8d75afa987705157c9
1 /*
2 Copyright (c) 2008, STMicroelectronics
3 All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of STMicroelectronics nor the names of its
15 contributors may be used to endorse or promote products derived
16 from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 Contact: cedric.vincent@st.com
33 #include <gelf.h> /* gelf_*(3), elf*(3), */
34 #include <stdlib.h> /* NULL, */
35 #include <string.h> /* strcmp(3), */
37 #include "meminfo.h"
39 /* Iterate over sections in an ELF. */
40 #define foreach_section(elf, iterator) \
41 while (((iterator) = elf_nextscn((elf), (iterator))) != NULL)
43 /* Iterate over data in a section. */
44 #define foreach_data(section, iterator) \
45 while (((iterator) = elf_getdata((section), (iterator))) != NULL)
47 /* Used to copy a GElf_Sym variable into a Elf_Sym(32|64) one. */
48 #define COPY_SYM(a, b) \
49 (a).st_name = (b).st_name; \
50 (a).st_value = (b).st_value; \
51 (a).st_size = (b).st_size; \
52 (a).st_info = (b).st_info; \
53 (a).st_other = (b).st_other; \
54 (a).st_shndx = (b).st_shndx;
56 /* Return a symbol from its name. */
57 static int meminfo_get_symbol(Elf *elf, char *name, GElf_Sym *result)
59 Elf_Scn *section = NULL;
61 foreach_section(elf, section) {
62 Elf_Data *data = NULL;
63 void *status = NULL;
64 GElf_Shdr header;
66 /* Get the section header. */
67 status = gelf_getshdr(section, &header);
68 if (status == NULL)
69 return -1;
71 /* Filter out non-SYMTAB sections. */
72 if (header.sh_type != SHT_SYMTAB)
73 continue;
75 /* Sanity check. */
76 if (header.sh_entsize == 0)
77 return -1;
79 foreach_data(section, data) {
80 GElf_Sym *symbols = NULL;
81 size_t nb_symbols = 0;
83 /* Filter SYM data. */
84 if (data->d_type == ELF_T_SYM) {
85 int i = 0;
87 nb_symbols = (size_t)(header.sh_size / header.sh_entsize);
88 symbols = (GElf_Sym *)data->d_buf;
90 /* Iterate over symbols in a data block. */
91 for (i = 0; i < (int)nb_symbols; i++) {
92 char *symbol_name = NULL;
94 /* Copy the generic symbol into a specialized one. */
95 switch(gelf_getclass(elf)) {
96 case ELFCLASS32:
97 COPY_SYM(*result, ((Elf32_Sym *)symbols)[i]);
98 break;
99 case ELFCLASS64:
100 COPY_SYM(*result, ((Elf64_Sym *)symbols)[i]);
101 break;
102 default:
103 return -1;
106 /* Get the string from its offset in a string table. */
107 symbol_name = elf_strptr(elf, header.sh_link, result->st_name);
108 if (symbol_name == NULL)
109 return -1;
111 if (strcmp(name, symbol_name) == 0)
112 return 0;
118 return -1;
121 #define CHANGE_ENDIANNESS(a) ((((a) & 0x000000FF) << 24) | \
122 (((a) & 0x0000FF00) << 8) | \
123 (((a) & 0x00FF0000) >> 8) | \
124 (((a) & 0xFF000000) >> 24))
126 /* Adjust the endianness of a meminfo entry, from the target format to the host one. */
127 static int meminfo_fix_endianness(Elf *elf, struct meminfo *meminfo)
129 GElf_Ehdr header;
131 /* Get the ELF header. */
132 if (gelf_getehdr(elf, &header) == NULL)
133 return -1;
135 /* Adjust only if endiannesses are different. */
136 if (header.e_ident[EI_DATA] != HOST_ENDIANNESS) {
137 meminfo->origin = CHANGE_ENDIANNESS(meminfo->origin);
138 meminfo->length = CHANGE_ENDIANNESS(meminfo->length);
139 meminfo->flags = CHANGE_ENDIANNESS(meminfo->flags);
140 meminfo->not_flags = CHANGE_ENDIANNESS(meminfo->not_flags);
143 return 0;
146 int meminfo_read_elf(void *elf, struct meminfo *meminfo[])
148 GElf_Sym meminfo_start;
149 GElf_Sym meminfo_end;
151 size_t meminfo_nb_entries = 0;
152 size_t meminfo_offset = 0;
153 size_t meminfo_size = 0;
155 Elf_Kind format = ELF_K_NONE;
156 Elf_Scn *section = NULL;
157 Elf_Data *data = NULL;
158 GElf_Shdr section_header;
159 GElf_Ehdr elf_header;
160 int status = 0;
161 int i = 0;
163 /* Check that is not an ARchive file. */
164 format = elf_kind(elf);
165 if (format != ELF_K_ELF)
166 return MEMINFO_ERROR_ELF;
168 /* Get the symbol __meminfo_start. */
169 status = meminfo_get_symbol(elf, "__meminfo_start", &meminfo_start);
170 if (status < 0)
171 return MEMINFO_ERROR_STRUCTURE;
173 /* Get the symbol __meminfo_end. */
174 status = meminfo_get_symbol(elf, "__meminfo_end", &meminfo_end);
175 if (status < 0)
176 return MEMINFO_ERROR_STRUCTURE;
178 /* Sanity check: __meminfo_start and __meminfo_end must be
179 in the same section. */
180 if (meminfo_start.st_shndx != meminfo_end.st_shndx)
181 return MEMINFO_ERROR_STRUCTURE;
183 meminfo_size = meminfo_end.st_value - meminfo_start.st_value;
185 /* Sanity check: the size of the meminfo table must be
186 a multiple of sizeof(struct meminfo). */
187 if ((meminfo_size % sizeof(struct meminfo)) != 0)
188 return MEMINFO_ERROR_STRUCTURE;
190 meminfo_nb_entries = meminfo_size / sizeof(struct meminfo);
192 /* Get the section form its index. */
193 section = elf_getscn(elf, meminfo_start.st_shndx);
194 if (section == NULL)
195 return MEMINFO_ERROR_ELF;
197 /* Get the section header. */
198 if (gelf_getshdr(section, &section_header) == NULL)
199 return MEMINFO_ERROR_ELF;
201 /* Get the ELF header. */
202 if (gelf_getehdr(elf, &elf_header) == NULL)
203 return MEMINFO_ERROR_ELF;
205 /* Make meminfo's bounds section-relative except for a relocatable
206 object (because they are already section-relative in this case). */
207 if (elf_header.e_type != ET_REL) {
208 meminfo_start.st_value -= section_header.sh_addr;
209 meminfo_end.st_value -= section_header.sh_addr;
212 /* The user should free(3) this memory later. */
213 *meminfo = (struct meminfo *)calloc(meminfo_nb_entries, sizeof(struct meminfo));
214 if (*meminfo == NULL)
215 return MEMINFO_ERROR_SYSTEM;
217 foreach_data(section, data) {
218 size_t blob_start = 0;
219 size_t blob_end = 0;
220 size_t blob_size = 0;
222 /* Get the offset of the table within this buffer. */
223 blob_start = meminfo_start.st_value - data->d_off;
224 blob_end = meminfo_end.st_value - data->d_off;
226 /* Check if meminfo_start is before this buffer.
227 * I can't use "blob_start < 0" here since it is unsigned. */
228 if (meminfo_start.st_value < data->d_off)
229 blob_start = 0;
231 /* Check if meminfo_end is after this buffer. */
232 if (blob_end > data->d_size)
233 blob_end = data->d_size;
235 blob_size = blob_end - blob_start;
237 /* Copy the partial content if meminfo_start is not after
238 and meminfo_end is not before this buffer, and there is
239 something to copy. */
240 if (blob_start < data->d_size && blob_end > 0 &&
241 blob_size > 0) {
242 memcpy(*meminfo + meminfo_offset, data->d_buf + blob_start, blob_size);
243 meminfo_offset += blob_size;
247 /* Sanity check. */
248 if (meminfo_offset != meminfo_size) {
249 free(*meminfo);
250 *meminfo = NULL;
251 return MEMINFO_ERROR_STRUCTURE;
254 /* Avoid buffer overflows and adjust the endianness. */
255 for (i = 0; i < meminfo_nb_entries; i++) {
256 (*meminfo)[i].name[31] = 0;
258 status = meminfo_fix_endianness(elf, &(*meminfo)[i]);
259 if (status < 0)
260 return MEMINFO_ERROR_ELF;
263 return meminfo_nb_entries;