diff options
Diffstat (limited to 'src/tools/mac')
6 files changed, 207 insertions, 230 deletions
diff --git a/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj index 5b644e24..c98b6ac5 100644 --- a/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj +++ b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj @@ -1251,7 +1251,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 8B3102D411F0D60300FCF3E4 /* BreakpadDebug.xcconfig */; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; HEADER_SEARCH_PATHS = ( ../../.., ../../../common/mac/include/, @@ -1264,7 +1264,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 8B3102D511F0D60300FCF3E4 /* BreakpadRelease.xcconfig */; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; HEADER_SEARCH_PATHS = ( ../../.., ../../../common/mac/include/, diff --git a/src/tools/mac/dump_syms/dump_syms_tool.cc b/src/tools/mac/dump_syms/dump_syms_tool.cc index 2e05cbf3..9fb8d13f 100644 --- a/src/tools/mac/dump_syms/dump_syms_tool.cc +++ b/src/tools/mac/dump_syms/dump_syms_tool.cc @@ -31,6 +31,10 @@ // dump_syms_tool.cc: Command line tool that uses the DumpSymbols class. // TODO(waylonis): accept stdin +#ifdef HAVE_CONFIG_H +#include <config.h> // Must come first +#endif + #include <mach-o/arch.h> #include <unistd.h> @@ -59,16 +63,20 @@ struct Options { cfi(true), handle_inter_cu_refs(true), handle_inlines(false), - enable_multiple(false) {} + enable_multiple(false), + module_name(), + prefer_extern_name(false) {} string srcPath; string dsymPath; - const NXArchInfo *arch; + std::optional<ArchInfo> arch; bool header_only; bool cfi; bool handle_inter_cu_refs; bool handle_inlines; bool enable_multiple; + string module_name; + bool prefer_extern_name; }; static bool StackFrameEntryComparator(const Module::StackFrameEntry* a, @@ -117,11 +125,12 @@ static void CopyCFIDataBetweenModules(Module* to_module, } static bool SetArchitecture(DumpSymbols& dump_symbols, - const NXArchInfo* arch, + const ArchInfo& arch, const std::string& filename) { - if (!dump_symbols.SetArchitecture(arch->cputype, arch->cpusubtype)) { + if (!dump_symbols.SetArchitecture(arch)) { fprintf(stderr, "%s: no architecture '%s' is present in file.\n", - filename.c_str(), arch->name); + filename.c_str(), + GetNameFromCPUType(arch.cputype, arch.cpusubtype)); size_t available_size; const SuperFatArch* available = dump_symbols.AvailableArchitectures(&available_size); @@ -131,14 +140,8 @@ static bool SetArchitecture(DumpSymbols& dump_symbols, fprintf(stderr, "architectures present in the file are:\n"); for (size_t i = 0; i < available_size; i++) { const SuperFatArch* arch = &available[i]; - const NXArchInfo* arch_info = - google_breakpad::BreakpadGetArchInfoFromCpuType(arch->cputype, - arch->cpusubtype); - if (arch_info) - fprintf(stderr, "%s (%s)\n", arch_info->name, arch_info->description); - else - fprintf(stderr, "unrecognized cpu type 0x%x, subtype 0x%x\n", - arch->cputype, arch->cpusubtype); + fprintf(stderr, "%s\n", + GetNameFromCPUType(arch->cputype, arch->cpusubtype)); } return false; } @@ -150,7 +153,8 @@ static bool Start(const Options& options) { (options.handle_inlines ? INLINES : NO_DATA) | (options.cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES; DumpSymbols dump_symbols(symbol_data, options.handle_inter_cu_refs, - options.enable_multiple); + options.enable_multiple, options.module_name, + options.prefer_extern_name); // For x86_64 binaries, the CFI data is in the __TEXT,__eh_frame of the // Mach-O file, which is not copied into the dSYM. Whereas in i386, the CFI @@ -169,7 +173,7 @@ static bool Start(const Options& options) { return false; if (options.arch && - !SetArchitecture(dump_symbols, options.arch, primary_file)) { + !SetArchitecture(dump_symbols, *options.arch, primary_file)) { return false; } @@ -189,7 +193,7 @@ static bool Start(const Options& options) { return false; if (options.arch && - !SetArchitecture(dump_symbols, options.arch, options.srcPath)) { + !SetArchitecture(dump_symbols, *options.arch, options.srcPath)) { return false; } Module* cfi_module = NULL; @@ -197,13 +201,38 @@ static bool Start(const Options& options) { return false; scoped_ptr<Module> scoped_cfi_module(cfi_module); + bool name_matches; + if (!options.module_name.empty()) { + // Ignore the basename of the dSYM and binary and use the passed-in module + // name. + name_matches = true; + } else { + name_matches = cfi_module->name() == module->name(); + } + // Ensure that the modules are for the same debug code file. - if (cfi_module->name() != module->name() || - cfi_module->os() != module->os() || + if (!name_matches || cfi_module->os() != module->os() || cfi_module->architecture() != module->architecture() || cfi_module->identifier() != module->identifier()) { fprintf(stderr, "Cannot generate a symbol file from split sources that do" " not match.\n"); + if (!name_matches) { + fprintf(stderr, "Name mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->name().c_str(), module->name().c_str()); + } + if (cfi_module->os() != module->os()) { + fprintf(stderr, "OS mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->os().c_str(), module->os().c_str()); + } + if (cfi_module->architecture() != module->architecture()) { + fprintf(stderr, "Architecture mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->architecture().c_str(), + module->architecture().c_str()); + } + if (cfi_module->identifier() != module->identifier()) { + fprintf(stderr, "Identifier mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->identifier().c_str(), module->identifier().c_str()); + } return false; } @@ -216,8 +245,10 @@ static bool Start(const Options& options) { //============================================================================= static void Usage(int argc, const char *argv[]) { fprintf(stderr, "Output a Breakpad symbol file from a Mach-o file.\n"); - fprintf(stderr, "Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] " - "<Mach-o file>\n", argv[0]); + fprintf(stderr, + "Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] " + "[-n MODULE] [-x] <Mach-o file>\n", + argv[0]); fprintf(stderr, "\t-i: Output module header information only.\n"); fprintf(stderr, "\t-a: Architecture type [default: native, or whatever is\n"); fprintf(stderr, "\t in the file, if it contains only one architecture]\n"); @@ -229,6 +260,12 @@ static void Usage(int argc, const char *argv[]) { fprintf(stderr, "\t-m: Enable writing the optional 'm' field on FUNC " "and PUBLIC, denoting multiple symbols for the address.\n"); + fprintf(stderr, + "\t-n: Use MODULE as the name of the module rather than \n" + "the basename of the Mach-O file/dSYM.\n"); + fprintf(stderr, + "\t-x: Prefer the PUBLIC (extern) name over the FUNC if\n" + "they do not match.\n"); fprintf(stderr, "\t-h: Usage\n"); fprintf(stderr, "\t-?: Usage\n"); } @@ -238,14 +275,13 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { extern int optind; signed char ch; - while ((ch = getopt(argc, (char* const*)argv, "ia:g:crdm?h")) != -1) { + while ((ch = getopt(argc, (char* const*)argv, "ia:g:crdm?hn:x")) != -1) { switch (ch) { case 'i': options->header_only = true; break; case 'a': { - const NXArchInfo *arch_info = - google_breakpad::BreakpadGetArchInfoFromName(optarg); + std::optional<ArchInfo> arch_info = GetArchInfoFromName(optarg); if (!arch_info) { fprintf(stderr, "%s: Invalid architecture: %s\n", argv[0], optarg); Usage(argc, argv); @@ -269,6 +305,12 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { case 'm': options->enable_multiple = true; break; + case 'n': + options->module_name = optarg; + break; + case 'x': + options->prefer_extern_name = true; + break; case '?': case 'h': Usage(argc, argv); diff --git a/src/tools/mac/dump_syms/macho_dump.cc b/src/tools/mac/dump_syms/macho_dump.cc deleted file mode 100644 index b724cc74..00000000 --- a/src/tools/mac/dump_syms/macho_dump.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2010 Google LLC -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google LLC nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// macho_dump.cc: Dump the contents of a Mach-O file. This is mostly -// a test program for the Mach_O::FatReader and Mach_O::Reader classes. - -#include <errno.h> -#include <fcntl.h> -#include <mach-o/arch.h> -#include <sys/mman.h> -#include <stdint.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <sstream> -#include <string> -#include <vector> - -#include "common/byte_cursor.h" -#include "common/mac/arch_utilities.h" -#include "common/mac/macho_reader.h" -#include "common/path_helper.h" - -using google_breakpad::ByteBuffer; -using std::ostringstream; -using std::string; -using std::vector; - -namespace { -namespace mach_o = google_breakpad::mach_o; - -string program_name; - -int check_syscall(int result, const char* operation, const char* filename) { - if (result < 0) { - fprintf(stderr, "%s: %s '%s': %s\n", - program_name.c_str(), operation, - filename, strerror(errno)); - exit(1); - } - return result; -} - -class DumpSection: public mach_o::Reader::SectionHandler { - public: - DumpSection() : index_(0) { } - bool HandleSection(const mach_o::Section& section) { - printf(" section %d '%s' in segment '%s'\n" - " address: 0x%llx\n" - " alignment: 1 << %d B\n" - " flags: %d\n" - " size: %ld\n", - index_++, section.section_name.c_str(), section.segment_name.c_str(), - section.address, section.align, - mach_o::SectionFlags(section.flags), - section.contents.Size()); - return true; - } - - private: - int index_; -}; - -class DumpCommand: public mach_o::Reader::LoadCommandHandler { - public: - DumpCommand(mach_o::Reader* reader) : reader_(reader), index_(0) { } - bool UnknownCommand(mach_o::LoadCommandType type, - const ByteBuffer& contents) { - printf(" load command %d: %d", index_++, type); - return true; - } - bool SegmentCommand(const mach_o::Segment& segment) { - printf(" load command %d: %s-bit segment '%s'\n" - " address: 0x%llx\n" - " memory size: 0x%llx\n" - " maximum protection: 0x%x\n" - " initial protection: 0x%x\n" - " flags: %d\n" - " section_list size: %ld B\n", - index_++, (segment.bits_64 ? "64" : "32"), segment.name.c_str(), - segment.vmaddr, segment.vmsize, segment.maxprot, - segment.initprot, mach_o::SegmentFlags(segment.flags), - segment.section_list.Size()); - - DumpSection dump_section; - return reader_->WalkSegmentSections(segment, &dump_section); - } - private: - mach_o::Reader* reader_; - int index_; -}; - -void DumpFile(const char* filename) { - int fd = check_syscall(open(filename, O_RDONLY), "opening", filename); - struct stat attributes; - check_syscall(fstat(fd, &attributes), - "getting file attributes for", filename); - void* mapping = mmap(NULL, attributes.st_size, PROT_READ, - MAP_PRIVATE, fd, 0); - close(fd); - check_syscall(mapping == (void*)-1 ? -1 : 0, - "mapping contents of", filename); - - mach_o::FatReader::Reporter fat_reporter(filename); - mach_o::FatReader fat_reader(&fat_reporter); - if (!fat_reader.Read(reinterpret_cast<uint8_t*>(mapping), - attributes.st_size)) { - exit(1); - } - printf("filename: %s\n", filename); - size_t object_files_size; - const SuperFatArch* super_fat_object_files = - fat_reader.object_files(&object_files_size); - struct fat_arch* object_files; - if (!super_fat_object_files->ConvertToFatArch(object_files)) { - exit(1); - } - printf(" object file count: %ld\n", object_files_size); - for (size_t i = 0; i < object_files_size; i++) { - const struct fat_arch& file = object_files[i]; - const NXArchInfo* fat_arch_info = - google_breakpad::BreakpadGetArchInfoFromCpuType( - file.cputype, file.cpusubtype); - printf("\n object file %ld:\n" - " fat header:\n:" - " CPU type: %s (%s)\n" - " size: %d B\n" - " alignment: 1<<%d B\n", - i, fat_arch_info->name, fat_arch_info->description, - file.size, file.align); - - ostringstream name; - name << filename; - if (object_files_size > 1) - name << ", object file #" << i; - ByteBuffer file_contents(reinterpret_cast<uint8_t*>(mapping) - + file.offset, file.size); - mach_o::Reader::Reporter reporter(name.str()); - mach_o::Reader reader(&reporter); - if (!reader.Read(file_contents, file.cputype, file.cpusubtype)) { - exit(1); - } - - const NXArchInfo* macho_arch_info = - NXGetArchInfoFromCpuType(reader.cpu_type(), - reader.cpu_subtype()); - printf(" Mach-O header:\n" - " word size: %s\n" - " CPU type: %s (%s)\n" - " File type: %d\n" - " flags: %x\n", - (reader.bits_64() ? "64 bits" : "32 bits"), - macho_arch_info->name, macho_arch_info->description, - reader.file_type(), reader.flags()); - - DumpCommand dump_command(&reader); - reader.WalkLoadCommands(&dump_command); - } - munmap(mapping, attributes.st_size); -} - -} // namespace - -int main(int argc, char** argv) { - program_name = google_breakpad::BaseName(argv[0]); - if (argc == 1) { - fprintf(stderr, "Usage: %s FILE ...\n" - "Dump the contents of the Mach-O or fat binary files " - "'FILE ...'.\n", program_name.c_str()); - } - for (int i = 1; i < argc; i++) { - DumpFile(argv[i]); - } -} diff --git a/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj b/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj index 903c66f1..b1dd6a11 100644 --- a/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj +++ b/src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj @@ -371,7 +371,7 @@ 1DEB927508733DD40010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; GCC_PREPROCESSOR_DEFINITIONS = HAVE_MACH_O_NLIST_H; HEADER_SEARCH_PATHS = ( ../../.., @@ -385,7 +385,7 @@ 1DEB927608733DD40010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", NDEBUG, diff --git a/src/tools/mac/upload_system_symbols/upload_system_symbols.go b/src/tools/mac/upload_system_symbols/upload_system_symbols.go index ba067276..f34c288a 100644 --- a/src/tools/mac/upload_system_symbols/upload_system_symbols.go +++ b/src/tools/mac/upload_system_symbols/upload_system_symbols.go @@ -163,6 +163,29 @@ func main() { } } +// manglePath reduces an absolute filesystem path to a string suitable as the +// base for a file name which encodes some of the original path. The result +// concatenates the leading initial from each path component except the last to +// the last path component; for example /System/Library/Frameworks/AppKit +// becomes SLFAppKit. +// Assumes ASCII. +func manglePath(path string) string { + components := strings.Split(path, "/") + n := len(components) + builder := strings.Builder{} + for i, component := range components { + if len(component) == 0 { + continue + } + if i < n-1 { + builder.WriteString(component[:1]) + } else { + builder.WriteString(component) + } + } + return builder.String() +} + type WorkerPool struct { wg sync.WaitGroup } @@ -296,7 +319,7 @@ func (dq *DumpQueue) worker() { dumpSyms := path.Join(*breakpadTools, "dump_syms") for req := range dq.queue { - filebase := path.Join(dq.dumpPath, strings.Replace(req.path, "/", "_", -1)) + filebase := path.Join(dq.dumpPath, manglePath(req.path)) symfile := fmt.Sprintf("%s_%s.sym", filebase, req.arch) f, err := os.Create(symfile) if err != nil { diff --git a/src/tools/mac/upload_system_symbols/upload_system_symbols.sh b/src/tools/mac/upload_system_symbols/upload_system_symbols.sh new file mode 100755 index 00000000..43fd98ed --- /dev/null +++ b/src/tools/mac/upload_system_symbols/upload_system_symbols.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Copyright 2023 Google LLC +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google LLC nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Finds the dyld_shared_cache on a system, extracts it, and dumps the symbols +# in Breakpad format to the directory passed as the first argument +# The script must be in the same directory as `dump_syms`, +# `upload_system_symbols` and `dsc_extractor` binaries. +# Exits with 0 if all supported architectures for this OS version were found and +# dumped, and nonzero otherwise. + +set -ex + +if [[ $# -ne 1 ]]; then + echo "usage: $0 <destination_directory>" >& 2 + exit 1 +fi + +destination_dir="$1" + +dir="$(dirname "$0")" +dir="$(cd "${dir}"; pwd)" +major_version=$(sw_vers -productVersion | cut -d . -f 1) +if [[ "${major_version}" -lt 13 ]]; then + dsc_directory="/System/Library/dyld" +else + dsc_directory="/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld" +fi + +working_dir=$(mktemp -d) +mkdir "${destination_dir}" +trap 'rm -rf "${working_dir}" "${destination_dir}"' EXIT + +architectures=(x86_64h) +missing_architectures=() +# macOS >= 13 on arm64 still has a x86_64 cache for Rosetta. +if [[ "${major_version}" -lt 13 ]] || [[ $(uname -p) == "arm" ]]; then + architectures+=( x86_64 ) +fi +if [[ "${major_version}" -ge 11 ]]; then + architectures+=( arm64e ) +fi + +for arch in "${architectures[@]}"; do + cache="${dsc_directory}/dyld_shared_cache_${arch}" + if [[ ! -f "${cache}" ]]; then + missing_architectures+=("${arch}") + continue + fi + "${dir}/dsc_extractor" \ + "${cache}" \ + "${working_dir}/${arch}" + "${dir}/upload_system_symbols" \ + --breakpad-tools="${dir}" \ + --system-root="${working_dir}/${arch}" \ + --dump-to="${destination_dir}" +done +if [[ "${#missing_architectures[@]}" -eq "${#architectures[@]}" ]]; then + echo "Couldn't locate dyld_shared_cache for any architectures" >& 2 + echo "in ${dsc_directory}. Exiting." >& 2 + exit 1 +fi + +rm -rf "${working_dir}" +# We have results now, so let's keep `destination_dir`. +trap '' EXIT + +"${dir}/upload_system_symbols" \ + --breakpad-tools="${dir}" \ + --system-root=/ \ + --dump-to="${destination_dir}" + +set +x +echo +echo "Dumped!" +echo "To upload, run:" +echo +echo "'${dir}/upload_system_symbols'" \\ +echo " --breakpad-tools='${dir}'" \\ +echo " --api-key=<YOUR API KEY>" \\ +echo " --upload-from='${destination_dir}'" + +if [[ "${#missing_architectures[@]}" -gt 0 ]]; then + echo "dyld_shared_cache not found for architecture(s):" >& 2 + echo " " "${missing_architectures[@]}" >& 2 + echo "You'll need to get symbols for them elsewhere." >& 2 + exit 1 +fi |