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 | 126k | static string_view ArrayToStr(const char* s, size_t maxlen) { |
39 | 126k | return string_view(s, strnlen(s, maxlen)); |
40 | 126k | } |
41 | | |
42 | 1.61M | uint32_t ReadMagic(string_view data) { |
43 | 1.61M | if (data.size() < sizeof(uint32_t)) { |
44 | 126 | THROW("Malformed Mach-O file"); |
45 | 126 | } |
46 | 1.61M | uint32_t magic; |
47 | 1.61M | memcpy(&magic, data.data(), sizeof(magic)); |
48 | 1.61M | return magic; |
49 | 1.61M | } |
50 | | |
51 | | template <class T> |
52 | 3.14M | const T* GetStructPointer(string_view data) { |
53 | 3.14M | if (sizeof(T) > data.size()) { |
54 | 4.33k | THROW("Premature EOF reading Mach-O data."); |
55 | 4.33k | } |
56 | 3.14M | return reinterpret_cast<const T*>(data.data()); |
57 | 3.14M | } 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 | 2.17k | const T* GetStructPointer(string_view data) { | 53 | 2.17k | if (sizeof(T) > data.size()) { | 54 | 126 | THROW("Premature EOF reading Mach-O data."); | 55 | 126 | } | 56 | 2.05k | return reinterpret_cast<const T*>(data.data()); | 57 | 2.17k | } |
symtab_command const* bloaty::macho::GetStructPointer<symtab_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 99.5k | const T* GetStructPointer(string_view data) { | 53 | 99.5k | if (sizeof(T) > data.size()) { | 54 | 126 | THROW("Premature EOF reading Mach-O data."); | 55 | 126 | } | 56 | 99.4k | return reinterpret_cast<const T*>(data.data()); | 57 | 99.5k | } |
dysymtab_command const* bloaty::macho::GetStructPointer<dysymtab_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 2.37k | const T* GetStructPointer(string_view data) { | 53 | 2.37k | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 2.25k | return reinterpret_cast<const T*>(data.data()); | 57 | 2.37k | } |
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 | 4.81k | const T* GetStructPointer(string_view data) { | 53 | 4.81k | if (sizeof(T) > data.size()) { | 54 | 726 | THROW("Premature EOF reading Mach-O data."); | 55 | 726 | } | 56 | 4.08k | return reinterpret_cast<const T*>(data.data()); | 57 | 4.81k | } |
mach_header const* bloaty::macho::GetStructPointer<mach_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 179k | const T* GetStructPointer(string_view data) { | 53 | 179k | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 179k | return reinterpret_cast<const T*>(data.data()); | 57 | 179k | } |
load_command const* bloaty::macho::GetStructPointer<load_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 553k | const T* GetStructPointer(string_view data) { | 53 | 553k | if (sizeof(T) > data.size()) { | 54 | 378 | THROW("Premature EOF reading Mach-O data."); | 55 | 378 | } | 56 | 553k | return reinterpret_cast<const T*>(data.data()); | 57 | 553k | } |
uuid_command const* bloaty::macho::GetStructPointer<uuid_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 522 | const T* GetStructPointer(string_view data) { | 53 | 522 | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 402 | return reinterpret_cast<const T*>(data.data()); | 57 | 522 | } |
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 | 270k | const T* GetStructPointer(string_view data) { | 53 | 270k | if (sizeof(T) > data.size()) { | 54 | 132 | THROW("Premature EOF reading Mach-O data."); | 55 | 132 | } | 56 | 270k | return reinterpret_cast<const T*>(data.data()); | 57 | 270k | } |
fat_header const* bloaty::macho::GetStructPointer<fat_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 1.24k | const T* GetStructPointer(string_view data) { | 53 | 1.24k | if (sizeof(T) > data.size()) { | 54 | 0 | THROW("Premature EOF reading Mach-O data."); | 55 | 0 | } | 56 | 1.24k | return reinterpret_cast<const T*>(data.data()); | 57 | 1.24k | } |
fat_arch const* bloaty::macho::GetStructPointer<fat_arch>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 650 | const T* GetStructPointer(string_view data) { | 53 | 650 | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 530 | return reinterpret_cast<const T*>(data.data()); | 57 | 650 | } |
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 | 27.8k | const T* GetStructPointer(string_view data) { | 53 | 27.8k | if (sizeof(T) > data.size()) { | 54 | 120 | THROW("Premature EOF reading Mach-O data."); | 55 | 120 | } | 56 | 27.7k | return reinterpret_cast<const T*>(data.data()); | 57 | 27.8k | } |
section_64 const* bloaty::macho::GetStructPointer<section_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 39.9k | const T* GetStructPointer(string_view data) { | 53 | 39.9k | if (sizeof(T) > data.size()) { | 54 | 964 | THROW("Premature EOF reading Mach-O data."); | 55 | 964 | } | 56 | 39.0k | return reinterpret_cast<const T*>(data.data()); | 57 | 39.9k | } |
segment_command const* bloaty::macho::GetStructPointer<segment_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 33.5k | const T* GetStructPointer(string_view data) { | 53 | 33.5k | if (sizeof(T) > data.size()) { | 54 | 144 | THROW("Premature EOF reading Mach-O data."); | 55 | 144 | } | 56 | 33.4k | return reinterpret_cast<const T*>(data.data()); | 57 | 33.5k | } |
section const* bloaty::macho::GetStructPointer<section>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 43.7k | const T* GetStructPointer(string_view data) { | 53 | 43.7k | if (sizeof(T) > data.size()) { | 54 | 1.14k | THROW("Premature EOF reading Mach-O data."); | 55 | 1.14k | } | 56 | 42.6k | return reinterpret_cast<const T*>(data.data()); | 57 | 43.7k | } |
nlist_64 const* bloaty::macho::GetStructPointer<nlist_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 1.20M | const T* GetStructPointer(string_view data) { | 53 | 1.20M | if (sizeof(T) > data.size()) { | 54 | 0 | THROW("Premature EOF reading Mach-O data."); | 55 | 0 | } | 56 | 1.20M | return reinterpret_cast<const T*>(data.data()); | 57 | 1.20M | } |
nlist const* bloaty::macho::GetStructPointer<nlist>(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 52 | 682k | const T* GetStructPointer(string_view data) { | 53 | 682k | if (sizeof(T) > data.size()) { | 54 | 0 | THROW("Premature EOF reading Mach-O data."); | 55 | 0 | } | 56 | 682k | return reinterpret_cast<const T*>(data.data()); | 57 | 682k | } |
|
58 | | |
59 | | template <class T> |
60 | 2.48M | const T* GetStructPointerAndAdvance(string_view* data) { |
61 | 2.48M | const T* ret = GetStructPointer<T>(*data); |
62 | 2.48M | *data = data->substr(sizeof(T)); |
63 | 2.48M | return ret; |
64 | 2.48M | } mach_header const* bloaty::macho::GetStructPointerAndAdvance<mach_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 179k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 179k | const T* ret = GetStructPointer<T>(*data); | 62 | 179k | *data = data->substr(sizeof(T)); | 63 | 179k | return ret; | 64 | 179k | } |
uuid_command const* bloaty::macho::GetStructPointerAndAdvance<uuid_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 522 | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 522 | const T* ret = GetStructPointer<T>(*data); | 62 | 522 | *data = data->substr(sizeof(T)); | 63 | 522 | return ret; | 64 | 522 | } |
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 | 270k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 270k | const T* ret = GetStructPointer<T>(*data); | 62 | 270k | *data = data->substr(sizeof(T)); | 63 | 270k | return ret; | 64 | 270k | } |
fat_header const* bloaty::macho::GetStructPointerAndAdvance<fat_header>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 1.24k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 1.24k | const T* ret = GetStructPointer<T>(*data); | 62 | 1.24k | *data = data->substr(sizeof(T)); | 63 | 1.24k | return ret; | 64 | 1.24k | } |
fat_arch const* bloaty::macho::GetStructPointerAndAdvance<fat_arch>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 650 | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 650 | const T* ret = GetStructPointer<T>(*data); | 62 | 650 | *data = data->substr(sizeof(T)); | 63 | 650 | return ret; | 64 | 650 | } |
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 | 27.8k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 27.8k | const T* ret = GetStructPointer<T>(*data); | 62 | 27.8k | *data = data->substr(sizeof(T)); | 63 | 27.8k | return ret; | 64 | 27.8k | } |
section_64 const* bloaty::macho::GetStructPointerAndAdvance<section_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 39.9k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 39.9k | const T* ret = GetStructPointer<T>(*data); | 62 | 39.9k | *data = data->substr(sizeof(T)); | 63 | 39.9k | return ret; | 64 | 39.9k | } |
segment_command const* bloaty::macho::GetStructPointerAndAdvance<segment_command>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 33.5k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 33.5k | const T* ret = GetStructPointer<T>(*data); | 62 | 33.5k | *data = data->substr(sizeof(T)); | 63 | 33.5k | return ret; | 64 | 33.5k | } |
section const* bloaty::macho::GetStructPointerAndAdvance<section>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 43.7k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 43.7k | const T* ret = GetStructPointer<T>(*data); | 62 | 43.7k | *data = data->substr(sizeof(T)); | 63 | 43.7k | return ret; | 64 | 43.7k | } |
nlist_64 const* bloaty::macho::GetStructPointerAndAdvance<nlist_64>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 1.20M | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 1.20M | const T* ret = GetStructPointer<T>(*data); | 62 | 1.20M | *data = data->substr(sizeof(T)); | 63 | 1.20M | return ret; | 64 | 1.20M | } |
nlist const* bloaty::macho::GetStructPointerAndAdvance<nlist>(std::__1::basic_string_view<char, std::__1::char_traits<char> >*) Line | Count | Source | 60 | 682k | const T* GetStructPointerAndAdvance(string_view* data) { | 61 | 682k | const T* ret = GetStructPointer<T>(*data); | 62 | 682k | *data = data->substr(sizeof(T)); | 63 | 682k | return ret; | 64 | 682k | } |
|
65 | | |
66 | 976k | void MaybeAddOverhead(RangeSink* sink, const char* label, string_view data) { |
67 | 976k | if (sink) { |
68 | 553k | sink->AddFileRange("macho_overhead", label, data); |
69 | 553k | } |
70 | 976k | } |
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 | 230k | bool Is64Bit() { return false; } |
81 | | |
82 | | template <> |
83 | 321k | 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 | 450k | Func&& loadcmd_func) { |
88 | 450k | string_view header_data = macho_data; |
89 | 450k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); |
90 | 450k | MaybeAddOverhead(overhead_sink, |
91 | 450k | "[Mach-O Headers]", |
92 | 450k | macho_data.substr(0, sizeof(Struct))); |
93 | 450k | uint32_t ncmds = header->ncmds; |
94 | | |
95 | 1.00M | for (uint32_t i = 0; i < ncmds; i++) { |
96 | 553k | 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 | 553k | if (command->cmdsize == 0) { |
102 | 738 | THROW("Mach-O load command had zero size."); |
103 | 738 | } |
104 | | |
105 | 552k | LoadCommand data; |
106 | 552k | data.is64bit = Is64Bit<Struct>(); |
107 | 552k | data.cmd = command->cmd; |
108 | 552k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); |
109 | 552k | data.file_data = macho_data; |
110 | 552k | std::forward<Func>(loadcmd_func)(data); |
111 | | |
112 | 552k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); |
113 | 552k | header_data = header_data.substr(command->cmdsize); |
114 | 552k | } |
115 | 450k | } 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 | 71.8k | Func&& loadcmd_func) { | 88 | 71.8k | string_view header_data = macho_data; | 89 | 71.8k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 71.8k | MaybeAddOverhead(overhead_sink, | 91 | 71.8k | "[Mach-O Headers]", | 92 | 71.8k | macho_data.substr(0, sizeof(Struct))); | 93 | 71.8k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 164k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 93.3k | 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 | 93.3k | if (command->cmdsize == 0) { | 102 | 474 | THROW("Mach-O load command had zero size."); | 103 | 474 | } | 104 | | | 105 | 92.8k | LoadCommand data; | 106 | 92.8k | data.is64bit = Is64Bit<Struct>(); | 107 | 92.8k | data.cmd = command->cmd; | 108 | 92.8k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 92.8k | data.file_data = macho_data; | 110 | 92.8k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 92.8k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 92.8k | header_data = header_data.substr(command->cmdsize); | 114 | 92.8k | } | 115 | 71.8k | } |
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 | 111k | Func&& loadcmd_func) { | 88 | 111k | string_view header_data = macho_data; | 89 | 111k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 111k | MaybeAddOverhead(overhead_sink, | 91 | 111k | "[Mach-O Headers]", | 92 | 111k | macho_data.substr(0, sizeof(Struct))); | 93 | 111k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 245k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 134k | 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 | 134k | if (command->cmdsize == 0) { | 102 | 264 | THROW("Mach-O load command had zero size."); | 103 | 264 | } | 104 | | | 105 | 133k | LoadCommand data; | 106 | 133k | data.is64bit = Is64Bit<Struct>(); | 107 | 133k | data.cmd = command->cmd; | 108 | 133k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 133k | data.file_data = macho_data; | 110 | 133k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 133k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 133k | header_data = header_data.substr(command->cmdsize); | 114 | 133k | } | 115 | 111k | } |
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 | 2.35k | Func&& loadcmd_func) { | 88 | 2.35k | string_view header_data = macho_data; | 89 | 2.35k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 2.35k | MaybeAddOverhead(overhead_sink, | 91 | 2.35k | "[Mach-O Headers]", | 92 | 2.35k | macho_data.substr(0, sizeof(Struct))); | 93 | 2.35k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 5.23k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 2.88k | 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.88k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 2.88k | LoadCommand data; | 106 | 2.88k | data.is64bit = Is64Bit<Struct>(); | 107 | 2.88k | data.cmd = command->cmd; | 108 | 2.88k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 2.88k | data.file_data = macho_data; | 110 | 2.88k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 2.88k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 2.88k | header_data = header_data.substr(command->cmdsize); | 114 | 2.88k | } | 115 | 2.35k | } |
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 | 3.94k | Func&& loadcmd_func) { | 88 | 3.94k | string_view header_data = macho_data; | 89 | 3.94k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 3.94k | MaybeAddOverhead(overhead_sink, | 91 | 3.94k | "[Mach-O Headers]", | 92 | 3.94k | macho_data.substr(0, sizeof(Struct))); | 93 | 3.94k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 8.82k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 4.88k | 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.88k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 4.88k | LoadCommand data; | 106 | 4.88k | data.is64bit = Is64Bit<Struct>(); | 107 | 4.88k | data.cmd = command->cmd; | 108 | 4.88k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 4.88k | data.file_data = macho_data; | 110 | 4.88k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 4.88k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 4.88k | header_data = header_data.substr(command->cmdsize); | 114 | 4.88k | } | 115 | 3.94k | } |
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 | 47.8k | Func&& loadcmd_func) { | 88 | 47.8k | string_view header_data = macho_data; | 89 | 47.8k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 47.8k | MaybeAddOverhead(overhead_sink, | 91 | 47.8k | "[Mach-O Headers]", | 92 | 47.8k | macho_data.substr(0, sizeof(Struct))); | 93 | 47.8k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 109k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 61.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 | 61.4k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 61.4k | LoadCommand data; | 106 | 61.4k | data.is64bit = Is64Bit<Struct>(); | 107 | 61.4k | data.cmd = command->cmd; | 108 | 61.4k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 61.4k | data.file_data = macho_data; | 110 | 61.4k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 61.4k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 61.4k | header_data = header_data.substr(command->cmdsize); | 114 | 61.4k | } | 115 | 47.8k | } |
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 | 68.5k | Func&& loadcmd_func) { | 88 | 68.5k | string_view header_data = macho_data; | 89 | 68.5k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 68.5k | MaybeAddOverhead(overhead_sink, | 91 | 68.5k | "[Mach-O Headers]", | 92 | 68.5k | macho_data.substr(0, sizeof(Struct))); | 93 | 68.5k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 149k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 81.3k | 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 | 81.3k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 81.3k | LoadCommand data; | 106 | 81.3k | data.is64bit = Is64Bit<Struct>(); | 107 | 81.3k | data.cmd = command->cmd; | 108 | 81.3k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 81.3k | data.file_data = macho_data; | 110 | 81.3k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 81.3k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 81.3k | header_data = header_data.substr(command->cmdsize); | 114 | 81.3k | } | 115 | 68.5k | } |
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 | 46.5k | Func&& loadcmd_func) { | 88 | 46.5k | string_view header_data = macho_data; | 89 | 46.5k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 46.5k | MaybeAddOverhead(overhead_sink, | 91 | 46.5k | "[Mach-O Headers]", | 92 | 46.5k | macho_data.substr(0, sizeof(Struct))); | 93 | 46.5k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 106k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 59.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 | 59.6k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 59.6k | LoadCommand data; | 106 | 59.6k | data.is64bit = Is64Bit<Struct>(); | 107 | 59.6k | data.cmd = command->cmd; | 108 | 59.6k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 59.6k | data.file_data = macho_data; | 110 | 59.6k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 59.6k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 59.6k | header_data = header_data.substr(command->cmdsize); | 114 | 59.6k | } | 115 | 46.5k | } |
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 | 70.9k | Func&& loadcmd_func) { | 88 | 70.9k | string_view header_data = macho_data; | 89 | 70.9k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 70.9k | MaybeAddOverhead(overhead_sink, | 91 | 70.9k | "[Mach-O Headers]", | 92 | 70.9k | macho_data.substr(0, sizeof(Struct))); | 93 | 70.9k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 154k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 83.3k | 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 | 83.3k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 83.3k | LoadCommand data; | 106 | 83.3k | data.is64bit = Is64Bit<Struct>(); | 107 | 83.3k | data.cmd = command->cmd; | 108 | 83.3k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 83.3k | data.file_data = macho_data; | 110 | 83.3k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 83.3k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 83.3k | header_data = header_data.substr(command->cmdsize); | 114 | 83.3k | } | 115 | 70.9k | } |
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 | 11.2k | Func&& loadcmd_func) { | 88 | 11.2k | string_view header_data = macho_data; | 89 | 11.2k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 11.2k | MaybeAddOverhead(overhead_sink, | 91 | 11.2k | "[Mach-O Headers]", | 92 | 11.2k | macho_data.substr(0, sizeof(Struct))); | 93 | 11.2k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 25.3k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 14.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 | 14.1k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 14.1k | LoadCommand data; | 106 | 14.1k | data.is64bit = Is64Bit<Struct>(); | 107 | 14.1k | data.cmd = command->cmd; | 108 | 14.1k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 14.1k | data.file_data = macho_data; | 110 | 14.1k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 14.1k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 14.1k | header_data = header_data.substr(command->cmdsize); | 114 | 14.1k | } | 115 | 11.2k | } |
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 | 15.5k | Func&& loadcmd_func) { | 88 | 15.5k | string_view header_data = macho_data; | 89 | 15.5k | auto header = GetStructPointerAndAdvance<Struct>(&header_data); | 90 | 15.5k | MaybeAddOverhead(overhead_sink, | 91 | 15.5k | "[Mach-O Headers]", | 92 | 15.5k | macho_data.substr(0, sizeof(Struct))); | 93 | 15.5k | uint32_t ncmds = header->ncmds; | 94 | | | 95 | 34.1k | for (uint32_t i = 0; i < ncmds; i++) { | 96 | 18.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 | 18.5k | if (command->cmdsize == 0) { | 102 | 0 | THROW("Mach-O load command had zero size."); | 103 | 0 | } | 104 | | | 105 | 18.5k | LoadCommand data; | 106 | 18.5k | data.is64bit = Is64Bit<Struct>(); | 107 | 18.5k | data.cmd = command->cmd; | 108 | 18.5k | data.command_data = StrictSubstr(header_data, 0, command->cmdsize); | 109 | 18.5k | data.file_data = macho_data; | 110 | 18.5k | std::forward<Func>(loadcmd_func)(data); | 111 | | | 112 | 18.5k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", data.command_data); | 113 | 18.5k | header_data = header_data.substr(command->cmdsize); | 114 | 18.5k | } | 115 | 15.5k | } |
|
116 | | |
117 | | template <class Func> |
118 | | void ParseMachOHeader(string_view macho_file, RangeSink* overhead_sink, |
119 | 450k | Func&& loadcmd_func) { |
120 | 450k | uint32_t magic = ReadMagic(macho_file); |
121 | 450k | switch (magic) { |
122 | 179k | 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 | 179k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, |
131 | 179k | std::forward<Func>(loadcmd_func)); |
132 | 179k | break; |
133 | 270k | case MH_MAGIC_64: |
134 | 270k | ParseMachOHeaderImpl<mach_header_64>( |
135 | 270k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); |
136 | 270k | break; |
137 | 18 | case MH_CIGAM: |
138 | 36 | 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 | 36 | THROW("We don't support cross-endian Mach-O files."); |
152 | 120 | default: |
153 | 120 | THROW("Corrupt Mach-O file"); |
154 | 450k | } |
155 | 450k | } 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 | 183k | Func&& loadcmd_func) { | 120 | 183k | uint32_t magic = ReadMagic(macho_file); | 121 | 183k | switch (magic) { | 122 | 71.8k | 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 | 71.8k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 71.8k | std::forward<Func>(loadcmd_func)); | 132 | 71.8k | break; | 133 | 111k | case MH_MAGIC_64: | 134 | 111k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 111k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 111k | break; | 137 | 18 | case MH_CIGAM: | 138 | 36 | 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 | 36 | THROW("We don't support cross-endian Mach-O files."); | 152 | 120 | default: | 153 | 120 | THROW("Corrupt Mach-O file"); | 154 | 183k | } | 155 | 183k | } |
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 | 6.29k | Func&& loadcmd_func) { | 120 | 6.29k | uint32_t magic = ReadMagic(macho_file); | 121 | 6.29k | switch (magic) { | 122 | 2.35k | 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 | 2.35k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 2.35k | std::forward<Func>(loadcmd_func)); | 132 | 2.35k | break; | 133 | 3.94k | case MH_MAGIC_64: | 134 | 3.94k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 3.94k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 3.94k | 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 | 6.29k | } | 155 | 6.29k | } |
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 | 116k | Func&& loadcmd_func) { | 120 | 116k | uint32_t magic = ReadMagic(macho_file); | 121 | 116k | switch (magic) { | 122 | 47.8k | 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 | 47.8k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 47.8k | std::forward<Func>(loadcmd_func)); | 132 | 47.8k | break; | 133 | 68.5k | case MH_MAGIC_64: | 134 | 68.5k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 68.5k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 68.5k | 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 | 116k | } | 155 | 116k | } |
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 | 117k | Func&& loadcmd_func) { | 120 | 117k | uint32_t magic = ReadMagic(macho_file); | 121 | 117k | switch (magic) { | 122 | 46.5k | 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 | 46.5k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 46.5k | std::forward<Func>(loadcmd_func)); | 132 | 46.5k | break; | 133 | 70.9k | case MH_MAGIC_64: | 134 | 70.9k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 70.9k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 70.9k | 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 | 117k | } | 155 | 117k | } |
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 | 26.7k | Func&& loadcmd_func) { | 120 | 26.7k | uint32_t magic = ReadMagic(macho_file); | 121 | 26.7k | switch (magic) { | 122 | 11.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 | 11.2k | ParseMachOHeaderImpl<mach_header>(macho_file, overhead_sink, | 131 | 11.2k | std::forward<Func>(loadcmd_func)); | 132 | 11.2k | break; | 133 | 15.5k | case MH_MAGIC_64: | 134 | 15.5k | ParseMachOHeaderImpl<mach_header_64>( | 135 | 15.5k | macho_file, overhead_sink, std::forward<Func>(loadcmd_func)); | 136 | 15.5k | 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 | 26.7k | } | 155 | 26.7k | } |
|
156 | | |
157 | | template <class Func> |
158 | | void ParseFatHeader(string_view fat_file, RangeSink* overhead_sink, |
159 | 1.24k | Func&& loadcmd_func) { |
160 | 1.24k | string_view header_data = fat_file; |
161 | 1.24k | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); |
162 | 1.24k | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", |
163 | 1.24k | fat_file.substr(0, sizeof(fat_header))); |
164 | 1.24k | assert(ByteSwap(header->magic) == FAT_MAGIC); |
165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); |
166 | 1.89k | for (uint32_t i = 0; i < nfat_arch; i++) { |
167 | 650 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); |
168 | 650 | string_view macho_data = StrictSubstr( |
169 | 650 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); |
170 | 650 | ParseMachOHeader(macho_data, overhead_sink, |
171 | 650 | std::forward<Func>(loadcmd_func)); |
172 | 650 | } |
173 | 1.24k | } 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 | 822 | Func&& loadcmd_func) { | 160 | 822 | string_view header_data = fat_file; | 161 | 822 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 822 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 822 | fat_file.substr(0, sizeof(fat_header))); | 164 | 822 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 1.45k | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 630 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 630 | string_view macho_data = StrictSubstr( | 169 | 630 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 630 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 630 | std::forward<Func>(loadcmd_func)); | 172 | 630 | } | 173 | 822 | } |
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 | 21 | Func&& loadcmd_func) { | 160 | 21 | string_view header_data = fat_file; | 161 | 21 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 21 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 21 | fat_file.substr(0, sizeof(fat_header))); | 164 | 21 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 22 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 1 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 1 | string_view macho_data = StrictSubstr( | 169 | 1 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 1 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 1 | std::forward<Func>(loadcmd_func)); | 172 | 1 | } | 173 | 21 | } |
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 | 189 | Func&& loadcmd_func) { | 160 | 189 | string_view header_data = fat_file; | 161 | 189 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 189 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 189 | fat_file.substr(0, sizeof(fat_header))); | 164 | 189 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 198 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 9 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 9 | string_view macho_data = StrictSubstr( | 169 | 9 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 9 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 9 | std::forward<Func>(loadcmd_func)); | 172 | 9 | } | 173 | 189 | } |
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 | 168 | Func&& loadcmd_func) { | 160 | 168 | string_view header_data = fat_file; | 161 | 168 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 168 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 168 | fat_file.substr(0, sizeof(fat_header))); | 164 | 168 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 176 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 8 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 8 | string_view macho_data = StrictSubstr( | 169 | 8 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 8 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 8 | std::forward<Func>(loadcmd_func)); | 172 | 8 | } | 173 | 168 | } |
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 | 42 | Func&& loadcmd_func) { | 160 | 42 | string_view header_data = fat_file; | 161 | 42 | auto header = GetStructPointerAndAdvance<fat_header>(&header_data); | 162 | 42 | MaybeAddOverhead(overhead_sink, "[Mach-O Headers]", | 163 | 42 | fat_file.substr(0, sizeof(fat_header))); | 164 | 42 | assert(ByteSwap(header->magic) == FAT_MAGIC); | 165 | 0 | uint32_t nfat_arch = ByteSwap(header->nfat_arch); | 166 | 44 | for (uint32_t i = 0; i < nfat_arch; i++) { | 167 | 2 | auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data); | 168 | 2 | string_view macho_data = StrictSubstr( | 169 | 2 | fat_file, ByteSwap(arch->offset), ByteSwap(arch->size)); | 170 | 2 | ParseMachOHeader(macho_data, overhead_sink, | 171 | 2 | std::forward<Func>(loadcmd_func)); | 172 | 2 | } | 173 | 42 | } |
|
174 | | |
175 | | template <class Func> |
176 | | void ForEachLoadCommand(string_view maybe_fat_file, RangeSink* overhead_sink, |
177 | 451k | Func&& loadcmd_func) { |
178 | 451k | uint32_t magic = ReadMagic(maybe_fat_file); |
179 | 451k | switch (magic) { |
180 | 179k | case MH_MAGIC: |
181 | 450k | case MH_MAGIC_64: |
182 | 450k | case MH_CIGAM: |
183 | 450k | case MH_CIGAM_64: |
184 | 450k | ParseMachOHeader(maybe_fat_file, overhead_sink, |
185 | 450k | std::forward<Func>(loadcmd_func)); |
186 | 450k | break; |
187 | 1.24k | case FAT_CIGAM: |
188 | 1.24k | ParseFatHeader(maybe_fat_file, overhead_sink, |
189 | 1.24k | std::forward<Func>(loadcmd_func)); |
190 | 1.24k | break; |
191 | 451k | } |
192 | 451k | } 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 | 184k | Func&& loadcmd_func) { | 178 | 184k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 184k | switch (magic) { | 180 | 71.8k | case MH_MAGIC: | 181 | 183k | case MH_MAGIC_64: | 182 | 183k | case MH_CIGAM: | 183 | 183k | case MH_CIGAM_64: | 184 | 183k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 183k | std::forward<Func>(loadcmd_func)); | 186 | 183k | break; | 187 | 822 | case FAT_CIGAM: | 188 | 822 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 822 | std::forward<Func>(loadcmd_func)); | 190 | 822 | break; | 191 | 184k | } | 192 | 184k | } |
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 | 6.31k | Func&& loadcmd_func) { | 178 | 6.31k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 6.31k | switch (magic) { | 180 | 2.35k | case MH_MAGIC: | 181 | 6.29k | case MH_MAGIC_64: | 182 | 6.29k | case MH_CIGAM: | 183 | 6.29k | case MH_CIGAM_64: | 184 | 6.29k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 6.29k | std::forward<Func>(loadcmd_func)); | 186 | 6.29k | break; | 187 | 21 | case FAT_CIGAM: | 188 | 21 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 21 | std::forward<Func>(loadcmd_func)); | 190 | 21 | break; | 191 | 6.31k | } | 192 | 6.31k | } |
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 | 116k | Func&& loadcmd_func) { | 178 | 116k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 116k | switch (magic) { | 180 | 47.8k | case MH_MAGIC: | 181 | 116k | case MH_MAGIC_64: | 182 | 116k | case MH_CIGAM: | 183 | 116k | case MH_CIGAM_64: | 184 | 116k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 116k | std::forward<Func>(loadcmd_func)); | 186 | 116k | break; | 187 | 189 | case FAT_CIGAM: | 188 | 189 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 189 | std::forward<Func>(loadcmd_func)); | 190 | 189 | break; | 191 | 116k | } | 192 | 116k | } |
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 | 117k | Func&& loadcmd_func) { | 178 | 117k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 117k | switch (magic) { | 180 | 46.5k | case MH_MAGIC: | 181 | 117k | case MH_MAGIC_64: | 182 | 117k | case MH_CIGAM: | 183 | 117k | case MH_CIGAM_64: | 184 | 117k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 117k | std::forward<Func>(loadcmd_func)); | 186 | 117k | break; | 187 | 168 | case FAT_CIGAM: | 188 | 168 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 168 | std::forward<Func>(loadcmd_func)); | 190 | 168 | break; | 191 | 117k | } | 192 | 117k | } |
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 | 26.8k | Func&& loadcmd_func) { | 178 | 26.8k | uint32_t magic = ReadMagic(maybe_fat_file); | 179 | 26.8k | switch (magic) { | 180 | 11.2k | case MH_MAGIC: | 181 | 26.7k | case MH_MAGIC_64: | 182 | 26.7k | case MH_CIGAM: | 183 | 26.7k | case MH_CIGAM_64: | 184 | 26.7k | ParseMachOHeader(maybe_fat_file, overhead_sink, | 185 | 26.7k | std::forward<Func>(loadcmd_func)); | 186 | 26.7k | break; | 187 | 42 | case FAT_CIGAM: | 188 | 42 | ParseFatHeader(maybe_fat_file, overhead_sink, | 189 | 42 | std::forward<Func>(loadcmd_func)); | 190 | 42 | break; | 191 | 26.8k | } | 192 | 26.8k | } |
|
193 | | |
194 | | template <class Segment, class Section> |
195 | | void AddSegmentAsFallback(string_view command_data, string_view file_data, |
196 | 29.0k | RangeSink* sink) { |
197 | 29.0k | auto segment = GetStructPointerAndAdvance<Segment>(&command_data); |
198 | | |
199 | 29.0k | if (segment->maxprot == VM_PROT_NONE) { |
200 | 16.8k | return; |
201 | 16.8k | } |
202 | | |
203 | 12.2k | string_view segname = ArrayToStr(segment->segname, 16); |
204 | | |
205 | 12.2k | uint32_t nsects = segment->nsects; |
206 | 69.9k | for (uint32_t j = 0; j < nsects; j++) { |
207 | 58.9k | auto section = GetStructPointerAndAdvance<Section>(&command_data); |
208 | | |
209 | | // filesize equals vmsize unless the section is zerofill |
210 | 58.9k | uint64_t filesize = section->size; |
211 | 58.9k | switch (section->flags & SECTION_TYPE) { |
212 | 6.64k | case S_ZEROFILL: |
213 | 9.05k | case S_GB_ZEROFILL: |
214 | 12.0k | case S_THREAD_LOCAL_ZEROFILL: |
215 | 12.0k | filesize = 0; |
216 | 12.0k | break; |
217 | 45.7k | default: |
218 | 45.7k | break; |
219 | 58.9k | } |
220 | | |
221 | 57.7k | std::string label = absl::StrJoin( |
222 | 57.7k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); |
223 | 57.7k | label = "[" + label + "]"; |
224 | 57.7k | sink->AddRange("macho_fallback", label, section->addr, section->size, |
225 | 57.7k | StrictSubstr(file_data, section->offset, filesize)); |
226 | 57.7k | } |
227 | | |
228 | 10.9k | sink->AddRange("macho_fallback", "[" + std::string(segname) + "]", |
229 | 10.9k | segment->vmaddr, segment->vmsize, |
230 | 10.9k | StrictSubstr(file_data, segment->fileoff, segment->filesize)); |
231 | 10.9k | } 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 | 13.0k | RangeSink* sink) { | 197 | 13.0k | auto segment = GetStructPointerAndAdvance<Segment>(&command_data); | 198 | | | 199 | 13.0k | if (segment->maxprot == VM_PROT_NONE) { | 200 | 7.63k | return; | 201 | 7.63k | } | 202 | | | 203 | 5.36k | string_view segname = ArrayToStr(segment->segname, 16); | 204 | | | 205 | 5.36k | uint32_t nsects = segment->nsects; | 206 | 33.5k | for (uint32_t j = 0; j < nsects; j++) { | 207 | 28.7k | auto section = GetStructPointerAndAdvance<Section>(&command_data); | 208 | | | 209 | | // filesize equals vmsize unless the section is zerofill | 210 | 28.7k | uint64_t filesize = section->size; | 211 | 28.7k | switch (section->flags & SECTION_TYPE) { | 212 | 3.27k | case S_ZEROFILL: | 213 | 4.44k | case S_GB_ZEROFILL: | 214 | 5.67k | case S_THREAD_LOCAL_ZEROFILL: | 215 | 5.67k | filesize = 0; | 216 | 5.67k | break; | 217 | 22.4k | default: | 218 | 22.4k | break; | 219 | 28.7k | } | 220 | | | 221 | 28.1k | std::string label = absl::StrJoin( | 222 | 28.1k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 223 | 28.1k | label = "[" + label + "]"; | 224 | 28.1k | sink->AddRange("macho_fallback", label, section->addr, section->size, | 225 | 28.1k | StrictSubstr(file_data, section->offset, filesize)); | 226 | 28.1k | } | 227 | | | 228 | 4.73k | sink->AddRange("macho_fallback", "[" + std::string(segname) + "]", | 229 | 4.73k | segment->vmaddr, segment->vmsize, | 230 | 4.73k | StrictSubstr(file_data, segment->fileoff, segment->filesize)); | 231 | 4.73k | } |
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 | 16.0k | RangeSink* sink) { | 197 | 16.0k | auto segment = GetStructPointerAndAdvance<Segment>(&command_data); | 198 | | | 199 | 16.0k | if (segment->maxprot == VM_PROT_NONE) { | 200 | 9.19k | return; | 201 | 9.19k | } | 202 | | | 203 | 6.87k | string_view segname = ArrayToStr(segment->segname, 16); | 204 | | | 205 | 6.87k | uint32_t nsects = segment->nsects; | 206 | 36.4k | for (uint32_t j = 0; j < nsects; j++) { | 207 | 30.2k | auto section = GetStructPointerAndAdvance<Section>(&command_data); | 208 | | | 209 | | // filesize equals vmsize unless the section is zerofill | 210 | 30.2k | uint64_t filesize = section->size; | 211 | 30.2k | switch (section->flags & SECTION_TYPE) { | 212 | 3.36k | case S_ZEROFILL: | 213 | 4.61k | case S_GB_ZEROFILL: | 214 | 6.34k | case S_THREAD_LOCAL_ZEROFILL: | 215 | 6.34k | filesize = 0; | 216 | 6.34k | break; | 217 | 23.2k | default: | 218 | 23.2k | break; | 219 | 30.2k | } | 220 | | | 221 | 29.5k | std::string label = absl::StrJoin( | 222 | 29.5k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 223 | 29.5k | label = "[" + label + "]"; | 224 | 29.5k | sink->AddRange("macho_fallback", label, section->addr, section->size, | 225 | 29.5k | StrictSubstr(file_data, section->offset, filesize)); | 226 | 29.5k | } | 227 | | | 228 | 6.24k | sink->AddRange("macho_fallback", "[" + std::string(segname) + "]", | 229 | 6.24k | segment->vmaddr, segment->vmsize, | 230 | 6.24k | StrictSubstr(file_data, segment->fileoff, segment->filesize)); | 231 | 6.24k | } |
|
232 | | |
233 | | template <class Segment, class Section> |
234 | 29.4k | void ParseSegment(LoadCommand cmd, RangeSink* sink) { |
235 | 29.4k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); |
236 | 29.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 | 29.4k | bool unmapped = segment->maxprot == VM_PROT_NONE; |
247 | | |
248 | 29.4k | if (sink->data_source() == DataSource::kSegments) { |
249 | 26.3k | if (unmapped) { |
250 | 14.9k | sink->AddFileRange( |
251 | 14.9k | "macho_segment", segname, |
252 | 14.9k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); |
253 | 14.9k | } else { |
254 | 11.4k | sink->AddRange( |
255 | 11.4k | "macho_segment", segname, segment->vmaddr, segment->vmsize, |
256 | 11.4k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); |
257 | 11.4k | } |
258 | 26.3k | } else if (sink->data_source() == DataSource::kSections) { |
259 | 2.87k | uint32_t nsects = segment->nsects; |
260 | 19.9k | for (uint32_t j = 0; j < nsects; j++) { |
261 | 17.6k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); |
262 | | |
263 | | // filesize equals vmsize unless the section is zerofill |
264 | 17.6k | uint64_t filesize = section->size; |
265 | 17.6k | switch (section->flags & SECTION_TYPE) { |
266 | 1.43k | case S_ZEROFILL: |
267 | 2.02k | case S_GB_ZEROFILL: |
268 | 2.78k | case S_THREAD_LOCAL_ZEROFILL: |
269 | 2.78k | filesize = 0; |
270 | 2.78k | break; |
271 | 14.3k | default: |
272 | 14.3k | break; |
273 | 17.6k | } |
274 | | |
275 | 17.1k | std::string label = absl::StrJoin( |
276 | 17.1k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); |
277 | 17.1k | if (unmapped) { |
278 | 14.3k | sink->AddFileRange( |
279 | 14.3k | "macho_section", label, |
280 | 14.3k | StrictSubstr(cmd.file_data, section->offset, filesize)); |
281 | 14.3k | } else { |
282 | 2.74k | sink->AddRange("macho_section", label, section->addr, section->size, |
283 | 2.74k | StrictSubstr(cmd.file_data, section->offset, filesize)); |
284 | 2.74k | } |
285 | 17.1k | } |
286 | 2.87k | } else { |
287 | 264 | BLOATY_UNREACHABLE(); |
288 | 264 | } |
289 | 29.4k | } void bloaty::macho::ParseSegment<segment_command_64, section_64>(bloaty::macho::LoadCommand, bloaty::RangeSink*) Line | Count | Source | 234 | 13.5k | void ParseSegment(LoadCommand cmd, RangeSink* sink) { | 235 | 13.5k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 236 | 13.5k | 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 | 13.5k | bool unmapped = segment->maxprot == VM_PROT_NONE; | 247 | | | 248 | 13.5k | if (sink->data_source() == DataSource::kSegments) { | 249 | 12.2k | if (unmapped) { | 250 | 6.84k | sink->AddFileRange( | 251 | 6.84k | "macho_segment", segname, | 252 | 6.84k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 253 | 6.84k | } else { | 254 | 5.35k | sink->AddRange( | 255 | 5.35k | "macho_segment", segname, segment->vmaddr, segment->vmsize, | 256 | 5.35k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 257 | 5.35k | } | 258 | 12.2k | } else if (sink->data_source() == DataSource::kSections) { | 259 | 1.26k | uint32_t nsects = segment->nsects; | 260 | 9.23k | for (uint32_t j = 0; j < nsects; j++) { | 261 | 8.21k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 262 | | | 263 | | // filesize equals vmsize unless the section is zerofill | 264 | 8.21k | uint64_t filesize = section->size; | 265 | 8.21k | switch (section->flags & SECTION_TYPE) { | 266 | 677 | case S_ZEROFILL: | 267 | 966 | case S_GB_ZEROFILL: | 268 | 1.32k | case S_THREAD_LOCAL_ZEROFILL: | 269 | 1.32k | filesize = 0; | 270 | 1.32k | break; | 271 | 6.65k | default: | 272 | 6.65k | break; | 273 | 8.21k | } | 274 | | | 275 | 7.97k | std::string label = absl::StrJoin( | 276 | 7.97k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 277 | 7.97k | if (unmapped) { | 278 | 6.78k | sink->AddFileRange( | 279 | 6.78k | "macho_section", label, | 280 | 6.78k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 281 | 6.78k | } else { | 282 | 1.19k | sink->AddRange("macho_section", label, section->addr, section->size, | 283 | 1.19k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 284 | 1.19k | } | 285 | 7.97k | } | 286 | 1.26k | } else { | 287 | 120 | BLOATY_UNREACHABLE(); | 288 | 120 | } | 289 | 13.5k | } |
void bloaty::macho::ParseSegment<segment_command, section>(bloaty::macho::LoadCommand, bloaty::RangeSink*) Line | Count | Source | 234 | 15.8k | void ParseSegment(LoadCommand cmd, RangeSink* sink) { | 235 | 15.8k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 236 | 15.8k | 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 | 15.8k | bool unmapped = segment->maxprot == VM_PROT_NONE; | 247 | | | 248 | 15.8k | if (sink->data_source() == DataSource::kSegments) { | 249 | 14.1k | if (unmapped) { | 250 | 8.09k | sink->AddFileRange( | 251 | 8.09k | "macho_segment", segname, | 252 | 8.09k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 253 | 8.09k | } else { | 254 | 6.04k | sink->AddRange( | 255 | 6.04k | "macho_segment", segname, segment->vmaddr, segment->vmsize, | 256 | 6.04k | StrictSubstr(cmd.file_data, segment->fileoff, segment->filesize)); | 257 | 6.04k | } | 258 | 14.1k | } else if (sink->data_source() == DataSource::kSections) { | 259 | 1.61k | uint32_t nsects = segment->nsects; | 260 | 10.7k | for (uint32_t j = 0; j < nsects; j++) { | 261 | 9.45k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 262 | | | 263 | | // filesize equals vmsize unless the section is zerofill | 264 | 9.45k | uint64_t filesize = section->size; | 265 | 9.45k | switch (section->flags & SECTION_TYPE) { | 266 | 756 | case S_ZEROFILL: | 267 | 1.05k | case S_GB_ZEROFILL: | 268 | 1.46k | case S_THREAD_LOCAL_ZEROFILL: | 269 | 1.46k | filesize = 0; | 270 | 1.46k | break; | 271 | 7.65k | default: | 272 | 7.65k | break; | 273 | 9.45k | } | 274 | | | 275 | 9.12k | std::string label = absl::StrJoin( | 276 | 9.12k | std::make_tuple(segname, ArrayToStr(section->sectname, 16)), ","); | 277 | 9.12k | if (unmapped) { | 278 | 7.57k | sink->AddFileRange( | 279 | 7.57k | "macho_section", label, | 280 | 7.57k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 281 | 7.57k | } else { | 282 | 1.55k | sink->AddRange("macho_section", label, section->addr, section->size, | 283 | 1.55k | StrictSubstr(cmd.file_data, section->offset, filesize)); | 284 | 1.55k | } | 285 | 9.12k | } | 286 | 1.61k | } else { | 287 | 144 | BLOATY_UNREACHABLE(); | 288 | 144 | } | 289 | 15.8k | } |
|
290 | | |
291 | 2.17k | static void ParseDyldInfo(const LoadCommand& cmd, RangeSink* sink) { |
292 | 2.17k | auto info = GetStructPointer<dyld_info_command>(cmd.command_data); |
293 | | |
294 | 2.17k | sink->AddFileRange( |
295 | 2.17k | "macho_dyld", "Rebase Info", |
296 | 2.17k | StrictSubstr(cmd.file_data, info->rebase_off, info->rebase_size)); |
297 | 2.17k | sink->AddFileRange( |
298 | 2.17k | "macho_dyld", "Binding Info", |
299 | 2.17k | StrictSubstr(cmd.file_data, info->bind_off, info->bind_size)); |
300 | 2.17k | sink->AddFileRange( |
301 | 2.17k | "macho_dyld", "Weak Binding Info", |
302 | 2.17k | StrictSubstr(cmd.file_data, info->weak_bind_off, info->weak_bind_size)); |
303 | 2.17k | sink->AddFileRange( |
304 | 2.17k | "macho_dyld", "Lazy Binding Info", |
305 | 2.17k | StrictSubstr(cmd.file_data, info->lazy_bind_off, info->lazy_bind_size)); |
306 | 2.17k | sink->AddFileRange( |
307 | 2.17k | "macho_dyld", "Export Info", |
308 | 2.17k | StrictSubstr(cmd.file_data, info->export_off, info->export_size)); |
309 | 2.17k | } |
310 | | |
311 | 79.7k | static void ParseSymbolTable(const LoadCommand& cmd, RangeSink* sink) { |
312 | 79.7k | auto symtab = GetStructPointer<symtab_command>(cmd.command_data); |
313 | | |
314 | 79.7k | size_t size = cmd.is64bit ? sizeof(nlist_64) : sizeof(struct nlist); |
315 | 79.7k | sink->AddFileRange( |
316 | 79.7k | "macho_symtab", "Symbol Table", |
317 | 79.7k | StrictSubstr(cmd.file_data, symtab->symoff, symtab->nsyms * size)); |
318 | 79.7k | sink->AddFileRange( |
319 | 79.7k | "macho_symtab", "String Table", |
320 | 79.7k | StrictSubstr(cmd.file_data, symtab->stroff, symtab->strsize)); |
321 | 79.7k | } |
322 | | |
323 | 2.37k | static void ParseDynamicSymbolTable(const LoadCommand& cmd, RangeSink* sink) { |
324 | 2.37k | auto dysymtab = GetStructPointer<dysymtab_command>(cmd.command_data); |
325 | | |
326 | 2.37k | sink->AddFileRange( |
327 | 2.37k | "macho_dynsymtab", "Table of Contents", |
328 | 2.37k | StrictSubstr(cmd.file_data, dysymtab->tocoff, |
329 | 2.37k | dysymtab->ntoc * sizeof(dylib_table_of_contents))); |
330 | 2.37k | sink->AddFileRange("macho_dynsymtab", "Module Table", |
331 | 2.37k | StrictSubstr(cmd.file_data, dysymtab->modtaboff, |
332 | 2.37k | dysymtab->nmodtab * sizeof(dylib_module_64))); |
333 | 2.37k | sink->AddFileRange( |
334 | 2.37k | "macho_dynsymtab", "Referenced Symbol Table", |
335 | 2.37k | StrictSubstr(cmd.file_data, dysymtab->extrefsymoff, |
336 | 2.37k | dysymtab->nextrefsyms * sizeof(dylib_reference))); |
337 | 2.37k | sink->AddFileRange("macho_dynsymtab", "Indirect Symbol Table", |
338 | 2.37k | StrictSubstr(cmd.file_data, dysymtab->indirectsymoff, |
339 | 2.37k | dysymtab->nindirectsyms * sizeof(uint32_t))); |
340 | 2.37k | sink->AddFileRange("macho_dynsymtab", "External Relocation Entries", |
341 | 2.37k | StrictSubstr(cmd.file_data, dysymtab->extreloff, |
342 | 2.37k | dysymtab->nextrel * sizeof(relocation_info))); |
343 | 2.37k | sink->AddFileRange( |
344 | 2.37k | "macho_dynsymtab", "Local Relocation Entries", |
345 | 2.37k | StrictSubstr(cmd.file_data, dysymtab->locreloff, |
346 | 2.37k | dysymtab->nlocrel * sizeof(struct relocation_info))); |
347 | 2.37k | } |
348 | | |
349 | | static void ParseLinkeditCommand(string_view label, const LoadCommand& cmd, |
350 | 4.81k | RangeSink* sink) { |
351 | 4.81k | auto linkedit = GetStructPointer<linkedit_data_command>(cmd.command_data); |
352 | 4.81k | sink->AddFileRange( |
353 | 4.81k | "macho_linkedit", label, |
354 | 4.81k | StrictSubstr(cmd.file_data, linkedit->dataoff, linkedit->datasize)); |
355 | 4.81k | } |
356 | | |
357 | 142k | void ParseLoadCommand(const LoadCommand& cmd, RangeSink* sink) { |
358 | 142k | switch (cmd.cmd) { |
359 | 13.5k | case LC_SEGMENT_64: |
360 | 13.5k | ParseSegment<segment_command_64, section_64>(cmd, sink); |
361 | 13.5k | break; |
362 | 15.8k | case LC_SEGMENT: |
363 | 15.8k | ParseSegment<segment_command, section>(cmd, sink); |
364 | 15.8k | break; |
365 | 1.10k | case LC_DYLD_INFO: |
366 | 2.17k | case LC_DYLD_INFO_ONLY: |
367 | 2.17k | ParseDyldInfo(cmd, sink); |
368 | 2.17k | break; |
369 | 79.7k | case LC_SYMTAB: |
370 | 79.7k | ParseSymbolTable(cmd, sink); |
371 | 79.7k | break; |
372 | 2.37k | case LC_DYSYMTAB: |
373 | 2.37k | ParseDynamicSymbolTable(cmd, sink); |
374 | 2.37k | break; |
375 | 716 | case LC_CODE_SIGNATURE: |
376 | 716 | ParseLinkeditCommand("Code Signature", cmd, sink); |
377 | 716 | break; |
378 | 654 | case LC_SEGMENT_SPLIT_INFO: |
379 | 654 | ParseLinkeditCommand("Segment Split Info", cmd, sink); |
380 | 654 | break; |
381 | 656 | case LC_FUNCTION_STARTS: |
382 | 656 | ParseLinkeditCommand("Function Start Addresses", cmd, sink); |
383 | 656 | break; |
384 | 688 | case LC_DATA_IN_CODE: |
385 | 688 | ParseLinkeditCommand("Table of Non-instructions", cmd, sink); |
386 | 688 | break; |
387 | 696 | case LC_DYLIB_CODE_SIGN_DRS: |
388 | 696 | ParseLinkeditCommand("Code Signing DRs", cmd, sink); |
389 | 696 | break; |
390 | 1.40k | case LC_LINKER_OPTIMIZATION_HINT: |
391 | 1.40k | ParseLinkeditCommand("Optimization Hints", cmd, sink); |
392 | 1.40k | break; |
393 | 142k | } |
394 | 142k | } |
395 | | |
396 | 117k | void ParseLoadCommands(RangeSink* sink) { |
397 | 117k | ForEachLoadCommand( |
398 | 117k | sink->input_file().data(), sink, |
399 | 142k | [sink](const LoadCommand& cmd) { ParseLoadCommand(cmd, sink); }); |
400 | 117k | } |
401 | | |
402 | | template <class NList> |
403 | | void ParseSymbolsFromSymbolTable(const LoadCommand& cmd, SymbolTable* table, |
404 | 19.8k | RangeSink* sink) { |
405 | 19.8k | auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data); |
406 | | |
407 | 19.8k | string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff, |
408 | 19.8k | symtab_cmd->nsyms * sizeof(NList)); |
409 | 19.8k | string_view strtab = |
410 | 19.8k | StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize); |
411 | | |
412 | 19.8k | uint32_t nsyms = symtab_cmd->nsyms; |
413 | 1.90M | for (uint32_t i = 0; i < nsyms; i++) { |
414 | 1.88M | auto sym = GetStructPointerAndAdvance<NList>(&symtab); |
415 | 1.88M | string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList)); |
416 | | |
417 | 1.88M | if (sym->n_type & N_STAB || sym->n_value == 0) { |
418 | 738k | continue; |
419 | 738k | } |
420 | | |
421 | 1.14M | string_view name_region = StrictSubstr(strtab, sym->n_un.n_strx); |
422 | 1.14M | string_view name = ReadNullTerminated(&name_region); |
423 | | |
424 | 1.14M | if (sink->data_source() >= DataSource::kSymbols) { |
425 | 1.13M | sink->AddVMRange("macho_symbols", sym->n_value, RangeSink::kUnknownSize, |
426 | 1.13M | ItaniumDemangle(name, sink->data_source())); |
427 | 1.13M | } |
428 | | |
429 | 1.14M | if (table) { |
430 | 566k | table->insert(std::make_pair( |
431 | 566k | name, std::make_pair(sym->n_value, RangeSink::kUnknownSize))); |
432 | 566k | } |
433 | | |
434 | | // Capture the trailing NULL. |
435 | 1.14M | name = string_view(name.data(), name.size() + 1); |
436 | 1.14M | sink->AddFileRangeForVMAddr("macho_symtab_name", sym->n_value, name); |
437 | 1.14M | sink->AddFileRangeForVMAddr("macho_symtab_sym", sym->n_value, sym_range); |
438 | 1.14M | } |
439 | 19.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 | 9.91k | RangeSink* sink) { | 405 | 9.91k | auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data); | 406 | | | 407 | 9.91k | string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff, | 408 | 9.91k | symtab_cmd->nsyms * sizeof(NList)); | 409 | 9.91k | string_view strtab = | 410 | 9.91k | StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize); | 411 | | | 412 | 9.91k | uint32_t nsyms = symtab_cmd->nsyms; | 413 | 1.21M | for (uint32_t i = 0; i < nsyms; i++) { | 414 | 1.20M | auto sym = GetStructPointerAndAdvance<NList>(&symtab); | 415 | 1.20M | string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList)); | 416 | | | 417 | 1.20M | if (sym->n_type & N_STAB || sym->n_value == 0) { | 418 | 388k | continue; | 419 | 388k | } | 420 | | | 421 | 814k | string_view name_region = StrictSubstr(strtab, sym->n_un.n_strx); | 422 | 814k | string_view name = ReadNullTerminated(&name_region); | 423 | | | 424 | 814k | if (sink->data_source() >= DataSource::kSymbols) { | 425 | 806k | sink->AddVMRange("macho_symbols", sym->n_value, RangeSink::kUnknownSize, | 426 | 806k | ItaniumDemangle(name, sink->data_source())); | 427 | 806k | } | 428 | | | 429 | 814k | if (table) { | 430 | 403k | table->insert(std::make_pair( | 431 | 403k | name, std::make_pair(sym->n_value, RangeSink::kUnknownSize))); | 432 | 403k | } | 433 | | | 434 | | // Capture the trailing NULL. | 435 | 814k | name = string_view(name.data(), name.size() + 1); | 436 | 814k | sink->AddFileRangeForVMAddr("macho_symtab_name", sym->n_value, name); | 437 | 814k | sink->AddFileRangeForVMAddr("macho_symtab_sym", sym->n_value, sym_range); | 438 | 814k | } | 439 | 9.91k | } |
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 | 9.89k | RangeSink* sink) { | 405 | 9.89k | auto symtab_cmd = GetStructPointer<symtab_command>(cmd.command_data); | 406 | | | 407 | 9.89k | string_view symtab = StrictSubstr(cmd.file_data, symtab_cmd->symoff, | 408 | 9.89k | symtab_cmd->nsyms * sizeof(NList)); | 409 | 9.89k | string_view strtab = | 410 | 9.89k | StrictSubstr(cmd.file_data, symtab_cmd->stroff, symtab_cmd->strsize); | 411 | | | 412 | 9.89k | uint32_t nsyms = symtab_cmd->nsyms; | 413 | 692k | for (uint32_t i = 0; i < nsyms; i++) { | 414 | 682k | auto sym = GetStructPointerAndAdvance<NList>(&symtab); | 415 | 682k | string_view sym_range(reinterpret_cast<const char*>(sym), sizeof(NList)); | 416 | | | 417 | 682k | if (sym->n_type & N_STAB || sym->n_value == 0) { | 418 | 350k | continue; | 419 | 350k | } | 420 | | | 421 | 332k | string_view name_region = StrictSubstr(strtab, sym->n_un.n_strx); | 422 | 332k | string_view name = ReadNullTerminated(&name_region); | 423 | | | 424 | 332k | if (sink->data_source() >= DataSource::kSymbols) { | 425 | 325k | sink->AddVMRange("macho_symbols", sym->n_value, RangeSink::kUnknownSize, | 426 | 325k | ItaniumDemangle(name, sink->data_source())); | 427 | 325k | } | 428 | | | 429 | 332k | if (table) { | 430 | 162k | table->insert(std::make_pair( | 431 | 162k | name, std::make_pair(sym->n_value, RangeSink::kUnknownSize))); | 432 | 162k | } | 433 | | | 434 | | // Capture the trailing NULL. | 435 | 332k | name = string_view(name.data(), name.size() + 1); | 436 | 332k | sink->AddFileRangeForVMAddr("macho_symtab_name", sym->n_value, name); | 437 | 332k | sink->AddFileRangeForVMAddr("macho_symtab_sym", sym->n_value, sym_range); | 438 | 332k | } | 439 | 9.89k | } |
|
440 | | |
441 | 26.8k | void ParseSymbols(string_view file_data, SymbolTable* symtab, RangeSink* sink) { |
442 | 26.8k | ForEachLoadCommand( |
443 | 26.8k | file_data, sink, |
444 | 32.6k | [symtab, sink](const LoadCommand& cmd) { |
445 | 32.6k | switch (cmd.cmd) { |
446 | 19.8k | case LC_SYMTAB: |
447 | 19.8k | if (cmd.is64bit) { |
448 | 9.91k | ParseSymbolsFromSymbolTable<nlist_64>(cmd, symtab, sink); |
449 | 9.91k | } else { |
450 | 9.89k | ParseSymbolsFromSymbolTable<struct nlist>(cmd, symtab, sink); |
451 | 9.89k | } |
452 | 19.8k | break; |
453 | 282 | case LC_DYSYMTAB: |
454 | | //ParseSymbolsFromDynamicSymbolTable(command_data, file_data, sink); |
455 | 282 | break; |
456 | 32.6k | } |
457 | 32.6k | }); |
458 | 26.8k | } |
459 | | |
460 | 116k | static void AddMachOFallback(RangeSink* sink) { |
461 | 116k | ForEachLoadCommand( |
462 | 116k | sink->input_file().data(), sink, |
463 | 142k | [sink](const LoadCommand& cmd) { |
464 | 142k | switch (cmd.cmd) { |
465 | 13.0k | case LC_SEGMENT_64: |
466 | 13.0k | AddSegmentAsFallback<segment_command_64, section_64>( |
467 | 13.0k | cmd.command_data, cmd.file_data, sink); |
468 | 13.0k | break; |
469 | 16.0k | case LC_SEGMENT: |
470 | 16.0k | AddSegmentAsFallback<segment_command, section>(cmd.command_data, |
471 | 16.0k | cmd.file_data, sink); |
472 | 16.0k | break; |
473 | 142k | } |
474 | 142k | }); |
475 | 116k | sink->AddFileRange("macho_fallback", "[Unmapped]", sink->input_file().data()); |
476 | 116k | } |
477 | | |
478 | | template <class Segment, class Section> |
479 | | void ReadDebugSectionsFromSegment(LoadCommand cmd, dwarf::File *dwarf, |
480 | 2.89k | RangeSink *sink) { |
481 | 2.89k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); |
482 | 2.89k | string_view segname = ArrayToStr(segment->segname, 16); |
483 | | |
484 | 2.89k | if (segname != "__DWARF") { |
485 | 1.93k | return; |
486 | 1.93k | } |
487 | | |
488 | 954 | uint32_t nsects = segment->nsects; |
489 | 7.78k | for (uint32_t j = 0; j < nsects; j++) { |
490 | 7.10k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); |
491 | 7.10k | string_view sectname = ArrayToStr(section->sectname, 16); |
492 | | |
493 | | // filesize equals vmsize unless the section is zerofill |
494 | 7.10k | uint64_t filesize = section->size; |
495 | 7.10k | switch (section->flags & SECTION_TYPE) { |
496 | 337 | case S_ZEROFILL: |
497 | 500 | case S_GB_ZEROFILL: |
498 | 748 | case S_THREAD_LOCAL_ZEROFILL: |
499 | 748 | filesize = 0; |
500 | 748 | break; |
501 | 6.08k | default: |
502 | 6.08k | break; |
503 | 7.10k | } |
504 | | |
505 | 6.83k | string_view contents = |
506 | 6.83k | StrictSubstr(cmd.file_data, section->offset, filesize); |
507 | | |
508 | 6.83k | if (sectname.find("__debug_") == 0) { |
509 | 50 | sectname.remove_prefix(string_view("__debug_").size()); |
510 | 50 | dwarf->SetFieldByName(sectname, contents); |
511 | 6.78k | } else if (sectname.find("__zdebug_") == 0) { |
512 | 39 | sectname.remove_prefix(string_view("__zdebug_").size()); |
513 | 39 | string_view *member = dwarf->GetFieldByName(sectname); |
514 | 39 | if (!member || ReadBytes(4, &contents) != "ZLIB") { |
515 | 37 | continue; |
516 | 37 | } |
517 | 2 | auto uncompressed_size = ReadBigEndian<uint64_t>(&contents); |
518 | 2 | *member = sink->ZlibDecompress(contents, uncompressed_size); |
519 | 2 | } |
520 | 6.83k | } |
521 | 954 | } void bloaty::macho::ReadDebugSectionsFromSegment<segment_command_64, section_64>(bloaty::macho::LoadCommand, bloaty::dwarf::File*, bloaty::RangeSink*) Line | Count | Source | 480 | 1.26k | RangeSink *sink) { | 481 | 1.26k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 482 | 1.26k | string_view segname = ArrayToStr(segment->segname, 16); | 483 | | | 484 | 1.26k | if (segname != "__DWARF") { | 485 | 867 | return; | 486 | 867 | } | 487 | | | 488 | 396 | uint32_t nsects = segment->nsects; | 489 | 3.28k | for (uint32_t j = 0; j < nsects; j++) { | 490 | 2.97k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 491 | 2.97k | string_view sectname = ArrayToStr(section->sectname, 16); | 492 | | | 493 | | // filesize equals vmsize unless the section is zerofill | 494 | 2.97k | uint64_t filesize = section->size; | 495 | 2.97k | switch (section->flags & SECTION_TYPE) { | 496 | 137 | case S_ZEROFILL: | 497 | 231 | case S_GB_ZEROFILL: | 498 | 359 | case S_THREAD_LOCAL_ZEROFILL: | 499 | 359 | filesize = 0; | 500 | 359 | break; | 501 | 2.53k | default: | 502 | 2.53k | break; | 503 | 2.97k | } | 504 | | | 505 | 2.88k | string_view contents = | 506 | 2.88k | StrictSubstr(cmd.file_data, section->offset, filesize); | 507 | | | 508 | 2.88k | if (sectname.find("__debug_") == 0) { | 509 | 22 | sectname.remove_prefix(string_view("__debug_").size()); | 510 | 22 | dwarf->SetFieldByName(sectname, contents); | 511 | 2.86k | } 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 | 2.88k | } | 521 | 396 | } |
void bloaty::macho::ReadDebugSectionsFromSegment<segment_command, section>(bloaty::macho::LoadCommand, bloaty::dwarf::File*, bloaty::RangeSink*) Line | Count | Source | 480 | 1.63k | RangeSink *sink) { | 481 | 1.63k | auto segment = GetStructPointerAndAdvance<Segment>(&cmd.command_data); | 482 | 1.63k | string_view segname = ArrayToStr(segment->segname, 16); | 483 | | | 484 | 1.63k | if (segname != "__DWARF") { | 485 | 1.07k | return; | 486 | 1.07k | } | 487 | | | 488 | 558 | uint32_t nsects = segment->nsects; | 489 | 4.50k | for (uint32_t j = 0; j < nsects; j++) { | 490 | 4.12k | auto section = GetStructPointerAndAdvance<Section>(&cmd.command_data); | 491 | 4.12k | string_view sectname = ArrayToStr(section->sectname, 16); | 492 | | | 493 | | // filesize equals vmsize unless the section is zerofill | 494 | 4.12k | uint64_t filesize = section->size; | 495 | 4.12k | switch (section->flags & SECTION_TYPE) { | 496 | 200 | case S_ZEROFILL: | 497 | 269 | case S_GB_ZEROFILL: | 498 | 389 | case S_THREAD_LOCAL_ZEROFILL: | 499 | 389 | filesize = 0; | 500 | 389 | break; | 501 | 3.55k | default: | 502 | 3.55k | break; | 503 | 4.12k | } | 504 | | | 505 | 3.94k | string_view contents = | 506 | 3.94k | StrictSubstr(cmd.file_data, section->offset, filesize); | 507 | | | 508 | 3.94k | if (sectname.find("__debug_") == 0) { | 509 | 28 | sectname.remove_prefix(string_view("__debug_").size()); | 510 | 28 | dwarf->SetFieldByName(sectname, contents); | 511 | 3.91k | } else if (sectname.find("__zdebug_") == 0) { | 512 | 16 | sectname.remove_prefix(string_view("__zdebug_").size()); | 513 | 16 | string_view *member = dwarf->GetFieldByName(sectname); | 514 | 16 | if (!member || ReadBytes(4, &contents) != "ZLIB") { | 515 | 15 | continue; | 516 | 15 | } | 517 | 1 | auto uncompressed_size = ReadBigEndian<uint64_t>(&contents); | 518 | 1 | *member = sink->ZlibDecompress(contents, uncompressed_size); | 519 | 1 | } | 520 | 3.94k | } | 521 | 558 | } |
|
522 | | |
523 | | static void ReadDebugSectionsFromMachO(const InputFile &file, |
524 | 6.31k | dwarf::File *dwarf, RangeSink *sink) { |
525 | 6.31k | dwarf->file = &file; |
526 | 6.31k | dwarf->open = &ReadDebugSectionsFromMachO; |
527 | 6.31k | ForEachLoadCommand( |
528 | 7.77k | file.data(), nullptr, [dwarf, sink](const LoadCommand &cmd) { |
529 | 7.77k | switch (cmd.cmd) { |
530 | 1.26k | case LC_SEGMENT_64: |
531 | 1.26k | ReadDebugSectionsFromSegment<segment_command_64, section_64>( |
532 | 1.26k | cmd, dwarf, sink); |
533 | 1.26k | break; |
534 | 1.63k | case LC_SEGMENT: |
535 | 1.63k | ReadDebugSectionsFromSegment<segment_command, section>(cmd, dwarf, |
536 | 1.63k | sink); |
537 | 1.63k | break; |
538 | 7.77k | } |
539 | 7.77k | }); |
540 | 6.31k | } |
541 | | |
542 | | class MachOObjectFile : public ObjectFile { |
543 | | public: |
544 | | MachOObjectFile(std::unique_ptr<InputFile> file_data) |
545 | 184k | : ObjectFile(std::move(file_data)) {} |
546 | | |
547 | 184k | std::string GetBuildId() const override { |
548 | 184k | std::string id; |
549 | | |
550 | 225k | ForEachLoadCommand(file_data().data(), nullptr, [&id](LoadCommand cmd) { |
551 | 225k | if (cmd.cmd == LC_UUID) { |
552 | 522 | auto uuid_cmd = |
553 | 522 | GetStructPointerAndAdvance<uuid_command>(&cmd.command_data); |
554 | 522 | if (!cmd.command_data.empty()) { |
555 | 120 | THROWF("Unexpected excess uuid data: $0", cmd.command_data.size()); |
556 | 120 | } |
557 | 402 | id.resize(sizeof(uuid_cmd->uuid)); |
558 | 402 | memcpy(&id[0], &uuid_cmd->uuid[0], sizeof(uuid_cmd->uuid)); |
559 | 402 | } |
560 | 225k | }); |
561 | | |
562 | 184k | return id; |
563 | 184k | } |
564 | | |
565 | 90.8k | void ProcessFile(const std::vector<RangeSink*>& sinks) const override { |
566 | 171k | for (auto sink : sinks) { |
567 | 171k | switch (sink->data_source()) { |
568 | 104k | case DataSource::kSegments: |
569 | 117k | case DataSource::kSections: |
570 | 117k | ParseLoadCommands(sink); |
571 | 117k | break; |
572 | 0 | case DataSource::kSymbols: |
573 | 0 | case DataSource::kRawSymbols: |
574 | 13.4k | case DataSource::kShortSymbols: |
575 | 13.4k | case DataSource::kFullSymbols: |
576 | 13.4k | ParseSymbols(debug_file().file_data().data(), nullptr, sink); |
577 | 13.4k | break; |
578 | 13.4k | case DataSource::kCompileUnits: { |
579 | 13.4k | SymbolTable symtab; |
580 | 13.4k | DualMap symbol_map; |
581 | 13.4k | NameMunger empty_munger; |
582 | 13.4k | RangeSink symbol_sink(&debug_file().file_data(), sink->options(), |
583 | 13.4k | DataSource::kRawSymbols, |
584 | 13.4k | &sinks[0]->MapAtIndex(0), nullptr); |
585 | 13.4k | symbol_sink.AddOutput(&symbol_map, &empty_munger); |
586 | 13.4k | ParseSymbols(debug_file().file_data().data(), &symtab, &symbol_sink); |
587 | 13.4k | dwarf::File dwarf; |
588 | 13.4k | ReadDebugSectionsFromMachO(debug_file().file_data(), &dwarf, sink); |
589 | 13.4k | ReadDWARFCompileUnits(dwarf, symbol_map, sink); |
590 | 13.4k | ParseSymbols(sink->input_file().data(), nullptr, sink); |
591 | 13.4k | break; |
592 | 13.4k | } |
593 | 13.4k | case DataSource::kArchiveMembers: |
594 | 26.8k | case DataSource::kInlines: |
595 | 26.8k | default: |
596 | 26.8k | THROW("Mach-O doesn't support this data source"); |
597 | 171k | } |
598 | 116k | AddMachOFallback(sink); |
599 | 116k | } |
600 | 90.8k | } |
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 | 710k | std::unique_ptr<ObjectFile> TryOpenMachOFile(std::unique_ptr<InputFile> &file) { |
613 | 710k | 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 | 710k | if (magic == MH_MAGIC || magic == MH_MAGIC_64 || magic == FAT_CIGAM) { |
619 | 184k | return std::unique_ptr<ObjectFile>( |
620 | 184k | new macho::MachOObjectFile(std::move(file))); |
621 | 184k | } |
622 | | |
623 | 525k | return nullptr; |
624 | 710k | } |
625 | | |
626 | | } // namespace bloaty |