Make X64Assembler short-lived
[hiphop-php.git] / hphp / runtime / vm / debug / elfwriter.cpp
blobfef41b5808ac33fecf5a34b27c72acf247b34344
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/debug/elfwriter.h"
17 #include "hphp/runtime/vm/debug/gdb-jit.h"
18 #include <elf.h>
19 #include <gelf.h>
20 #include <elf.h>
21 #include <string>
22 #include <vector>
23 #include <stdio.h>
24 #include <stdlib.h>
26 #include "hphp/util/trace.h"
27 #include "hphp/util/asm-x64.h"
29 #include "hphp/runtime/base/runtime-option.h"
30 #include "hphp/runtime/vm/jit/translator-x64.h"
32 using namespace HPHP::Transl;
34 namespace HPHP {
35 namespace Debug {
37 TRACE_SET_MOD(debuginfo);
38 static const uint8_t CFA_OFFSET = 16;
40 void ElfWriter::logError(const string& msg) {
41 perror("");
42 std::cerr << msg << '\n';
45 int ElfWriter::dwarfCallback(char *name, int size, Dwarf_Unsigned type,
46 Dwarf_Unsigned flags, Dwarf_Unsigned link, Dwarf_Unsigned info) {
47 if (!strncmp(name, ".rel", 4))
48 return 0;
49 return newSection(name, size, type, flags);
52 bool ElfWriter::initElfHeader() {
53 if (elf_version(EV_CURRENT) == EV_NONE) {
54 logError("ELF library initialization failed");
55 return false;
58 m_elf = elf_begin(m_fd, ELF_C_WRITE, 0);
59 if (!m_elf) {
60 logError("Unable to create elf with elf_begin()");
61 return false;
64 m_ehdr = elf64_newehdr(m_elf);
65 if (!m_ehdr) {
66 logError("Unable to create elf header with elf64_newehdr()");
67 return false;
70 m_ehdr->e_ident[EI_MAG0] = ELFMAG0;
71 m_ehdr->e_ident[EI_MAG1] = ELFMAG1;
72 m_ehdr->e_ident[EI_MAG2] = ELFMAG2;
73 m_ehdr->e_ident[EI_MAG3] = ELFMAG3;
74 m_ehdr->e_ident[EI_CLASS] = ELFCLASS64;
75 m_ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
76 m_ehdr->e_ident[EI_VERSION] = EV_CURRENT;
77 m_ehdr->e_machine = EM_X86_64;
78 m_ehdr->e_type = ET_EXEC;
79 m_ehdr->e_version = EV_CURRENT;
81 return true;
84 int ElfWriter::addSectionString(const string& name) {
85 int off = m_strtab.size();
86 for (unsigned int i = 0; i < name.size(); i++) {
87 m_strtab.push_back(name[i]);
89 m_strtab.push_back('\0');
90 return off;
93 void ElfWriter::initStrtab() {
94 addSectionString("");
97 bool ElfWriter::initDwarfProducer() {
98 Dwarf_Error error = 0;
99 /* m_dwarfProducer is the handle used for interaction for libdwarf */
100 m_dwarfProducer = dwarf_producer_init_c(
101 DW_DLC_WRITE | DW_DLC_SIZE_64 | DW_DLC_SYMBOLIC_RELOCATIONS,
102 g_dwarfCallback,
103 nullptr,
104 nullptr,
105 reinterpret_cast<Dwarf_Ptr>(this),
106 &error);
107 if (m_dwarfProducer == reinterpret_cast<Dwarf_P_Debug>(DW_DLV_BADADDR)) {
108 logError("Unable to create dwarf producer");
109 return false;
111 return true;
114 Dwarf_P_Die ElfWriter::addFunctionInfo(FunctionInfo* f, Dwarf_P_Die type) {
115 Dwarf_Error error = 0;
117 /* top level DIE for each function */
118 Dwarf_P_Die func = dwarf_new_die(m_dwarfProducer,
119 DW_TAG_subprogram, nullptr, nullptr, nullptr, nullptr, &error);
120 if (reinterpret_cast<Dwarf_Addr>(func) == DW_DLV_BADADDR) {
121 logError("unable to create child DIE");
122 return nullptr;
125 Dwarf_Signed file;
126 FileDB::iterator it = m_fileDB.find(f->file);
127 /* if this function is from an unseen file, register file name
128 * and get index to file name */
129 if (it == m_fileDB.end()) {
130 file = dwarf_add_file_decl(m_dwarfProducer,
131 (char *)f->file, 0, 0, 1000, &error);
132 if (file == DW_DLV_NOCOUNT) {
133 logError("unable to add file declaration");
134 return nullptr;
136 m_fileDB[f->file] = file;
137 } else {
138 file = it->second;
141 /* add function name attribute to function DIE */
142 Dwarf_P_Attribute at;
143 at = dwarf_add_AT_name(func, (char *)f->name.c_str(), &error);
144 if (reinterpret_cast<Dwarf_Addr>(at) == DW_DLV_BADADDR) {
145 logError("unable to add name attribute to function");
146 return nullptr;
149 /* Add lower PC bound to function DIE */
150 at = dwarf_add_AT_targ_address(m_dwarfProducer, func, DW_AT_low_pc,
151 reinterpret_cast<Dwarf_Unsigned>(f->range.begin()), 0, &error);
152 if (reinterpret_cast<Dwarf_Addr>(at) == DW_DLV_BADADDR) {
153 logError("unable to add low_pc attribute to function");
154 return nullptr;
157 /* add upper PC bound to function DIE */
158 at = dwarf_add_AT_targ_address(m_dwarfProducer, func, DW_AT_high_pc,
159 reinterpret_cast<Dwarf_Unsigned>(f->range.end()), 0, &error);
160 if (reinterpret_cast<Dwarf_Addr>(at) == DW_DLV_BADADDR) {
161 logError("unable to add high_pc attribute to function");
162 return nullptr;
165 /* register line number information for function:
166 * 1. register start address */
167 Dwarf_Unsigned u;
168 u = dwarf_lne_set_address(m_dwarfProducer,
169 reinterpret_cast<Dwarf_Addr>(f->range.begin()), 0, &error);
170 if (u != 0) {
171 logError("unable to set line start address");
172 return nullptr;
175 /* 2. register line number info for each tracelet in function */
176 std::vector<LineEntry>::iterator it2;
177 for (it2 = f->m_lineTable.begin(); it2 != f->m_lineTable.end(); it2++) {
178 u = dwarf_add_line_entry(m_dwarfProducer,
179 file, reinterpret_cast<Dwarf_Addr>(it2->range.begin()), it2->lineNumber,
180 0, 1, 0, &error);
181 if (u != 0) {
182 logError("unable to add line entry");
183 return nullptr;
185 TRACE(1, "elfwriter tracelet: %s %p %p\n",
186 m_filename.c_str(), it2->range.begin(), it2->range.end());
189 /* 3. register end address of function */
190 u = dwarf_lne_end_sequence(m_dwarfProducer,
191 reinterpret_cast<Dwarf_Addr>(f->range.end()), &error);
192 if (u != 0) {
193 logError("unable to set line end address");
194 return nullptr;
197 /* 4. register frame base of function */
198 Dwarf_P_Expr locExpr = dwarf_new_expr(m_dwarfProducer, &error);
199 if (locExpr == nullptr) {
200 logError("unable to create new location expression");
201 return nullptr;
204 u = dwarf_add_expr_gen(locExpr, DW_OP_call_frame_cfa, 0, 0, &error);
205 if (u == DW_DLV_NOCOUNT) {
206 logError("unable to add subexpression to location expression");
207 return nullptr;
210 u = dwarf_add_expr_gen(locExpr, DW_OP_const1u, CFA_OFFSET, 0, &error);
211 if (u == DW_DLV_NOCOUNT) {
212 logError("unable to add subexpression to location expression");
213 return nullptr;
216 u = dwarf_add_expr_gen(locExpr, DW_OP_minus, 0, 0, &error);
217 if (u == DW_DLV_NOCOUNT) {
218 logError("unable to add subexpression to location expression");
219 return nullptr;
222 Dwarf_P_Attribute frameBaseAttr = dwarf_add_AT_location_expr(m_dwarfProducer,
223 func, DW_AT_frame_base, locExpr, &error);
224 if (reinterpret_cast<Dwarf_Addr>(frameBaseAttr) == DW_DLV_BADADDR) {
225 logError("unable to add frame_base attribute");
226 return nullptr;
229 /* 5. register all the named locals of function */
230 Dwarf_P_Die lastLocal = nullptr;
231 int i = 1;
232 for (std::vector<std::string>::iterator it = f->m_namedLocals.begin();
233 it != f->m_namedLocals.end(); it++) {
234 Dwarf_P_Die localVar = dwarf_new_die(m_dwarfProducer,
235 DW_TAG_variable, nullptr, nullptr, nullptr, nullptr, &error);
236 if (reinterpret_cast<Dwarf_Addr>(localVar) == DW_DLV_BADADDR) {
237 logError("unable to create new DIE for local variable");
238 return nullptr;
241 /* Create location expression defined w.r.t DW_AT_frame_base */
242 Dwarf_P_Expr locExpr = dwarf_new_expr(m_dwarfProducer, &error);
243 if (locExpr == nullptr) {
244 logError("unable to create new location expression");
245 return nullptr;
248 u = dwarf_add_expr_gen(locExpr, DW_OP_fbreg,
249 -(i * sizeof(TypedValue)), 0, &error);
250 ++i;
251 if (u == DW_DLV_NOCOUNT) {
252 logError("unable to add subexpression to location expression");
253 return nullptr;
256 Dwarf_P_Attribute locAttr = dwarf_add_AT_location_expr(m_dwarfProducer,
257 localVar, DW_AT_location, locExpr, &error);
258 if (reinterpret_cast<Dwarf_Addr>(locAttr) == DW_DLV_BADADDR) {
259 logError("unable to add location attribute to local variable");
260 return nullptr;
263 Dwarf_P_Attribute nameAttr = dwarf_add_AT_name(localVar, const_cast<char *>(it->data()), &error);
264 if (reinterpret_cast<Dwarf_Addr>(nameAttr) == DW_DLV_BADADDR) {
265 logError("unable to add name attribute to local variable");
266 return nullptr;
269 Dwarf_P_Attribute varTypeAttr = dwarf_add_AT_reference(m_dwarfProducer,
270 localVar, DW_AT_type, type, &error);
271 if (reinterpret_cast<Dwarf_Addr>(varTypeAttr) == DW_DLV_BADADDR) {
272 logError("unable to add type attribute to local variable DIE");
273 return nullptr;
276 Dwarf_P_Die res = 0;
277 if (lastLocal != nullptr) {
278 res = dwarf_die_link(localVar, nullptr, nullptr, lastLocal, nullptr, &error);
279 } else {
280 res = dwarf_die_link(localVar, func, nullptr, nullptr, nullptr, &error);
282 if (reinterpret_cast<Dwarf_Addr>(res) == DW_DLV_BADADDR) {
283 logError("unable to link die");
284 return nullptr;
286 lastLocal = localVar;
289 return func;
292 Dwarf_P_Die ElfWriter::makeLocalTypeDie() {
293 Dwarf_Error error = 0;
294 Dwarf_P_Die typedValueType = dwarf_new_die(m_dwarfProducer,
295 DW_TAG_structure_type, nullptr, nullptr, nullptr, nullptr, &error);
296 if (reinterpret_cast<Dwarf_Addr>(typedValueType) == DW_DLV_BADADDR) {
297 logError("unable to create new DIE for TypedValue type");
298 return nullptr;
301 /* hard coding the name of 'HPHP::TypedValue' */
302 Dwarf_P_Attribute at;
303 at = dwarf_add_AT_name(typedValueType, "HPHP::TypedValue", &error);
304 if (reinterpret_cast<Dwarf_Addr>(at) == DW_DLV_BADADDR) {
305 logError("unable to add name attribute to TypedValue type DIE");
306 return nullptr;
309 at = dwarf_add_AT_flag(m_dwarfProducer,
310 typedValueType, DW_AT_declaration, 1, &error);
311 if (reinterpret_cast<Dwarf_Addr>(at) == DW_DLV_BADADDR) {
312 logError("unable to add declaration attribute to TypedValue type DIE");
313 return nullptr;
316 at = dwarf_add_AT_unsigned_const(m_dwarfProducer,
317 typedValueType, DW_AT_byte_size, sizeof(TypedValue), &error);
318 if (reinterpret_cast<Dwarf_Addr>(at) == DW_DLV_BADADDR) {
319 logError("unable to add byte_size attribute to TypedValue type DIE");
320 return nullptr;
322 return typedValueType;
325 bool ElfWriter::addSymbolInfo(DwarfChunk* d) {
326 Dwarf_Error error = 0;
328 /* create a top level DIE (debug information entry)
329 * all subsequent DIEs' will be children of this DIE
331 Dwarf_P_Die codeUnit = dwarf_new_die(m_dwarfProducer,
332 DW_TAG_compile_unit, nullptr, nullptr, nullptr, nullptr, &error);
333 if (reinterpret_cast<Dwarf_Addr>(codeUnit) == DW_DLV_BADADDR) {
334 logError("unable to create code unit DIE");
335 return false;
338 Dwarf_P_Die lastChild = nullptr;
339 FuncPtrDB::iterator it;
340 Dwarf_P_Die type = makeLocalTypeDie();
341 if (type == nullptr) {
342 logError("unable to create type DIE");
343 return false;
345 Dwarf_P_Die linkRes;
346 linkRes = dwarf_die_link(type, codeUnit, nullptr, nullptr, nullptr, &error);
347 if (reinterpret_cast<Dwarf_Addr>(linkRes) == DW_DLV_BADADDR) {
348 logError("unable to link die");
349 return false;
352 for (it = d->m_functions.begin(); it != d->m_functions.end(); it++) {
353 /* for each function, add DIE entries with information about name,
354 * line number, file, etc */
355 Dwarf_P_Die func = addFunctionInfo(*it, type);
356 if (func == nullptr) {
357 logError("unable to create child DIE");
358 return false;
361 if (lastChild) {
362 linkRes = dwarf_die_link(func, nullptr, nullptr, lastChild, nullptr, &error);
363 } else {
364 linkRes = dwarf_die_link(func, codeUnit, nullptr, nullptr, nullptr, &error);
366 if (reinterpret_cast<Dwarf_Addr>(linkRes) == DW_DLV_BADADDR) {
367 logError("unable to link die");
368 return false;
370 lastChild = func;
373 /* register top level DIE */
374 Dwarf_Unsigned res = dwarf_add_die_to_debug(m_dwarfProducer,
375 codeUnit, &error);
376 if (res != DW_DLV_OK) {
377 logError("unable to add DIE to DWARF");
378 return false;
381 return true;
384 bool ElfWriter::addFrameInfo(DwarfChunk* d) {
385 Dwarf_Error error = 0;
386 DwarfBuf& b = d->m_buf;
387 b.clear();
388 /* Define common set of rules for unwinding frames in the VM stack*/
390 /* Frame pointer (CFA) for previous frame is in RBP + 16 */
391 b.dwarf_cfa_def_cfa(RBP, CFA_OFFSET);
392 /* Previous RIP is at CFA - 1 . DWARF_DATA_ALIGN (8) */
393 b.dwarf_cfa_offset_extended_sf(RIP, -1);
394 /* Previous RBP is at CFA - 2 . DWARF_DATA_ALIGN (8) */
395 b.dwarf_cfa_offset_extended_sf(RBP, -2);
397 * RSP is unchanged in VM frames, except for some rare cases with
398 * calls to functions that we assume don't throw. (Technically
399 * debug information will be wrong if we stop under one of those
400 * cases.)
402 * Note: if rVmSp is ever changed to refer to rsp, this code needs
403 * to change.
405 b.dwarf_cfa_same_value(RSP);
407 /* register above rules in a CIE (common information entry) */
408 Dwarf_Signed cie_index = dwarf_add_frame_cie(
409 m_dwarfProducer,
411 DWARF_CODE_ALIGN,
412 DWARF_DATA_ALIGN,
413 RIP,
414 (void *)b.getBuf(),
415 b.size(),
416 &error
418 if (cie_index == DW_DLV_NOCOUNT) {
419 logError("Unable to add CIE frame");
420 return false;
423 /* for each tracelet, register tracelet address ranges in
424 * an FDE (Frame Description entry) */
425 FuncPtrDB::iterator it;
426 for (it = d->m_functions.begin(); it != d->m_functions.end(); it++) {
427 Dwarf_P_Fde fde = dwarf_new_fde(m_dwarfProducer, &error);
428 if (reinterpret_cast<Dwarf_Addr>(fde) == DW_DLV_BADADDR) {
429 logError("Unable to create FDE");
430 return false;
432 DwarfBuf buf;
433 int err = dwarf_insert_fde_inst_bytes(
434 m_dwarfProducer, fde, buf.size(), buf.getBuf(), &error);
435 if (err == DW_DLV_ERROR) {
436 logError("Unable to add instructions to fde");
437 return false;
439 Dwarf_Unsigned fde_index = dwarf_add_frame_fde(
440 m_dwarfProducer, fde, 0, cie_index,
441 (Dwarf_Unsigned)((*it)->range.begin()),
442 (*it)->range.size(),
443 0, &error);
444 if (fde_index == DW_DLV_BADADDR) {
445 logError("Unable to add FDE");
446 return false;
449 return true;
452 int ElfWriter::newSection(char *name,
453 uint64_t size, uint32_t type, uint64_t flags,
454 uint64_t addr/* = 0*/) {
455 Elf_Scn *scn = elf_newscn(m_elf);
456 if (!scn) {
457 logError("Unable to create new section");
458 return -1;
460 Elf64_Shdr *sectionHdr = elf64_getshdr(scn);
461 if (!sectionHdr) {
462 logError("Unable to create section header");
463 return -1;
465 int nameOffset = addSectionString(name);
466 sectionHdr->sh_name = nameOffset;
467 sectionHdr->sh_type = type;
468 sectionHdr->sh_flags = flags;
469 sectionHdr->sh_size = size;
470 sectionHdr->sh_addr = addr;
471 sectionHdr->sh_offset = 0;
472 sectionHdr->sh_link = 0;
473 sectionHdr->sh_info = 0;
474 sectionHdr->sh_addralign = 1;
475 sectionHdr->sh_entsize = 0;
477 return elf_ndxscn(scn);
480 bool ElfWriter::addSectionData(int section_index, void *data, uint64_t size) {
481 Elf_Scn *scn = elf_getscn(m_elf, section_index);
482 if (!scn) {
483 logError("Unable to retrieve section number");
484 return false;
486 Elf_Data *elfData = elf_newdata(scn);
487 if (!elfData) {
488 logError("Unable to add section data");
489 return false;
491 elfData->d_buf = data;
492 elfData->d_type = ELF_T_BYTE;
493 elfData->d_size = size;
494 elfData->d_off = 0;
495 elfData->d_align = 1;
496 elfData->d_version = EV_CURRENT;
497 return true;
500 bool ElfWriter::writeDwarfInfo() {
501 Dwarf_Signed sections = dwarf_transform_to_disk_form (m_dwarfProducer, 0);
503 Dwarf_Signed i = 0;
504 Dwarf_Signed elf_section_index = 0;
505 Dwarf_Unsigned length = 0;
507 for (i = 0; i < sections; i++) {
508 Dwarf_Ptr bytes = dwarf_get_section_bytes(
509 m_dwarfProducer, 0, &elf_section_index, &length, 0);
511 if (!addSectionData(elf_section_index, bytes, length)) {
512 logError("Unable to create section");
513 return false;
516 return true;
519 int ElfWriter::writeStringSection() {
520 int section = -1;
521 if ((section = newSection(
522 ".shstrtab", m_strtab.size(), SHT_STRTAB, SHF_STRINGS)) < 0) {
523 logError("unable to create string section");
524 return -1;
526 if (!addSectionData(section, &m_strtab[0], m_strtab.size())) {
527 logError("unable to add string data");
528 return -1;
530 return section;
533 int ElfWriter::writeTextSection() {
534 int section = -1;
535 CodeBlock& a = TranslatorX64::Get()->mainCode;
536 if ((section = newSection(
537 ".text.tracelets", a.capacity(), SHT_NOBITS, SHF_ALLOC | SHF_EXECINSTR,
538 reinterpret_cast<uint64_t>(a.base()))) < 0) {
539 logError("unable to create text section");
540 return -1;
542 if (!addSectionData(section, nullptr, a.capacity())) {
543 logError("unable to add text data");
544 return -1;
546 return section;
549 ElfWriter::ElfWriter(DwarfChunk* d):
550 m_fd(-1), m_elf(nullptr), m_dwarfProducer(nullptr) {
551 off_t elf_size;
552 char *symfile;
554 m_filename = string("/tmp/vm_dwarf.XXXXXX");
555 m_fd = mkstemp((char *)m_filename.c_str());
556 if (m_fd < 0) {
557 logError("Unable to open file for writing.");
558 return;
560 if (!initElfHeader())
561 return;
562 initStrtab();
563 if (!initDwarfProducer())
564 return;
565 if (!addFrameInfo(d))
566 return;
567 if (!addSymbolInfo(d))
568 return;
569 if (!writeDwarfInfo())
570 return;
571 if (!writeTextSection())
572 return;
573 int stringIndex;
574 if ((stringIndex = writeStringSection()) < 0) {
575 logError("Unable to create string section");
576 return;
578 m_ehdr->e_shstrndx = stringIndex;
580 if ((elf_size = elf_update(m_elf, ELF_C_WRITE)) == -1) {
581 logError("Error writing ELF to disk");
582 return;
585 if (lseek(m_fd, 0, SEEK_SET) != 0) {
586 logError("Unable to seek to beginning of ELF file");
587 return;
590 symfile = (char*)malloc(elf_size);
591 if (read(m_fd, (void *)symfile, elf_size) != elf_size) {
592 logError("Unable to read elf file");
593 return;
595 register_gdb_hook(symfile, elf_size, d);
596 d->setSynced();
599 ElfWriter::~ElfWriter() {
600 if (m_elf != nullptr)
601 elf_end(m_elf);
602 if (m_fd != -1)
603 close(m_fd);
604 if (!RuntimeOption::EvalJitKeepDbgFiles) {
605 unlink(m_filename.c_str());
607 if (m_dwarfProducer != nullptr)
608 dwarf_producer_finish(m_dwarfProducer, 0);