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_LIBELFFILE_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
18 #define ART_LIBELFFILE_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 Vector = std::vector<uint8_t>>
34 class DebugLineOpCodeWriter final : private Writer<Vector> {
35   static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
36 
37  public:
38   static constexpr int kOpcodeBase = 13;
39   static constexpr bool kDefaultIsStmt = false;
40   static constexpr int kLineBase = -5;
41   static constexpr int kLineRange = 14;
42 
AddRow()43   void AddRow() {
44     this->PushUint8(DW_LNS_copy);
45   }
46 
AdvancePC(uint64_t absolute_address)47   void AdvancePC(uint64_t absolute_address) {
48     DCHECK_NE(current_address_, 0u);  // Use SetAddress for the first advance.
49     DCHECK_GE(absolute_address, current_address_);
50     if (absolute_address != current_address_) {
51       uint64_t delta = FactorCodeOffset(absolute_address - current_address_);
52       if (delta <= INT32_MAX) {
53         this->PushUint8(DW_LNS_advance_pc);
54         this->PushUleb128(static_cast<int>(delta));
55         current_address_ = absolute_address;
56       } else {
57         SetAddress(absolute_address);
58       }
59     }
60   }
61 
AdvanceLine(int absolute_line)62   void AdvanceLine(int absolute_line) {
63     int delta = absolute_line - current_line_;
64     if (delta != 0) {
65       this->PushUint8(DW_LNS_advance_line);
66       this->PushSleb128(delta);
67       current_line_ = absolute_line;
68     }
69   }
70 
SetFile(int file)71   void SetFile(int file) {
72     if (current_file_ != file) {
73       this->PushUint8(DW_LNS_set_file);
74       this->PushUleb128(file);
75       current_file_ = file;
76     }
77   }
78 
SetColumn(int column)79   void SetColumn(int column) {
80     this->PushUint8(DW_LNS_set_column);
81     this->PushUleb128(column);
82   }
83 
SetIsStmt(bool is_stmt)84   void SetIsStmt(bool is_stmt) {
85     if (is_stmt_ != is_stmt) {
86       this->PushUint8(DW_LNS_negate_stmt);
87       is_stmt_ = is_stmt;
88     }
89   }
90 
SetBasicBlock()91   void SetBasicBlock() {
92     this->PushUint8(DW_LNS_set_basic_block);
93   }
94 
SetPrologueEnd()95   void SetPrologueEnd() {
96     uses_dwarf3_features_ = true;
97     this->PushUint8(DW_LNS_set_prologue_end);
98   }
99 
SetEpilogueBegin()100   void SetEpilogueBegin() {
101     uses_dwarf3_features_ = true;
102     this->PushUint8(DW_LNS_set_epilogue_begin);
103   }
104 
SetISA(int isa)105   void SetISA(int isa) {
106     uses_dwarf3_features_ = true;
107     this->PushUint8(DW_LNS_set_isa);
108     this->PushUleb128(isa);
109   }
110 
EndSequence()111   void EndSequence() {
112     this->PushUint8(0);
113     this->PushUleb128(1);
114     this->PushUint8(DW_LNE_end_sequence);
115     current_address_ = 0;
116     current_file_ = 1;
117     current_line_ = 1;
118     is_stmt_ = kDefaultIsStmt;
119   }
120 
121   // Uncoditionally set address using the long encoding.
122   // This gives the linker opportunity to relocate the address.
SetAddress(uint64_t absolute_address)123   void SetAddress(uint64_t absolute_address) {
124     DCHECK_GE(absolute_address, current_address_);
125     FactorCodeOffset(absolute_address);  // Check if it is factorable.
126     this->PushUint8(0);
127     if (use_64bit_address_) {
128       this->PushUleb128(1 + 8);
129       this->PushUint8(DW_LNE_set_address);
130       patch_locations_.push_back(this->data()->size());
131       this->PushUint64(absolute_address);
132     } else {
133       this->PushUleb128(1 + 4);
134       this->PushUint8(DW_LNE_set_address);
135       patch_locations_.push_back(this->data()->size());
136       this->PushUint32(absolute_address);
137     }
138     current_address_ = absolute_address;
139   }
140 
DefineFile(const char * filename,int directory_index,int modification_time,int file_size)141   void DefineFile(const char* filename,
142                   int directory_index,
143                   int modification_time,
144                   int file_size) {
145     int size = 1 +
146                strlen(filename) + 1 +
147                UnsignedLeb128Size(directory_index) +
148                UnsignedLeb128Size(modification_time) +
149                UnsignedLeb128Size(file_size);
150     this->PushUint8(0);
151     this->PushUleb128(size);
152     size_t start = data()->size();
153     this->PushUint8(DW_LNE_define_file);
154     this->PushString(filename);
155     this->PushUleb128(directory_index);
156     this->PushUleb128(modification_time);
157     this->PushUleb128(file_size);
158     DCHECK_EQ(start + size, data()->size());
159   }
160 
161   // Compact address and line opcode.
AddRow(uint64_t absolute_address,int absolute_line)162   void AddRow(uint64_t absolute_address, int absolute_line) {
163     DCHECK_GE(absolute_address, current_address_);
164 
165     // If the address is definitely too far, use the long encoding.
166     uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_);
167     if (delta_address > UINT8_MAX) {
168       AdvancePC(absolute_address);
169       delta_address = 0;
170     }
171 
172     // If the line is definitely too far, use the long encoding.
173     int delta_line = absolute_line - current_line_;
174     if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) {
175       AdvanceLine(absolute_line);
176       delta_line = 0;
177     }
178 
179     // Both address and line should be reasonable now.  Use the short encoding.
180     int opcode = kOpcodeBase + (delta_line - kLineBase) +
181                  (static_cast<int>(delta_address) * kLineRange);
182     if (opcode > UINT8_MAX) {
183       // If the address is still too far, try to increment it by const amount.
184       int const_advance = (0xFF - kOpcodeBase) / kLineRange;
185       opcode -= (kLineRange * const_advance);
186       if (opcode <= UINT8_MAX) {
187         this->PushUint8(DW_LNS_const_add_pc);
188       } else {
189         // Give up and use long encoding for address.
190         AdvancePC(absolute_address);
191         // Still use the opcode to do line advance and copy.
192         opcode = kOpcodeBase + (delta_line - kLineBase);
193       }
194     }
195     DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF);
196     this->PushUint8(opcode);  // Special opcode.
197     current_line_ = absolute_line;
198     current_address_ = absolute_address;
199   }
200 
GetCodeFactorBits()201   int GetCodeFactorBits() const {
202     return code_factor_bits_;
203   }
204 
CurrentAddress()205   uint64_t CurrentAddress() const {
206     return current_address_;
207   }
208 
CurrentFile()209   int CurrentFile() const {
210     return current_file_;
211   }
212 
CurrentLine()213   int CurrentLine() const {
214     return current_line_;
215   }
216 
GetPatchLocations()217   const std::vector<uintptr_t>& GetPatchLocations() const {
218     return patch_locations_;
219   }
220 
221   using Writer<Vector>::data;
222 
223   DebugLineOpCodeWriter(bool use64bitAddress,
224                         int codeFactorBits,
225                         const typename Vector::allocator_type& alloc =
226                             typename Vector::allocator_type())
227       : Writer<Vector>(&opcodes_),
228         opcodes_(alloc),
229         uses_dwarf3_features_(false),
230         use_64bit_address_(use64bitAddress),
231         code_factor_bits_(codeFactorBits),
232         current_address_(0),
233         current_file_(1),
234         current_line_(1),
235         is_stmt_(kDefaultIsStmt) {
236   }
237 
238  private:
FactorCodeOffset(uint64_t offset)239   uint64_t FactorCodeOffset(uint64_t offset) const {
240     DCHECK_GE(code_factor_bits_, 0);
241     DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset);
242     return offset >> code_factor_bits_;
243   }
244 
245   Vector opcodes_;
246   bool uses_dwarf3_features_;
247   bool use_64bit_address_;
248   int code_factor_bits_;
249   uint64_t current_address_;
250   int current_file_;
251   int current_line_;
252   bool is_stmt_;
253   std::vector<uintptr_t> patch_locations_;
254 
255   DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
256 };
257 
258 }  // namespace dwarf
259 }  // namespace art
260 
261 #endif  // ART_LIBELFFILE_DWARF_DEBUG_LINE_OPCODE_WRITER_H_
262