• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 "src/trace_processor/importers/elf/binary_info.h"
18 
19 #include <fcntl.h>
20 
21 #include <cstdint>
22 #include <limits>
23 #include <optional>
24 #include <string>
25 
26 #include "perfetto/base/logging.h"
27 #include "perfetto/ext/base/file_utils.h"
28 #include "perfetto/ext/base/scoped_mmap.h"
29 #include "src/trace_processor/importers/elf/elf.h"
30 
31 namespace perfetto::trace_processor::elf {
32 namespace {
33 
InRange(const void * base,size_t total_size,const void * ptr,size_t size)34 bool InRange(const void* base,
35              size_t total_size,
36              const void* ptr,
37              size_t size) {
38   return ptr >= base && static_cast<const char*>(ptr) + size <=
39                             static_cast<const char*>(base) + total_size;
40 }
41 
42 template <typename E>
GetElfLoadBias(const void * mem,size_t size)43 std::optional<uint64_t> GetElfLoadBias(const void* mem, size_t size) {
44   const typename E::Ehdr* ehdr = static_cast<const typename E::Ehdr*>(mem);
45   if (!InRange(mem, size, ehdr, sizeof(typename E::Ehdr))) {
46     PERFETTO_ELOG("Corrupted ELF.");
47     return std::nullopt;
48   }
49   for (size_t i = 0; i < ehdr->e_phnum; ++i) {
50     const typename E::Phdr* phdr = GetPhdr<E>(mem, ehdr, i);
51     if (!InRange(mem, size, phdr, sizeof(typename E::Phdr))) {
52       PERFETTO_ELOG("Corrupted ELF.");
53       return std::nullopt;
54     }
55     if (phdr->p_type == PT_LOAD && phdr->p_flags & PF_X) {
56       return phdr->p_vaddr - phdr->p_offset;
57     }
58   }
59   return 0u;
60 }
61 
62 template <typename E>
GetElfBuildId(const void * mem,size_t size)63 std::optional<std::string> GetElfBuildId(const void* mem, size_t size) {
64   const typename E::Ehdr* ehdr = static_cast<const typename E::Ehdr*>(mem);
65   if (!InRange(mem, size, ehdr, sizeof(typename E::Ehdr))) {
66     PERFETTO_ELOG("Corrupted ELF.");
67     return std::nullopt;
68   }
69   for (size_t i = 0; i < ehdr->e_shnum; ++i) {
70     const typename E::Shdr* shdr = GetShdr<E>(mem, ehdr, i);
71     if (!InRange(mem, size, shdr, sizeof(typename E::Shdr))) {
72       PERFETTO_ELOG("Corrupted ELF.");
73       return std::nullopt;
74     }
75 
76     if (shdr->sh_type != SHT_NOTE)
77       continue;
78 
79     auto offset = shdr->sh_offset;
80     while (offset < shdr->sh_offset + shdr->sh_size) {
81       const typename E::Nhdr* nhdr = reinterpret_cast<const typename E::Nhdr*>(
82           static_cast<const char*>(mem) + offset);
83 
84       if (!InRange(mem, size, nhdr, sizeof(typename E::Nhdr))) {
85         PERFETTO_ELOG("Corrupted ELF.");
86         return std::nullopt;
87       }
88       if (nhdr->n_type == NT_GNU_BUILD_ID && nhdr->n_namesz == 4) {
89         const char* name = reinterpret_cast<const char*>(nhdr) + sizeof(*nhdr);
90         if (!InRange(mem, size, name, 4)) {
91           PERFETTO_ELOG("Corrupted ELF.");
92           return std::nullopt;
93         }
94         if (memcmp(name, "GNU", 3) == 0) {
95           const char* value = reinterpret_cast<const char*>(nhdr) +
96                               sizeof(*nhdr) + base::AlignUp<4>(nhdr->n_namesz);
97 
98           if (!InRange(mem, size, value, nhdr->n_descsz)) {
99             PERFETTO_ELOG("Corrupted ELF.");
100             return std::nullopt;
101           }
102           return std::string(value, nhdr->n_descsz);
103         }
104       }
105       offset += sizeof(*nhdr) + base::AlignUp<4>(nhdr->n_namesz) +
106                 base::AlignUp<4>(nhdr->n_descsz);
107     }
108   }
109   return std::nullopt;
110 }
111 
112 constexpr uint32_t kMachO64Magic = 0xfeedfacf;
113 
IsMachO64(const uint8_t * mem,size_t size)114 bool IsMachO64(const uint8_t* mem, size_t size) {
115   if (size < sizeof(kMachO64Magic))
116     return false;
117   return memcmp(mem, &kMachO64Magic, sizeof(kMachO64Magic)) == 0;
118 }
119 
120 struct mach_header_64 {
121   uint32_t magic;      /* mach magic number identifier */
122   int32_t cputype;     /* cpu specifier */
123   int32_t cpusubtype;  /* machine specifier */
124   uint32_t filetype;   /* type of file */
125   uint32_t ncmds;      /* number of load commands */
126   uint32_t sizeofcmds; /* the size of all the load commands */
127   uint32_t flags;      /* flags */
128   uint32_t reserved;   /* reserved */
129 };
130 
131 struct load_command {
132   uint32_t cmd;     /* type of load command */
133   uint32_t cmdsize; /* total size of command in bytes */
134 };
135 
136 struct segment_64_command {
137   uint32_t cmd;      /* LC_SEGMENT_64 */
138   uint32_t cmdsize;  /* includes sizeof section_64 structs */
139   char segname[16];  /* segment name */
140   uint64_t vmaddr;   /* memory address of this segment */
141   uint64_t vmsize;   /* memory size of this segment */
142   uint64_t fileoff;  /* file offset of this segment */
143   uint64_t filesize; /* amount to map from the file */
144   uint32_t maxprot;  /* maximum VM protection */
145   uint32_t initprot; /* initial VM protection */
146   uint32_t nsects;   /* number of sections in segment */
147   uint32_t flags;    /* flags */
148 };
149 
GetMachOBinaryInfo(const uint8_t * mem,size_t size)150 std::optional<BinaryInfo> GetMachOBinaryInfo(const uint8_t* mem, size_t size) {
151   if (size < sizeof(mach_header_64))
152     return {};
153 
154   mach_header_64 header;
155   memcpy(&header, mem, sizeof(mach_header_64));
156 
157   if (size < sizeof(mach_header_64) + header.sizeofcmds)
158     return {};
159 
160   std::optional<std::string> build_id;
161   uint64_t load_bias = 0;
162 
163   const uint8_t* pcmd = mem + sizeof(mach_header_64);
164   const uint8_t* pcmds_end = pcmd + header.sizeofcmds;
165   while (pcmd < pcmds_end) {
166     load_command cmd_header;
167     memcpy(&cmd_header, pcmd, sizeof(load_command));
168 
169     constexpr uint32_t LC_SEGMENT_64 = 0x19;
170     constexpr uint32_t LC_UUID = 0x1b;
171 
172     switch (cmd_header.cmd) {
173       case LC_UUID: {
174         build_id = std::string(
175             reinterpret_cast<const char*>(pcmd) + sizeof(load_command),
176             cmd_header.cmdsize - sizeof(load_command));
177         break;
178       }
179       case LC_SEGMENT_64: {
180         segment_64_command seg_cmd;
181         memcpy(&seg_cmd, pcmd, sizeof(segment_64_command));
182         if (strcmp(seg_cmd.segname, "__TEXT") == 0) {
183           load_bias = seg_cmd.vmaddr;
184         }
185         break;
186       }
187       default:
188         break;
189     }
190 
191     pcmd += cmd_header.cmdsize;
192   }
193 
194   if (build_id) {
195     constexpr uint32_t MH_DSYM = 0xa;
196     BinaryType type = header.filetype == MH_DSYM ? BinaryType::kMachODsym
197                                                  : BinaryType::kMachO;
198     return BinaryInfo{*build_id, load_bias, type};
199   }
200   return {};
201 }
202 
203 }  // namespace
204 
IsElf(const uint8_t * mem,size_t size)205 bool IsElf(const uint8_t* mem, size_t size) {
206   if (size <= EI_MAG3)
207     return false;
208   return (mem[EI_MAG0] == ELFMAG0 && mem[EI_MAG1] == ELFMAG1 &&
209           mem[EI_MAG2] == ELFMAG2 && mem[EI_MAG3] == ELFMAG3);
210 }
211 
GetBinaryInfo(const uint8_t * mem,size_t size)212 std::optional<BinaryInfo> GetBinaryInfo(const uint8_t* mem, size_t size) {
213   static_assert(EI_MAG3 + 1 == sizeof(kMachO64Magic));
214   static_assert(EI_CLASS > EI_MAG3, "mem[EI_MAG?] accesses are in range.");
215   if (size <= EI_CLASS) {
216     return std::nullopt;
217   }
218   std::optional<std::string> build_id;
219   std::optional<uint64_t> load_bias;
220   if (IsElf(mem, size)) {
221     switch (mem[EI_CLASS]) {
222       case ELFCLASS32:
223         build_id = GetElfBuildId<Elf32>(mem, size);
224         load_bias = GetElfLoadBias<Elf32>(mem, size);
225         break;
226       case ELFCLASS64:
227         build_id = GetElfBuildId<Elf64>(mem, size);
228         load_bias = GetElfLoadBias<Elf64>(mem, size);
229         break;
230       default:
231         return std::nullopt;
232     }
233     if (load_bias) {
234       return BinaryInfo{build_id, *load_bias, BinaryType::kElf};
235     }
236   } else if (IsMachO64(mem, size)) {
237     return GetMachOBinaryInfo(mem, size);
238   }
239   return std::nullopt;
240 }
241 
242 }  // namespace perfetto::trace_processor::elf
243