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