1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* This program reads an ELF file and computes information about
24 //----------------------------------------------------------------------
26 char* opt_type
= "func";
27 char* opt_section
= ".text";
29 //----------------------------------------------------------------------
32 hexdump(ostream
& out
, const char* bytes
, size_t count
)
38 out
.form("%08lx: ", off
);
40 const char* p
= bytes
+ off
;
44 out
.form("%02x", p
[j
++] & 0xff);
48 out
.form("%02x ", p
[j
++] & 0xff);
55 out
<< ((j
%2) ? " " : " ");
57 for (j
= 0; j
< 16; ++j
) {
59 out
.put(isprint(p
[j
]) ? p
[j
] : '.');
67 //----------------------------------------------------------------------
70 verify_elf_header(const Elf32_Ehdr
* hdr
)
72 if (hdr
->e_ident
[EI_MAG0
] != ELFMAG0
73 || hdr
->e_ident
[EI_MAG1
] != ELFMAG1
74 || hdr
->e_ident
[EI_MAG2
] != ELFMAG2
75 || hdr
->e_ident
[EI_MAG3
] != ELFMAG3
) {
76 cerr
<< "not an elf file" << endl
;
80 if (hdr
->e_ident
[EI_CLASS
] != ELFCLASS32
) {
81 cerr
<< "not a 32-bit elf file" << endl
;
85 if (hdr
->e_ident
[EI_DATA
] != ELFDATA2LSB
) {
86 cerr
<< "not a little endian elf file" << endl
;
90 if (hdr
->e_ident
[EI_VERSION
] != EV_CURRENT
) {
91 cerr
<< "incompatible version" << endl
;
98 //----------------------------------------------------------------------
100 class elf_symbol
: public Elf32_Sym
103 elf_symbol(const Elf32_Sym
& sym
)
104 { ::memcpy(static_cast<Elf32_Sym
*>(this), &sym
, sizeof(Elf32_Sym
)); }
106 friend bool operator==(const elf_symbol
& lhs
, const elf_symbol
& rhs
) {
107 return 0 == ::memcmp(static_cast<const Elf32_Sym
*>(&lhs
),
108 static_cast<const Elf32_Sym
*>(&rhs
),
109 sizeof(Elf32_Sym
)); }
112 //----------------------------------------------------------------------
115 st_bind(unsigned char info
)
117 switch (ELF32_ST_BIND(info
)) {
118 case STB_LOCAL
: return "local";
119 case STB_GLOBAL
: return "global";
120 case STB_WEAK
: return "weak";
121 default: return "unknown";
126 st_type(unsigned char info
)
128 switch (ELF32_ST_TYPE(info
)) {
129 case STT_NOTYPE
: return "none";
130 case STT_OBJECT
: return "object";
131 case STT_FUNC
: return "func";
132 case STT_SECTION
: return "section";
133 case STT_FILE
: return "file";
134 default: return "unknown";
139 st_type(const char* type
)
141 if (strcmp(type
, "none") == 0) {
144 else if (strcmp(type
, "object") == 0) {
147 else if (strcmp(type
, "func") == 0) {
155 //----------------------------------------------------------------------
157 typedef vector
<elf_symbol
> elf_symbol_table
;
158 typedef map
< basic_string
<char>, elf_symbol_table
> elf_text_map
;
161 process_mapping(char* mapping
, size_t size
)
163 const Elf32_Ehdr
* ehdr
= reinterpret_cast<Elf32_Ehdr
*>(mapping
);
164 if (verify_elf_header(ehdr
) < 0)
167 // find the section headers
168 const Elf32_Shdr
* shdrs
= reinterpret_cast<Elf32_Shdr
*>(mapping
+ ehdr
->e_shoff
);
170 // find the section header string table, .shstrtab
171 const Elf32_Shdr
* shstrtabsh
= shdrs
+ ehdr
->e_shstrndx
;
172 const char* shstrtab
= mapping
+ shstrtabsh
->sh_offset
;
174 // find the sections we care about
175 const Elf32_Shdr
*symtabsh
, *strtabsh
, *textsh
;
178 for (int i
= 0; i
< ehdr
->e_shnum
; ++i
) {
179 basic_string
<char> name(shstrtab
+ shdrs
[i
].sh_name
);
180 if (name
== opt_section
) {
184 else if (name
== ".symtab") {
185 symtabsh
= shdrs
+ i
;
187 else if (name
== ".strtab") {
188 strtabsh
= shdrs
+ i
;
193 char* strtab
= mapping
+ strtabsh
->sh_offset
;
196 char* text
= mapping
+ textsh
->sh_offset
;
197 int textaddr
= textsh
->sh_addr
;
199 // find the symbol table
200 int nentries
= symtabsh
->sh_size
/ sizeof(Elf32_Sym
);
201 Elf32_Sym
* symtab
= reinterpret_cast<Elf32_Sym
*>(mapping
+ symtabsh
->sh_offset
);
203 // look for symbols in the .text section
204 elf_text_map textmap
;
206 for (int i
= 0; i
< nentries
; ++i
) {
207 const Elf32_Sym
* sym
= symtab
+ i
;
208 if (sym
->st_shndx
== textndx
&&
209 ELF32_ST_TYPE(sym
->st_info
) == st_type(opt_type
) &&
211 basic_string
<char> functext(text
+ sym
->st_value
- textaddr
, sym
->st_size
);
213 elf_symbol_table
& syms
= textmap
[functext
];
214 if (syms
.end() == find(syms
.begin(), syms
.end(), elf_symbol(*sym
)))
215 syms
.insert(syms
.end(), *sym
);
219 int uniquebytes
= 0, totalbytes
= 0;
220 int uniquecount
= 0, totalcount
= 0;
222 for (elf_text_map::const_iterator entry
= textmap
.begin();
223 entry
!= textmap
.end();
225 const elf_symbol_table
& syms
= entry
->second
;
227 if (syms
.size() <= 1)
230 int sz
= syms
.begin()->st_size
;
232 totalbytes
+= sz
* syms
.size();
234 totalcount
+= syms
.size();
236 for (elf_symbol_table::const_iterator sym
= syms
.begin(); sym
!= syms
.end(); ++sym
)
237 cout
<< strtab
+ sym
->st_name
<< endl
;
240 cout
<< syms
.size() << " copies of " << sz
<< " bytes";
241 cout
<< " (" << ((syms
.size() - 1) * sz
) << " redundant bytes)" << endl
;
243 hexdump(cout
, entry
->first
.data(), entry
->first
.size());
248 cout
<< "bytes unique=" << uniquebytes
<< ", total=" << totalbytes
<< endl
;
249 cout
<< "entries unique=" << uniquecount
<< ", total=" << totalcount
<< endl
;
253 process_file(const char* name
)
255 int fd
= open(name
, O_RDWR
);
258 if (fstat(fd
, &statbuf
) >= 0) {
259 size_t size
= statbuf
.st_size
;
261 void* mapping
= mmap(0, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
262 if (mapping
!= MAP_FAILED
) {
263 process_mapping(static_cast<char*>(mapping
), size
);
264 munmap(mapping
, size
);
274 cerr
<< "foldelf [--section=<section>] [--type=<type>] [file ...]\n\
275 --section, -s the section of the ELF file to scan; defaults\n\
276 to ``.text''. Valid values include any section\n\
278 --type, -t the type of object to examine in the section;\n\
279 defaults to ``func''. Valid values include\n\
280 ``none'', ``func'', or ``object''.\n";
284 static struct option opts
[] = {
285 { "type", required_argument
, 0, 't' },
286 { "section", required_argument
, 0, 's' },
287 { "help", no_argument
, 0, '?' },
292 main(int argc
, char* argv
[])
295 int option_index
= 0;
296 int c
= getopt_long(argc
, argv
, "t:s:", opts
, &option_index
);
306 opt_section
= optarg
;
315 for (int i
= optind
; i
< argc
; ++i
)
316 process_file(argv
[i
]);