eh-frame.h 9.29 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#ifndef V8_DIAGNOSTICS_EH_FRAME_H_
#define V8_DIAGNOSTICS_EH_FRAME_H_
7

8
#include "src/base/compiler-specific.h"
9
#include "src/base/memory.h"
10
#include "src/codegen/register-arch.h"
11
#include "src/common/globals.h"
12
#include "src/zone/zone-containers.h"
13 14 15 16

namespace v8 {
namespace internal {

17 18
class CodeDesc;

19 20
class V8_EXPORT_PRIVATE EhFrameConstants final
    : public NON_EXPORTED_BASE(AllStatic) {
21 22 23 24 25 26
 public:
  enum class DwarfOpcodes : byte {
    kNop = 0x00,
    kAdvanceLoc1 = 0x02,
    kAdvanceLoc2 = 0x03,
    kAdvanceLoc4 = 0x04,
27
    kRestoreExtended = 0x06,
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    kSameValue = 0x08,
    kDefCfa = 0x0c,
    kDefCfaRegister = 0x0d,
    kDefCfaOffset = 0x0e,
    kOffsetExtendedSf = 0x11,
  };

  enum DwarfEncodingSpecifiers : byte {
    kUData4 = 0x03,
    kSData4 = 0x0b,
    kPcRel = 0x10,
    kDataRel = 0x30,
    kOmit = 0xff,
  };

  static const int kLocationTag = 1;
  static const int kLocationMask = 0x3f;
  static const int kLocationMaskSize = 6;

  static const int kSavedRegisterTag = 2;
  static const int kSavedRegisterMask = 0x3f;
  static const int kSavedRegisterMaskSize = 6;

  static const int kFollowInitialRuleTag = 3;
  static const int kFollowInitialRuleMask = 0x3f;
  static const int kFollowInitialRuleMaskSize = 6;

  static const int kProcedureAddressOffsetInFde = 2 * kInt32Size;
  static const int kProcedureSizeOffsetInFde = 3 * kInt32Size;

  static const int kInitialStateOffsetInCie = 19;
  static const int kEhFrameTerminatorSize = 4;

  // Defined in eh-writer-<arch>.cc
  static const int kCodeAlignmentFactor;
  static const int kDataAlignmentFactor;

  static const int kFdeVersionSize = 1;
  static const int kFdeEncodingSpecifiersSize = 3;

  static const int kEhFrameHdrVersion = 1;
  static const int kEhFrameHdrSize = 20;
};

72
class V8_EXPORT_PRIVATE EhFrameWriter {
73 74
 public:
  explicit EhFrameWriter(Zone* zone);
75 76
  EhFrameWriter(const EhFrameWriter&) = delete;
  EhFrameWriter& operator=(const EhFrameWriter&) = delete;
77 78 79 80 81 82

  // The empty frame is a hack to trigger fp-based unwinding in Linux perf
  // compiled with libunwind support when processing DWARF-based call graphs.
  //
  // It is effectively a valid eh_frame_hdr with an empty look up table.
  //
83
  static void WriteEmptyEhFrame(std::ostream& stream);
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

  // Write the CIE and FDE header. Call it before any other method.
  void Initialize();

  void AdvanceLocation(int pc_offset);

  // The <base_address> is the one to which all <offset>s in SaveRegisterToStack
  // directives are relative. It is given by <base_register> + <base_offset>.
  //
  // The <base_offset> must be positive or 0.
  //
  void SetBaseAddressRegister(Register base_register);
  void SetBaseAddressOffset(int base_offset);
  void IncreaseBaseAddressOffset(int base_delta) {
    SetBaseAddressOffset(base_offset_ + base_delta);
  }
  void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset);

  // Register saved at location <base_address> + <offset>.
  // The <offset> must be a multiple of EhFrameConstants::kDataAlignment.
  void RecordRegisterSavedToStack(Register name, int offset) {
    RecordRegisterSavedToStack(RegisterToDwarfCode(name), offset);
  }

108 109 110 111
  // Directly accepts a DWARF register code, needed for
  // handling pseudo-registers on some platforms.
  void RecordRegisterSavedToStack(int dwarf_register_code, int offset);

112 113
  // The register has not been modified from the previous frame.
  void RecordRegisterNotModified(Register name);
114
  void RecordRegisterNotModified(int dwarf_register_code);
115 116 117

  // The register follows the rule defined in the CIE.
  void RecordRegisterFollowsInitialRule(Register name);
118
  void RecordRegisterFollowsInitialRule(int dwarf_register_code);
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

  void Finish(int code_size);

  // Remember to call Finish() before GetEhFrame().
  //
  // The EhFrameWriter instance owns the buffer pointed by
  // CodeDesc::unwinding_info, and must outlive any use of the CodeDesc.
  //
  void GetEhFrame(CodeDesc* desc);

  int last_pc_offset() const { return last_pc_offset_; }
  Register base_register() const { return base_register_; }
  int base_offset() const { return base_offset_; }

 private:
  enum class InternalState { kUndefined, kInitialized, kFinalized };

  static const uint32_t kInt32Placeholder = 0xdeadc0de;

  void WriteSLeb128(int32_t value);
  void WriteULeb128(uint32_t value);

  void WriteByte(byte value) { eh_frame_buffer_.push_back(value); }
  void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode) {
    WriteByte(static_cast<byte>(opcode));
  }
  void WriteBytes(const byte* start, int size) {
    eh_frame_buffer_.insert(eh_frame_buffer_.end(), start, start + size);
  }
  void WriteInt16(uint16_t value) {
    WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
  }
  void WriteInt32(uint32_t value) {
    WriteBytes(reinterpret_cast<const byte*>(&value), sizeof(value));
  }
  void PatchInt32(int base_offset, uint32_t value) {
155
    DCHECK_EQ(
156 157
        base::ReadUnalignedValue<uint32_t>(
            reinterpret_cast<Address>(eh_frame_buffer_.data()) + base_offset),
158
        kInt32Placeholder);
159
    DCHECK_LT(base_offset + kInt32Size, eh_frame_offset());
160
    base::WriteUnalignedValue<uint32_t>(
161 162
        reinterpret_cast<Address>(eh_frame_buffer_.data()) + base_offset,
        value);
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
  }

  // Write the common information entry, which includes encoding specifiers,
  // alignment factors, the return address (pseudo) register code and the
  // directives to construct the initial state of the unwinding table.
  void WriteCie();

  // Write the header of the function data entry, containing a pointer to the
  // correspondent CIE and the position and size of the associated routine.
  void WriteFdeHeader();

  // Write the contents of the .eh_frame_hdr section, including encoding
  // specifiers and the routine => FDE lookup table.
  void WriteEhFrameHdr(int code_size);

  // Write nops until the size reaches a multiple of 8 bytes.
  void WritePaddingToAlignedSize(int unpadded_size);

  int GetProcedureAddressOffset() const {
    return fde_offset() + EhFrameConstants::kProcedureAddressOffsetInFde;
  }

  int GetProcedureSizeOffset() const {
    return fde_offset() + EhFrameConstants::kProcedureSizeOffsetInFde;
  }

  int eh_frame_offset() const {
    return static_cast<int>(eh_frame_buffer_.size());
  }

  int fde_offset() const { return cie_size_; }

  // Platform specific functions implemented in eh-frame-<arch>.cc

  static int RegisterToDwarfCode(Register name);

  // Write directives to build the initial state in the CIE.
  void WriteInitialStateInCie();

  // Write the return address (pseudo) register code.
  void WriteReturnAddressRegisterCode();

  int cie_size_;
  int last_pc_offset_;
  InternalState writer_state_;
  Register base_register_;
  int base_offset_;
  ZoneVector<byte> eh_frame_buffer_;
};

213
class V8_EXPORT_PRIVATE EhFrameIterator {
214
 public:
215 216 217 218 219 220 221
  EhFrameIterator(const byte* start, const byte* end)
      : start_(start), next_(start), end_(end) {
    DCHECK_LE(start, end);
  }

  void SkipCie() {
    DCHECK_EQ(next_, start_);
222 223 224
    next_ +=
        base::ReadUnalignedValue<uint32_t>(reinterpret_cast<Address>(next_)) +
        kInt32Size;
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
  }

  void SkipToFdeDirectives() {
    SkipCie();
    // Skip the FDE header.
    Skip(kDirectivesOffsetInFde);
  }

  void Skip(int how_many) {
    DCHECK_GE(how_many, 0);
    next_ += how_many;
    DCHECK_LE(next_, end_);
  }

  uint32_t GetNextUInt32() { return GetNextValue<uint32_t>(); }
  uint16_t GetNextUInt16() { return GetNextValue<uint16_t>(); }
  byte GetNextByte() { return GetNextValue<byte>(); }
  EhFrameConstants::DwarfOpcodes GetNextOpcode() {
    return static_cast<EhFrameConstants::DwarfOpcodes>(GetNextByte());
  }

  uint32_t GetNextULeb128();
  int32_t GetNextSLeb128();

  bool Done() const {
    DCHECK_LE(next_, end_);
    return next_ == end_;
  }
253

254 255 256 257
  int GetCurrentOffset() const {
    DCHECK_GE(next_, start_);
    return static_cast<int>(next_ - start_);
  }
258

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
  int GetBufferSize() { return static_cast<int>(end_ - start_); }

  const void* current_address() const {
    return reinterpret_cast<const void*>(next_);
  }

 private:
  static const int kDirectivesOffsetInFde = 4 * kInt32Size + 1;

  static uint32_t DecodeULeb128(const byte* encoded, int* encoded_size);
  static int32_t DecodeSLeb128(const byte* encoded, int* encoded_size);

  template <typename T>
  T GetNextValue() {
    T result;
    DCHECK_LE(next_ + sizeof(result), end_);
275
    result = base::ReadUnalignedValue<T>(reinterpret_cast<Address>(next_));
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
    next_ += sizeof(result);
    return result;
  }

  const byte* start_;
  const byte* next_;
  const byte* end_;
};

#ifdef ENABLE_DISASSEMBLER

class EhFrameDisassembler final {
 public:
  EhFrameDisassembler(const byte* start, const byte* end)
      : start_(start), end_(end) {
    DCHECK_LT(start, end);
  }
293 294
  EhFrameDisassembler(const EhFrameDisassembler&) = delete;
  EhFrameDisassembler& operator=(const EhFrameDisassembler&) = delete;
295

296
  void DisassembleToStream(std::ostream& stream);
297 298

 private:
299 300
  static void DumpDwarfDirectives(std::ostream& stream, const byte* start,
                                  const byte* end);
301 302 303 304 305

  static const char* DwarfRegisterCodeToString(int code);

  const byte* start_;
  const byte* end_;
306 307
};

308 309
#endif

310 311 312
}  // namespace internal
}  // namespace v8

313
#endif  // V8_DIAGNOSTICS_EH_FRAME_H_