2 +----------------------------------------------------------------------+
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"
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
;
37 TRACE_SET_MOD(debuginfo
);
38 static const uint8_t CFA_OFFSET
= 16;
40 void ElfWriter::logError(const string
& msg
) {
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))
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");
58 m_elf
= elf_begin(m_fd
, ELF_C_WRITE
, 0);
60 logError("Unable to create elf with elf_begin()");
64 m_ehdr
= elf64_newehdr(m_elf
);
66 logError("Unable to create elf header with elf64_newehdr()");
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
;
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');
93 void ElfWriter::initStrtab() {
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
,
105 reinterpret_cast<Dwarf_Ptr
>(this),
107 if (m_dwarfProducer
== reinterpret_cast<Dwarf_P_Debug
>(DW_DLV_BADADDR
)) {
108 logError("Unable to create dwarf producer");
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");
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");
136 m_fileDB
[f
->file
] = file
;
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");
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");
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");
165 /* register line number information for function:
166 * 1. register start address */
168 u
= dwarf_lne_set_address(m_dwarfProducer
,
169 reinterpret_cast<Dwarf_Addr
>(f
->range
.begin()), 0, &error
);
171 logError("unable to set line start address");
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
,
182 logError("unable to add line entry");
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
);
193 logError("unable to set line end address");
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");
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");
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");
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");
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");
229 /* 5. register all the named locals of function */
230 Dwarf_P_Die lastLocal
= nullptr;
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");
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");
248 u
= dwarf_add_expr_gen(locExpr
, DW_OP_fbreg
,
249 -(i
* sizeof(TypedValue
)), 0, &error
);
251 if (u
== DW_DLV_NOCOUNT
) {
252 logError("unable to add subexpression to location expression");
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");
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");
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");
277 if (lastLocal
!= nullptr) {
278 res
= dwarf_die_link(localVar
, nullptr, nullptr, lastLocal
, nullptr, &error
);
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");
286 lastLocal
= localVar
;
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");
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");
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");
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");
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");
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");
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");
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");
362 linkRes
= dwarf_die_link(func
, nullptr, nullptr, lastChild
, nullptr, &error
);
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");
373 /* register top level DIE */
374 Dwarf_Unsigned res
= dwarf_add_die_to_debug(m_dwarfProducer
,
376 if (res
!= DW_DLV_OK
) {
377 logError("unable to add DIE to DWARF");
384 bool ElfWriter::addFrameInfo(DwarfChunk
* d
) {
385 Dwarf_Error error
= 0;
386 DwarfBuf
& b
= d
->m_buf
;
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
402 * Note: if rVmSp is ever changed to refer to rsp, this code needs
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(
418 if (cie_index
== DW_DLV_NOCOUNT
) {
419 logError("Unable to add CIE frame");
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");
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");
439 Dwarf_Unsigned fde_index
= dwarf_add_frame_fde(
440 m_dwarfProducer
, fde
, 0, cie_index
,
441 (Dwarf_Unsigned
)((*it
)->range
.begin()),
444 if (fde_index
== DW_DLV_BADADDR
) {
445 logError("Unable to add FDE");
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
);
457 logError("Unable to create new section");
460 Elf64_Shdr
*sectionHdr
= elf64_getshdr(scn
);
462 logError("Unable to create section header");
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
);
483 logError("Unable to retrieve section number");
486 Elf_Data
*elfData
= elf_newdata(scn
);
488 logError("Unable to add section data");
491 elfData
->d_buf
= data
;
492 elfData
->d_type
= ELF_T_BYTE
;
493 elfData
->d_size
= size
;
495 elfData
->d_align
= 1;
496 elfData
->d_version
= EV_CURRENT
;
500 bool ElfWriter::writeDwarfInfo() {
501 Dwarf_Signed sections
= dwarf_transform_to_disk_form (m_dwarfProducer
, 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");
519 int ElfWriter::writeStringSection() {
521 if ((section
= newSection(
522 ".shstrtab", m_strtab
.size(), SHT_STRTAB
, SHF_STRINGS
)) < 0) {
523 logError("unable to create string section");
526 if (!addSectionData(section
, &m_strtab
[0], m_strtab
.size())) {
527 logError("unable to add string data");
533 int ElfWriter::writeTextSection() {
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");
542 if (!addSectionData(section
, nullptr, a
.capacity())) {
543 logError("unable to add text data");
549 ElfWriter::ElfWriter(DwarfChunk
* d
):
550 m_fd(-1), m_elf(nullptr), m_dwarfProducer(nullptr) {
554 m_filename
= string("/tmp/vm_dwarf.XXXXXX");
555 m_fd
= mkstemp((char *)m_filename
.c_str());
557 logError("Unable to open file for writing.");
560 if (!initElfHeader())
563 if (!initDwarfProducer())
565 if (!addFrameInfo(d
))
567 if (!addSymbolInfo(d
))
569 if (!writeDwarfInfo())
571 if (!writeTextSection())
574 if ((stringIndex
= writeStringSection()) < 0) {
575 logError("Unable to create string section");
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");
585 if (lseek(m_fd
, 0, SEEK_SET
) != 0) {
586 logError("Unable to seek to beginning of ELF file");
590 symfile
= (char*)malloc(elf_size
);
591 if (read(m_fd
, (void *)symfile
, elf_size
) != elf_size
) {
592 logError("Unable to read elf file");
595 register_gdb_hook(symfile
, elf_size
, d
);
599 ElfWriter::~ElfWriter() {
600 if (m_elf
!= nullptr)
604 if (!RuntimeOption::EvalJitKeepDbgFiles
) {
605 unlink(m_filename
.c_str());
607 if (m_dwarfProducer
!= nullptr)
608 dwarf_producer_finish(m_dwarfProducer
, 0);