aboutsummaryrefslogtreecommitdiff
path: root/reference_bytes_mixer.cc
blob: 6855853285ad72655c6dec024e65057f4453e78a (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
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/zucchini/reference_bytes_mixer.h"

#include <algorithm>

#include "base/check_op.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "components/zucchini/disassembler.h"
#include "components/zucchini/disassembler_elf.h"

namespace zucchini {

/******** ReferenceBytesMixer ********/

// Default implementation is a stub, i.e., for architectures whose references
// have operation bits and payload bits stored in separate bytes. So during
// patch application, payload bits are copied for matched blocks, ignored by
// bytewise corrections, and fixed by reference target corrections.
ReferenceBytesMixer::ReferenceBytesMixer() {}

ReferenceBytesMixer::~ReferenceBytesMixer() = default;

// static.
std::unique_ptr<ReferenceBytesMixer> ReferenceBytesMixer::Create(
    const Disassembler& src_dis,
    const Disassembler& dst_dis) {
  ExecutableType exe_type = src_dis.GetExeType();
  DCHECK_EQ(exe_type, dst_dis.GetExeType());
  if (exe_type == kExeTypeElfAArch32)
    return std::make_unique<ReferenceBytesMixerElfArm>(exe_type);
  if (exe_type == kExeTypeElfAArch64)
    return std::make_unique<ReferenceBytesMixerElfArm>(exe_type);
  return std::make_unique<ReferenceBytesMixer>();
}

// Stub implementation.
int ReferenceBytesMixer::NumBytes(uint8_t type) const {
  return 0;
}

// Base class implementation is a stub that should not be called.
ConstBufferView ReferenceBytesMixer::Mix(uint8_t type,
                                         ConstBufferView old_view,
                                         offset_t old_offset,
                                         ConstBufferView new_view,
                                         offset_t new_offset) {
  NOTREACHED() << "Stub.";
  return ConstBufferView();
}

/******** ReferenceBytesMixerElfArm ********/

ReferenceBytesMixerElfArm::ReferenceBytesMixerElfArm(ExecutableType exe_type)
    : exe_type_(exe_type), out_buffer_(4) {}  // 4 is a bound on NumBytes().

ReferenceBytesMixerElfArm::~ReferenceBytesMixerElfArm() = default;

int ReferenceBytesMixerElfArm::NumBytes(uint8_t type) const {
  if (exe_type_ == kExeTypeElfAArch32) {
    switch (type) {
      case AArch32ReferenceType::kRel32_A24:  // Falls through.
      case AArch32ReferenceType::kRel32_T20:
      case AArch32ReferenceType::kRel32_T24:
        return 4;
      case AArch32ReferenceType::kRel32_T8:  // Falls through.
      case AArch32ReferenceType::kRel32_T11:
        return 2;
    }
  } else if (exe_type_ == kExeTypeElfAArch64) {
    switch (type) {
      case AArch64ReferenceType::kRel32_Immd14:  // Falls through.
      case AArch64ReferenceType::kRel32_Immd19:
      case AArch64ReferenceType::kRel32_Immd26:
        return 4;
    }
  }
  return 0;
}

ConstBufferView ReferenceBytesMixerElfArm::Mix(uint8_t type,
                                               ConstBufferView old_view,
                                               offset_t old_offset,
                                               ConstBufferView new_view,
                                               offset_t new_offset) {
  int num_bytes = NumBytes(type);
  ConstBufferView::const_iterator new_it = new_view.begin() + new_offset;
  DCHECK_LE(static_cast<size_t>(num_bytes), out_buffer_.size());
  MutableBufferView out_buffer_view(&out_buffer_[0], num_bytes);
  std::copy(new_it, new_it + num_bytes, out_buffer_view.begin());

  ArmCopyDispFun copier = GetCopier(type);
  DCHECK_NE(copier, nullptr);

  if (!copier(old_view, old_offset, out_buffer_view, 0U)) {
    // Failed to mix old payload bits with new operation bits. The main cause of
    // of this rare failure is when BL (encoding T1) with payload bits
    // representing disp % 4 == 2 transforms into BLX (encoding T2). Error
    // arises because BLX requires payload bits to have disp == 0 (mod 4).
    // Mixing failures are not fatal to patching; we simply fall back to direct
    // copy and forgo benefits from mixing for these cases.
    // TODO(huangs, etiennep): Ongoing discussion on whether we should just
    // nullify all payload disp so we won't have to deal with this case, but at
    // the cost of having Zucchini-apply do more work.
    static int output_quota = 10;
    if (output_quota > 0) {
      LOG(WARNING) << "Reference byte mix failed with type = "
                   << static_cast<uint32_t>(type) << "." << std::endl;
      --output_quota;
      if (!output_quota)
        LOG(WARNING) << "(Additional output suppressed)";
    }
    // Fall back to direct copy.
    std::copy(new_it, new_it + num_bytes, out_buffer_view.begin());
  }
  return ConstBufferView(out_buffer_view);
}

ArmCopyDispFun ReferenceBytesMixerElfArm::GetCopier(uint8_t type) const {
  if (exe_type_ == kExeTypeElfAArch32) {
    switch (type) {
      case AArch32ReferenceType::kRel32_A24:
        return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_A24>;
      case AArch32ReferenceType::kRel32_T8:
        return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T8>;
      case AArch32ReferenceType::kRel32_T11:
        return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T11>;
      case AArch32ReferenceType::kRel32_T20:
        return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T20>;
      case AArch32ReferenceType::kRel32_T24:
        return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T24>;
    }
  } else if (exe_type_ == kExeTypeElfAArch64) {
    switch (type) {
      case AArch64ReferenceType::kRel32_Immd14:
        return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd14>;
      case AArch64ReferenceType::kRel32_Immd19:
        return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd19>;
      case AArch64ReferenceType::kRel32_Immd26:
        return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd26>;
    }
  }
  DLOG(FATAL) << "NOTREACHED";
  return nullptr;
}

}  // namespace zucchini