summaryrefslogtreecommitdiff
path: root/lib/Target/AArch64/Disassembler/AArch64ExternalSymbolizer.cpp
blob: 24663684a3fdf29f6595e835a7dcbd664b097afd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
155
156
157
158
159
160
161
162
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
213
214
215
216
217
218
219
220
221
//===- AArch64ExternalSymbolizer.cpp - Symbolizer for AArch64 ---*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "AArch64ExternalSymbolizer.h"
#include "AArch64Subtarget.h"
#include "MCTargetDesc/AArch64AddressingModes.h"
#include "Utils/AArch64BaseInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

#define DEBUG_TYPE "aarch64-disassembler"

static MCSymbolRefExpr::VariantKind
getVariant(uint64_t LLVMDisassembler_VariantKind) {
  switch (LLVMDisassembler_VariantKind) {
  case LLVMDisassembler_VariantKind_None:
    return MCSymbolRefExpr::VK_None;
  case LLVMDisassembler_VariantKind_ARM64_PAGE:
    return MCSymbolRefExpr::VK_PAGE;
  case LLVMDisassembler_VariantKind_ARM64_PAGEOFF:
    return MCSymbolRefExpr::VK_PAGEOFF;
  case LLVMDisassembler_VariantKind_ARM64_GOTPAGE:
    return MCSymbolRefExpr::VK_GOTPAGE;
  case LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF:
    return MCSymbolRefExpr::VK_GOTPAGEOFF;
  case LLVMDisassembler_VariantKind_ARM64_TLVP:
  case LLVMDisassembler_VariantKind_ARM64_TLVOFF:
  default:
    assert(0 && "bad LLVMDisassembler_VariantKind");
    return MCSymbolRefExpr::VK_None;
  }
}

/// tryAddingSymbolicOperand - tryAddingSymbolicOperand trys to add a symbolic
/// operand in place of the immediate Value in the MCInst.  The immediate
/// Value has not had any PC adjustment made by the caller. If the instruction
/// is a branch that adds the PC to the immediate Value then isBranch is
/// Success, else Fail. If GetOpInfo is non-null, then it is called to get any
/// symbolic information at the Address for this instrution.  If that returns
/// non-zero then the symbolic information it returns is used to create an
/// MCExpr and that is added as an operand to the MCInst.  If GetOpInfo()
/// returns zero and isBranch is Success then a symbol look up for
/// Address + Value is done and if a symbol is found an MCExpr is created with
/// that, else an MCExpr with Address + Value is created.  If GetOpInfo()
/// returns zero and isBranch is Fail then the the Opcode of the MCInst is
/// tested and for ADRP an other instructions that help to load of pointers
/// a symbol look up is done to see it is returns a specific reference type
/// to add to the comment stream.  This function returns Success if it adds
/// an operand to the MCInst and Fail otherwise.
bool AArch64ExternalSymbolizer::tryAddingSymbolicOperand(
    MCInst &MI, raw_ostream &CommentStream, int64_t Value, uint64_t Address,
    bool IsBranch, uint64_t Offset, uint64_t InstSize) {
  // FIXME: This method shares a lot of code with
  //        MCExternalSymbolizer::tryAddingSymbolicOperand. It may be possible
  //        refactor the MCExternalSymbolizer interface to allow more of this
  //        implementation to be shared.
  //
  struct LLVMOpInfo1 SymbolicOp;
  memset(&SymbolicOp, '\0', sizeof(struct LLVMOpInfo1));
  SymbolicOp.Value = Value;
  uint64_t ReferenceType;
  const char *ReferenceName;
  if (!GetOpInfo ||
      !GetOpInfo(DisInfo, Address, 0 /* Offset */, InstSize, 1, &SymbolicOp)) {
    if (IsBranch) {
      ReferenceType = LLVMDisassembler_ReferenceType_In_Branch;
      const char *Name = SymbolLookUp(DisInfo, Address + Value, &ReferenceType,
                                      Address, &ReferenceName);
      if (Name) {
        SymbolicOp.AddSymbol.Name = Name;
        SymbolicOp.AddSymbol.Present = true;
        SymbolicOp.Value = 0;
      } else {
        SymbolicOp.Value = Address + Value;
      }
      if (ReferenceType == LLVMDisassembler_ReferenceType_Out_SymbolStub)
        CommentStream << "symbol stub for: " << ReferenceName;
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_Objc_Message)
        CommentStream << "Objc message: " << ReferenceName;
    } else if (MI.getOpcode() == AArch64::ADRP) {
        ReferenceType = LLVMDisassembler_ReferenceType_In_ARM64_ADRP;
        // otool expects the fully encoded ADRP instruction to be passed in as
        // the value here, so reconstruct it:
        const MCRegisterInfo &MCRI = *Ctx.getRegisterInfo();
        uint32_t EncodedInst = 0x90000000;
        EncodedInst |= (Value & 0x3) << 29; // immlo
        EncodedInst |= ((Value >> 2) & 0x7FFFF) << 5; // immhi
        EncodedInst |= MCRI.getEncodingValue(MI.getOperand(0).getReg()); // reg
        SymbolLookUp(DisInfo, EncodedInst, &ReferenceType, Address,
                     &ReferenceName);
        CommentStream << format("0x%llx",
                                0xfffffffffffff000LL & (Address + Value));
    } else if (MI.getOpcode() == AArch64::ADDXri ||
               MI.getOpcode() == AArch64::LDRXui ||
               MI.getOpcode() == AArch64::LDRXl ||
               MI.getOpcode() == AArch64::ADR) {
      if (MI.getOpcode() == AArch64::ADDXri)
        ReferenceType = LLVMDisassembler_ReferenceType_In_ARM64_ADDXri;
      else if (MI.getOpcode() == AArch64::LDRXui)
        ReferenceType = LLVMDisassembler_ReferenceType_In_ARM64_LDRXui;
      if (MI.getOpcode() == AArch64::LDRXl) {
        ReferenceType = LLVMDisassembler_ReferenceType_In_ARM64_LDRXl;
        SymbolLookUp(DisInfo, Address + Value, &ReferenceType, Address,
                     &ReferenceName);
      } else if (MI.getOpcode() == AArch64::ADR) {
        ReferenceType = LLVMDisassembler_ReferenceType_In_ARM64_ADR;
        SymbolLookUp(DisInfo, Address + Value, &ReferenceType, Address,
                            &ReferenceName);
      } else {
        const MCRegisterInfo &MCRI = *Ctx.getRegisterInfo();
        // otool expects the fully encoded ADD/LDR instruction to be passed in
        // as the value here, so reconstruct it:
        unsigned EncodedInst =
          MI.getOpcode() == AArch64::ADDXri ? 0x91000000: 0xF9400000;
        EncodedInst |= Value << 10; // imm12 [+ shift:2 for ADD]
        EncodedInst |=
          MCRI.getEncodingValue(MI.getOperand(1).getReg()) << 5; // Rn
        EncodedInst |= MCRI.getEncodingValue(MI.getOperand(0).getReg()); // Rd

        SymbolLookUp(DisInfo, EncodedInst, &ReferenceType, Address,
                     &ReferenceName);
      }
      if (ReferenceType == LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr)
        CommentStream << "literal pool symbol address: " << ReferenceName;
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr)
        CommentStream << "literal pool for: \"" << ReferenceName << "\"";
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref)
        CommentStream << "Objc cfstring ref: @\"" << ReferenceName << "\"";
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_Objc_Message)
        CommentStream << "Objc message: " << ReferenceName;
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref)
        CommentStream << "Objc message ref: " << ReferenceName;
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref)
        CommentStream << "Objc selector ref: " << ReferenceName;
      else if (ReferenceType ==
               LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref)
        CommentStream << "Objc class ref: " << ReferenceName;
      // For these instructions, the SymbolLookUp() above is just to get the
      // ReferenceType and ReferenceName.  We want to make sure not to
      // fall through so we don't build an MCExpr to leave the disassembly
      // of the immediate values of these instructions to the InstPrinter.
      return false;
    } else {
      return false;
    }
  }

  const MCExpr *Add = nullptr;
  if (SymbolicOp.AddSymbol.Present) {
    if (SymbolicOp.AddSymbol.Name) {
      StringRef Name(SymbolicOp.AddSymbol.Name);
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(Name);
      MCSymbolRefExpr::VariantKind Variant = getVariant(SymbolicOp.VariantKind);
      if (Variant != MCSymbolRefExpr::VK_None)
        Add = MCSymbolRefExpr::Create(Sym, Variant, Ctx);
      else
        Add = MCSymbolRefExpr::Create(Sym, Ctx);
    } else {
      Add = MCConstantExpr::Create(SymbolicOp.AddSymbol.Value, Ctx);
    }
  }

  const MCExpr *Sub = nullptr;
  if (SymbolicOp.SubtractSymbol.Present) {
    if (SymbolicOp.SubtractSymbol.Name) {
      StringRef Name(SymbolicOp.SubtractSymbol.Name);
      MCSymbol *Sym = Ctx.GetOrCreateSymbol(Name);
      Sub = MCSymbolRefExpr::Create(Sym, Ctx);
    } else {
      Sub = MCConstantExpr::Create(SymbolicOp.SubtractSymbol.Value, Ctx);
    }
  }

  const MCExpr *Off = nullptr;
  if (SymbolicOp.Value != 0)
    Off = MCConstantExpr::Create(SymbolicOp.Value, Ctx);

  const MCExpr *Expr;
  if (Sub) {
    const MCExpr *LHS;
    if (Add)
      LHS = MCBinaryExpr::CreateSub(Add, Sub, Ctx);
    else
      LHS = MCUnaryExpr::CreateMinus(Sub, Ctx);
    if (Off)
      Expr = MCBinaryExpr::CreateAdd(LHS, Off, Ctx);
    else
      Expr = LHS;
  } else if (Add) {
    if (Off)
      Expr = MCBinaryExpr::CreateAdd(Add, Off, Ctx);
    else
      Expr = Add;
  } else {
    if (Off)
      Expr = Off;
    else
      Expr = MCConstantExpr::Create(0, Ctx);
  }

  MI.addOperand(MCOperand::CreateExpr(Expr));

  return true;
}