blob: da3230f8ceb8514ca7647c376d9f1c8da948b087 [file] [log] [blame]
David Srbeckyc5bfa972016-02-05 15:49:10 +00001/*
2 * Copyright (C) 2016 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#include "elf_debug_writer.h"
18
David Srbeckybe50f9a2018-12-05 10:48:42 +000019#include <type_traits>
David Srbecky56da23c2017-09-08 19:59:15 +010020#include <unordered_map>
David Srbeckybe50f9a2018-12-05 10:48:42 +000021#include <vector>
David Srbeckyc5bfa972016-02-05 15:49:10 +000022
David Brazdild9c90372016-09-14 16:53:55 +010023#include "base/array_ref.h"
David Srbecky0b21e412018-12-05 13:24:06 +000024#include "base/stl_util.h"
David Srbeckyc5bfa972016-02-05 15:49:10 +000025#include "debug/elf_compilation_unit.h"
26#include "debug/elf_debug_frame_writer.h"
27#include "debug/elf_debug_info_writer.h"
28#include "debug/elf_debug_line_writer.h"
29#include "debug/elf_debug_loc_writer.h"
David Srbeckyc5bfa972016-02-05 15:49:10 +000030#include "debug/elf_symtab_writer.h"
31#include "debug/method_debug_info.h"
David Srbecky2faab002019-02-12 16:35:48 +000032#include "dwarf/dwarf_constants.h"
David Srbecky2faab002019-02-12 16:35:48 +000033#include "elf/elf_builder.h"
34#include "elf/elf_debug_reader.h"
David Srbecky50928112019-03-22 17:06:28 +000035#include "elf/elf_utils.h"
David Srbecky2faab002019-02-12 16:35:48 +000036#include "elf/xz_utils.h"
Andreas Gamped4901292017-05-30 18:41:34 -070037#include "oat.h"
David Srbecky2faab002019-02-12 16:35:48 +000038#include "stream/vector_output_stream.h"
David Srbeckyc5bfa972016-02-05 15:49:10 +000039
40namespace art {
41namespace debug {
42
David Srbeckybe50f9a2018-12-05 10:48:42 +000043using ElfRuntimeTypes = std::conditional<sizeof(void*) == 4, ElfTypes32, ElfTypes64>::type;
44
David Srbeckyc5bfa972016-02-05 15:49:10 +000045template <typename ElfTypes>
David Srbecky2faab002019-02-12 16:35:48 +000046void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
David Srbecky7370d922019-02-12 14:00:30 +000047 const DebugInfo& debug_info) {
David Srbecky09c2a6b2016-03-11 17:11:44 +000048 // Write .strtab and .symtab.
Andreas Gampe3db70682018-12-26 15:12:03 -080049 WriteDebugSymbols(builder, /* mini-debug-info= */ false, debug_info);
David Srbeckyc5bfa972016-02-05 15:49:10 +000050
David Srbecky09c2a6b2016-03-11 17:11:44 +000051 // Write .debug_frame.
David Srbecky7370d922019-02-12 14:00:30 +000052 WriteCFISection(builder, debug_info.compiled_methods);
David Srbecky09c2a6b2016-03-11 17:11:44 +000053
David Srbecky56da23c2017-09-08 19:59:15 +010054 // Group the methods into compilation units based on class.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -080055 std::unordered_map<const dex::ClassDef*, ElfCompilationUnit> class_to_compilation_unit;
David Srbecky32210b92017-12-04 14:39:21 +000056 for (const MethodDebugInfo& mi : debug_info.compiled_methods) {
David Srbecky09c2a6b2016-03-11 17:11:44 +000057 if (mi.dex_file != nullptr) {
58 auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index);
David Srbecky56da23c2017-09-08 19:59:15 +010059 ElfCompilationUnit& cu = class_to_compilation_unit[&dex_class_def];
David Srbecky09c2a6b2016-03-11 17:11:44 +000060 cu.methods.push_back(&mi);
61 // All methods must have the same addressing mode otherwise the min/max below does not work.
62 DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative);
63 cu.is_code_address_text_relative = mi.is_code_address_text_relative;
64 cu.code_address = std::min(cu.code_address, mi.code_address);
65 cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size);
David Srbeckyc5bfa972016-02-05 15:49:10 +000066 }
David Srbeckyc5bfa972016-02-05 15:49:10 +000067 }
68
David Srbecky56da23c2017-09-08 19:59:15 +010069 // Sort compilation units to make the compiler output deterministic.
70 std::vector<ElfCompilationUnit> compilation_units;
71 compilation_units.reserve(class_to_compilation_unit.size());
72 for (auto& it : class_to_compilation_unit) {
73 // The .debug_line section requires the methods to be sorted by code address.
74 std::stable_sort(it.second.methods.begin(),
75 it.second.methods.end(),
76 [](const MethodDebugInfo* a, const MethodDebugInfo* b) {
77 return a->code_address < b->code_address;
78 });
79 compilation_units.push_back(std::move(it.second));
80 }
81 std::sort(compilation_units.begin(),
82 compilation_units.end(),
83 [](ElfCompilationUnit& a, ElfCompilationUnit& b) {
84 // Sort by index of the first method within the method_infos array.
85 // This assumes that the order of method_infos is deterministic.
86 // Code address is not good for sorting due to possible duplicates.
87 return a.methods.front() < b.methods.front();
88 });
89
David Srbeckyc5bfa972016-02-05 15:49:10 +000090 // Write .debug_line section.
91 if (!compilation_units.empty()) {
92 ElfDebugLineWriter<ElfTypes> line_writer(builder);
93 line_writer.Start();
94 for (auto& compilation_unit : compilation_units) {
95 line_writer.WriteCompilationUnit(compilation_unit);
96 }
David Srbecky7370d922019-02-12 14:00:30 +000097 line_writer.End();
David Srbeckyc5bfa972016-02-05 15:49:10 +000098 }
99
100 // Write .debug_info section.
101 if (!compilation_units.empty()) {
102 ElfDebugInfoWriter<ElfTypes> info_writer(builder);
103 info_writer.Start();
104 for (const auto& compilation_unit : compilation_units) {
105 ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
106 cu_writer.Write(compilation_unit);
107 }
David Srbecky7370d922019-02-12 14:00:30 +0000108 info_writer.End();
David Srbeckyc5bfa972016-02-05 15:49:10 +0000109 }
110}
111
David Srbecky154c57f2018-06-03 12:00:27 +0100112template <typename ElfTypes>
113static std::vector<uint8_t> MakeMiniDebugInfoInternal(
114 InstructionSet isa,
David Srbecky2faab002019-02-12 16:35:48 +0000115 const InstructionSetFeatures* features ATTRIBUTE_UNUSED,
David Srbecky154c57f2018-06-03 12:00:27 +0100116 typename ElfTypes::Addr text_section_address,
117 size_t text_section_size,
118 typename ElfTypes::Addr dex_section_address,
119 size_t dex_section_size,
120 const DebugInfo& debug_info) {
121 std::vector<uint8_t> buffer;
122 buffer.reserve(KB);
David Srbecky2faab002019-02-12 16:35:48 +0000123 VectorOutputStream out("Mini-debug-info ELF file", &buffer);
124 std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
Andreas Gampe3db70682018-12-26 15:12:03 -0800125 builder->Start(/* write_program_headers= */ false);
David Srbecky154c57f2018-06-03 12:00:27 +0100126 // Mirror ELF sections as NOBITS since the added symbols will reference them.
David Srbecky49b2b202019-02-01 13:35:48 +0000127 if (text_section_size != 0) {
128 builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
129 }
David Srbecky154c57f2018-06-03 12:00:27 +0100130 if (dex_section_size != 0) {
131 builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size);
132 }
David Srbecky49b2b202019-02-01 13:35:48 +0000133 if (!debug_info.Empty()) {
134 WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
135 }
136 if (!debug_info.compiled_methods.empty()) {
David Srbecky7370d922019-02-12 14:00:30 +0000137 WriteCFISection(builder.get(), debug_info.compiled_methods);
David Srbecky49b2b202019-02-01 13:35:48 +0000138 }
David Srbecky154c57f2018-06-03 12:00:27 +0100139 builder->End();
140 CHECK(builder->Good());
141 std::vector<uint8_t> compressed_buffer;
142 compressed_buffer.reserve(buffer.size() / 4);
David Srbeckycf1af732018-12-04 14:31:32 +0000143 XzCompress(ArrayRef<const uint8_t>(buffer), &compressed_buffer);
David Srbecky154c57f2018-06-03 12:00:27 +0100144 return compressed_buffer;
145}
146
David Srbeckyc5bfa972016-02-05 15:49:10 +0000147std::vector<uint8_t> MakeMiniDebugInfo(
148 InstructionSet isa,
David Srbecky5d811202016-03-08 13:21:22 +0000149 const InstructionSetFeatures* features,
David Srbecky32210b92017-12-04 14:39:21 +0000150 uint64_t text_section_address,
151 size_t text_section_size,
152 uint64_t dex_section_address,
153 size_t dex_section_size,
154 const DebugInfo& debug_info) {
David Srbeckyc5bfa972016-02-05 15:49:10 +0000155 if (Is64BitInstructionSet(isa)) {
David Srbecky5d811202016-03-08 13:21:22 +0000156 return MakeMiniDebugInfoInternal<ElfTypes64>(isa,
157 features,
David Srbecky32210b92017-12-04 14:39:21 +0000158 text_section_address,
159 text_section_size,
160 dex_section_address,
161 dex_section_size,
162 debug_info);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000163 } else {
David Srbecky5d811202016-03-08 13:21:22 +0000164 return MakeMiniDebugInfoInternal<ElfTypes32>(isa,
165 features,
David Srbecky32210b92017-12-04 14:39:21 +0000166 text_section_address,
167 text_section_size,
168 dex_section_address,
169 dex_section_size,
170 debug_info);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000171 }
172}
173
David Srbeckybe50f9a2018-12-05 10:48:42 +0000174std::vector<uint8_t> MakeElfFileForJIT(
David Srbeckyfe736b72016-03-09 11:44:44 +0000175 InstructionSet isa,
David Srbecky2faab002019-02-12 16:35:48 +0000176 const InstructionSetFeatures* features ATTRIBUTE_UNUSED,
David Srbeckyf4886df2017-12-11 16:06:29 +0000177 bool mini_debug_info,
David Srbeckybe50f9a2018-12-05 10:48:42 +0000178 const MethodDebugInfo& method_info) {
179 using ElfTypes = ElfRuntimeTypes;
180 CHECK_EQ(sizeof(ElfTypes::Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa)));
181 CHECK_EQ(method_info.is_code_address_text_relative, false);
David Srbecky32210b92017-12-04 14:39:21 +0000182 DebugInfo debug_info{};
David Srbeckybe50f9a2018-12-05 10:48:42 +0000183 debug_info.compiled_methods = ArrayRef<const MethodDebugInfo>(&method_info, 1);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000184 std::vector<uint8_t> buffer;
185 buffer.reserve(KB);
David Srbecky2faab002019-02-12 16:35:48 +0000186 VectorOutputStream out("Debug ELF file", &buffer);
187 std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
David Srbeckyc5bfa972016-02-05 15:49:10 +0000188 // No program headers since the ELF file is not linked and has no allocated sections.
Andreas Gampe3db70682018-12-26 15:12:03 -0800189 builder->Start(/* write_program_headers= */ false);
David Srbeckybe50f9a2018-12-05 10:48:42 +0000190 builder->GetText()->AllocateVirtualMemory(method_info.code_address, method_info.code_size);
David Srbeckyf4886df2017-12-11 16:06:29 +0000191 if (mini_debug_info) {
David Srbeckybe50f9a2018-12-05 10:48:42 +0000192 // The compression is great help for multiple methods but it is not worth it for a
193 // single method due to the overheads so skip the compression here for performance.
Andreas Gampe3db70682018-12-26 15:12:03 -0800194 WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
David Srbecky7370d922019-02-12 14:00:30 +0000195 WriteCFISection(builder.get(), debug_info.compiled_methods);
David Srbeckyf4886df2017-12-11 16:06:29 +0000196 } else {
David Srbecky7370d922019-02-12 14:00:30 +0000197 WriteDebugInfo(builder.get(), debug_info);
David Srbeckyf4886df2017-12-11 16:06:29 +0000198 }
David Srbeckyc5bfa972016-02-05 15:49:10 +0000199 builder->End();
200 CHECK(builder->Good());
David Srbecky0b21e412018-12-05 13:24:06 +0000201 // Verify the ELF file by reading it back using the trivial reader.
202 if (kIsDebugBuild) {
203 using Elf_Sym = typename ElfTypes::Sym;
David Srbecky0b21e412018-12-05 13:24:06 +0000204 size_t num_syms = 0;
David Srbecky53eb07f2019-02-12 16:34:55 +0000205 size_t num_cies = 0;
206 size_t num_fdes = 0;
207 using Reader = ElfDebugReader<ElfTypes>;
208 Reader reader(buffer);
209 reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) {
210 DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
211 DCHECK_EQ(sym.st_size, method_info.code_size);
212 num_syms++;
213 });
214 reader.VisitDebugFrame([&](const Reader::CIE* cie ATTRIBUTE_UNUSED) {
215 num_cies++;
216 }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
217 DCHECK_EQ(fde->sym_addr, method_info.code_address);
218 DCHECK_EQ(fde->sym_size, method_info.code_size);
219 num_fdes++;
220 });
David Srbecky0b21e412018-12-05 13:24:06 +0000221 DCHECK_EQ(num_syms, 1u);
David Srbecky53eb07f2019-02-12 16:34:55 +0000222 DCHECK_LE(num_cies, 1u);
223 DCHECK_LE(num_fdes, 1u);
David Srbecky0b21e412018-12-05 13:24:06 +0000224 }
Vladimir Marko93205e32016-04-13 11:59:46 +0100225 return buffer;
David Srbeckyc5bfa972016-02-05 15:49:10 +0000226}
227
David Srbecky0b21e412018-12-05 13:24:06 +0000228// Combine several mini-debug-info ELF files into one, while filtering some symbols.
229std::vector<uint8_t> PackElfFileForJIT(
230 InstructionSet isa,
David Srbecky2faab002019-02-12 16:35:48 +0000231 const InstructionSetFeatures* features ATTRIBUTE_UNUSED,
David Srbecky53eb07f2019-02-12 16:34:55 +0000232 std::vector<ArrayRef<const uint8_t>>& added_elf_files,
David Srbecky0b21e412018-12-05 13:24:06 +0000233 std::vector<const void*>& removed_symbols,
David Srbecky76b9c692019-04-01 19:36:33 +0100234 bool compress,
David Srbecky0b21e412018-12-05 13:24:06 +0000235 /*out*/ size_t* num_symbols) {
236 using ElfTypes = ElfRuntimeTypes;
237 using Elf_Addr = typename ElfTypes::Addr;
238 using Elf_Sym = typename ElfTypes::Sym;
239 CHECK_EQ(sizeof(Elf_Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa)));
David Srbecky0b21e412018-12-05 13:24:06 +0000240 auto is_removed_symbol = [&removed_symbols](Elf_Addr addr) {
241 const void* code_ptr = reinterpret_cast<const void*>(addr);
242 return std::binary_search(removed_symbols.begin(), removed_symbols.end(), code_ptr);
243 };
244 uint64_t min_address = std::numeric_limits<uint64_t>::max();
245 uint64_t max_address = 0;
246
247 // Produce the inner ELF file.
248 // It will contain the symbols (.symtab) and unwind information (.debug_frame).
249 std::vector<uint8_t> inner_elf_file;
250 {
251 inner_elf_file.reserve(1 * KB); // Approximate size of ELF file with a single symbol.
David Srbecky2faab002019-02-12 16:35:48 +0000252 VectorOutputStream out("Mini-debug-info ELF file for JIT", &inner_elf_file);
253 std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
David Srbecky0b21e412018-12-05 13:24:06 +0000254 builder->Start(/*write_program_headers=*/ false);
255 auto* text = builder->GetText();
256 auto* strtab = builder->GetStrTab();
257 auto* symtab = builder->GetSymTab();
258 auto* debug_frame = builder->GetDebugFrame();
259 std::deque<Elf_Sym> symbols;
David Srbecky53eb07f2019-02-12 16:34:55 +0000260
261 using Reader = ElfDebugReader<ElfTypes>;
262 std::deque<Reader> readers;
263 for (ArrayRef<const uint8_t> added_elf_file : added_elf_files) {
264 readers.emplace_back(added_elf_file);
265 }
David Srbecky0b21e412018-12-05 13:24:06 +0000266
267 // Write symbols names. All other data is buffered.
268 strtab->Start();
269 strtab->Write(""); // strtab should start with empty string.
David Srbecky53eb07f2019-02-12 16:34:55 +0000270 for (Reader& reader : readers) {
271 reader.VisitFunctionSymbols([&](Elf_Sym sym, const char* name) {
272 if (is_removed_symbol(sym.st_value)) {
273 return;
274 }
275 sym.st_name = strtab->Write(name);
276 symbols.push_back(sym);
277 min_address = std::min<uint64_t>(min_address, sym.st_value);
278 max_address = std::max<uint64_t>(max_address, sym.st_value + sym.st_size);
279 });
David Srbecky0b21e412018-12-05 13:24:06 +0000280 }
281 strtab->End();
282
283 // Create .text covering the code range. Needed for gdb to find the symbols.
284 if (max_address > min_address) {
285 text->AllocateVirtualMemory(min_address, max_address - min_address);
286 }
287
288 // Add the symbols.
289 *num_symbols = symbols.size();
290 for (; !symbols.empty(); symbols.pop_front()) {
291 symtab->Add(symbols.front(), text);
292 }
293 symtab->WriteCachedSection();
294
295 // Add the CFI/unwind section.
296 debug_frame->Start();
David Srbecky53eb07f2019-02-12 16:34:55 +0000297 // ART always produces the same CIE, so we copy the first one and ignore the rest.
298 bool copied_cie = false;
299 for (Reader& reader : readers) {
300 reader.VisitDebugFrame([&](const Reader::CIE* cie) {
301 if (!copied_cie) {
302 debug_frame->WriteFully(cie->data(), cie->size());
303 copied_cie = true;
304 }
305 }, [&](const Reader::FDE* fde, const Reader::CIE* cie ATTRIBUTE_UNUSED) {
306 DCHECK(copied_cie);
307 DCHECK_EQ(fde->cie_pointer, 0);
308 if (!is_removed_symbol(fde->sym_addr)) {
309 debug_frame->WriteFully(fde->data(), fde->size());
310 }
311 });
312 }
David Srbecky0b21e412018-12-05 13:24:06 +0000313 debug_frame->End();
314
315 builder->End();
316 CHECK(builder->Good());
317 }
318
319 // Produce the outer ELF file.
320 // It contains only the inner ELF file compressed as .gnu_debugdata section.
321 // This extra wrapping is not necessary but the compression saves space.
David Srbecky76b9c692019-04-01 19:36:33 +0100322 if (compress) {
323 std::vector<uint8_t> outer_elf_file;
David Srbecky0b21e412018-12-05 13:24:06 +0000324 std::vector<uint8_t> gnu_debugdata;
325 gnu_debugdata.reserve(inner_elf_file.size() / 4);
326 XzCompress(ArrayRef<const uint8_t>(inner_elf_file), &gnu_debugdata);
327
328 outer_elf_file.reserve(KB + gnu_debugdata.size());
David Srbecky2faab002019-02-12 16:35:48 +0000329 VectorOutputStream out("Mini-debug-info ELF file for JIT", &outer_elf_file);
330 std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
David Srbecky0b21e412018-12-05 13:24:06 +0000331 builder->Start(/*write_program_headers=*/ false);
332 if (max_address > min_address) {
333 builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
334 }
335 builder->WriteSection(".gnu_debugdata", &gnu_debugdata);
336 builder->End();
337 CHECK(builder->Good());
David Srbecky76b9c692019-04-01 19:36:33 +0100338 return outer_elf_file;
339 } else {
340 return inner_elf_file;
David Srbecky0b21e412018-12-05 13:24:06 +0000341 }
David Srbecky0b21e412018-12-05 13:24:06 +0000342}
343
David Srbeckybe50f9a2018-12-05 10:48:42 +0000344std::vector<uint8_t> WriteDebugElfFileForClasses(
David Srbeckyfe736b72016-03-09 11:44:44 +0000345 InstructionSet isa,
David Srbecky2faab002019-02-12 16:35:48 +0000346 const InstructionSetFeatures* features ATTRIBUTE_UNUSED,
David Srbecky5d811202016-03-08 13:21:22 +0000347 const ArrayRef<mirror::Class*>& types)
Andreas Gampebdf7f1c2016-08-30 16:38:47 -0700348 REQUIRES_SHARED(Locks::mutator_lock_) {
David Srbeckybe50f9a2018-12-05 10:48:42 +0000349 using ElfTypes = ElfRuntimeTypes;
350 CHECK_EQ(sizeof(ElfTypes::Addr), static_cast<size_t>(GetInstructionSetPointerSize(isa)));
David Srbeckyc5bfa972016-02-05 15:49:10 +0000351 std::vector<uint8_t> buffer;
352 buffer.reserve(KB);
David Srbecky2faab002019-02-12 16:35:48 +0000353 VectorOutputStream out("Debug ELF file", &buffer);
354 std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
David Srbeckyc5bfa972016-02-05 15:49:10 +0000355 // No program headers since the ELF file is not linked and has no allocated sections.
Andreas Gampe3db70682018-12-26 15:12:03 -0800356 builder->Start(/* write_program_headers= */ false);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000357 ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
358 info_writer.Start();
359 ElfCompilationUnitWriter<ElfTypes> cu_writer(&info_writer);
360 cu_writer.Write(types);
David Srbecky7370d922019-02-12 14:00:30 +0000361 info_writer.End();
David Srbeckyc5bfa972016-02-05 15:49:10 +0000362
363 builder->End();
364 CHECK(builder->Good());
Vladimir Marko93205e32016-04-13 11:59:46 +0100365 return buffer;
David Srbeckyc5bfa972016-02-05 15:49:10 +0000366}
367
David Srbeckyc5bfa972016-02-05 15:49:10 +0000368// Explicit instantiations
369template void WriteDebugInfo<ElfTypes32>(
David Srbecky2faab002019-02-12 16:35:48 +0000370 ElfBuilder<ElfTypes32>* builder,
David Srbecky7370d922019-02-12 14:00:30 +0000371 const DebugInfo& debug_info);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000372template void WriteDebugInfo<ElfTypes64>(
David Srbecky2faab002019-02-12 16:35:48 +0000373 ElfBuilder<ElfTypes64>* builder,
David Srbecky7370d922019-02-12 14:00:30 +0000374 const DebugInfo& debug_info);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000375
376} // namespace debug
377} // namespace art