C++ strings: missing space for null terminator
[helenos.git] / uspace / app / taskdump / symtab.c
blob27cff4043b5800f33db6dac140d9ca2a06237edb
1 /*
2 * Copyright (c) 2010 Jiri Svoboda
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 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup debug
30 * @{
32 /** @file Handling of ELF symbol tables.
34 * This module allows one to load a symbol table from an ELF file and
35 * use it to lookup symbol names/addresses in both directions.
38 #include <elf/elf.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stddef.h>
42 #include <errno.h>
43 #include <str.h>
44 #include <str_error.h>
45 #include <vfs/vfs.h>
47 #include "include/symtab.h"
49 static errno_t elf_hdr_check(elf_header_t *hdr);
50 static errno_t section_hdr_load(int fd, const elf_header_t *ehdr, int idx,
51 elf_section_header_t *shdr);
52 static errno_t chunk_load(int fd, off64_t start, size_t size, void **ptr);
54 /** Load symbol table from an ELF file.
56 * @param file_name Name of the ELF file to read from.
57 * @param symtab Place to save pointer to new symtab structure.
59 * @return EOK on success, ENOENT if file could not be open,
60 * ENOTSUP if file parsing failed.
62 errno_t symtab_load(const char *file_name, symtab_t **symtab)
64 symtab_t *stab;
65 elf_header_t elf_hdr;
66 elf_section_header_t sec_hdr;
67 off64_t shstrt_start;
68 size_t shstrt_size;
69 char *shstrt, *sec_name;
70 void *data;
71 aoff64_t pos = 0;
73 int fd;
74 errno_t rc;
75 size_t nread;
76 int i;
78 bool load_sec, sec_is_symtab;
80 *symtab = NULL;
82 stab = calloc(1, sizeof(symtab_t));
83 if (stab == NULL)
84 return ENOMEM;
86 rc = vfs_lookup_open(file_name, WALK_REGULAR, MODE_READ, &fd);
87 if (rc != EOK) {
88 printf("failed opening file '%s': %s\n", file_name, str_error(rc));
89 free(stab);
90 return ENOENT;
93 rc = vfs_read(fd, &pos, &elf_hdr, sizeof(elf_header_t), &nread);
94 if (rc != EOK || nread != sizeof(elf_header_t)) {
95 printf("failed reading elf header\n");
96 free(stab);
97 return EIO;
100 rc = elf_hdr_check(&elf_hdr);
101 if (rc != EOK) {
102 printf("failed header check\n");
103 free(stab);
104 return ENOTSUP;
108 * Load section header string table.
111 rc = section_hdr_load(fd, &elf_hdr, elf_hdr.e_shstrndx, &sec_hdr);
112 if (rc != EOK) {
113 printf("failed reading shstrt header\n");
114 free(stab);
115 return ENOTSUP;
118 shstrt_start = sec_hdr.sh_offset;
119 shstrt_size = sec_hdr.sh_size;
121 rc = chunk_load(fd, shstrt_start, shstrt_size, (void **) &shstrt);
122 if (rc != EOK) {
123 printf("failed loading shstrt\n");
124 free(stab);
125 return ENOTSUP;
128 /* Read all section headers. */
129 for (i = 0; i < elf_hdr.e_shnum; ++i) {
130 rc = section_hdr_load(fd, &elf_hdr, i, &sec_hdr);
131 if (rc != EOK) {
132 free(shstrt);
133 free(stab);
134 return ENOTSUP;
137 sec_name = shstrt + sec_hdr.sh_name;
138 if (str_cmp(sec_name, ".symtab") == 0 &&
139 sec_hdr.sh_type == SHT_SYMTAB) {
140 load_sec = true;
141 sec_is_symtab = true;
142 } else if (str_cmp(sec_name, ".strtab") == 0 &&
143 sec_hdr.sh_type == SHT_STRTAB) {
144 load_sec = true;
145 sec_is_symtab = false;
146 } else {
147 load_sec = false;
150 if (load_sec) {
151 rc = chunk_load(fd, sec_hdr.sh_offset, sec_hdr.sh_size,
152 &data);
153 if (rc != EOK) {
154 free(shstrt);
155 free(stab);
156 return ENOTSUP;
159 if (sec_is_symtab) {
160 stab->sym = data;
161 stab->sym_size = sec_hdr.sh_size;
162 } else {
163 stab->strtab = data;
164 stab->strtab_size = sec_hdr.sh_size;
169 free(shstrt);
170 vfs_put(fd);
172 if (stab->sym == NULL || stab->strtab == NULL) {
173 /* Tables not found. */
174 printf("Symbol table or string table section not found\n");
175 free(stab);
176 return ENOTSUP;
179 *symtab = stab;
181 return EOK;
184 /** Delete a symtab structure.
186 * Deallocates all resources used by the symbol table.
188 void symtab_delete(symtab_t *st)
190 free(st->sym);
191 st->sym = NULL;
193 free(st->strtab);
194 st->strtab = NULL;
196 free(st);
199 /** Convert symbol name to address.
201 * @param st Symbol table.
202 * @param name Name of the symbol.
203 * @param addr Place to store address for symbol, if found.
205 * @return EOK on success, ENOENT if no such symbol was found.
207 errno_t symtab_name_to_addr(symtab_t *st, const char *name, uintptr_t *addr)
209 size_t i;
210 char *sname;
211 unsigned stype;
213 for (i = 0; i < st->sym_size / sizeof(elf_symbol_t); ++i) {
214 if (st->sym[i].st_name == 0)
215 continue;
217 stype = elf_st_type(st->sym[i].st_info);
218 if (stype != STT_OBJECT && stype != STT_FUNC)
219 continue;
221 sname = st->strtab + st->sym[i].st_name;
223 if (str_cmp(sname, name) == 0) {
224 *addr = st->sym[i].st_value;
225 return EOK;
229 return ENOENT;
232 /** Convert symbol address to name.
234 * This function finds the symbol which starts at the highest address
235 * less than or equal to @a addr.
237 * @param st Symbol table.
238 * @param addr Address for lookup.
239 * @param name Place to store pointer name of symbol, if found.
240 * This is valid while @a st exists.
242 * @return EOK on success or ENOENT if no matching symbol was found.
244 errno_t symtab_addr_to_name(symtab_t *st, uintptr_t addr, char **name,
245 size_t *offs)
247 size_t i;
248 uintptr_t saddr, best_addr;
249 char *sname, *best_name;
250 unsigned stype;
252 best_name = NULL;
253 best_addr = 0;
255 for (i = 0; i < st->sym_size / sizeof(elf_symbol_t); ++i) {
256 if (st->sym[i].st_name == 0)
257 continue;
259 stype = elf_st_type(st->sym[i].st_info);
260 if (stype != STT_OBJECT && stype != STT_FUNC &&
261 stype != STT_NOTYPE) {
262 continue;
265 saddr = st->sym[i].st_value;
266 sname = st->strtab + st->sym[i].st_name;
268 /* An ugly hack to filter out some special ARM symbols. */
269 if (sname[0] == '$')
270 continue;
272 if (saddr <= addr && (best_name == NULL || saddr > best_addr)) {
273 best_name = sname;
274 best_addr = saddr;
278 if (best_name == NULL)
279 return ENOENT;
281 *name = best_name;
282 *offs = addr - best_addr;
283 return EOK;
286 /** Check if ELF header is valid.
288 * @return EOK on success or an error code.
290 static errno_t elf_hdr_check(elf_header_t *ehdr)
292 /* TODO */
293 return EOK;
296 /** Load ELF section header.
298 * @param fd File descriptor of ELF file.
299 * @param elf_hdr Pointer to ELF file header in memory.
300 * @param idx Index of section whose header to load (0 = first).
301 * @param sec_hdr Place to store section header data.
303 * @return EOK on success or EIO if I/O failed.
305 static errno_t section_hdr_load(int fd, const elf_header_t *elf_hdr, int idx,
306 elf_section_header_t *sec_hdr)
308 errno_t rc;
309 size_t nread;
310 aoff64_t pos = elf_hdr->e_shoff + idx * sizeof(elf_section_header_t);
312 rc = vfs_read(fd, &pos, sec_hdr, sizeof(elf_section_header_t), &nread);
313 if (rc != EOK || nread != sizeof(elf_section_header_t))
314 return EIO;
316 return EOK;
319 /** Load a segment of bytes from a file and return it as a new memory block.
321 * This function fails if it cannot read exactly @a size bytes from the file.
323 * @param fd File to read from.
324 * @param start Position in file where to start reading.
325 * @param size Number of bytes to read.
326 * @param ptr Place to store pointer to newly allocated block.
328 * @return EOK on success or EIO on failure.
330 static errno_t chunk_load(int fd, off64_t start, size_t size, void **ptr)
332 errno_t rc;
333 size_t nread;
334 aoff64_t pos = start;
336 *ptr = malloc(size);
337 if (*ptr == NULL) {
338 printf("failed allocating memory\n");
339 return ENOMEM;
342 rc = vfs_read(fd, &pos, *ptr, size, &nread);
343 if (rc != EOK || nread != size) {
344 printf("failed reading chunk\n");
345 free(*ptr);
346 *ptr = NULL;
347 return EIO;
350 return EOK;
353 /** @}