[M108 Migration][HBBTV] Implement ewk_context_register_jsplugin_mime_types API
[platform/framework/web/chromium-efl.git] / courgette / rel32_finder_x64.cc
1 // Copyright 2016 The Chromium Authors
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 "courgette/rel32_finder_x64.h"
6
7 namespace courgette {
8
9 Rel32FinderX64::Rel32FinderX64(RVA relocs_start_rva,
10                                RVA relocs_end_rva,
11                                RVA size_of_image)
12     : Rel32Finder(relocs_start_rva, relocs_end_rva),
13       size_of_image_(size_of_image) {}
14
15 // Scan for opcodes matching the following instructions :
16 //  rel32 JMP/CALL
17 //  rip MOV/LEA
18 //  Jcc (excluding JPO/JPE)
19 // Falsely detected rel32 that collide with known abs32 or that point outside
20 // valid regions are discarded.
21 void Rel32FinderX64::Find(const uint8_t* start_pointer,
22                           const uint8_t* end_pointer,
23                           RVA start_rva,
24                           RVA end_rva,
25                           const std::vector<RVA>& abs32_locations) {
26   // Quick way to convert from Pointer to RVA within a single Section is to
27   // subtract |adjust_pointer_to_rva|.
28   const uint8_t* const adjust_pointer_to_rva = start_pointer - start_rva;
29
30   std::vector<RVA>::const_iterator abs32_pos = abs32_locations.begin();
31
32   // Find the rel32 relocations.
33   const uint8_t* p = start_pointer;
34   while (p < end_pointer) {
35     RVA current_rva = static_cast<RVA>(p - adjust_pointer_to_rva);
36
37     // Skip the base reloation table if we encounter it.
38     // Note: We're not bothering to handle the edge case where a Rel32 pointer
39     // collides with |relocs_start_rva_| by being {1, 2, 3}-bytes before it.
40     if (current_rva >= relocs_start_rva_ && current_rva < relocs_end_rva_) {
41       p += relocs_end_rva_ - current_rva;
42       continue;
43     }
44
45     // Heuristic discovery of rel32 locations in instruction stream: are the
46     // next few bytes the start of an instruction containing a rel32
47     // addressing mode?
48     const uint8_t* rel32 = nullptr;
49     bool is_rip_relative = false;
50
51     if (p + 5 <= end_pointer) {
52       if (p[0] == 0xE8 || p[0] == 0xE9)  // jmp rel32 and call rel32
53         rel32 = p + 1;
54     }
55     if (p + 6 <= end_pointer) {
56       if (p[0] == 0x0F && (p[1] & 0xF0) == 0x80) {  // Jcc long form
57         if (p[1] != 0x8A && p[1] != 0x8B)           // JPE/JPO unlikely
58           rel32 = p + 2;
59       } else if ((p[0] == 0xFF &&
60                  (p[1] == 0x15 || p[1] == 0x25)) ||
61                 ((p[0] == 0x89 || p[0] == 0x8B || p[0] == 0x8D) &&
62                  (p[1] & 0xC7) == 0x05)) {
63         // 6-byte instructions:
64         // [2-byte opcode] [disp32]:
65         //  Opcode
66         //   FF 15: call QWORD PTR [rip+disp32]
67         //   FF 25: jmp  QWORD PTR [rip+disp32]
68         //
69         // [1-byte opcode] [ModR/M] [disp32]:
70         //  Opcode
71         //   89: mov DWORD PTR [rip+disp32],reg
72         //   8B: mov reg,DWORD PTR [rip+disp32]
73         //   8D: lea reg,[rip+disp32]
74         //  ModR/M : MMRRRMMM
75         //   MM = 00 & MMM = 101 => rip+disp32
76         //   RRR: selects reg operand from [eax|ecx|...|edi]
77         rel32 = p + 2;
78         is_rip_relative = true;
79       }
80     }
81     // TODO(huangs): Maybe skip checking prefixes,
82     // and let 6-byte instructions take care of this?
83     if (p + 7 <= end_pointer) {
84       if (((p[0] & 0xF2) == 0x40 || p[0] == 0x66) &&
85            (p[1] == 0x89 || p[1] == 0x8B || p[1] == 0x8D) &&
86            (p[2] & 0xC7) == 0x05) {
87         // 7-byte instructions:
88         // [REX.W prefix] [1-byte opcode] [ModR/M] [disp32]
89         //  REX Prefix : 0100WR0B
90         //   W: 0 = Default Operation Size
91         //      1 = 64 Bit Operand Size
92         //   R: 0 = REG selects from [rax|rcx|...|rdi].
93         //      1 = REG selects from [r9|r10|...|r15].
94         //   B: ModR/M r/m field extension (not used).
95         //  Opcode
96         //   89: mov QWORD PTR [rip+disp32],reg
97         //   8B: mov reg,QWORD PTR [rip+disp32]
98         //   8D: lea reg,[rip+disp32]
99         //  ModR/M : MMRRRMMM
100         //   MM = 00 & MMM = 101 => rip+disp32
101         //   RRR: selects reg operand
102         //
103         // 66 [1-byte opcode] [ModR/M] [disp32]
104         //  Prefix
105         //   66: Operand size override
106         //  Opcode
107         //   89: mov WORD PTR [rip+disp32],reg
108         //   8B: mov reg,WORD PTR [rip+disp32]
109         //   8D: lea reg,[rip+disp32]
110         //  ModR/M : MMRRRMMM
111         //   MM = 00 & MMM = 101 = rip+disp32
112         //   RRR selects reg operand from [ax|cx|...|di]
113         rel32 = p + 3;
114         is_rip_relative = true;
115       }
116     }
117
118     if (rel32) {
119       RVA rel32_rva = static_cast<RVA>(rel32 - adjust_pointer_to_rva);
120
121       // Is there an abs32 reloc overlapping the candidate?
122       while (abs32_pos != abs32_locations.end() && *abs32_pos < rel32_rva - 3)
123         ++abs32_pos;
124       // Now: (*abs32_pos > rel32_rva - 4) i.e. the lowest addressed 4-byte
125       // region that could overlap rel32_rva.
126       if (abs32_pos != abs32_locations.end()) {
127         if (*abs32_pos < rel32_rva + 4) {
128           // Beginning of abs32 reloc is before end of rel32 reloc so they
129           // overlap. Skip four bytes past the abs32 reloc.
130           p += (*abs32_pos + 4) - current_rva;
131           continue;
132         }
133       }
134
135       // + 4 since offset is relative to start of next instruction.
136       RVA target_rva = rel32_rva + 4 + Read32LittleEndian(rel32);
137       // To be valid, rel32 target must be within image, and within this
138       // section.
139       if (target_rva < size_of_image_ &&  // Subsumes rva != kUnassignedRVA.
140           (is_rip_relative ||
141            (start_rva <= target_rva && target_rva < end_rva))) {
142         rel32_locations_.push_back(rel32_rva);
143 #if COURGETTE_HISTOGRAM_TARGETS
144         ++rel32_target_rvas_[target_rva];
145 #endif
146         p = rel32 + 4;
147         continue;
148       }
149     }
150     p += 1;
151   }
152 }
153
154 }  // namespace courgette