2 * Copyright (c) 2010 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
44 #include <str_error.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
)
66 elf_section_header_t sec_hdr
;
69 char *shstrt
, *sec_name
;
78 bool load_sec
, sec_is_symtab
;
82 stab
= calloc(1, sizeof(symtab_t
));
86 rc
= vfs_lookup_open(file_name
, WALK_REGULAR
, MODE_READ
, &fd
);
88 printf("failed opening file '%s': %s\n", file_name
, str_error(rc
));
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");
100 rc
= elf_hdr_check(&elf_hdr
);
102 printf("failed header check\n");
108 * Load section header string table.
111 rc
= section_hdr_load(fd
, &elf_hdr
, elf_hdr
.e_shstrndx
, &sec_hdr
);
113 printf("failed reading shstrt header\n");
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
);
123 printf("failed loading shstrt\n");
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
);
137 sec_name
= shstrt
+ sec_hdr
.sh_name
;
138 if (str_cmp(sec_name
, ".symtab") == 0 &&
139 sec_hdr
.sh_type
== SHT_SYMTAB
) {
141 sec_is_symtab
= true;
142 } else if (str_cmp(sec_name
, ".strtab") == 0 &&
143 sec_hdr
.sh_type
== SHT_STRTAB
) {
145 sec_is_symtab
= false;
151 rc
= chunk_load(fd
, sec_hdr
.sh_offset
, sec_hdr
.sh_size
,
161 stab
->sym_size
= sec_hdr
.sh_size
;
164 stab
->strtab_size
= sec_hdr
.sh_size
;
172 if (stab
->sym
== NULL
|| stab
->strtab
== NULL
) {
173 /* Tables not found. */
174 printf("Symbol table or string table section not found\n");
184 /** Delete a symtab structure.
186 * Deallocates all resources used by the symbol table.
188 void symtab_delete(symtab_t
*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
)
213 for (i
= 0; i
< st
->sym_size
/ sizeof(elf_symbol_t
); ++i
) {
214 if (st
->sym
[i
].st_name
== 0)
217 stype
= elf_st_type(st
->sym
[i
].st_info
);
218 if (stype
!= STT_OBJECT
&& stype
!= STT_FUNC
)
221 sname
= st
->strtab
+ st
->sym
[i
].st_name
;
223 if (str_cmp(sname
, name
) == 0) {
224 *addr
= st
->sym
[i
].st_value
;
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
,
248 uintptr_t saddr
, best_addr
;
249 char *sname
, *best_name
;
255 for (i
= 0; i
< st
->sym_size
/ sizeof(elf_symbol_t
); ++i
) {
256 if (st
->sym
[i
].st_name
== 0)
259 stype
= elf_st_type(st
->sym
[i
].st_info
);
260 if (stype
!= STT_OBJECT
&& stype
!= STT_FUNC
&&
261 stype
!= STT_NOTYPE
) {
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. */
272 if (saddr
<= addr
&& (best_name
== NULL
|| saddr
> best_addr
)) {
278 if (best_name
== NULL
)
282 *offs
= addr
- best_addr
;
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
)
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
)
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
))
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
)
334 aoff64_t pos
= start
;
338 printf("failed allocating memory\n");
342 rc
= vfs_read(fd
, &pos
, *ptr
, size
, &nread
);
343 if (rc
!= EOK
|| nread
!= size
) {
344 printf("failed reading chunk\n");