// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/40284755): Remove this and spanify to fix the errors. #pragma allow_unsafe_buffers #endif #include "base/debug/dwarf_line_no.h" #include "partition_alloc/pointers/raw_ref.h" #ifdef USE_SYMBOLIZE #include <algorithm> #include <charconv> #include <cstdint> #include <limits> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "base/debug/buffered_dwarf_reader.h" #include "base/third_party/symbolize/symbolize.h" #include "partition_alloc/pointers/raw_ptr.h" namespace base { namespace debug { namespace { constexpr uint64_t kMaxOffset = …; // These numbers are suitable for most compilation units for chrome and // content_shell. If a compilation unit has bigger number of directories or // filenames, the additional directories/filenames will be ignored, and the // stack frames pointing to these directories/filenames will not get line // numbers. We can't set these numbers too big because they affect the size of // ProgramInfo which is allocated in the stack. constexpr int kMaxDirectories = …; constexpr size_t kMaxFilenames = …; // DWARF-4 line number program header, section 6.2.4 struct ProgramInfo { … }; // DWARF-4 line number program registers, section 6.2.2 struct LineNumberRegisters { … }; struct LineNumberInfo { … }; // Evaluates a Line Number Program as defined by the rules in section 6.2.5. void EvaluateLineNumberProgram(const int fd, LineNumberInfo* info, uint64_t base_address, uint64_t start, const ProgramInfo& program_info) { … } // Parses a 32-bit DWARF-4 line number program header per section 6.2.4. // `cu_name_offset` is the module offset for the 0th entry of the file table. bool ParseDwarf4ProgramInfo(BufferedDwarfReader* reader, bool is_64bit, uint64_t cu_name_offset, ProgramInfo* program_info) { … } // Returns the offset of the next byte to read. // `program_info.program_end` is guaranteed to be initlialized to either // `kMaxOffset` if the program length could not be processed, or to // the byte after the end of this program. bool ReadProgramInfo(const int fd, uint64_t start, uint64_t cu_name_offset, ProgramInfo* program_info) { … } // Attempts to find line-number info for all of |info|. Returns the number of // entries that do not have info yet. uint64_t GetLineNumbersInProgram(const int fd, LineNumberInfo* info, uint64_t base_address, uint64_t start, uint64_t cu_name_offset) { … } // Scans the .debug_abbrev entry until it finds the Attribute List matching the // `wanted_abbreviation_code`. This is called when parsing a DIE in .debug_info. bool AdvancedReaderToAttributeList(BufferedDwarfReader& reader, uint64_t table_end, uint64_t wanted_abbreviation_code, uint64_t& tag, bool& has_children) { … } // This reads through a .debug_info compile unit entry to try and extract // the `cu_name_offset` as well as the `debug_line_offset` (offset into the // .debug_lines table` corresponding to `pc`. // // The .debug_info sections are a packed set of bytes whose format is defined // by a corresponding .debug_abbrev entry. Basically .debug_abbrev describes // a struct and .debug_info has a header that tells which struct it is followed // by a bunch of bytes. // // The control flow is to find the .debug_abbrev entry for each .debug_info // entry, then walk through the .debug_abbrev entry to parse the bytes of the // .debug_info entry. A successful parse calculates the address range that the // .debug_info entry covers. When that is retrieved, `pc` can be compared to // the range and a corresponding .debug_info can be found. // // The `debug_info_start` be the start of the whole .debug_info section or an // offset into the section if it was known ahead of time (perhaps by consulting // .debug_aranges). // // To fully interpret this data, the .debug_ranges and .debug_str sections // also need to be interpreted. bool GetCompileUnitName(int fd, uint64_t debug_info_start, uint64_t debug_info_end, uint64_t pc, uint64_t module_base_address, uint64_t* debug_line_offset, uint64_t* cu_name_offset) { … } // Thin wrapper over `GetCompileUnitName` that opens the .debug_info section. bool ReadCompileUnit(int fd, uint64_t pc, uint64_t cu_offset, uint64_t base_address, uint64_t* debug_line_offset, uint64_t* cu_name_offset) { … } // Takes the information from `info` and renders the data located in the // object file `fd` into `out`. The format looks like: // // [../path/to/foo.cc:10:40] // // which would indicate line 10 column 40 in ../path/to/foo.cc void SerializeLineNumberInfoToString(int fd, const LineNumberInfo& info, char* out, size_t out_size) { … } // Reads the Line Number info for a compile unit. bool GetLineNumberInfoFromObject(int fd, uint64_t pc, uint64_t cu_offset, uint64_t base_address, char* out, size_t out_size) { … } struct FrameInfo { … }; // Returns the number of frames still missing info. // // The aranges table is a mapping of ranges to compilation units. Given an array // of `frame_info`, this finds the compile units for each of the frames doing // only one pass over the table. It does not preserve the order of `frame_info`. // // The main benefit of this function is preserving the single pass through the // table which is important for performance. size_t ProcessFlatArangeSet(BufferedDwarfReader* reader, uint64_t next_set, uint8_t address_size, uint64_t base_address, uint64_t cu_offset, FrameInfo* frame_info, size_t num_frames) { … } // This is a pre-step that uses the .debug_aranges table to find all the compile // units for a given set of frames. This allows code to avoid iterating over // all compile units at a later step in the symbolization process. void PopulateCompileUnitOffsets(int fd, FrameInfo* frame_info, size_t num_frames, uint64_t base_address) { … } } // namespace bool GetDwarfSourceLineNumber(const void* pc, uint64_t cu_offset, char* out, size_t out_size) { … } void GetDwarfCompileUnitOffsets(const void* const* trace, uint64_t* cu_offsets, size_t num_frames) { … } } // namespace debug } // namespace base #else // USE_SYMBOLIZE #include <cstring> namespace base { namespace debug { bool GetDwarfSourceLineNumber(const void* pc, uint64_t cu_offset, char* out, size_t out_size) { return false; } void GetDwarfCompileUnitOffsets(const void* const* trace, uint64_t* cu_offsets, size_t num_frames) { // Provide defined values even in the stub. memset(cu_offsets, 0, sizeof(cu_offsets) * num_frames); } } // namespace debug } // namespace base #endif