Coverage Report

Created: 2024-07-27 14:16

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