xref: /aosp_15_r20/external/zucchini/reference_bytes_mixer.cc (revision a03ca8b91e029cd15055c20c78c2e087c84792e4)
1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/zucchini/reference_bytes_mixer.h"
6 
7 #include <algorithm>
8 
9 #include "base/check_op.h"
10 #include "base/logging.h"
11 #include "base/notreached.h"
12 #include "components/zucchini/disassembler.h"
13 #include "components/zucchini/disassembler_elf.h"
14 
15 namespace zucchini {
16 
17 /******** ReferenceBytesMixer ********/
18 
19 // Default implementation is a stub, i.e., for architectures whose references
20 // have operation bits and payload bits stored in separate bytes. So during
21 // patch application, payload bits are copied for matched blocks, ignored by
22 // bytewise corrections, and fixed by reference target corrections.
ReferenceBytesMixer()23 ReferenceBytesMixer::ReferenceBytesMixer() {}
24 
25 ReferenceBytesMixer::~ReferenceBytesMixer() = default;
26 
27 // static.
Create(const Disassembler & src_dis,const Disassembler & dst_dis)28 std::unique_ptr<ReferenceBytesMixer> ReferenceBytesMixer::Create(
29     const Disassembler& src_dis,
30     const Disassembler& dst_dis) {
31   ExecutableType exe_type = src_dis.GetExeType();
32   DCHECK_EQ(exe_type, dst_dis.GetExeType());
33   if (exe_type == kExeTypeElfAArch32)
34     return std::make_unique<ReferenceBytesMixerElfArm>(exe_type);
35   if (exe_type == kExeTypeElfAArch64)
36     return std::make_unique<ReferenceBytesMixerElfArm>(exe_type);
37   return std::make_unique<ReferenceBytesMixer>();
38 }
39 
40 // Stub implementation.
NumBytes(uint8_t type) const41 int ReferenceBytesMixer::NumBytes(uint8_t type) const {
42   return 0;
43 }
44 
45 // Base class implementation is a stub that should not be called.
Mix(uint8_t type,ConstBufferView old_view,offset_t old_offset,ConstBufferView new_view,offset_t new_offset)46 ConstBufferView ReferenceBytesMixer::Mix(uint8_t type,
47                                          ConstBufferView old_view,
48                                          offset_t old_offset,
49                                          ConstBufferView new_view,
50                                          offset_t new_offset) {
51   NOTREACHED() << "Stub.";
52   return ConstBufferView();
53 }
54 
55 /******** ReferenceBytesMixerElfArm ********/
56 
ReferenceBytesMixerElfArm(ExecutableType exe_type)57 ReferenceBytesMixerElfArm::ReferenceBytesMixerElfArm(ExecutableType exe_type)
58     : exe_type_(exe_type), out_buffer_(4) {}  // 4 is a bound on NumBytes().
59 
60 ReferenceBytesMixerElfArm::~ReferenceBytesMixerElfArm() = default;
61 
NumBytes(uint8_t type) const62 int ReferenceBytesMixerElfArm::NumBytes(uint8_t type) const {
63   if (exe_type_ == kExeTypeElfAArch32) {
64     switch (type) {
65       case AArch32ReferenceType::kRel32_A24:  // Falls through.
66       case AArch32ReferenceType::kRel32_T20:
67       case AArch32ReferenceType::kRel32_T24:
68         return 4;
69       case AArch32ReferenceType::kRel32_T8:  // Falls through.
70       case AArch32ReferenceType::kRel32_T11:
71         return 2;
72     }
73   } else if (exe_type_ == kExeTypeElfAArch64) {
74     switch (type) {
75       case AArch64ReferenceType::kRel32_Immd14:  // Falls through.
76       case AArch64ReferenceType::kRel32_Immd19:
77       case AArch64ReferenceType::kRel32_Immd26:
78         return 4;
79     }
80   }
81   return 0;
82 }
83 
Mix(uint8_t type,ConstBufferView old_view,offset_t old_offset,ConstBufferView new_view,offset_t new_offset)84 ConstBufferView ReferenceBytesMixerElfArm::Mix(uint8_t type,
85                                                ConstBufferView old_view,
86                                                offset_t old_offset,
87                                                ConstBufferView new_view,
88                                                offset_t new_offset) {
89   int num_bytes = NumBytes(type);
90   ConstBufferView::const_iterator new_it = new_view.begin() + new_offset;
91   DCHECK_LE(static_cast<size_t>(num_bytes), out_buffer_.size());
92   MutableBufferView out_buffer_view(&out_buffer_[0], num_bytes);
93   std::copy(new_it, new_it + num_bytes, out_buffer_view.begin());
94 
95   ArmCopyDispFun copier = GetCopier(type);
96   DCHECK_NE(copier, nullptr);
97 
98   if (!copier(old_view, old_offset, out_buffer_view, 0U)) {
99     // Failed to mix old payload bits with new operation bits. The main cause of
100     // of this rare failure is when BL (encoding T1) with payload bits
101     // representing disp % 4 == 2 transforms into BLX (encoding T2). Error
102     // arises because BLX requires payload bits to have disp == 0 (mod 4).
103     // Mixing failures are not fatal to patching; we simply fall back to direct
104     // copy and forgo benefits from mixing for these cases.
105     // TODO(huangs, etiennep): Ongoing discussion on whether we should just
106     // nullify all payload disp so we won't have to deal with this case, but at
107     // the cost of having Zucchini-apply do more work.
108     static int output_quota = 10;
109     if (output_quota > 0) {
110       LOG(WARNING) << "Reference byte mix failed with type = "
111                    << static_cast<uint32_t>(type) << "." << std::endl;
112       --output_quota;
113       if (!output_quota)
114         LOG(WARNING) << "(Additional output suppressed)";
115     }
116     // Fall back to direct copy.
117     std::copy(new_it, new_it + num_bytes, out_buffer_view.begin());
118   }
119   return ConstBufferView(out_buffer_view);
120 }
121 
GetCopier(uint8_t type) const122 ArmCopyDispFun ReferenceBytesMixerElfArm::GetCopier(uint8_t type) const {
123   if (exe_type_ == kExeTypeElfAArch32) {
124     switch (type) {
125       case AArch32ReferenceType::kRel32_A24:
126         return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_A24>;
127       case AArch32ReferenceType::kRel32_T8:
128         return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T8>;
129       case AArch32ReferenceType::kRel32_T11:
130         return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T11>;
131       case AArch32ReferenceType::kRel32_T20:
132         return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T20>;
133       case AArch32ReferenceType::kRel32_T24:
134         return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T24>;
135     }
136   } else if (exe_type_ == kExeTypeElfAArch64) {
137     switch (type) {
138       case AArch64ReferenceType::kRel32_Immd14:
139         return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd14>;
140       case AArch64ReferenceType::kRel32_Immd19:
141         return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd19>;
142       case AArch64ReferenceType::kRel32_Immd26:
143         return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd26>;
144     }
145   }
146   DLOG(FATAL) << "NOTREACHED";
147   return nullptr;
148 }
149 
150 }  // namespace zucchini
151