Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <iostream> |
16 | | #include "string.h" |
17 | | #include "bloaty.h" |
18 | | #include "util.h" |
19 | | |
20 | | #include <cassert> |
21 | | |
22 | | #include "absl/strings/str_join.h" |
23 | | #include "absl/strings/string_view.h" |
24 | | #include "absl/strings/substitute.h" |
25 | | #include "third_party/darwin_xnu_macho/mach-o/loader.h" |
26 | | #include "third_party/darwin_xnu_macho/mach-o/fat.h" |
27 | | #include "third_party/darwin_xnu_macho/mach-o/nlist.h" |
28 | | #include "third_party/darwin_xnu_macho/mach-o/reloc.h" |
29 | | |
30 | | using absl::string_view; |
31 | | |
32 | | namespace bloaty { |
33 | | namespace macho { |
34 | | |
35 | | // segname (& sectname) may NOT be NULL-terminated, |
36 | | // i.e. can use up all 16 chars, e.g. '__gcc_except_tab' (no '\0'!) |
37 | | // hence specifying size when constructing std::string |
38 | 134k | static string_view ArrayToStr(const char* s, size_t maxlen) { |
39 | 134k | return string_view(s, strnlen(s, maxlen)); |
40 | 134k | } |
41 | | |
42 | 1.33M | uint32_t ReadMagic(string_view data) { |
43 | 1.33M | if (data.size() < sizeof(uint32_t)) { |
44 | 126 | THROW("Malformed Mach-O file"); |
45 | 126 | } |
46 | 1.33M | uint32_t magic; |
47 | 1.33M | memcpy(&magic, data.data(), sizeof(magic)); |
48 | 1.33M | return magic; |
49 | 1.33M | } |
50 | | |
51 | | template <class T> |
52 | 2.59M | const T* GetStructPointer(string_view data) { |
53 | 2.59M | if (sizeof(T) > data.size()) { |
54 | 4.04k | THROW("Premature EOF reading Mach-O data."); |
55 | 4.04k | } |
56 | 2.59M | return reinterpret_cast<const T*>(data.data()); |
57 | 2.59M | } dyld_info_command const* bloaty::macho::GetStructPointer<dyld_info_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 1.85k | const T* GetStructPointer(string_view data) { | 53 | 1.85k | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 1.73k | return reinterpret_cast<const T*>(data.data()); | 57 | 1.85k | } |
symtab_command const* bloaty::macho::GetStructPointer<symtab_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 124k | const T* GetStructPointer(string_view data) { | 53 | 124k | if (sizeof(T) > data.size()) { | 54 | 150 | THROW("Premature EOF reading Mach-O data."); | 55 | 150 | } | 56 | 124k | return reinterpret_cast<const T*>(data.data()); | 57 | 124k | } |
dysymtab_command const* bloaty::macho::GetStructPointer<dysymtab_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 1.53k | const T* GetStructPointer(string_view data) { | 53 | 1.53k | if (sizeof(T) > data.size()) { | 54 | 150 | THROW("Premature EOF reading Mach-O data."); | 55 | 150 | } | 56 | 1.38k | return reinterpret_cast<const T*>(data.data()); | 57 | 1.53k | } |
linkedit_data_command const* bloaty::macho::GetStructPointer<linkedit_data_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 1.90k | const T* GetStructPointer(string_view data) { | 53 | 1.90k | if (sizeof(T) > data.size()) { | 54 | 204 | THROW("Premature EOF reading Mach-O data."); | 55 | 204 | } | 56 | 1.69k | return reinterpret_cast<const T*>(data.data()); | 57 | 1.90k | } |
mach_header const* bloaty::macho::GetStructPointer<mach_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 160k | const T* GetStructPointer(string_view data) { | 53 | 160k | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 160k | return reinterpret_cast<const T*>(data.data()); | 57 | 160k | } |
load_command const* bloaty::macho::GetStructPointer<load_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 603k | const T* GetStructPointer(string_view data) { | 53 | 603k | if (sizeof(T) > data.size()) { | 54 | 474 | THROW("Premature EOF reading Mach-O data."); | 55 | 474 | } | 56 | 602k | return reinterpret_cast<const T*>(data.data()); | 57 | 603k | } |
uuid_command const* bloaty::macho::GetStructPointer<uuid_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 456 | const T* GetStructPointer(string_view data) { | 53 | 456 | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 336 | return reinterpret_cast<const T*>(data.data()); | 57 | 456 | } |
mach_header_64 const* bloaty::macho::GetStructPointer<mach_header_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 355k | const T* GetStructPointer(string_view data) { | 53 | 355k | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 355k | return reinterpret_cast<const T*>(data.data()); | 57 | 355k | } |
fat_header const* bloaty::macho::GetStructPointer<fat_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 1.23k | const T* GetStructPointer(string_view data) { | 53 | 1.23k | if (sizeof(T) > data.size()) { | 54 | 0 | THROW("Premature EOF reading Mach-O data."); | 55 | 0 | } | 56 | 1.23k | return reinterpret_cast<const T*>(data.data()); | 57 | 1.23k | } |
fat_arch const* bloaty::macho::GetStructPointer<fat_arch>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 510 | const T* GetStructPointer(string_view data) { | 53 | 510 | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 390 | return reinterpret_cast<const T*>(data.data()); | 57 | 510 | } |
segment_command_64 const* bloaty::macho::GetStructPointer<segment_command_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 26.0k | const T* GetStructPointer(string_view data) { | 53 | 26.0k | if (sizeof(T) > data.size()) { | 54 | 102 | THROW("Premature EOF reading Mach-O data."); | 55 | 102 | } | 56 | 25.9k | return reinterpret_cast<const T*>(data.data()); | 57 | 26.0k | } |
section_64 const* bloaty::macho::GetStructPointer<section_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 26.8k | const T* GetStructPointer(string_view data) { | 53 | 26.8k | if (sizeof(T) > data.size()) { | 54 | 911 | THROW("Premature EOF reading Mach-O data."); | 55 | 911 | } | 56 | 25.8k | return reinterpret_cast<const T*>(data.data()); | 57 | 26.8k | } |
segment_command const* bloaty::macho::GetStructPointer<segment_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 39.0k | const T* GetStructPointer(string_view data) { | 53 | 39.0k | if (sizeof(T) > data.size()) { | 54 | 294 | THROW("Premature EOF reading Mach-O data."); | 55 | 294 | } | 56 | 38.8k | return reinterpret_cast<const T*>(data.data()); | 57 | 39.0k | } |
section const* bloaty::macho::GetStructPointer<section>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 64.6k | const T* GetStructPointer(string_view data) { | 53 | 64.6k | if (sizeof(T) > data.size()) { | 54 | 1.16k | THROW("Premature EOF reading Mach-O data."); | 55 | 1.16k | } | 56 | 63.5k | return reinterpret_cast<const T*>(data.data()); | 57 | 64.6k | } |
nlist_64 const* bloaty::macho::GetStructPointer<nlist_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 808k | const T* GetStructPointer(string_view data) { | 53 | 808k | if (sizeof(T) > data.size()) { | 54 | 0 | THROW("Premature EOF reading Mach-O data."); | 55 | 0 | } | 56 | 808k | return reinterpret_cast<const T*>(data.data()); | 57 | 808k | } |
nlist const* bloaty::macho::GetStructPointer<nlist>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 378k | const T* GetStructPointer(string_view data) { | 53 | 378k | if (sizeof(T) > data.size()) { | 54 | 0 | THROW("Premature EOF reading Mach-O data."); | 55 | 0 | } | 56 | 378k | return reinterpret_cast<const T*>(data.data()); | 57 | 378k | } |
|
58 | | |
59 | | template <class T> |
60 | 1.86M | const T* GetStructPointerAndAdvance(string_view* data) { |
61 | 1.86M | const T* ret = GetStructPointer<T>(*data); |
62 | 1.86M | *data = data->substr(sizeof(T)); |
63 | 1.86M | return ret; |
64 | 1.86M | } mach_header const* bloaty::macho::GetStructPointerAndAdvance<mach_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 160k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 160k | const T* ret = GetStructPointer<T>(*data); | 62 | 160k | *data = data->substr(sizeof(T)); | 63 | 160k | return ret; | 64 | 160k | } |
uuid_command const* bloaty::macho::GetStructPointerAndAdvance<uuid_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 456 | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 456 | const T* ret = GetStructPointer<T>(*data); | 62 | 456 | *data = data->substr(sizeof(T)); | 63 | 456 | return ret; | 64 | 456 | } |
mach_header_64 const* bloaty::macho::GetStructPointerAndAdvance<mach_header_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 355k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 355k | const T* ret = GetStructPointer<T>(*data); | 62 | 355k | *data = data->substr(sizeof(T)); | 63 | 355k | return ret; | 64 | 355k | } |
fat_header const* bloaty::macho::GetStructPointerAndAdvance<fat_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 1.23k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 1.23k | const T* ret = GetStructPointer<T>(*data); | 62 | 1.23k | *data = data->substr(sizeof(T)); | 63 | 1.23k | return ret; | 64 | 1.23k | } |
fat_arch const* bloaty::macho::GetStructPointerAndAdvance<fat_arch>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 510 | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 510 | const T* ret = GetStructPointer<T>(*data); | 62 | 510 | *data = data->substr(sizeof(T)); | 63 | 510 | return ret; | 64 | 510 | } |
segment_command_64 const* bloaty::macho::GetStructPointerAndAdvance<segment_command_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 26.0k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 26.0k | const T* ret = GetStructPointer<T>(*data); | 62 | 26.0k | *data = data->substr(sizeof(T)); | 63 | 26.0k | return ret; | 64 | 26.0k | } |
section_64 const* bloaty::macho::GetStructPointerAndAdvance<section_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 26.8k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 26.8k | const T* ret = GetStructPointer<T>(*data); | 62 | 26.8k | *data = data->substr(sizeof(T)); | 63 | 26.8k | return ret; | 64 | 26.8k | } |
segment_command const* bloaty::macho::GetStructPointerAndAdvance<segment_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 39.0k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 39.0k | const T* ret = GetStructPointer<T>(*data); | 62 | 39.0k | *data = data->substr(sizeof(T)); | 63 | 39.0k | return ret; | 64 | 39.0k | } |
section const* bloaty::macho::GetStructPointerAndAdvance<section>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 64.6k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 64.6k | const T* ret = GetStructPointer<T>(*data); | 62 | 64.6k | *data = data->substr(sizeof(T)); | 63 | 64.6k | return ret; | 64 | 64.6k | } |
nlist_64 const* bloaty::macho::GetStructPointerAndAdvance<nlist_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 808k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 808k | const T* ret = GetStructPointer<T>(*data); | 62 | 808k | *data = data->substr(sizeof(T)); | 63 | 808k | return ret; | 64 | 808k | } |
nlist const* bloaty::macho::GetStructPointerAndAdvance<nlist>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 378k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 378k | const T* ret = GetStructPointer<T>(*data); | 62 | 378k | *data = data->substr(sizeof(T)); | 63 | 378k | return ret; | 64 | 378k | } |
|
65 | | |
66 | 1.08M | void MaybeAddOverhead(RangeSink* sink, const char* label, string_view data) { |
67 | 1.08M | if (sink) { |
68 | 617k | sink->AddFileRange("macho_overhead", label, data); |
69 | 617k | } |
70 | 1.08M | } |
71 | | |
72 | | struct LoadCommand { |
73 | | bool is64bit; |
74 | | uint32_t cmd; |
75 | | string_view command_data; |
76 | | string_view file_data; |
77 | | }; |
78 | | |
79 | | template <class Struct> |
80 | 213k | bool Is64Bit() { return false; } |
81 | | |
82 | | template <> |
83 | 388k | bool Is64Bit<mach_header_64>() { return true; } |
84 | | |
85 | | template <class Struct, class Func> |
86 | | void ParseMachOHeaderImpl(string_view macho_data, RangeSink* overhead_sink, |
87 | 516k | Func&& loadcmd_func) { |
88 | 516k | string_view header_data = macho_data; |
89 | 516k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); |
90 | 516k | MaybeAddOverhead(overhead_sink, |
91 | 516k | "[Mach-O Headers]", |
92 | 516k | macho_data.substr(0, sizeof(Struct))); |
93 | 516k | uint32_t ncmds = header->ncmds; |
94 | | |
95 | 1.11M | for (uint32_t i = 0; i < ncmds; i++) { |
96 | 603k | auto command = GetStructPointer<load_command>(header_data); |
97 | | |
98 | | // We test for this because otherwise a large ncmds can make bloaty hang for |
99 | | // a while, even on a small file. Hopefully there are no real cases where a |
100 | | // zero-size loadcmd exists. |
101 | 603k | if (command->cmdsize == 0) { |
102 | 846 | THROW("Mach-O load command had zero size."); |
103 | 846 | } |
104 | | |
105 | 602k | LoadCommand data; |
106 | 602k | data.is64bit = Is64Bit<Struct>(); |
107 | 602k | data.cmd = command->cmd; |
108 | 602k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); |
109 | 602k | data.file_data = macho_data; |
110 | 602k | std::forward<Func>(loadcmd_func)(data); |
111 | | |
112 | 602k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); |
113 | 602k | header_data = header_data.substr(command->cmdsize); |
114 | 602k | } |
115 | 516k | } void bloaty::macho::ParseMachOHeaderImpl<mach_header, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}>(std::__1::basic_string_view<char, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}&&) Line | Count | Source | 87 | 66.2k | Func&& loadcmd_func) { | 88 | 66.2k | string_view header_data = macho_data; | 89 | 66.2k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 66.2k | MaybeAddOverhead(overhead_sink, | 91 | 66.2k | "[Mach-O Headers]", | 92 | 66.2k | macho_data.substr(0, sizeof(Struct))); | 93 | 66.2k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 154k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 89.1k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 89.1k | if (command->cmdsize == 0) { | 102 | 486 | THROW("Mach-O load command had zero size."); | 103 | 486 | } | 104 | | | 105 | 88.6k | LoadCommand data; | 106 | 88.6k | data.is64bit = Is64Bit<Struct>(); | 107 | 88.6k | data.cmd = command->cmd; | 108 | 88.6k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 88.6k | data.file_data = macho_data; | 110 | 88.6k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 88.6k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 88.6k | header_data = header_data.substr(command->cmdsize); | 114 | 88.6k | } | 115 | 66.2k | } |
void bloaty::macho::ParseMachOHeaderImpl<mach_header_64, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}>(std::__1::basic_string_view<char, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}&&) Line | Count | Source | 87 | 143k | Func&& loadcmd_func) { | 88 | 143k | string_view header_data = macho_data; | 89 | 143k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 143k | MaybeAddOverhead(overhead_sink, | 91 | 143k | "[Mach-O Headers]", | 92 | 143k | macho_data.substr(0, sizeof(Struct))); | 93 | 143k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 302k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 159k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 159k | if (command->cmdsize == 0) { | 102 | 360 | THROW("Mach-O load command had zero size."); | 103 | 360 | } | 104 | | | 105 | 158k | LoadCommand data; | 106 | 158k | data.is64bit = Is64Bit<Struct>(); | 107 | 158k | data.cmd = command->cmd; | 108 | 158k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 158k | data.file_data = macho_data; | 110 | 158k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 158k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 158k | header_data = header_data.substr(command->cmdsize); | 114 | 158k | } | 115 | 143k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3&&) Line | Count | Source | 87 | 1.63k | Func&& loadcmd_func) { | 88 | 1.63k | string_view header_data = macho_data; | 89 | 1.63k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 1.63k | MaybeAddOverhead(overhead_sink, | 91 | 1.63k | "[Mach-O Headers]", | 92 | 1.63k | macho_data.substr(0, sizeof(Struct))); | 93 | 1.63k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 3.76k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 2.13k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 2.13k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 2.13k | LoadCommand data; | 106 | 2.13k | data.is64bit = Is64Bit<Struct>(); | 107 | 2.13k | data.cmd = command->cmd; | 108 | 2.13k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 2.13k | data.file_data = macho_data; | 110 | 2.13k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 2.13k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 2.13k | header_data = header_data.substr(command->cmdsize); | 114 | 2.13k | } | 115 | 1.63k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header_64, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3&&) Line | Count | Source | 87 | 4.03k | Func&& loadcmd_func) { | 88 | 4.03k | string_view header_data = macho_data; | 89 | 4.03k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 4.03k | MaybeAddOverhead(overhead_sink, | 91 | 4.03k | "[Mach-O Headers]", | 92 | 4.03k | macho_data.substr(0, sizeof(Struct))); | 93 | 4.03k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 8.71k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 4.67k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 4.67k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 4.67k | LoadCommand data; | 106 | 4.67k | data.is64bit = Is64Bit<Struct>(); | 107 | 4.67k | data.cmd = command->cmd; | 108 | 4.67k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 4.67k | data.file_data = macho_data; | 110 | 4.67k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 4.67k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 4.67k | header_data = header_data.substr(command->cmdsize); | 114 | 4.67k | } | 115 | 4.03k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2&&) Line | Count | Source | 87 | 41.3k | Func&& loadcmd_func) { | 88 | 41.3k | string_view header_data = macho_data; | 89 | 41.3k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 41.3k | MaybeAddOverhead(overhead_sink, | 91 | 41.3k | "[Mach-O Headers]", | 92 | 41.3k | macho_data.substr(0, sizeof(Struct))); | 93 | 41.3k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 95.8k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 54.5k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 54.5k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 54.5k | LoadCommand data; | 106 | 54.5k | data.is64bit = Is64Bit<Struct>(); | 107 | 54.5k | data.cmd = command->cmd; | 108 | 54.5k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 54.5k | data.file_data = macho_data; | 110 | 54.5k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 54.5k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 54.5k | header_data = header_data.substr(command->cmdsize); | 114 | 54.5k | } | 115 | 41.3k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header_64, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2&&) Line | Count | Source | 87 | 92.8k | Func&& loadcmd_func) { | 88 | 92.8k | string_view header_data = macho_data; | 89 | 92.8k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 92.8k | MaybeAddOverhead(overhead_sink, | 91 | 92.8k | "[Mach-O Headers]", | 92 | 92.8k | macho_data.substr(0, sizeof(Struct))); | 93 | 92.8k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 193k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 100k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 100k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 100k | LoadCommand data; | 106 | 100k | data.is64bit = Is64Bit<Struct>(); | 107 | 100k | data.cmd = command->cmd; | 108 | 100k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 100k | data.file_data = macho_data; | 110 | 100k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 100k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 100k | header_data = header_data.substr(command->cmdsize); | 114 | 100k | } | 115 | 92.8k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0&&) Line | Count | Source | 87 | 42.1k | Func&& loadcmd_func) { | 88 | 42.1k | string_view header_data = macho_data; | 89 | 42.1k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 42.1k | MaybeAddOverhead(overhead_sink, | 91 | 42.1k | "[Mach-O Headers]", | 92 | 42.1k | macho_data.substr(0, sizeof(Struct))); | 93 | 42.1k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 97.7k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 55.6k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 55.6k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 55.6k | LoadCommand data; | 106 | 55.6k | data.is64bit = Is64Bit<Struct>(); | 107 | 55.6k | data.cmd = command->cmd; | 108 | 55.6k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 55.6k | data.file_data = macho_data; | 110 | 55.6k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 55.6k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 55.6k | header_data = header_data.substr(command->cmdsize); | 114 | 55.6k | } | 115 | 42.1k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header_64, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0&&) Line | Count | Source | 87 | 93.0k | Func&& loadcmd_func) { | 88 | 93.0k | string_view header_data = macho_data; | 89 | 93.0k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 93.0k | MaybeAddOverhead(overhead_sink, | 91 | 93.0k | "[Mach-O Headers]", | 92 | 93.0k | macho_data.substr(0, sizeof(Struct))); | 93 | 93.0k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 194k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 101k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 101k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 101k | LoadCommand data; | 106 | 101k | data.is64bit = Is64Bit<Struct>(); | 107 | 101k | data.cmd = command->cmd; | 108 | 101k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 101k | data.file_data = macho_data; | 110 | 101k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 101k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 101k | header_data = header_data.substr(command->cmdsize); | 114 | 101k | } | 115 | 93.0k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1&&) Line | Count | Source | 87 | 9.62k | Func&& loadcmd_func) { | 88 | 9.62k | string_view header_data = macho_data; | 89 | 9.62k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 9.62k | MaybeAddOverhead(overhead_sink, | 91 | 9.62k | "[Mach-O Headers]", | 92 | 9.62k | macho_data.substr(0, sizeof(Struct))); | 93 | 9.62k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 22.2k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 12.6k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 12.6k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 12.6k | LoadCommand data; | 106 | 12.6k | data.is64bit = Is64Bit<Struct>(); | 107 | 12.6k | data.cmd = command->cmd; | 108 | 12.6k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 12.6k | data.file_data = macho_data; | 110 | 12.6k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 12.6k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 12.6k | header_data = header_data.substr(command->cmdsize); | 114 | 12.6k | } | 115 | 9.62k | } |
macho.cc:void bloaty::macho::ParseMachOHeaderImpl<mach_header_64, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1&&) Line | Count | Source | 87 | 21.7k | Func&& loadcmd_func) { | 88 | 21.7k | string_view header_data = macho_data; | 89 | 21.7k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 21.7k | MaybeAddOverhead(overhead_sink, | 91 | 21.7k | "[Mach-O Headers]", | 92 | 21.7k | macho_data.substr(0, sizeof(Struct))); | 93 | 21.7k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 45.1k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 23.4k | auto command = GetStructPointer<load_command>(header_data); | 97 | | | 98 | | // We test for this because otherwise a large ncmds can make bloaty hang for | 99 | | // a while, even on a small file. Hopefully there are no real cases where a | 100 | | // zero-size loadcmd exists. | 101 | 23.4k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 23.4k | LoadCommand data; | 106 | 23.4k | data.is64bit = Is64Bit<Struct>(); | 107 | 23.4k | data.cmd = command->cmd; | 108 | 23.4k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 23.4k | data.file_data = macho_data; | 110 | 23.4k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 23.4k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 23.4k | header_data = header_data.substr(command->cmdsize); | 114 | 23.4k | } | 115 | 21.7k | } |
|
116 | | |
117 | | template <class Func> |
118 | | void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, |
119 | 516k | Func&& loadcmd_func) { |
120 | 516k | uint32_t magic = ReadMagic(macho_file); |
121 | 516k | switch (magic) { |
122 | 160k | case MH_MAGIC: |
123 | | // We don't expect to see many 32-bit binaries out in the wild. |
124 | | // Apple is aggressively phasing out support for 32-bit binaries: |
125 | | // https://www.macrumors.com/2017/06/06/apple-to-phase-out-32-bit-mac-apps/ |
126 | | // |
127 | | // Still, you can build 32-bit binaries as of this writing, and |
128 | | // there are existing 32-bit binaries floating around, so we might |
129 | | // as well support them. |
130 | 160k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, |
131 | 160k | std::forward<Func>(loadcmd_func)); |
132 | 160k | break; |
133 | 355k | case MH_MAGIC_64: |
134 | 355k | ParseMachOHeaderImpl<mach_header_64>( |
135 | 355k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); |
136 | 355k | break; |
137 | 6 | case MH_CIGAM: |
138 | 6 | case MH_CIGAM_64: |
139 | | // OS X and Darwin currently only run on x86/x86-64 (little-endian |
140 | | // platforms), so we expect basically all Mach-O files to be |
141 | | // little-endian. Additionally, pretty much all CPU architectures |
142 | | // are little-endian these days. ARM has the option to be |
143 | | // big-endian, but I can't find any OS that is actually compiled to |
144 | | // use big-endian mode. debian-mips is the only big-endian OS I can |
145 | | // find (and maybe SPARC). |
146 | | // |
147 | | // All of this is to say, this case should only happen if you are |
148 | | // running Bloaty on debian-mips. I consider that uncommon enough |
149 | | // (and hard enough to test) that we don't support this until there |
150 | | // is a demonstrated need. |
151 | 6 | THROW("We don't support cross-endian Mach-O files."); |
152 | 120 | default: |
153 | 120 | THROW("Corrupt Mach-O file"); |
154 | 516k | } |
155 | 516k | } void bloaty::macho::ParseMachOHeader<bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}>(std::__1::basic_string_view<char, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}&&) Line | Count | Source | 119 | 210k | Func&& loadcmd_func) { | 120 | 210k | uint32_t magic = ReadMagic(macho_file); | 121 | 210k | switch (magic) { | 122 | 66.2k | case MH_MAGIC: | 123 | | // We don't expect to see many 32-bit binaries out in the wild. | 124 | | // Apple is aggressively phasing out support for 32-bit binaries: | 125 | | // https://www.macrumors.com/2017/06/06/apple-to-phase-out-32-bit-mac-apps/ | 126 | | // | 127 | | // Still, you can build 32-bit binaries as of this writing, and | 128 | | // there are existing 32-bit binaries floating around, so we might | 129 | | // as well support them. | 130 | 66.2k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 66.2k | std::forward<Func>(loadcmd_func)); | 132 | 66.2k | break; | 133 | 143k | case MH_MAGIC_64: | 134 | 143k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 143k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 143k | break; | 137 | 6 | case MH_CIGAM: | 138 | 6 | case MH_CIGAM_64: | 139 | | // OS X and Darwin currently only run on x86/x86-64 (little-endian | 140 | | // platforms), so we expect basically all Mach-O files to be | 141 | | // little-endian. Additionally, pretty much all CPU architectures | 142 | | // are little-endian these days. ARM has the option to be | 143 | | // big-endian, but I can't find any OS that is actually compiled to | 144 | | // use big-endian mode. debian-mips is the only big-endian OS I can | 145 | | // find (and maybe SPARC). | 146 | | // | 147 | | // All of this is to say, this case should only happen if you are | 148 | | // running Bloaty on debian-mips. I consider that uncommon enough | 149 | | // (and hard enough to test) that we don't support this until there | 150 | | // is a demonstrated need. | 151 | 6 | THROW("We don't support cross-endian Mach-O files."); | 152 | 120 | default: | 153 | 120 | THROW("Corrupt Mach-O file"); | 154 | 210k | } | 155 | 210k | } |
macho.cc:void bloaty::macho::ParseMachOHeader<bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3&&) Line | Count | Source | 119 | 5.67k | Func&& loadcmd_func) { | 120 | 5.67k | uint32_t magic = ReadMagic(macho_file); | 121 | 5.67k | switch (magic) { | 122 | 1.63k | case MH_MAGIC: | 123 | | // We don't expect to see many 32-bit binaries out in the wild. | 124 | | // Apple is aggressively phasing out support for 32-bit binaries: | 125 | | // https://www.macrumors.com/2017/06/06/apple-to-phase-out-32-bit-mac-apps/ | 126 | | // | 127 | | // Still, you can build 32-bit binaries as of this writing, and | 128 | | // there are existing 32-bit binaries floating around, so we might | 129 | | // as well support them. | 130 | 1.63k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 1.63k | std::forward<Func>(loadcmd_func)); | 132 | 1.63k | break; | 133 | 4.03k | case MH_MAGIC_64: | 134 | 4.03k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 4.03k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 4.03k | break; | 137 | 0 | case MH_CIGAM: | 138 | 0 | case MH_CIGAM_64: | 139 | | // OS X and Darwin currently only run on x86/x86-64 (little-endian | 140 | | // platforms), so we expect basically all Mach-O files to be | 141 | | // little-endian. Additionally, pretty much all CPU architectures | 142 | | // are little-endian these days. ARM has the option to be | 143 | | // big-endian, but I can't find any OS that is actually compiled to | 144 | | // use big-endian mode. debian-mips is the only big-endian OS I can | 145 | | // find (and maybe SPARC). | 146 | | // | 147 | | // All of this is to say, this case should only happen if you are | 148 | | // running Bloaty on debian-mips. I consider that uncommon enough | 149 | | // (and hard enough to test) that we don't support this until there | 150 | | // is a demonstrated need. | 151 | 0 | THROW("We don't support cross-endian Mach-O files."); | 152 | 0 | default: | 153 | 0 | THROW("Corrupt Mach-O file"); | 154 | 5.67k | } | 155 | 5.67k | } |
macho.cc:void bloaty::macho::ParseMachOHeader<bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2&&) Line | Count | Source | 119 | 134k | Func&& loadcmd_func) { | 120 | 134k | uint32_t magic = ReadMagic(macho_file); | 121 | 134k | switch (magic) { | 122 | 41.3k | case MH_MAGIC: | 123 | | // We don't expect to see many 32-bit binaries out in the wild. | 124 | | // Apple is aggressively phasing out support for 32-bit binaries: | 125 | | // https://www.macrumors.com/2017/06/06/apple-to-phase-out-32-bit-mac-apps/ | 126 | | // | 127 | | // Still, you can build 32-bit binaries as of this writing, and | 128 | | // there are existing 32-bit binaries floating around, so we might | 129 | | // as well support them. | 130 | 41.3k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 41.3k | std::forward<Func>(loadcmd_func)); | 132 | 41.3k | break; | 133 | 92.8k | case MH_MAGIC_64: | 134 | 92.8k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 92.8k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 92.8k | break; | 137 | 0 | case MH_CIGAM: | 138 | 0 | case MH_CIGAM_64: | 139 | | // OS X and Darwin currently only run on x86/x86-64 (little-endian | 140 | | // platforms), so we expect basically all Mach-O files to be | 141 | | // little-endian. Additionally, pretty much all CPU architectures | 142 | | // are little-endian these days. ARM has the option to be | 143 | | // big-endian, but I can't find any OS that is actually compiled to | 144 | | // use big-endian mode. debian-mips is the only big-endian OS I can | 145 | | // find (and maybe SPARC). | 146 | | // | 147 | | // All of this is to say, this case should only happen if you are | 148 | | // running Bloaty on debian-mips. I consider that uncommon enough | 149 | | // (and hard enough to test) that we don't support this until there | 150 | | // is a demonstrated need. | 151 | 0 | THROW("We don't support cross-endian Mach-O files."); | 152 | 0 | default: | 153 | 0 | THROW("Corrupt Mach-O file"); | 154 | 134k | } | 155 | 134k | } |
macho.cc:void bloaty::macho::ParseMachOHeader<bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0&&) Line | Count | Source | 119 | 135k | Func&& loadcmd_func) { | 120 | 135k | uint32_t magic = ReadMagic(macho_file); | 121 | 135k | switch (magic) { | 122 | 42.1k | case MH_MAGIC: | 123 | | // We don't expect to see many 32-bit binaries out in the wild. | 124 | | // Apple is aggressively phasing out support for 32-bit binaries: | 125 | | // https://www.macrumors.com/2017/06/06/apple-to-phase-out-32-bit-mac-apps/ | 126 | | // | 127 | | // Still, you can build 32-bit binaries as of this writing, and | 128 | | // there are existing 32-bit binaries floating around, so we might | 129 | | // as well support them. | 130 | 42.1k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 42.1k | std::forward<Func>(loadcmd_func)); | 132 | 42.1k | break; | 133 | 93.0k | case MH_MAGIC_64: | 134 | 93.0k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 93.0k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 93.0k | break; | 137 | 0 | case MH_CIGAM: | 138 | 0 | case MH_CIGAM_64: | 139 | | // OS X and Darwin currently only run on x86/x86-64 (little-endian | 140 | | // platforms), so we expect basically all Mach-O files to be | 141 | | // little-endian. Additionally, pretty much all CPU architectures | 142 | | // are little-endian these days. ARM has the option to be | 143 | | // big-endian, but I can't find any OS that is actually compiled to | 144 | | // use big-endian mode. debian-mips is the only big-endian OS I can | 145 | | // find (and maybe SPARC). | 146 | | // | 147 | | // All of this is to say, this case should only happen if you are | 148 | | // running Bloaty on debian-mips. I consider that uncommon enough | 149 | | // (and hard enough to test) that we don't support this until there | 150 | | // is a demonstrated need. | 151 | 0 | THROW("We don't support cross-endian Mach-O files."); | 152 | 0 | default: | 153 | 0 | THROW("Corrupt Mach-O file"); | 154 | 135k | } | 155 | 135k | } |
macho.cc:void bloaty::macho::ParseMachOHeader<bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1&&) Line | Count | Source | 119 | 31.4k | Func&& loadcmd_func) { | 120 | 31.4k | uint32_t magic = ReadMagic(macho_file); | 121 | 31.4k | switch (magic) { | 122 | 9.62k | case MH_MAGIC: | 123 | | // We don't expect to see many 32-bit binaries out in the wild. | 124 | | // Apple is aggressively phasing out support for 32-bit binaries: | 125 | | // https://www.macrumors.com/2017/06/06/apple-to-phase-out-32-bit-mac-apps/ | 126 | | // | 127 | | // Still, you can build 32-bit binaries as of this writing, and | 128 | | // there are existing 32-bit binaries floating around, so we might | 129 | | // as well support them. | 130 | 9.62k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 9.62k | std::forward<Func>(loadcmd_func)); | 132 | 9.62k | break; | 133 | 21.7k | case MH_MAGIC_64: | 134 | 21.7k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 21.7k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 21.7k | break; | 137 | 0 | case MH_CIGAM: | 138 | 0 | case MH_CIGAM_64: | 139 | | // OS X and Darwin currently only run on x86/x86-64 (little-endian | 140 | | // platforms), so we expect basically all Mach-O files to be | 141 | | // little-endian. Additionally, pretty much all CPU architectures | 142 | | // are little-endian these days. ARM has the option to be | 143 | | // big-endian, but I can't find any OS that is actually compiled to | 144 | | // use big-endian mode. debian-mips is the only big-endian OS I can | 145 | | // find (and maybe SPARC). | 146 | | // | 147 | | // All of this is to say, this case should only happen if you are | 148 | | // running Bloaty on debian-mips. I consider that uncommon enough | 149 | | // (and hard enough to test) that we don't support this until there | 150 | | // is a demonstrated need. | 151 | 0 | THROW("We don't support cross-endian Mach-O files."); | 152 | 0 | default: | 153 | 0 | THROW("Corrupt Mach-O file"); | 154 | 31.4k | } | 155 | 31.4k | } |
|
156 | | |
157 | | template <class Func> |
158 | | void ParseFatHeader(string_view fat_file, RangeSink* overhead_sink, |
159 | 1.23k | Func&& loadcmd_func) { |
160 | 1.23k | string_view header_data = fat_file; |
161 | 1.23k | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); |
162 | 1.23k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", |
163 | 1.23k | fat_file.substr(0, sizeof(fat_header))); |
164 | 1.23k | assert(ByteSwap(header->magic) == FAT_MAGIC); |
165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); |
166 | 1.74k | for (uint32_t i = 0; i < nfat_arch; i++) { |
167 | 510 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); |
168 | 510 | string_view macho_data = StrictSubstr( |
169 | 510 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); |
170 | 510 | ParseMachOHeader(macho_data, overhead_sink, |
171 | 510 | std::forward<Func>(loadcmd_func)); |
172 | 510 | } |
173 | 1.23k | } void bloaty::macho::ParseFatHeader<bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}>(std::__1::basic_string_view<char, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}&&) Line | Count | Source | 159 | 774 | Func&& loadcmd_func) { | 160 | 774 | string_view header_data = fat_file; | 161 | 774 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 774 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 774 | fat_file.substr(0, sizeof(fat_header))); | 164 | 774 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 1.28k | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 510 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 510 | string_view macho_data = StrictSubstr( | 169 | 510 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 510 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 510 | std::forward<Func>(loadcmd_func)); | 172 | 510 | } | 173 | 774 | } |
macho.cc:void bloaty::macho::ParseFatHeader<bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3&&) Line | Count | Source | 159 | 23 | Func&& loadcmd_func) { | 160 | 23 | string_view header_data = fat_file; | 161 | 23 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 23 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 23 | fat_file.substr(0, sizeof(fat_header))); | 164 | 23 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 23 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 0 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 0 | string_view macho_data = StrictSubstr( | 169 | 0 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 0 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 0 | std::forward<Func>(loadcmd_func)); | 172 | 0 | } | 173 | 23 | } |
macho.cc:void bloaty::macho::ParseFatHeader<bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2&&) Line | Count | Source | 159 | 207 | Func&& loadcmd_func) { | 160 | 207 | string_view header_data = fat_file; | 161 | 207 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 207 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 207 | fat_file.substr(0, sizeof(fat_header))); | 164 | 207 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 207 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 0 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 0 | string_view macho_data = StrictSubstr( | 169 | 0 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 0 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 0 | std::forward<Func>(loadcmd_func)); | 172 | 0 | } | 173 | 207 | } |
macho.cc:void bloaty::macho::ParseFatHeader<bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0&&) Line | Count | Source | 159 | 184 | Func&& loadcmd_func) { | 160 | 184 | string_view header_data = fat_file; | 161 | 184 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 184 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 184 | fat_file.substr(0, sizeof(fat_header))); | 164 | 184 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 184 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 0 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 0 | string_view macho_data = StrictSubstr( | 169 | 0 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 0 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 0 | std::forward<Func>(loadcmd_func)); | 172 | 0 | } | 173 | 184 | } |
macho.cc:void bloaty::macho::ParseFatHeader<bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1&&) Line | Count | Source | 159 | 46 | Func&& loadcmd_func) { | 160 | 46 | string_view header_data = fat_file; | 161 | 46 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 46 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 46 | fat_file.substr(0, sizeof(fat_header))); | 164 | 46 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 46 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 0 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 0 | string_view macho_data = StrictSubstr( | 169 | 0 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 0 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 0 | std::forward<Func>(loadcmd_func)); | 172 | 0 | } | 173 | 46 | } |
|
174 | | |
175 | | template <class Func> |
176 | | void ForEachLoadCommand(string_view maybe_fat_file, RangeSink* overhead_sink, |
177 | 517k | Func&& loadcmd_func) { |
178 | 517k | uint32_t magic = ReadMagic(maybe_fat_file); |
179 | 517k | switch (magic) { |
180 | 160k | case MH_MAGIC: |
181 | 516k | case MH_MAGIC_64: |
182 | 516k | case MH_CIGAM: |
183 | 516k | case MH_CIGAM_64: |
184 | 516k | ParseMachOHeader(maybe_fat_file, overhead_sink, |
185 | 516k | std::forward<Func>(loadcmd_func)); |
186 | 516k | break; |
187 | 1.23k | case FAT_CIGAM: |
188 | 1.23k | ParseFatHeader(maybe_fat_file, overhead_sink, |
189 | 1.23k | std::forward<Func>(loadcmd_func)); |
190 | 1.23k | break; |
191 | 517k | } |
192 | 517k | } void bloaty::macho::ForEachLoadCommand<bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}>(std::__1::basic_string_view<char, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::MachOObjectFile::GetBuildId() const::{lambda(bloaty::macho::LoadCommand)#1}&&) Line | Count | Source | 177 | 210k | Func&& loadcmd_func) { | 178 | 210k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 210k | switch (magic) { | 180 | 66.2k | case MH_MAGIC: | 181 | 210k | case MH_MAGIC_64: | 182 | 210k | case MH_CIGAM: | 183 | 210k | case MH_CIGAM_64: | 184 | 210k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 210k | std::forward<Func>(loadcmd_func)); | 186 | 210k | break; | 187 | 774 | case FAT_CIGAM: | 188 | 774 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 774 | std::forward<Func>(loadcmd_func)); | 190 | 774 | break; | 191 | 210k | } | 192 | 210k | } |
macho.cc:void bloaty::macho::ForEachLoadCommand<bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ReadDebugSectionsFromMachO(bloaty::InputFile const&, bloaty::dwarf::File*, bloaty::RangeSink*)::$_3&&) Line | Count | Source | 177 | 5.69k | Func&& loadcmd_func) { | 178 | 5.69k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 5.69k | switch (magic) { | 180 | 1.63k | case MH_MAGIC: | 181 | 5.67k | case MH_MAGIC_64: | 182 | 5.67k | case MH_CIGAM: | 183 | 5.67k | case MH_CIGAM_64: | 184 | 5.67k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 5.67k | std::forward<Func>(loadcmd_func)); | 186 | 5.67k | break; | 187 | 23 | case FAT_CIGAM: | 188 | 23 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 23 | std::forward<Func>(loadcmd_func)); | 190 | 23 | break; | 191 | 5.69k | } | 192 | 5.69k | } |
macho.cc:void bloaty::macho::ForEachLoadCommand<bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::AddMachOFallback(bloaty::RangeSink*)::$_2&&) Line | Count | Source | 177 | 134k | Func&& loadcmd_func) { | 178 | 134k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 134k | switch (magic) { | 180 | 41.3k | case MH_MAGIC: | 181 | 134k | case MH_MAGIC_64: | 182 | 134k | case MH_CIGAM: | 183 | 134k | case MH_CIGAM_64: | 184 | 134k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 134k | std::forward<Func>(loadcmd_func)); | 186 | 134k | break; | 187 | 207 | case FAT_CIGAM: | 188 | 207 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 207 | std::forward<Func>(loadcmd_func)); | 190 | 207 | break; | 191 | 134k | } | 192 | 134k | } |
macho.cc:void bloaty::macho::ForEachLoadCommand<bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseLoadCommands(bloaty::RangeSink*)::$_0&&) Line | Count | Source | 177 | 135k | Func&& loadcmd_func) { | 178 | 135k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 135k | switch (magic) { | 180 | 42.1k | case MH_MAGIC: | 181 | 135k | case MH_MAGIC_64: | 182 | 135k | case MH_CIGAM: | 183 | 135k | case MH_CIGAM_64: | 184 | 135k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 135k | std::forward<Func>(loadcmd_func)); | 186 | 135k | break; | 187 | 184 | case FAT_CIGAM: | 188 | 184 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 184 | std::forward<Func>(loadcmd_func)); | 190 | 184 | break; | 191 | 135k | } | 192 | 135k | } |
macho.cc:void bloaty::macho::ForEachLoadCommand<bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*, bloaty::macho::ParseSymbols(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*)::$_1&&) Line | Count | Source | 177 | 31.4k | Func&& loadcmd_func) { | 178 | 31.4k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 31.4k | switch (magic) { | 180 | 9.62k | case MH_MAGIC: | 181 | 31.4k | case MH_MAGIC_64: | 182 | 31.4k | case MH_CIGAM: | 183 | 31.4k | case MH_CIGAM_64: | 184 | 31.4k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 31.4k | std::forward<Func>(loadcmd_func)); | 186 | 31.4k | break; | 187 | 46 | case FAT_CIGAM: | 188 | 46 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 46 | std::forward<Func>(loadcmd_func)); | 190 | 46 | break; | 191 | 31.4k | } | 192 | 31.4k | } |
|
193 | | |
194 | | template <class Segment, class Section> |
195 | | void AddSegmentAsFallback(string_view command_data, string_view file_data, |
196 | 30.7k | RangeSink* sink) { |
197 | 30.7k | auto segment = GetStructPointerAndAdvance<Segment>(&command_data); |
198 | | |
199 | 30.7k | if (segment->maxprot == VM_PROT_NONE) { |
200 | 19.4k | return; |
201 | 19.4k | } |
202 | | |
203 | 11.2k | string_view segname = ArrayToStr(segment->segname, 16); |
204 | | |
205 | 11.2k | uint32_t nsects = segment->nsects; |
206 | 67.1k | for (uint32_t j = 0; j < nsects; j++) { |
207 | 56.8k | auto section = GetStructPointerAndAdvance<Section>(&command_data); |
208 | | |
209 | | // filesize equals vmsize unless the section is zerofill |
210 | 56.8k | uint64_t filesize = section->size; |
211 | 56.8k | switch (section->flags & SECTION_TYPE) { |
212 | 2.81k | case S_ZEROFILL: |
213 | 4.77k | case S_GB_ZEROFILL: |
214 | 6.34k | case S_THREAD_LOCAL_ZEROFILL: |
215 | 6.34k | filesize = 0; |
216 | 6.34k | break; |
217 | 49.5k | default: |
218 | 49.5k | break; |
219 | 56.8k | } |
220 | | |
221 | 55.9k | std::string label = absl::StrJoin( |
222 | 55.9k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); |
223 | 55.9k | label = "[" + label + "]"; |
224 | 55.9k | sink->AddRange("macho_fallback", label, section->addr, section->size, |
225 | 55.9k | StrictSubstr(file_data, section->offset, filesize)); |
226 | 55.9k | } |
227 | | |
228 | 10.2k | sink->AddRange("macho_fallback", "[" + std::string(segname) + "]", |
229 | 10.2k | segment->vmaddr, segment->vmsize, |
230 | 10.2k | StrictSubstr(file_data, segment->fileoff, segment->filesize)); |
231 | 10.2k | } void bloaty::macho::AddSegmentAsFallback<segment_command_64, section_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*) Line | Count | Source | 196 | 12.1k | RangeSink* sink) { | 197 | 12.1k | auto segment = GetStructPointerAndAdvance<Segment>(&command_data); | 198 | | | 199 | 12.1k | if (segment->maxprot == VM_PROT_NONE) { | 200 | 7.96k | return; | 201 | 7.96k | } | 202 | | | 203 | 4.22k | string_view segname = ArrayToStr(segment->segname, 16); | 204 | | | 205 | 4.22k | uint32_t nsects = segment->nsects; | 206 | 19.3k | for (uint32_t j = 0; j < nsects; j++) { | 207 | 15.6k | auto section = GetStructPointerAndAdvance<Section>(&command_data); | 208 | | | 209 | | // filesize equals vmsize unless the section is zerofill | 210 | 15.6k | uint64_t filesize = section->size; | 211 | 15.6k | switch (section->flags & SECTION_TYPE) { | 212 | 1.30k | case S_ZEROFILL: | 213 | 2.02k | case S_GB_ZEROFILL: | 214 | 2.77k | case S_THREAD_LOCAL_ZEROFILL: | 215 | 2.77k | filesize = 0; | 216 | 2.77k | break; | 217 | 12.3k | default: | 218 | 12.3k | break; | 219 | 15.6k | } | 220 | | | 221 | 15.1k | std::string label = absl::StrJoin( | 222 | 15.1k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 223 | 15.1k | label = "[" + label + "]"; | 224 | 15.1k | sink->AddRange("macho_fallback", label, section->addr, section->size, | 225 | 15.1k | StrictSubstr(file_data, section->offset, filesize)); | 226 | 15.1k | } | 227 | | | 228 | 3.78k | sink->AddRange("macho_fallback", "[" + std::string(segname) + "]", | 229 | 3.78k | segment->vmaddr, segment->vmsize, | 230 | 3.78k | StrictSubstr(file_data, segment->fileoff, segment->filesize)); | 231 | 3.78k | } |
void bloaty::macho::AddSegmentAsFallback<segment_command, section>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, bloaty::RangeSink*) Line | Count | Source | 196 | 18.5k | RangeSink* sink) { | 197 | 18.5k | auto segment = GetStructPointerAndAdvance<Segment>(&command_data); | 198 | | | 199 | 18.5k | if (segment->maxprot == VM_PROT_NONE) { | 200 | 11.5k | return; | 201 | 11.5k | } | 202 | | | 203 | 7.01k | string_view segname = ArrayToStr(segment->segname, 16); | 204 | | | 205 | 7.01k | uint32_t nsects = segment->nsects; | 206 | 47.7k | for (uint32_t j = 0; j < nsects; j++) { | 207 | 41.2k | auto section = GetStructPointerAndAdvance<Section>(&command_data); | 208 | | | 209 | | // filesize equals vmsize unless the section is zerofill | 210 | 41.2k | uint64_t filesize = section->size; | 211 | 41.2k | switch (section->flags & SECTION_TYPE) { | 212 | 1.51k | case S_ZEROFILL: | 213 | 2.75k | case S_GB_ZEROFILL: | 214 | 3.56k | case S_THREAD_LOCAL_ZEROFILL: | 215 | 3.56k | filesize = 0; | 216 | 3.56k | break; | 217 | 37.1k | default: | 218 | 37.1k | break; | 219 | 41.2k | } | 220 | | | 221 | 40.7k | std::string label = absl::StrJoin( | 222 | 40.7k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 223 | 40.7k | label = "[" + label + "]"; | 224 | 40.7k | sink->AddRange("macho_fallback", label, section->addr, section->size, | 225 | 40.7k | StrictSubstr(file_data, section->offset, filesize)); | 226 | 40.7k | } | 227 | | | 228 | 6.49k | sink->AddRange("macho_fallback", "[" + std::string(segname) + "]", | 229 | 6.49k | segment->vmaddr, segment->vmsize, | 230 | 6.49k | StrictSubstr(file_data, segment->fileoff, segment->filesize)); | 231 | 6.49k | } |
|
232 | | |
233 | | template <class Segment, class Section> |
234 | 31.4k | void ParseSegment(LoadCommand cmd, RangeSink* sink) { |
235 | 31.4k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); |
236 | 31.4k | string_view segname = ArrayToStr(segment->segname, 16); |
237 | | |
238 | | // For unknown reasons, some load commands will have maxprot = NONE |
239 | | // indicating they are not accessible, but will also contain a vmaddr |
240 | | // and vmsize. In practice the vmaddr/vmsize of a section sometimes |
241 | | // fall within the segment, but sometimes exceed it, leading to an |
242 | | // error about exceeding the base map. |
243 | | // |
244 | | // Since such segments should not be mapped, we simply ignore the |
245 | | // vmaddr/vmsize of such segments. |
246 | 31.4k | bool unmapped = segment->maxprot == VM_PROT_NONE; |
247 | | |
248 | 31.4k | if (sink->data_source() == DataSource::kSegments) { |
249 | 27.9k | if (unmapped) { |
250 | 17.2k | sink->AddFileRange( |
251 | 17.2k | "macho_segment", segname, |
252 | 17.2k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); |
253 | 17.2k | } else { |
254 | 10.6k | sink->AddRange( |
255 | 10.6k | "macho_segment", segname, segment->vmaddr, segment->vmsize, |
256 | 10.6k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); |
257 | 10.6k | } |
258 | 27.9k | } else if (sink->data_source() == DataSource::kSections) { |
259 | 3.04k | uint32_t nsects = segment->nsects; |
260 | 23.9k | for (uint32_t j = 0; j < nsects; j++) { |
261 | 21.5k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); |
262 | | |
263 | | // filesize equals vmsize unless the section is zerofill |
264 | 21.5k | uint64_t filesize = section->size; |
265 | 21.5k | switch (section->flags & SECTION_TYPE) { |
266 | 861 | case S_ZEROFILL: |
267 | 1.47k | case S_GB_ZEROFILL: |
268 | 1.99k | case S_THREAD_LOCAL_ZEROFILL: |
269 | 1.99k | filesize = 0; |
270 | 1.99k | break; |
271 | 18.9k | default: |
272 | 18.9k | break; |
273 | 21.5k | } |
274 | | |
275 | 20.9k | std::string label = absl::StrJoin( |
276 | 20.9k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); |
277 | 20.9k | if (unmapped) { |
278 | 19.0k | sink->AddFileRange( |
279 | 19.0k | "macho_section", label, |
280 | 19.0k | StrictSubstr(cmd.file_data, section->offset, filesize)); |
281 | 19.0k | } else { |
282 | 1.88k | sink->AddRange("macho_section", label, section->addr, section->size, |
283 | 1.88k | StrictSubstr(cmd.file_data, section->offset, filesize)); |
284 | 1.88k | } |
285 | 20.9k | } |
286 | 3.04k | } else { |
287 | 396 | BLOATY_UNREACHABLE(); |
288 | 396 | } |
289 | 31.4k | } void bloaty::macho::ParseSegment<segment_command_64, section_64>(bloaty::macho::LoadCommand, bloaty::RangeSink*) Line | Count | Source | 234 | 12.6k | void ParseSegment(LoadCommand cmd, RangeSink* sink) { | 235 | 12.6k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 236 | 12.6k | string_view segname = ArrayToStr(segment->segname, 16); | 237 | | | 238 | | // For unknown reasons, some load commands will have maxprot = NONE | 239 | | // indicating they are not accessible, but will also contain a vmaddr | 240 | | // and vmsize. In practice the vmaddr/vmsize of a section sometimes | 241 | | // fall within the segment, but sometimes exceed it, leading to an | 242 | | // error about exceeding the base map. | 243 | | // | 244 | | // Since such segments should not be mapped, we simply ignore the | 245 | | // vmaddr/vmsize of such segments. | 246 | 12.6k | bool unmapped = segment->maxprot == VM_PROT_NONE; | 247 | | | 248 | 12.6k | if (sink->data_source() == DataSource::kSegments) { | 249 | 11.3k | if (unmapped) { | 250 | 7.13k | sink->AddFileRange( | 251 | 7.13k | "macho_segment", segname, | 252 | 7.13k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 253 | 7.13k | } else { | 254 | 4.22k | sink->AddRange( | 255 | 4.22k | "macho_segment", segname, segment->vmaddr, segment->vmsize, | 256 | 4.22k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 257 | 4.22k | } | 258 | 11.3k | } else if (sink->data_source() == DataSource::kSections) { | 259 | 1.19k | uint32_t nsects = segment->nsects; | 260 | 7.60k | for (uint32_t j = 0; j < nsects; j++) { | 261 | 6.70k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 262 | | | 263 | | // filesize equals vmsize unless the section is zerofill | 264 | 6.70k | uint64_t filesize = section->size; | 265 | 6.70k | switch (section->flags & SECTION_TYPE) { | 266 | 333 | case S_ZEROFILL: | 267 | 606 | case S_GB_ZEROFILL: | 268 | 808 | case S_THREAD_LOCAL_ZEROFILL: | 269 | 808 | filesize = 0; | 270 | 808 | break; | 271 | 5.60k | default: | 272 | 5.60k | break; | 273 | 6.70k | } | 274 | | | 275 | 6.41k | std::string label = absl::StrJoin( | 276 | 6.41k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 277 | 6.41k | if (unmapped) { | 278 | 5.87k | sink->AddFileRange( | 279 | 5.87k | "macho_section", label, | 280 | 5.87k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 281 | 5.87k | } else { | 282 | 541 | sink->AddRange("macho_section", label, section->addr, section->size, | 283 | 541 | StrictSubstr(cmd.file_data, section->offset, filesize)); | 284 | 541 | } | 285 | 6.41k | } | 286 | 1.19k | } else { | 287 | 102 | BLOATY_UNREACHABLE(); | 288 | 102 | } | 289 | 12.6k | } |
void bloaty::macho::ParseSegment<segment_command, section>(bloaty::macho::LoadCommand, bloaty::RangeSink*) Line | Count | Source | 234 | 18.7k | void ParseSegment(LoadCommand cmd, RangeSink* sink) { | 235 | 18.7k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 236 | 18.7k | string_view segname = ArrayToStr(segment->segname, 16); | 237 | | | 238 | | // For unknown reasons, some load commands will have maxprot = NONE | 239 | | // indicating they are not accessible, but will also contain a vmaddr | 240 | | // and vmsize. In practice the vmaddr/vmsize of a section sometimes | 241 | | // fall within the segment, but sometimes exceed it, leading to an | 242 | | // error about exceeding the base map. | 243 | | // | 244 | | // Since such segments should not be mapped, we simply ignore the | 245 | | // vmaddr/vmsize of such segments. | 246 | 18.7k | bool unmapped = segment->maxprot == VM_PROT_NONE; | 247 | | | 248 | 18.7k | if (sink->data_source() == DataSource::kSegments) { | 249 | 16.5k | if (unmapped) { | 250 | 10.1k | sink->AddFileRange( | 251 | 10.1k | "macho_segment", segname, | 252 | 10.1k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 253 | 10.1k | } else { | 254 | 6.46k | sink->AddRange( | 255 | 6.46k | "macho_segment", segname, segment->vmaddr, segment->vmsize, | 256 | 6.46k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 257 | 6.46k | } | 258 | 16.5k | } else if (sink->data_source() == DataSource::kSections) { | 259 | 1.85k | uint32_t nsects = segment->nsects; | 260 | 16.3k | for (uint32_t j = 0; j < nsects; j++) { | 261 | 14.8k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 262 | | | 263 | | // filesize equals vmsize unless the section is zerofill | 264 | 14.8k | uint64_t filesize = section->size; | 265 | 14.8k | switch (section->flags & SECTION_TYPE) { | 266 | 528 | case S_ZEROFILL: | 267 | 873 | case S_GB_ZEROFILL: | 268 | 1.18k | case S_THREAD_LOCAL_ZEROFILL: | 269 | 1.18k | filesize = 0; | 270 | 1.18k | break; | 271 | 13.3k | default: | 272 | 13.3k | break; | 273 | 14.8k | } | 274 | | | 275 | 14.5k | std::string label = absl::StrJoin( | 276 | 14.5k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 277 | 14.5k | if (unmapped) { | 278 | 13.1k | sink->AddFileRange( | 279 | 13.1k | "macho_section", label, | 280 | 13.1k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 281 | 13.1k | } else { | 282 | 1.34k | sink->AddRange("macho_section", label, section->addr, section->size, | 283 | 1.34k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 284 | 1.34k | } | 285 | 14.5k | } | 286 | 1.85k | } else { | 287 | 294 | BLOATY_UNREACHABLE(); | 288 | 294 | } | 289 | 18.7k | } |
|
290 | | |
291 | 1.85k | static void ParseDyldInfo(const LoadCommand& cmd, RangeSink* sink) { |
292 | 1.85k | auto info = GetStructPointer<dyld_info_command>(cmd.command_data); |
293 | | |
294 | 1.85k | sink->AddFileRange( |
295 | 1.85k | "macho_dyld", "Rebase Info", |
296 | 1.85k | StrictSubstr(cmd.file_data, info->rebase_off, info->rebase_size)); |
297 | 1.85k | sink->AddFileRange( |
298 | 1.85k | "macho_dyld", "Binding Info", |
299 | 1.85k | StrictSubstr(cmd.file_data, info->bind_off, info->bind_size)); |
300 | 1.85k | sink->AddFileRange( |
301 | 1.85k | "macho_dyld", "Weak Binding Info", |
302 | 1.85k | StrictSubstr(cmd.file_data, info->weak_bind_off, info->weak_bind_size)); |
303 | 1.85k | sink->AddFileRange( |
304 | 1.85k | "macho_dyld", "Lazy Binding Info", |
305 | 1.85k | StrictSubstr(cmd.file_data, info->lazy_bind_off, info->lazy_bind_size)); |
306 | 1.85k | sink->AddFileRange( |
307 | 1.85k | "macho_dyld", "Export Info", |
308 | 1.85k | StrictSubstr(cmd.file_data, info->export_off, info->export_size)); |
309 | 1.85k | } |
310 | | |
311 | 100k | static void ParseSymbolTable(const LoadCommand& cmd, RangeSink* sink) { |
312 | 100k | auto symtab = GetStructPointer<symtab_command>(cmd.command_data); |
313 | | |
314 | 100k | size_t size = cmd.is64bit ? sizeof(nlist_64) : sizeof(struct nlist); |
315 | 100k | sink->AddFileRange( |
316 | 100k | "macho_symtab", "Symbol Table", |
317 | 100k | StrictSubstr(cmd.file_data, symtab->symoff, symtab->nsyms * size)); |
318 | 100k | sink->AddFileRange( |
319 | 100k | "macho_symtab", "String Table", |
320 | 100k | StrictSubstr(cmd.file_data, symtab->stroff, symtab->strsize)); |
321 | 100k | } |
322 | | |
323 | 1.53k | static void ParseDynamicSymbolTable(const LoadCommand& cmd, RangeSink* sink) { |
324 | 1.53k | auto dysymtab = GetStructPointer<dysymtab_command>(cmd.command_data); |
325 | | |
326 | 1.53k | sink->AddFileRange( |
327 | 1.53k | "macho_dynsymtab", "Table of Contents", |
328 | 1.53k | StrictSubstr(cmd.file_data, dysymtab->tocoff, |
329 | 1.53k | dysymtab->ntoc * sizeof(dylib_table_of_contents))); |
330 | 1.53k | sink->AddFileRange("macho_dynsymtab", "Module Table", |
331 | 1.53k | StrictSubstr(cmd.file_data, dysymtab->modtaboff, |
332 | 1.53k | dysymtab->nmodtab * sizeof(dylib_module_64))); |
333 | 1.53k | sink->AddFileRange( |
334 | 1.53k | "macho_dynsymtab", "Referenced Symbol Table", |
335 | 1.53k | StrictSubstr(cmd.file_data, dysymtab->extrefsymoff, |
336 | 1.53k | dysymtab->nextrefsyms * sizeof(dylib_reference))); |
337 | 1.53k | sink->AddFileRange("macho_dynsymtab", "Indirect Symbol Table", |
338 | 1.53k | StrictSubstr(cmd.file_data, dysymtab->indirectsymoff, |
339 | 1.53k | dysymtab->nindirectsyms * sizeof(uint32_t))); |
340 | 1.53k | sink->AddFileRange("macho_dynsymtab", "External Relocation Entries", |
341 | 1.53k | StrictSubstr(cmd.file_data, dysymtab->extreloff, |
342 | 1.53k | dysymtab->nextrel * sizeof(relocation_info))); |
343 | 1.53k | sink->AddFileRange( |
344 | 1.53k | "macho_dynsymtab", "Local Relocation Entries", |
345 | 1.53k | StrictSubstr(cmd.file_data, dysymtab->locreloff, |
346 | 1.53k | dysymtab->nlocrel * sizeof(struct relocation_info))); |
347 | 1.53k | } |
348 | | |
349 | | static void ParseLinkeditCommand(string_view label, const LoadCommand& cmd, |
350 | 1.90k | RangeSink* sink) { |
351 | 1.90k | auto linkedit = GetStructPointer<linkedit_data_command>(cmd.command_data); |
352 | 1.90k | sink->AddFileRange( |
353 | 1.90k | "macho_linkedit", label, |
354 | 1.90k | StrictSubstr(cmd.file_data, linkedit->dataoff, linkedit->datasize)); |
355 | 1.90k | } |
356 | | |
357 | 156k | void ParseLoadCommand(const LoadCommand& cmd, RangeSink* sink) { |
358 | 156k | switch (cmd.cmd) { |
359 | 12.6k | case LC_SEGMENT_64: |
360 | 12.6k | ParseSegment<segment_command_64, section_64>(cmd, sink); |
361 | 12.6k | break; |
362 | 18.7k | case LC_SEGMENT: |
363 | 18.7k | ParseSegment<segment_command, section>(cmd, sink); |
364 | 18.7k | break; |
365 | 1.35k | case LC_DYLD_INFO: |
366 | 1.85k | case LC_DYLD_INFO_ONLY: |
367 | 1.85k | ParseDyldInfo(cmd, sink); |
368 | 1.85k | break; |
369 | 100k | case LC_SYMTAB: |
370 | 100k | ParseSymbolTable(cmd, sink); |
371 | 100k | break; |
372 | 1.53k | case LC_DYSYMTAB: |
373 | 1.53k | ParseDynamicSymbolTable(cmd, sink); |
374 | 1.53k | break; |
375 | 329 | case LC_CODE_SIGNATURE: |
376 | 329 | ParseLinkeditCommand("Code Signature", cmd, sink); |
377 | 329 | break; |
378 | 242 | case LC_SEGMENT_SPLIT_INFO: |
379 | 242 | ParseLinkeditCommand("Segment Split Info", cmd, sink); |
380 | 242 | break; |
381 | 354 | case LC_FUNCTION_STARTS: |
382 | 354 | ParseLinkeditCommand("Function Start Addresses", cmd, sink); |
383 | 354 | break; |
384 | 350 | case LC_DATA_IN_CODE: |
385 | 350 | ParseLinkeditCommand("Table of Non-instructions", cmd, sink); |
386 | 350 | break; |
387 | 304 | case LC_DYLIB_CODE_SIGN_DRS: |
388 | 304 | ParseLinkeditCommand("Code Signing DRs", cmd, sink); |
389 | 304 | break; |
390 | 322 | case LC_LINKER_OPTIMIZATION_HINT: |
391 | 322 | ParseLinkeditCommand("Optimization Hints", cmd, sink); |
392 | 322 | break; |
393 | 156k | } |
394 | 156k | } |
395 | | |
396 | 135k | void ParseLoadCommands(RangeSink* sink) { |
397 | 135k | ForEachLoadCommand( |
398 | 135k | sink->input_file().data(), sink, |
399 | 156k | [sink](const LoadCommand& cmd) { ParseLoadCommand(cmd, sink); }); |
400 | 135k | } |
401 | | |
402 | | template <class NList> |
403 | | void ParseSymbolsFromSymbolTable(const LoadCommand& cmd, SymbolTable* table, |
404 | 24.8k | RangeSink* sink) { |
405 | 24.8k | auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data); |
406 | | |
407 | 24.8k | string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff, |
408 | 24.8k | symtab_cmd->nsyms * sizeof(NList)); |
409 | 24.8k | string_view strtab = |
410 | 24.8k | StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize); |
411 | | |
412 | 24.8k | uint32_t nsyms = symtab_cmd->nsyms; |
413 | 1.21M | for (uint32_t i = 0; i < nsyms; i++) { |
414 | 1.18M | auto sym = GetStructPointerAndAdvance<NList>(&symtab); |
415 | 1.18M | string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList)); |
416 | | |
417 | 1.18M | if (sym->n_type & N_STAB || sym->n_value == 0) { |
418 | 604k | continue; |
419 | 604k | } |
420 | | |
421 | 582k | string_view name_region = StrictSubstr(strtab, sym->n_un.n_strx); |
422 | 582k | string_view name = ReadNullTerminated(&name_region); |
423 | | |
424 | 582k | if (sink->data_source() >= DataSource::kSymbols) { |
425 | 562k | sink->AddVMRange("macho_symbols", sym->n_value, RangeSink::kUnknownSize, |
426 | 562k | ItaniumDemangle(name, sink->data_source())); |
427 | 562k | } |
428 | | |
429 | 582k | if (table) { |
430 | 281k | table->insert(std::make_pair( |
431 | 281k | name, std::make_pair(sym->n_value, RangeSink::kUnknownSize))); |
432 | 281k | } |
433 | | |
434 | | // Capture the trailing NULL. |
435 | 582k | name = string_view(name.data(), name.size() + 1); |
436 | 582k | sink->AddFileRangeForVMAddr("macho_symtab_name", sym->n_value, name); |
437 | 582k | sink->AddFileRangeForVMAddr("macho_symtab_sym", sym->n_value, sym_range); |
438 | 582k | } |
439 | 24.8k | } void bloaty::macho::ParseSymbolsFromSymbolTable<nlist_64>(bloaty::macho::LoadCommand const&, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*) Line | Count | Source | 404 | 17.6k | RangeSink* sink) { | 405 | 17.6k | auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data); | 406 | | | 407 | 17.6k | string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff, | 408 | 17.6k | symtab_cmd->nsyms * sizeof(NList)); | 409 | 17.6k | string_view strtab = | 410 | 17.6k | StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize); | 411 | | | 412 | 17.6k | uint32_t nsyms = symtab_cmd->nsyms; | 413 | 826k | for (uint32_t i = 0; i < nsyms; i++) { | 414 | 808k | auto sym = GetStructPointerAndAdvance<NList>(&symtab); | 415 | 808k | string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList)); | 416 | | | 417 | 808k | if (sym->n_type & N_STAB || sym->n_value == 0) { | 418 | 349k | continue; | 419 | 349k | } | 420 | | | 421 | 458k | string_view name_region = StrictSubstr(strtab, sym->n_un.n_strx); | 422 | 458k | string_view name = ReadNullTerminated(&name_region); | 423 | | | 424 | 458k | if (sink->data_source() >= DataSource::kSymbols) { | 425 | 445k | sink->AddVMRange("macho_symbols", sym->n_value, RangeSink::kUnknownSize, | 426 | 445k | ItaniumDemangle(name, sink->data_source())); | 427 | 445k | } | 428 | | | 429 | 458k | if (table) { | 430 | 222k | table->insert(std::make_pair( | 431 | 222k | name, std::make_pair(sym->n_value, RangeSink::kUnknownSize))); | 432 | 222k | } | 433 | | | 434 | | // Capture the trailing NULL. | 435 | 458k | name = string_view(name.data(), name.size() + 1); | 436 | 458k | sink->AddFileRangeForVMAddr("macho_symtab_name", sym->n_value, name); | 437 | 458k | sink->AddFileRangeForVMAddr("macho_symtab_sym", sym->n_value, sym_range); | 438 | 458k | } | 439 | 17.6k | } |
void bloaty::macho::ParseSymbolsFromSymbolTable<nlist>(bloaty::macho::LoadCommand const&, std::__1::map<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::pair<unsigned long, unsigned long>, std::__1::less<std::__1::basic_string_view<char, std::__1::char_traits<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string_view<char, std::__1::char_traits<char> > const, std::__1::pair<unsigned long, unsigned long> > > >*, bloaty::RangeSink*) Line | Count | Source | 404 | 7.24k | RangeSink* sink) { | 405 | 7.24k | auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data); | 406 | | | 407 | 7.24k | string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff, | 408 | 7.24k | symtab_cmd->nsyms * sizeof(NList)); | 409 | 7.24k | string_view strtab = | 410 | 7.24k | StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize); | 411 | | | 412 | 7.24k | uint32_t nsyms = symtab_cmd->nsyms; | 413 | 385k | for (uint32_t i = 0; i < nsyms; i++) { | 414 | 378k | auto sym = GetStructPointerAndAdvance<NList>(&symtab); | 415 | 378k | string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList)); | 416 | | | 417 | 378k | if (sym->n_type & N_STAB || sym->n_value == 0) { | 418 | 254k | continue; | 419 | 254k | } | 420 | | | 421 | 123k | string_view name_region = StrictSubstr(strtab, sym->n_un.n_strx); | 422 | 123k | string_view name = ReadNullTerminated(&name_region); | 423 | | | 424 | 123k | if (sink->data_source() >= DataSource::kSymbols) { | 425 | 117k | sink->AddVMRange("macho_symbols", sym->n_value, RangeSink::kUnknownSize, | 426 | 117k | ItaniumDemangle(name, sink->data_source())); | 427 | 117k | } | 428 | | | 429 | 123k | if (table) { | 430 | 58.5k | table->insert(std::make_pair( | 431 | 58.5k | name, std::make_pair(sym->n_value, RangeSink::kUnknownSize))); | 432 | 58.5k | } | 433 | | | 434 | | // Capture the trailing NULL. | 435 | 123k | name = string_view(name.data(), name.size() + 1); | 436 | 123k | sink->AddFileRangeForVMAddr("macho_symtab_name", sym->n_value, name); | 437 | 123k | sink->AddFileRangeForVMAddr("macho_symtab_sym", sym->n_value, sym_range); | 438 | 123k | } | 439 | 7.24k | } |
|
440 | | |
441 | 31.4k | void ParseSymbols(string_view file_data, SymbolTable* symtab, RangeSink* sink) { |
442 | 31.4k | ForEachLoadCommand( |
443 | 31.4k | file_data, sink, |
444 | 36.0k | [symtab, sink](const LoadCommand& cmd) { |
445 | 36.0k | switch (cmd.cmd) { |
446 | 24.8k | case LC_SYMTAB: |
447 | 24.8k | if (cmd.is64bit) { |
448 | 17.6k | ParseSymbolsFromSymbolTable<nlist_64>(cmd, symtab, sink); |
449 | 17.6k | } else { |
450 | 7.24k | ParseSymbolsFromSymbolTable<struct nlist>(cmd, symtab, sink); |
451 | 7.24k | } |
452 | 24.8k | break; |
453 | 116 | case LC_DYSYMTAB: |
454 | | //ParseSymbolsFromDynamicSymbolTable(command_data, file_data, sink); |
455 | 116 | break; |
456 | 36.0k | } |
457 | 36.0k | }); |
458 | 31.4k | } |
459 | | |
460 | 134k | static void AddMachOFallback(RangeSink* sink) { |
461 | 134k | ForEachLoadCommand( |
462 | 134k | sink->input_file().data(), sink, |
463 | 155k | [sink](const LoadCommand& cmd) { |
464 | 155k | switch (cmd.cmd) { |
465 | 12.1k | case LC_SEGMENT_64: |
466 | 12.1k | AddSegmentAsFallback<segment_command_64, section_64>( |
467 | 12.1k | cmd.command_data, cmd.file_data, sink); |
468 | 12.1k | break; |
469 | 18.5k | case LC_SEGMENT: |
470 | 18.5k | AddSegmentAsFallback<segment_command, section>(cmd.command_data, |
471 | 18.5k | cmd.file_data, sink); |
472 | 18.5k | break; |
473 | 155k | } |
474 | 155k | }); |
475 | 134k | sink->AddFileRange("macho_fallback", "[Unmapped]", sink->input_file().data()); |
476 | 134k | } |
477 | | |
478 | | template <class Segment, class Section> |
479 | | void ReadDebugSectionsFromSegment(LoadCommand cmd, dwarf::File *dwarf, |
480 | 3.01k | RangeSink *sink) { |
481 | 3.01k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); |
482 | 3.01k | string_view segname = ArrayToStr(segment->segname, 16); |
483 | | |
484 | 3.01k | if (segname != "__DWARF") { |
485 | 1.58k | return; |
486 | 1.58k | } |
487 | | |
488 | 1.42k | uint32_t nsects = segment->nsects; |
489 | 13.9k | for (uint32_t j = 0; j < nsects; j++) { |
490 | 13.0k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); |
491 | 13.0k | string_view sectname = ArrayToStr(section->sectname, 16); |
492 | | |
493 | | // filesize equals vmsize unless the section is zerofill |
494 | 13.0k | uint64_t filesize = section->size; |
495 | 13.0k | switch (section->flags & SECTION_TYPE) { |
496 | 370 | case S_ZEROFILL: |
497 | 708 | case S_GB_ZEROFILL: |
498 | 978 | case S_THREAD_LOCAL_ZEROFILL: |
499 | 978 | filesize = 0; |
500 | 978 | break; |
501 | 11.5k | default: |
502 | 11.5k | break; |
503 | 13.0k | } |
504 | | |
505 | 12.5k | string_view contents = |
506 | 12.5k | StrictSubstr(cmd.file_data, section->offset, filesize); |
507 | | |
508 | 12.5k | if (sectname.find("__debug_") == 0) { |
509 | 72 | sectname.remove_prefix(string_view("__debug_").size()); |
510 | 72 | dwarf->SetFieldByName(sectname, contents); |
511 | 12.4k | } else if (sectname.find("__zdebug_") == 0) { |
512 | 92 | sectname.remove_prefix(string_view("__zdebug_").size()); |
513 | 92 | string_view *member = dwarf->GetFieldByName(sectname); |
514 | 92 | if (!member || ReadBytes(4, &contents) != "ZLIB") { |
515 | 88 | continue; |
516 | 88 | } |
517 | 4 | auto uncompressed_size = ReadBigEndian<uint64_t>(&contents); |
518 | 4 | *member = sink->ZlibDecompress(contents, uncompressed_size); |
519 | 4 | } |
520 | 12.5k | } |
521 | 1.42k | } void bloaty::macho::ReadDebugSectionsFromSegment<segment_command_64, section_64>(bloaty::macho::LoadCommand, bloaty::dwarf::File*, bloaty::RangeSink*) Line | Count | Source | 480 | 1.19k | RangeSink *sink) { | 481 | 1.19k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 482 | 1.19k | string_view segname = ArrayToStr(segment->segname, 16); | 483 | | | 484 | 1.19k | if (segname != "__DWARF") { | 485 | 626 | return; | 486 | 626 | } | 487 | | | 488 | 570 | uint32_t nsects = segment->nsects; | 489 | 4.88k | for (uint32_t j = 0; j < nsects; j++) { | 490 | 4.49k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 491 | 4.49k | string_view sectname = ArrayToStr(section->sectname, 16); | 492 | | | 493 | | // filesize equals vmsize unless the section is zerofill | 494 | 4.49k | uint64_t filesize = section->size; | 495 | 4.49k | switch (section->flags & SECTION_TYPE) { | 496 | 143 | case S_ZEROFILL: | 497 | 323 | case S_GB_ZEROFILL: | 498 | 405 | case S_THREAD_LOCAL_ZEROFILL: | 499 | 405 | filesize = 0; | 500 | 405 | break; | 501 | 3.90k | default: | 502 | 3.90k | break; | 503 | 4.49k | } | 504 | | | 505 | 4.31k | string_view contents = | 506 | 4.31k | StrictSubstr(cmd.file_data, section->offset, filesize); | 507 | | | 508 | 4.31k | if (sectname.find("__debug_") == 0) { | 509 | 21 | sectname.remove_prefix(string_view("__debug_").size()); | 510 | 21 | dwarf->SetFieldByName(sectname, contents); | 511 | 4.29k | } else if (sectname.find("__zdebug_") == 0) { | 512 | 23 | sectname.remove_prefix(string_view("__zdebug_").size()); | 513 | 23 | string_view *member = dwarf->GetFieldByName(sectname); | 514 | 23 | if (!member || ReadBytes(4, &contents) != "ZLIB") { | 515 | 22 | continue; | 516 | 22 | } | 517 | 1 | auto uncompressed_size = ReadBigEndian<uint64_t>(&contents); | 518 | 1 | *member = sink->ZlibDecompress(contents, uncompressed_size); | 519 | 1 | } | 520 | 4.31k | } | 521 | 570 | } |
void bloaty::macho::ReadDebugSectionsFromSegment<segment_command, section>(bloaty::macho::LoadCommand, bloaty::dwarf::File*, bloaty::RangeSink*) Line | Count | Source | 480 | 1.81k | RangeSink *sink) { | 481 | 1.81k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 482 | 1.81k | string_view segname = ArrayToStr(segment->segname, 16); | 483 | | | 484 | 1.81k | if (segname != "__DWARF") { | 485 | 963 | return; | 486 | 963 | } | 487 | | | 488 | 854 | uint32_t nsects = segment->nsects; | 489 | 9.11k | for (uint32_t j = 0; j < nsects; j++) { | 490 | 8.51k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 491 | 8.51k | string_view sectname = ArrayToStr(section->sectname, 16); | 492 | | | 493 | | // filesize equals vmsize unless the section is zerofill | 494 | 8.51k | uint64_t filesize = section->size; | 495 | 8.51k | switch (section->flags & SECTION_TYPE) { | 496 | 227 | case S_ZEROFILL: | 497 | 385 | case S_GB_ZEROFILL: | 498 | 573 | case S_THREAD_LOCAL_ZEROFILL: | 499 | 573 | filesize = 0; | 500 | 573 | break; | 501 | 7.68k | default: | 502 | 7.68k | break; | 503 | 8.51k | } | 504 | | | 505 | 8.25k | string_view contents = | 506 | 8.25k | StrictSubstr(cmd.file_data, section->offset, filesize); | 507 | | | 508 | 8.25k | if (sectname.find("__debug_") == 0) { | 509 | 51 | sectname.remove_prefix(string_view("__debug_").size()); | 510 | 51 | dwarf->SetFieldByName(sectname, contents); | 511 | 8.20k | } else if (sectname.find("__zdebug_") == 0) { | 512 | 69 | sectname.remove_prefix(string_view("__zdebug_").size()); | 513 | 69 | string_view *member = dwarf->GetFieldByName(sectname); | 514 | 69 | if (!member || ReadBytes(4, &contents) != "ZLIB") { | 515 | 66 | continue; | 516 | 66 | } | 517 | 3 | auto uncompressed_size = ReadBigEndian<uint64_t>(&contents); | 518 | 3 | *member = sink->ZlibDecompress(contents, uncompressed_size); | 519 | 3 | } | 520 | 8.25k | } | 521 | 854 | } |
|
522 | | |
523 | | static void ReadDebugSectionsFromMachO(const InputFile &file, |
524 | 5.69k | dwarf::File *dwarf, RangeSink *sink) { |
525 | 5.69k | dwarf->file = &file; |
526 | 5.69k | dwarf->open = &ReadDebugSectionsFromMachO; |
527 | 5.69k | ForEachLoadCommand( |
528 | 6.81k | file.data(), nullptr, [dwarf, sink](const LoadCommand &cmd) { |
529 | 6.81k | switch (cmd.cmd) { |
530 | 1.19k | case LC_SEGMENT_64: |
531 | 1.19k | ReadDebugSectionsFromSegment<segment_command_64, section_64>( |
532 | 1.19k | cmd, dwarf, sink); |
533 | 1.19k | break; |
534 | 1.81k | case LC_SEGMENT: |
535 | 1.81k | ReadDebugSectionsFromSegment<segment_command, section>(cmd, dwarf, |
536 | 1.81k | sink); |
537 | 1.81k | break; |
538 | 6.81k | } |
539 | 6.81k | }); |
540 | 5.69k | } |
541 | | |
542 | | class MachOObjectFile : public ObjectFile { |
543 | | public: |
544 | | MachOObjectFile(std::unique_ptr<InputFile> file_data) |
545 | 210k | : ObjectFile(std::move(file_data)) {} |
546 | | |
547 | 210k | std::string GetBuildId() const override { |
548 | 210k | std::string id; |
549 | | |
550 | 246k | ForEachLoadCommand(file_data().data(), nullptr, [&id](LoadCommand cmd) { |
551 | 246k | if (cmd.cmd == LC_UUID) { |
552 | 456 | auto uuid_cmd = |
553 | 456 | GetStructPointerAndAdvance<uuid_command>(&cmd.command_data); |
554 | 456 | if (!cmd.command_data.empty()) { |
555 | 120 | THROWF("Unexpected excess uuid data: $0", cmd.command_data.size()); |
556 | 120 | } |
557 | 336 | id.resize(sizeof(uuid_cmd->uuid)); |
558 | 336 | memcpy(&id[0], &uuid_cmd->uuid[0], sizeof(uuid_cmd->uuid)); |
559 | 336 | } |
560 | 246k | }); |
561 | | |
562 | 210k | return id; |
563 | 210k | } |
564 | | |
565 | 103k | void ProcessFile(const std::vector<RangeSink*>& sinks) const override { |
566 | 198k | for (auto sink : sinks) { |
567 | 198k | switch (sink->data_source()) { |
568 | 119k | case DataSource::kSegments: |
569 | 135k | case DataSource::kSections: |
570 | 135k | ParseLoadCommands(sink); |
571 | 135k | break; |
572 | 0 | case DataSource::kSymbols: |
573 | 0 | case DataSource::kRawSymbols: |
574 | 15.7k | case DataSource::kShortSymbols: |
575 | 15.7k | case DataSource::kFullSymbols: |
576 | 15.7k | ParseSymbols(debug_file().file_data().data(), nullptr, sink); |
577 | 15.7k | break; |
578 | 15.7k | case DataSource::kCompileUnits: { |
579 | 15.7k | SymbolTable symtab; |
580 | 15.7k | DualMap symbol_map; |
581 | 15.7k | NameMunger empty_munger; |
582 | 15.7k | RangeSink symbol_sink(&debug_file().file_data(), sink->options(), |
583 | 15.7k | DataSource::kRawSymbols, |
584 | 15.7k | &sinks[0]->MapAtIndex(0), nullptr); |
585 | 15.7k | symbol_sink.AddOutput(&symbol_map, &empty_munger); |
586 | 15.7k | ParseSymbols(debug_file().file_data().data(), &symtab, &symbol_sink); |
587 | 15.7k | dwarf::File dwarf; |
588 | 15.7k | ReadDebugSectionsFromMachO(debug_file().file_data(), &dwarf, sink); |
589 | 15.7k | ReadDWARFCompileUnits(dwarf, symbol_map, sink); |
590 | 15.7k | ParseSymbols(sink->input_file().data(), nullptr, sink); |
591 | 15.7k | break; |
592 | 15.7k | } |
593 | 15.7k | case DataSource::kArchiveMembers: |
594 | 31.4k | case DataSource::kInlines: |
595 | 31.4k | default: |
596 | 31.4k | THROW("Mach-O doesn't support this data source"); |
597 | 198k | } |
598 | 134k | AddMachOFallback(sink); |
599 | 134k | } |
600 | 103k | } |
601 | | |
602 | | bool GetDisassemblyInfo(absl::string_view /*symbol*/, |
603 | | DataSource /*symbol_source*/, |
604 | 0 | DisassemblyInfo* /*info*/) const override { |
605 | 0 | WARN("Mach-O files do not support disassembly yet"); |
606 | 0 | return false; |
607 | 0 | } |
608 | | }; |
609 | | |
610 | | } // namespace macho |
611 | | |
612 | 296k | std::unique_ptr<ObjectFile> TryOpenMachOFile(std::unique_ptr<InputFile> &file) { |
613 | 296k | uint32_t magic = macho::ReadMagic(file->data()); |
614 | | |
615 | | // We only support little-endian host and little endian binaries (see |
616 | | // ParseMachOHeader() for more rationale). Fat headers are always on disk as |
617 | | // big-endian. |
618 | 296k | if (magic == MH_MAGIC || magic == MH_MAGIC_64 || magic == FAT_CIGAM) { |
619 | 210k | return std::unique_ptr<ObjectFile>( |
620 | 210k | new macho::MachOObjectFile(std::move(file))); |
621 | 210k | } |
622 | | |
623 | 85.3k | return nullptr; |
624 | 296k | } |
625 | | |
626 | | } // namespace bloaty |