aboutsummaryrefslogtreecommitdiff
path: root/reference_bytes_mixer.h
blob: f20b0ef984a74202e7432cf630a5b1a32ead89ef (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
// 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.

#ifndef COMPONENTS_ZUCCHINI_REFERENCE_BYTES_MIXER_H_
#define COMPONENTS_ZUCCHINI_REFERENCE_BYTES_MIXER_H_

#include <stdint.h>

#include <memory>

#include "components/zucchini/buffer_view.h"
#include "components/zucchini/image_utils.h"
#include "components/zucchini/rel32_utils.h"

namespace zucchini {

class Disassembler;

// References encoding may be quite complex in some architectures (e.g., ARM),
// requiring bit-level manipulation. In general, bits in a reference body fall
// under 2 categories:
// - Operation bits: Instruction op code, conditionals, or structural data.
// - Payload bits: Actual target data of the reference. These may be absolute,
//   or be displacements relative to instruction pointer / program counter.
// During patch application,
//   Old reference bytes = {old operation, old payload},
// is transformed to
//   New reference bytes = {new operation, new payload}.
// New image bytes are written by three sources:
//   (1) Direct copy from old image to new image for matched blocks.
//   (2) Bytewise diff correction.
//   (3) Dedicated reference target correction.
//
// For references whose operation and payload bits are stored in easily
// separable bytes (e.g., rel32 reference in X86), (2) can exclude payload bits.
// So during patch application, (1) naively copies everything, (2) fixes
// operation bytes only, and (3) fixes payload bytes only.
//
// For architectures with references whose operation and payload bits may mix
// within shared bytes (e.g., ARM rel32), a dilemma arises:
// - (2) cannot ignores shared bytes, since otherwise new operation bits not
//   properly transfer.
// - Having (2) always overwrite these bytes would reduce the benefits of
//   reference correction, since references are likely to change.
//
// Our solution applies a hybrid approach: For each matching old / new reference
// pair, define:
//   Mixed reference bytes = {new operation, old payload},
//
// During patch generation, we compute bytewise correction from old reference
// bytes to the mixed reference bytes. So during patch application, (2) only
// corrects operation bit changes (and skips if they don't change), and (3)
// overwrites old payload bits to new payload bits.

// A base class for (stateful) mixed reference byte generation. This base class
// serves as a stub. Architectures whose references store operation bits and
// payload bits can share common bytes (e.g., ARM rel32) should override this.
class ReferenceBytesMixer {
 public:
  ReferenceBytesMixer();
  ReferenceBytesMixer(const ReferenceBytesMixer&) = delete;
  const ReferenceBytesMixer& operator=(const ReferenceBytesMixer&) = delete;
  virtual ~ReferenceBytesMixer();

  // Returns a new ReferenceBytesMixer instance that's owned by the caller.
  static std::unique_ptr<ReferenceBytesMixer> Create(
      const Disassembler& src_dis,
      const Disassembler& dst_dis);

  // Returns the number of bytes that need to be mixed for references with given
  // |type|. Returns 0 if no mixing is required.
  virtual int NumBytes(uint8_t type) const;

  // Computes mixed reference bytes by combining (a) "payload bits" from an
  // "old" reference of |type| at |old_view[old_offset]| with (b) "operation
  // bits" from a "new" reference of |type| at |new_view[new_offset]|. Returns
  // the result as ConstBufferView, which is valid only until the next call to
  // Mix().
  virtual ConstBufferView Mix(uint8_t type,
                              ConstBufferView old_view,
                              offset_t old_offset,
                              ConstBufferView new_view,
                              offset_t new_offset);
};

// In AArch32 and AArch64, instructions mix operation bits and payload bits in
// complex ways. This is the main use case of ReferenceBytesMixer.
class ReferenceBytesMixerElfArm : public ReferenceBytesMixer {
 public:
  // |exe_type| must be EXE_TYPE_ELF_ARM or EXE_TYPE_ELF_AARCH64.
  explicit ReferenceBytesMixerElfArm(ExecutableType exe_type);
  ReferenceBytesMixerElfArm(const ReferenceBytesMixerElfArm&) = delete;
  const ReferenceBytesMixerElfArm& operator=(const ReferenceBytesMixerElfArm&) =
      delete;
  ~ReferenceBytesMixerElfArm() override;

  // ReferenceBytesMixer:
  int NumBytes(uint8_t type) const override;
  ConstBufferView Mix(uint8_t type,
                      ConstBufferView old_view,
                      offset_t old_offset,
                      ConstBufferView new_view,
                      offset_t new_offset) override;

 private:
  ArmCopyDispFun GetCopier(uint8_t type) const;

  // For simplicity, 32-bit vs. 64-bit distinction is represented by state
  // |exe_type_|, instead of creating derived classes.
  const ExecutableType exe_type_;

  std::vector<uint8_t> out_buffer_;
};

}  // namespace zucchini

#endif  // COMPONENTS_ZUCCHINI_REFERENCE_BYTES_MIXER_H_