1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <elf.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <inttypes.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 
29 #include <unwindstack/DwarfSection.h>
30 #include <unwindstack/DwarfStructs.h>
31 #include <unwindstack/Elf.h>
32 #include <unwindstack/ElfInterface.h>
33 #include <unwindstack/Log.h>
34 #include <unwindstack/Memory.h>
35 
36 #include "ArmExidx.h"
37 #include "ElfInterfaceArm.h"
38 
39 namespace unwindstack {
40 
DumpArm(Elf * elf,ElfInterfaceArm * interface)41 void DumpArm(Elf* elf, ElfInterfaceArm* interface) {
42   if (interface == nullptr) {
43     printf("No ARM Unwind Information.\n\n");
44     return;
45   }
46 
47   printf("ARM Unwind Information:\n");
48   uint64_t load_bias = elf->GetLoadBias();
49   for (const auto& entry : interface->pt_loads()) {
50     printf(" PC Range 0x%" PRIx64 " - 0x%" PRIx64 "\n", entry.second.offset + load_bias,
51            entry.second.offset + entry.second.table_size + load_bias);
52     for (auto pc : *interface) {
53       SharedString name;
54       printf("  PC 0x%" PRIx64, pc + load_bias);
55       uint64_t func_offset;
56       if (elf->GetFunctionName(pc + load_bias, &name, &func_offset) && !name.empty()) {
57         printf(" <%s>", name.c_str());
58       }
59       printf("\n");
60       uint64_t entry;
61       if (!interface->FindEntry(pc, &entry)) {
62         printf("    Cannot find entry for address.\n");
63         continue;
64       }
65       ArmExidx arm(nullptr, interface->memory(), nullptr);
66       arm.set_log(ARM_LOG_FULL);
67       arm.set_log_skip_execution(true);
68       arm.set_log_indent(2);
69       if (!arm.ExtractEntryData(entry)) {
70         if (arm.status() != ARM_STATUS_NO_UNWIND) {
71           printf("    Error trying to extract data.\n");
72         }
73         continue;
74       }
75       if (arm.data()->size() > 0) {
76         if (!arm.Eval() && arm.status() != ARM_STATUS_NO_UNWIND) {
77           printf("      Error trying to evaluate dwarf data.\n");
78         }
79       }
80     }
81   }
82   printf("\n");
83 }
84 
DumpDwarfSection(Elf * elf,DwarfSection * section,uint64_t)85 void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
86   for (const DwarfFde* fde : *section) {
87     // Sometimes there are entries that have empty length, skip those since
88     // they don't contain any interesting information.
89     if (fde == nullptr || fde->pc_start == fde->pc_end) {
90       continue;
91     }
92     printf("\n  PC 0x%" PRIx64 "-0x%" PRIx64, fde->pc_start, fde->pc_end);
93     SharedString name;
94     uint64_t func_offset;
95     if (elf->GetFunctionName(fde->pc_start, &name, &func_offset) && !name.empty()) {
96       printf(" <%s>", name.c_str());
97     }
98     printf("\n");
99     if (!section->Log(2, UINT64_MAX, fde, elf->arch())) {
100       printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
101     }
102   }
103 }
104 
GetElfInfo(const char * file,uint64_t offset)105 int GetElfInfo(const char* file, uint64_t offset) {
106   // Send all log messages to stdout.
107   log_to_stdout(true);
108 
109   Elf elf(Memory::CreateFileMemory(file, offset).release());
110   if (!elf.Init() || !elf.valid()) {
111     printf("%s is not a valid elf file.\n", file);
112     return 1;
113   }
114 
115   std::string soname(elf.GetSoname());
116   if (!soname.empty()) {
117     printf("Soname: %s\n", soname.c_str());
118   }
119 
120   std::string build_id = elf.GetBuildID();
121   if (!build_id.empty()) {
122     printf("Build ID: ");
123     for (size_t i = 0; i < build_id.size(); ++i) {
124       printf("%02hhx", build_id[i]);
125     }
126     printf("\n");
127   }
128 
129   ElfInterface* interface = elf.interface();
130   if (elf.machine_type() == EM_ARM) {
131     DumpArm(&elf, reinterpret_cast<ElfInterfaceArm*>(interface));
132     printf("\n");
133   }
134 
135   if (interface->eh_frame() != nullptr) {
136     printf("eh_frame information:\n");
137     DumpDwarfSection(&elf, interface->eh_frame(), elf.GetLoadBias());
138     printf("\n");
139   } else {
140     printf("\nno eh_frame information\n");
141   }
142 
143   if (interface->debug_frame() != nullptr) {
144     printf("\ndebug_frame information:\n");
145     DumpDwarfSection(&elf, interface->debug_frame(), elf.GetLoadBias());
146     printf("\n");
147   } else {
148     printf("\nno debug_frame information\n");
149   }
150 
151   // If there is a gnu_debugdata interface, dump the information for that.
152   ElfInterface* gnu_debugdata_interface = elf.gnu_debugdata_interface();
153   if (gnu_debugdata_interface != nullptr) {
154     if (gnu_debugdata_interface->eh_frame() != nullptr) {
155       printf("\ngnu_debugdata (eh_frame):\n");
156       DumpDwarfSection(&elf, gnu_debugdata_interface->eh_frame(), 0);
157       printf("\n");
158     }
159     if (gnu_debugdata_interface->debug_frame() != nullptr) {
160       printf("\ngnu_debugdata (debug_frame):\n");
161       DumpDwarfSection(&elf, gnu_debugdata_interface->debug_frame(), 0);
162       printf("\n");
163     }
164   } else {
165     printf("\nno valid gnu_debugdata information\n");
166   }
167 
168   return 0;
169 }
170 
171 }  // namespace unwindstack
172 
main(int argc,char ** argv)173 int main(int argc, char** argv) {
174   if (argc != 2 && argc != 3) {
175     printf("Usage: unwind_info ELF_FILE [OFFSET]\n");
176     printf("  ELF_FILE\n");
177     printf("    The path to an elf file.\n");
178     printf("  OFFSET\n");
179     printf("    Use the offset into the ELF file as the beginning of the elf.\n");
180     return 1;
181   }
182 
183   struct stat st;
184   if (stat(argv[1], &st) == -1) {
185     printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
186     return 1;
187   }
188   if (!S_ISREG(st.st_mode)) {
189     printf("%s is not a regular file.\n", argv[1]);
190     return 1;
191   }
192 
193   uint64_t offset = 0;
194   if (argc == 3) {
195     char* end;
196     offset = strtoull(argv[2], &end, 16);
197     if (*end != '\0') {
198       printf("Malformed OFFSET value: %s\n", argv[2]);
199       return 1;
200     }
201   }
202 
203   return unwindstack::GetElfInfo(argv[1], offset);
204 }
205