When a processor faults/is interrupted/gets an exception, it will stop running code and jump to an exception catcher routine. Most processors will store the pc that was executing in a system register, and the catcher functions have special instructions to retrieve that & possibly other registers. It may then save those values to stack, and the author can add .cfi directives to tell lldb's unwinder where to find those saved values. ARM Cortex-M (microcontroller) processors have a simpler mechanism where a fixed set of registers are saved to the stack on an exception, and a unique value is put in the link register to indicate to the caller that this has taken place. No special handling needs to be written into the exception catcher, unless it wants to inspect these preserved values. And it is possible for a general stack walker to walk the stack with no special knowledge about what the catch function does. This patch adds an Architecture plugin method to allow an Architecture to override/augment the UnwindPlan that lldb would use for a stack frame, given the contents of the return address register. It resembles a feature where the LanguageRuntime can replace/augment the unwind plan for a function, but it is doing it at offset by one level. The LanguageRuntime is looking at the local register context and/or symbol name to decide if it will override the unwind rules. For the Cortex-M exception unwinds, we need to modify THIS frame's unwind plan if the CALLER's LR had a specific value. RegisterContextUnwind has to retrieve the caller's LR value before it has completely decided on the UnwindPlan it will use for THIS stack frame. This does mean that we will need one additional read of stack memory than we currently do when unwinding, on Armv7 Cortex-M targets. The unwinder walks the stack lazily, as stack frames are requested, and so now if you ask for 2 stack frames, we will read enough stack to walk 2 frames, plus we will read one extra word of memory, the spilled LR value from the stack. In practice, with 512-byte memory cache reads, this is unlikely to be a real performance hit. This PR includes a test with a yaml corefile description and a JSON ObjectFile, incorporating all of the necessary stack memory and symbol names from a real debug session I worked on. The architectural default unwind plans are used for all stack frames except the 0th because there's no instructions for the functions, and no unwind info. I may need to add an encoding of unwind fules to ObjectFileJSON in the future as we create more test cases like this. This PR depends on the yaml2macho-core utility from https://github.com/llvm/llvm-project/pull/153911 to run its API test. rdar://110663219
129 lines
4.2 KiB
C++
129 lines
4.2 KiB
C++
//===-- ObjectFileJSON.h -------------------------------------- -*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H
|
|
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H
|
|
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "llvm/Support/JSON.h"
|
|
|
|
namespace lldb_private {
|
|
|
|
class ObjectFileJSON : public ObjectFile {
|
|
public:
|
|
static void Initialize();
|
|
static void Terminate();
|
|
|
|
static llvm::StringRef GetPluginNameStatic() { return "JSON"; }
|
|
|
|
static const char *GetPluginDescriptionStatic() {
|
|
return "JSON object file reader.";
|
|
}
|
|
|
|
static ObjectFile *
|
|
CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP data_sp,
|
|
lldb::offset_t data_offset, const FileSpec *file,
|
|
lldb::offset_t file_offset, lldb::offset_t length);
|
|
|
|
static ObjectFile *CreateMemoryInstance(const lldb::ModuleSP &module_sp,
|
|
lldb::WritableDataBufferSP data_sp,
|
|
const lldb::ProcessSP &process_sp,
|
|
lldb::addr_t header_addr);
|
|
|
|
static size_t GetModuleSpecifications(const FileSpec &file,
|
|
lldb::DataBufferSP &data_sp,
|
|
lldb::offset_t data_offset,
|
|
lldb::offset_t file_offset,
|
|
lldb::offset_t length,
|
|
ModuleSpecList &specs);
|
|
|
|
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
|
|
|
|
// LLVM RTTI support
|
|
static char ID;
|
|
bool isA(const void *ClassID) const override {
|
|
return ClassID == &ID || ObjectFile::isA(ClassID);
|
|
}
|
|
static bool classof(const ObjectFile *obj) { return obj->isA(&ID); }
|
|
|
|
bool ParseHeader() override;
|
|
|
|
lldb::ByteOrder GetByteOrder() const override {
|
|
return m_arch.GetByteOrder();
|
|
}
|
|
|
|
bool IsExecutable() const override { return false; }
|
|
|
|
uint32_t GetAddressByteSize() const override {
|
|
return m_arch.GetAddressByteSize();
|
|
}
|
|
|
|
AddressClass GetAddressClass(lldb::addr_t file_addr) override {
|
|
return AddressClass::eInvalid;
|
|
}
|
|
|
|
void ParseSymtab(lldb_private::Symtab &symtab) override;
|
|
|
|
bool IsStripped() override { return false; }
|
|
|
|
void CreateSections(SectionList &unified_section_list) override;
|
|
|
|
void Dump(Stream *s) override {}
|
|
|
|
ArchSpec GetArchitecture() override { return m_arch; }
|
|
|
|
UUID GetUUID() override { return m_uuid; }
|
|
|
|
uint32_t GetDependentModules(FileSpecList &files) override { return 0; }
|
|
|
|
Type CalculateType() override { return m_type; }
|
|
|
|
Strata CalculateStrata() override { return eStrataUser; }
|
|
|
|
bool SetLoadAddress(Target &target, lldb::addr_t value,
|
|
bool value_is_offset) override;
|
|
|
|
static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset,
|
|
lldb::addr_t length);
|
|
|
|
struct Header {
|
|
std::string triple;
|
|
std::string uuid;
|
|
std::optional<ObjectFile::Type> type;
|
|
};
|
|
|
|
struct Body {
|
|
std::vector<JSONSection> sections;
|
|
std::vector<JSONSymbol> symbols;
|
|
};
|
|
|
|
private:
|
|
ArchSpec m_arch;
|
|
UUID m_uuid;
|
|
ObjectFile::Type m_type;
|
|
std::optional<uint64_t> m_size;
|
|
std::vector<JSONSymbol> m_symbols;
|
|
std::vector<JSONSection> m_sections;
|
|
|
|
ObjectFileJSON(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
|
|
lldb::offset_t data_offset, const FileSpec *file,
|
|
lldb::offset_t offset, lldb::offset_t length, ArchSpec arch,
|
|
UUID uuid, Type type, std::vector<JSONSymbol> symbols,
|
|
std::vector<JSONSection> sections);
|
|
};
|
|
|
|
bool fromJSON(const llvm::json::Value &value, ObjectFileJSON::Header &header,
|
|
llvm::json::Path path);
|
|
|
|
bool fromJSON(const llvm::json::Value &value, ObjectFileJSON::Body &body,
|
|
llvm::json::Path path);
|
|
|
|
} // namespace lldb_private
|
|
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_JSON_OBJECTFILEJSON_H
|