Coverage Report

Created: 2023-09-24 15:55

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