1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
18 #define ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
19 
20 #include <cstdint>
21 
22 #include "dwarf/dwarf_constants.h"
23 #include "dwarf/writer.h"
24 
25 namespace art {
26 namespace dwarf {
27 
28 // Writer for the .debug_line opcodes (DWARF-3).
29 // The writer is very light-weight, however it will do the following for you:
30 //  * Choose the most compact encoding of a given opcode.
31 //  * Keep track of current state and convert absolute values to deltas.
32 //  * Divide by header-defined factors as appropriate.
33 template<typename Allocator = std::allocator<uint8_t>>
34 class DebugLineOpCodeWriter FINAL : private Writer<Allocator> {
35  public:
36   static constexpr int kOpcodeBase = 13;
37   static constexpr bool kDefaultIsStmt = true;
38   static constexpr int kLineBase = -5;
39   static constexpr int kLineRange = 14;
40 
AddRow()41   void AddRow() {
42     this->PushUint8(DW_LNS_copy);
43   }
44 
AdvancePC(uint64_t absolute_address)45   void AdvancePC(uint64_t absolute_address) {
46     DCHECK_NE(current_address_, 0u);  // Use SetAddress for the first advance.
47     DCHECK_GE(absolute_address, current_address_);
48     if (absolute_address != current_address_) {
49       uint64_t delta = FactorCodeOffset(absolute_address - current_address_);
50       if (delta <= INT32_MAX) {
51         this->PushUint8(DW_LNS_advance_pc);
52         this->PushUleb128(static_cast<int>(delta));
53         current_address_ = absolute_address;
54       } else {
55         SetAddress(absolute_address);
56       }
57     }
58   }
59 
AdvanceLine(int absolute_line)60   void AdvanceLine(int absolute_line) {
61     int delta = absolute_line - current_line_;
62     if (delta != 0) {
63       this->PushUint8(DW_LNS_advance_line);
64       this->PushSleb128(delta);
65       current_line_ = absolute_line;
66     }
67   }
68 
SetFile(int file)69   void SetFile(int file) {
70     if (current_file_ != file) {
71       this->PushUint8(DW_LNS_set_file);
72       this->PushUleb128(file);
73       current_file_ = file;
74     }
75   }
76 
SetColumn(int column)77   void SetColumn(int column) {
78     this->PushUint8(DW_LNS_set_column);
79     this->PushUleb128(column);
80   }
81 
NegateStmt()82   void NegateStmt() {
83     this->PushUint8(DW_LNS_negate_stmt);
84   }
85 
SetBasicBlock()86   void SetBasicBlock() {
87     this->PushUint8(DW_LNS_set_basic_block);
88   }
89 
SetPrologueEnd()90   void SetPrologueEnd() {
91     uses_dwarf3_features_ = true;
92     this->PushUint8(DW_LNS_set_prologue_end);
93   }
94 
SetEpilogueBegin()95   void SetEpilogueBegin() {
96     uses_dwarf3_features_ = true;
97     this->PushUint8(DW_LNS_set_epilogue_begin);
98   }
99 
SetISA(int isa)100   void SetISA(int isa) {
101     uses_dwarf3_features_ = true;
102     this->PushUint8(DW_LNS_set_isa);
103     this->PushUleb128(isa);
104   }
105 
EndSequence()106   void EndSequence() {
107     this->PushUint8(0);
108     this->PushUleb128(1);
109     this->PushUint8(DW_LNE_end_sequence);
110     current_address_ = 0;
111     current_file_ = 1;
112     current_line_ = 1;
113   }
114 
115   // Uncoditionally set address using the long encoding.
116   // This gives the linker opportunity to relocate the address.
SetAddress(uint64_t absolute_address)117   void SetAddress(uint64_t absolute_address) {
118     DCHECK_GE(absolute_address, current_address_);
119     FactorCodeOffset(absolute_address);  // Check if it is factorable.
120     this->PushUint8(0);
121     if (use_64bit_address_) {
122       this->PushUleb128(1 + 8);
123       this->PushUint8(DW_LNE_set_address);
124       patch_locations_.push_back(this->data()->size());
125       this->PushUint64(absolute_address);
126     } else {
127       this->PushUleb128(1 + 4);
128       this->PushUint8(DW_LNE_set_address);
129       patch_locations_.push_back(this->data()->size());
130       this->PushUint32(absolute_address);
131     }
132     current_address_ = absolute_address;
133   }
134 
DefineFile(const char * filename,int directory_index,int modification_time,int file_size)135   void DefineFile(const char* filename,
136                   int directory_index,
137                   int modification_time,
138                   int file_size) {
139     int size = 1 +
140                strlen(filename) + 1 +
141                UnsignedLeb128Size(directory_index) +
142                UnsignedLeb128Size(modification_time) +
143                UnsignedLeb128Size(file_size);
144     this->PushUint8(0);
145     this->PushUleb128(size);
146     size_t start = data()->size();
147     this->PushUint8(DW_LNE_define_file);
148     this->PushString(filename);
149     this->PushUleb128(directory_index);
150     this->PushUleb128(modification_time);
151     this->PushUleb128(file_size);
152     DCHECK_EQ(start + size, data()->size());
153   }
154 
155   // Compact address and line opcode.
AddRow(uint64_t absolute_address,int absolute_line)156   void AddRow(uint64_t absolute_address, int absolute_line) {
157     DCHECK_GE(absolute_address, current_address_);
158 
159     // If the address is definitely too far, use the long encoding.
160     uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_);
161     if (delta_address > UINT8_MAX) {
162       AdvancePC(absolute_address);
163       delta_address = 0;
164     }
165 
166     // If the line is definitely too far, use the long encoding.
167     int delta_line = absolute_line - current_line_;
168     if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) {
169       AdvanceLine(absolute_line);
170       delta_line = 0;
171     }
172 
173     // Both address and line should be reasonable now.  Use the short encoding.
174     int opcode = kOpcodeBase + (delta_line - kLineBase) +
175                  (static_cast<int>(delta_address) * kLineRange);
176     if (opcode > UINT8_MAX) {
177       // If the address is still too far, try to increment it by const amount.
178       int const_advance = (0xFF - kOpcodeBase) / kLineRange;
179       opcode -= (kLineRange * const_advance);
180       if (opcode <= UINT8_MAX) {
181         this->PushUint8(DW_LNS_const_add_pc);
182       } else {
183         // Give up and use long encoding for address.
184         AdvancePC(absolute_address);
185         // Still use the opcode to do line advance and copy.
186         opcode = kOpcodeBase + (delta_line - kLineBase);
187       }
188     }
189     DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF);
190     this->PushUint8(opcode);  // Special opcode.
191     current_line_ = absolute_line;
192     current_address_ = absolute_address;
193   }
194 
GetCodeFactorBits()195   int GetCodeFactorBits() const {
196     return code_factor_bits_;
197   }
198 
CurrentAddress()199   uint64_t CurrentAddress() const {
200     return current_address_;
201   }
202 
CurrentFile()203   int CurrentFile() const {
204     return current_file_;
205   }
206 
CurrentLine()207   int CurrentLine() const {
208     return current_line_;
209   }
210 
GetPatchLocations()211   const std::vector<uintptr_t>& GetPatchLocations() const {
212     return patch_locations_;
213   }
214 
215   using Writer<Allocator>::data;
216 
217   DebugLineOpCodeWriter(bool use64bitAddress,
218                         int codeFactorBits,
219                         const Allocator& alloc = Allocator())
220       : Writer<Allocator>(&opcodes_),
221         opcodes_(alloc),
222         uses_dwarf3_features_(false),
223         use_64bit_address_(use64bitAddress),
224         code_factor_bits_(codeFactorBits),
225         current_address_(0),
226         current_file_(1),
227         current_line_(1) {
228   }
229 
230  private:
FactorCodeOffset(uint64_t offset)231   uint64_t FactorCodeOffset(uint64_t offset) const {
232     DCHECK_GE(code_factor_bits_, 0);
233     DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset);
234     return offset >> code_factor_bits_;
235   }
236 
237   std::vector<uint8_t, Allocator> opcodes_;
238   bool uses_dwarf3_features_;
239   bool use_64bit_address_;
240   int code_factor_bits_;
241   uint64_t current_address_;
242   int current_file_;
243   int current_line_;
244   std::vector<uintptr_t> patch_locations_;
245 
246   DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
247 };
248 
249 }  // namespace dwarf
250 }  // namespace art
251 
252 #endif  // ART_COMPILER_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
253