// balst_stacktraceresolver_dwarfreader.h -*-C++-*- #ifndef INCLUDED_BALST_STACKTRACERESOLVER_DWARFREADER #define INCLUDED_BALST_STACKTRACERESOLVER_DWARFREADER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide mechanism for reading DWARF information from object files. // //@CLASSES: // balst::StackTraceResolver_DwarfReader: reading mechanism // //@SEE_ALSO: balst_stacktraceresolverimpl_elf // balst_stacktraceresolver_filehelper // //@DESCRIPTION: This component provides a class, // 'balst::StackTraceResolver_DwarfReader', that is optimized for reading // information from object files that are in the DWARF format. The Elf object // file format is used on Linux and Solaris platforms, and the DWARF file // format is used within ELF files to encode source file name and line number // information. The Elf format is described by documents at: //: o 'http://en.wikipedia.org/wiki/Executable_and_Linkable_Format' //: o 'http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf' //: o 'http://www.sco.com/developers/gabi/latest/contents.html' // The DWARF format is described by documents at: //: o 'http://dwarfstd.org' // // Note that this file does not include everything necessary to resolve DWARF // information. Most of that functionality is in // 'balst_stacktraceresolverimpl_elf', and this component only describe a tool // used within that effort. // ///Usage ///----- // This component is an implementation detail of 'balst' and is *not* intended // for direct client use. It is subject to change without notice. As such, a // usage example is not provided. #include <balscm_version.h> #include <balst_objectfileformat.h> #include <balst_stacktraceresolver_filehelper.h> #include <bdlb_bitutil.h> #include <bdls_filesystemutil.h> #include <bslmf_assert.h> #include <bsls_assert.h> #include <bsls_platform.h> #include <bsls_review.h> #include <bsls_types.h> #include <bsl_cstddef.h> #include <bsl_cstring.h> #include <bsl_string.h> namespace BloombergLP { namespace balst { #if defined(BALST_OBJECTFILEFORMAT_RESOLVER_DWARF) // ==================================== // class StackTraceResolver_DwarfReader // ==================================== class StackTraceResolver_DwarfReader { public: // PUBLIC TYPES typedef bdls::FilesystemUtil::Offset Offset; typedef bsls::Types::UintPtr UintPtr; typedef bsls::Types::IntPtr IntPtr; typedef bsls::Types::Uint64 Uint64; struct Section { // Refers to one section of a segment. // DATA Offset d_offset; // offset of the section in the file Offset d_size; // size of that section in bytes // CREATOR Section(); // Create a zero-value 'Section' object. // MANIPULATOR void reset(Offset offset = 0, Offset size = 0); // Reset this 'Section' object to have the specified 'offset' and // the specified 'size'. }; // PUBLIC CONSTANTS enum { k_SCRATCH_BUF_LEN = 32 * 1024 - 64 }; // length in bytes of d_buffer_p; 32K minus a little so we don't // waste a page // DWARF enums // The DWARF documents are at: http://www.dwarfstd.org/Download.php. // // The DWARF specs specify no 'struct's, only several hundred identifiers // of the form 'DW_*' defining integer values. Some sources provide // include files 'dwarf.h' specifying 'enum's for all these values, but // accessing these include files from DPKG proved problematic, and the // versions available only provided DWARF 3 identifiers and we needed some // DWARF 4 id's too. The 'dwarf.h' available from Red Hat was LGPL'ed, // posing potential licensing problems with copying or cut/pasting it into // the BDE code base, which is licensed more permissively than LGPL. // // The simplest approach was to implement these enums directly copying them // from the spec. The following are a subset of all the id's in the spec, // limited to the ones we use in this package. // // Rather than name the enum's 'DW_*' as they appear in the spec, we name // them 'e_DW_*' according to BDE convention. enum Dwarf3Enums { // These values were obtained from the DWARF 3 Spec. e_DW_AT_sibling = 0x01, e_DW_AT_location = 0x02, e_DW_AT_name = 0x03, e_DW_AT_ordering = 0x09, e_DW_AT_byte_size = 0x0b, e_DW_AT_bit_offset = 0x0c, e_DW_AT_bit_size = 0x0d, e_DW_AT_stmt_list = 0x10, e_DW_AT_low_pc = 0x11, e_DW_AT_high_pc = 0x12, e_DW_AT_language = 0x13, e_DW_AT_discr = 0x15, e_DW_AT_discr_value = 0x16, e_DW_AT_visibility = 0x17, e_DW_AT_import = 0x18, e_DW_AT_string_length = 0x19, e_DW_AT_common_reference = 0x1a, e_DW_AT_comp_dir = 0x1b, e_DW_AT_const_value = 0x1c, e_DW_AT_containing_type = 0x1d, e_DW_AT_default_value = 0x1e, e_DW_AT_inline = 0x20, e_DW_AT_is_optional = 0x21, e_DW_AT_lower_bound = 0x22, e_DW_AT_producer = 0x25, e_DW_AT_prototyped = 0x27, e_DW_AT_return_addr = 0x2a, e_DW_AT_start_scope = 0x2c, e_DW_AT_bit_stride = 0x2e, e_DW_AT_upper_bound = 0x2f, e_DW_AT_abstract_origin = 0x31, e_DW_AT_accessibility = 0x32, e_DW_AT_address_class = 0x33, e_DW_AT_artificial = 0x34, e_DW_AT_base_types = 0x35, e_DW_AT_calling_convention = 0x36, e_DW_AT_count = 0x37, e_DW_AT_data_member_location = 0x38, e_DW_AT_decl_column = 0x39, e_DW_AT_decl_file = 0x3a, e_DW_AT_decl_line = 0x3b, e_DW_AT_declaration = 0x3c, e_DW_AT_discr_list = 0x3d, e_DW_AT_encoding = 0x3e, e_DW_AT_external = 0x3f, e_DW_AT_frame_base = 0x40, e_DW_AT_friend = 0x41, e_DW_AT_identifier_case = 0x42, e_DW_AT_macro_info = 0x43, e_DW_AT_namelist_item = 0x44, e_DW_AT_priority = 0x45, e_DW_AT_segment = 0x46, e_DW_AT_specification = 0x47, e_DW_AT_static_link = 0x48, e_DW_AT_type = 0x49, e_DW_AT_use_location = 0x4a, e_DW_AT_variable_parameter = 0x4b, e_DW_AT_virtuality = 0x4c, e_DW_AT_vtable_elem_location = 0x4d, e_DW_AT_allocated = 0x4e, e_DW_AT_associated = 0x4f, e_DW_AT_data_location = 0x50, e_DW_AT_byte_stride = 0x51, e_DW_AT_entry_pc = 0x52, e_DW_AT_use_UTF8 = 0x53, e_DW_AT_extension = 0x54, e_DW_AT_ranges = 0x55, e_DW_AT_trampoline = 0x56, e_DW_AT_call_column = 0x57, e_DW_AT_call_file = 0x58, e_DW_AT_call_line = 0x59, e_DW_AT_description = 0x5a, e_DW_AT_binary_scale = 0x5b, e_DW_AT_decimal_scale = 0x5c, e_DW_AT_small = 0x5d, e_DW_AT_decimal_sign = 0x5e, e_DW_AT_digit_count = 0x5f, e_DW_AT_picture_string = 0x60, e_DW_AT_mutable = 0x61, e_DW_AT_threads_scaled = 0x62, e_DW_AT_explicit = 0x63, e_DW_AT_object_pointer = 0x64, e_DW_AT_endianity = 0x65, e_DW_AT_elemental = 0x66, e_DW_AT_pure = 0x67, e_DW_AT_recursive = 0x68, e_DW_CHILDREN_no = 0x00, e_DW_CHILDREN_yes = 0x01, e_DW_FORM_addr = 0x01, e_DW_FORM_block2 = 0x03, e_DW_FORM_block4 = 0x04, e_DW_FORM_data2 = 0x05, e_DW_FORM_data4 = 0x06, e_DW_FORM_data8 = 0x07, e_DW_FORM_string = 0x08, e_DW_FORM_block = 0x09, e_DW_FORM_block1 = 0x0a, e_DW_FORM_data1 = 0x0b, e_DW_FORM_flag = 0x0c, e_DW_FORM_sdata = 0x0d, e_DW_FORM_strp = 0x0e, e_DW_FORM_udata = 0x0f, e_DW_FORM_ref_addr = 0x10, e_DW_FORM_ref1 = 0x11, e_DW_FORM_ref2 = 0x12, e_DW_FORM_ref4 = 0x13, e_DW_FORM_ref8 = 0x14, e_DW_FORM_ref_udata = 0x15, e_DW_FORM_indirect = 0x16, e_DW_INL_declared_inlined = 0x03, e_DW_LNE_end_sequence = 0x01, e_DW_LNE_set_address = 0x02, e_DW_LNE_define_file = 0x03, e_DW_LNS_copy = 0x01, e_DW_LNS_advance_pc = 0x02, e_DW_LNS_advance_line = 0x03, e_DW_LNS_set_file = 0x04, e_DW_LNS_set_column = 0x05, e_DW_LNS_negate_stmt = 0x06, e_DW_LNS_set_basic_block = 0x07, e_DW_LNS_const_add_pc = 0x08, e_DW_LNS_fixed_advance_pc = 0x09, e_DW_LNS_set_prologue_end = 0x0a, e_DW_LNS_set_epilogue_begin = 0x0b, e_DW_LNS_set_isa = 0x0c, e_DW_TAG_array_type = 0x01, e_DW_TAG_class_type = 0x02, e_DW_TAG_entry_point = 0x03, e_DW_TAG_enumeration_type = 0x04, e_DW_TAG_formal_parameter = 0x05, e_DW_TAG_imported_declaration = 0x08, e_DW_TAG_label = 0x0a, e_DW_TAG_lexical_block = 0x0b, e_DW_TAG_member = 0x0d, e_DW_TAG_pointer_type = 0x0f, e_DW_TAG_reference_type = 0x10, e_DW_TAG_compile_unit = 0x11, e_DW_TAG_string_type = 0x12, e_DW_TAG_structure_type = 0x13, e_DW_TAG_subroutine_type = 0x15, e_DW_TAG_typedef = 0x16, e_DW_TAG_union_type = 0x17, e_DW_TAG_unspecified_parameters = 0x18, e_DW_TAG_variant = 0x19, e_DW_TAG_common_block = 0x1a, e_DW_TAG_common_inclusion = 0x1b, e_DW_TAG_inheritance = 0x1c, e_DW_TAG_inlined_subroutine = 0x1d, e_DW_TAG_module = 0x1e, e_DW_TAG_ptr_to_member_type = 0x1f, e_DW_TAG_set_type = 0x20, e_DW_TAG_subrange_type = 0x21, e_DW_TAG_with_stmt = 0x22, e_DW_TAG_access_declaration = 0x23, e_DW_TAG_base_type = 0x24, e_DW_TAG_catch_block = 0x25, e_DW_TAG_const_type = 0x26, e_DW_TAG_constant = 0x27, e_DW_TAG_enumerator = 0x28, e_DW_TAG_file_type = 0x29, e_DW_TAG_friend = 0x2a, e_DW_TAG_namelist = 0x2b, e_DW_TAG_namelist_item = 0x2c, e_DW_TAG_packed_type = 0x2d, e_DW_TAG_subprogram = 0x2e, e_DW_TAG_template_type_parameter = 0x2f, e_DW_TAG_template_value_parameter = 0x30, e_DW_TAG_thrown_type = 0x31, e_DW_TAG_try_block = 0x32, e_DW_TAG_variant_part = 0x33, e_DW_TAG_variable = 0x34, e_DW_TAG_volatile_type = 0x35, e_DW_TAG_dwarf_procedure = 0x36, e_DW_TAG_restrict_type = 0x37, e_DW_TAG_interface_type = 0x38, e_DW_TAG_namespace = 0x39, e_DW_TAG_imported_module = 0x3a, e_DW_TAG_unspecified_type = 0x3b, e_DW_TAG_partial_unit = 0x3c, e_DW_TAG_imported_unit = 0x3d, e_DW_TAG_condition = 0x3f, e_DW_TAG_shared_type = 0x40, e_DW_TAG_lo_user = 0x4080, e_DW_TAG_hi_user = 0xffff }; enum Dwarf4Enums { // These values were obtained from the DWARF 4 Spec. e_DW_AT_signature = 0x69, e_DW_AT_main_subprogram = 0x6a, e_DW_AT_data_bit_offset = 0x6b, e_DW_AT_const_expr = 0x6c, e_DW_AT_enum_class = 0x6d, e_DW_AT_linkage_name = 0x6e, e_DW_AT_lo_user = 0x2000, e_DW_AT_hi_user = 0x3fff, e_DW_FORM_sec_offset = 0x17, e_DW_FORM_exprloc = 0x18, e_DW_FORM_flag_present = 0x19, e_DW_FORM_ref_sig8 = 0x20, e_DW_LNE_set_discriminator = 0x04, e_DW_TAG_mutable_type = 0x3e, e_DW_TAG_type_unit = 0x41, e_DW_TAG_rvalue_reference_type = 0x42, e_DW_TAG_template_alias = 0x43 }; enum Dwarf5Enums { e_DW_UT_compile = 0x1, e_DW_UT_type = 0x2, e_DW_UT_partial = 0x3, e_DW_UT_skeleton = 0x4, e_DW_UT_split_compile = 0x5, e_DW_UT_split_type = 0x6, e_DW_UT_lo_user = 0x80, e_DW_UT_hi_user = 0xff }; private: // DATA balst::StackTraceResolver_FileHelper *d_helper_p; // filehelper for current // segment char *d_buffer_p; // buffer. // k_SCRATCH_BUF_LEN long Offset d_offset; // offset last read from Offset d_beginOffset; // beg of current section Offset d_endOffset; // end of current section const char *d_readPtr; // current place to read // from (in the buffer) const char *d_endPtr; // end of what's in buffer int d_offsetSize; // offset size determined // by 'readInitalLength' int d_addressSize; // address read by // 'getAddress' or set by // 'setAddressSize'. private: // NOT IMPLEMENTED StackTraceResolver_DwarfReader(const StackTraceResolver_DwarfReader&); StackTraceResolver_DwarfReader& operator=( const StackTraceResolver_DwarfReader&); private: // PRIVATE MANIPULATORS int needBytes(bsl::size_t numBytes); // Determine if we are able to read the specified 'numBytes' from // 'd_buffer'. If not, 'reload' is used to attempt to accomodate this // 'needBytes' request. Return 0 if we are able to read 'numBytes' // from 'd_buffer' after this invocation of 'needBytes', and a non-zero // value otherwise. int reload(bsl::size_t numBytes); // Reload the buffer to accomodate a read of at least the specified // 'numBytes'. If possible, read up to the end of the section or the // size of the buffer, whichever is shorter. Return 0 on success, and // a non-zero value otherwise. public: // CLASS METHODS static const char *stringForAt(unsigned id); // Return the string equivalent of the specified 'e_DW_AT_*' 'id'. static const char *stringForForm(unsigned id); // Return the string equivalent of the specified 'e_DW_FORM_*' 'id'. static const char *stringForInlineState(unsigned inlineState); // Return the string equivalent of the specified 'e_DW_INL_*' // 'inlineState'. static const char *stringForLNE(unsigned id); // Return the string equivalent of the specified 'e_DW_LNE_*' 'id'. static const char *stringForLNS(unsigned id); // Return the string equivalent of the specified 'e_DW_LNS_*' 'id'. static const char *stringForTag(unsigned tag); // Return the string equivalent of the specified 'e_DW_TAG_*' 'tag'. // CREATORS StackTraceResolver_DwarfReader(); // Create a 'Reader' object in a null state. // ~StackTraceResolver_DwarfReader() = default; // MANIPULATORS void disable(); // Disable this object for further use. int init(balst::StackTraceResolver_FileHelper *fileHelper, char *buffer, const Section& section, Offset libraryFileSize); // Initialize this 'Reader' object using the specified 'fileHelper' and // the specified 'buffer', to operate on the specified 'section', where // the specified 'libraryFileSize' is the size of the library or // executable file. 'buffer' is assumed to be at least // 'k_SCRATCH_BUF_LEN' long. int readAddress(UintPtr *dst); // Read to the specified 'dst'. This function will fail if // 'd_addressSize' has not been initialized by 'readAddressSize' or // 'setAddressSize'. Return 0 on success and a non-zero value // otherwise. int readAddress(UintPtr *dst, unsigned form); // Read to the specified 'dst' according to the specified 'form'. // Return 0 on success and a non-zero value otherwise. int readAddressSize(); // Read the address size from a single unsigned byte, check it, and // assign 'd_addressSize' to it. Return 0 on success and a non-zero // value otherwise. It is an error if address size is not equal to the // size of an 'unsigned int' or of a 'void *'. int readInitialLength(Offset *dst); // Read the initial length of the object according to the DWARF // specification to the specified '*dst', which is an 8-byte value. // Read '*dst' first as a 4 byte value, setting the high-order 4 bytes // to 0, and if the value is below 0xfffffff0, then that indicates that // section offsets are to be read as 4 byte values within the object // whose length is specified. If the value is 0xffffffff, read the // next 8 bytes into '*dst' and that indicates that section offsets are // to be 8 bytes within the object whose length is specified. // Initialize 'd_offsetSize' accordingly. Values in the range // '[0xfffffff0, 0xffffffff)' are illegal for that first 4 bytes. // Return 0 on success and a non-zero value otherwise. Note that when // we read a 4-byte value, we do not extend sign to the high order 4 // bytes of '*dst'. template <class TYPE> int readLEB128(TYPE *dst); // Read a signed, variable-length number into the specified '*dst'. // Return 0 on success and a non-zero value otherwise. template <class TYPE> int readULEB128(TYPE *dst); // Read an unsigned, variable-length number into the specified '*dst'. // Return 0 on success and a non-zero value otherwise. int readOffset(Offset *dst, bsl::size_t offsetSize); // Read to the specified '*dst', where the specified 'offsetSize' is // the number of low-order bytes to be read into the offset, where // '*dst' is 8 bytes, and extra high-order bytes are to be set to 0. // Do not extend sign. Return 0 on success and a non-zero value // otherwise. int readOffsetFromForm(Offset *dst, unsigned form); // Read to the specified '*dst' according to the specified 'form', // where 'form' is a DWARF enum of the 'e_DW_FORM_*' category. Return // 0 on success and a non-zero value otherwise. int readSectionOffset(Offset *dst); // Read to the specified offset '*dst' according to 'd_offsetSize'. // Return 0 on success and a non-zero value otherwise. Note that when // the offset read is only 4 bytes, no sign extension takes place as // we always expect a positive result. int readString(bsl::string *dst = 0); // Read a null-terminated string to the specified '*dst'. If no 'dst' // is specified, skip over the string without copying it. This // function will fail if the string length is greater than // 'k_SCRATCH_BUFFER_LEN - 1'. int readStringAt(bsl::string *dst, Offset offset); // Read a null terminated string to the specified '*dst' from the // specified 'offset' plus 'd_beginOffset'. Note that, unlike most of // the other 'read' functions, this one is intended for random access // so does not read a full buffer ahead, instead reading a fairly // minimal amount of data near the specified location. int readStringFromForm(bsl::string *dst, StackTraceResolver_DwarfReader *stringReader, unsigned form); // Read to the specified string either from the current reader (if the // specified 'form' is 'e_DW_FORM_string') or read an offset from the // current reader, then use that to read the string from the specified // '*stringReader' (if 'form' is 'e_DW_FORM_strp'). Return 0 on // success and a non-zero value otherwise. template <class TYPE> int readValue(TYPE *dst); // Read a value into the specified '*dst', assuming that it is // represented by 'sizeof(*dst)' bytes. int setAddressSize(unsigned size); // Explicitly set the 'd_addressSize' of this reader to the specified // 'size'. This function will fail unless the size is the // 'sizeof(unsigned) == size' or 'sizeof(UintPtr) == size'. int setEndOffset(Offset newOffset); // Set the end offset to the specified 'newOffset'. int skipBytes(Offset bytes); // Skip forward over the specified 'bytes' without reading them. int skipString(); // Skip over a null terminated string without copying it. int skipForm(unsigned form); // Skip over data according to the specified 'form', which is an enum // of type 'e_DW_FORM_*'. int skipTo(Offset offset); // Skip to the specified 'offset', which must be in the section // associated with this reader. Return 0 on success and a non-zero // value otherwise. int skipULEB128(); // Skip a variable-length integer. Note this will work for both // LEB128's and ULEB128's. // ACCESSORS int addressSize() const; // Return the address size field. bool atEndOfSection() const; // Return 'true' if the reader has reached the end of the section and // 'false' otherwise. Offset offset() const; // Return the current offset taking the 'd_reader' position into // account. Offset offsetSize() const; // Return the offset length that was set by the 'readInitialLength' // function. }; // PRIVATE MANIPULATORS inline int StackTraceResolver_DwarfReader::needBytes(bsl::size_t numBytes) { IntPtr diff = d_endPtr - d_readPtr; if (diff < static_cast<IntPtr>(numBytes)) { BSLS_ASSERT(0 <= diff); return reload(numBytes); // RETURN } return 0; } template <class TYPE> int StackTraceResolver_DwarfReader::readLEB128(TYPE *dst) // DWARF doc 7.6 { BSLMF_ASSERT(static_cast<TYPE>(-1) < 0); // 'TYPE' must be signed int rc; Uint64 tmpDst = 0; unsigned char u = 0x80; enum { k_MAX_SHIFT = sizeof(*dst) * 8 }; unsigned shift = -7; do { rc = readValue(&u); if (rc) { // Assign to '*dst' to silence the confused optimizer complaining // about 'maybe used before set' in caller. *dst = 0; return -1; // RETURN } const Uint64 masked = 0x7f & u; shift += 7; tmpDst |= masked << shift; } while (0x80 & u); if (static_cast<TYPE>(-1) < 0) { // signed type, extend sign const Uint64 negFlag = static_cast<Uint64>(0x40) << shift; if (negFlag & tmpDst) { tmpDst |= ~(negFlag - 1); } } *dst = static_cast<TYPE>(tmpDst); return 0; } template <class TYPE> int StackTraceResolver_DwarfReader::readULEB128(TYPE *dst) // DWARF doc 7.6 { Uint64 tmpDst = 0; unsigned char u = 0x80; unsigned shift = 0; for (; (0x80 & u); shift += 7) { int rc = readValue(&u); if (0 != rc) { // Assign to '*dst' to silence the confused optimizer complaining // about 'maybe used before set' in caller. *dst = 0; return -1; // RETURN } const Uint64 masked = 0x7f & u; tmpDst |= masked << shift; } #if defined(BSLS_ASSERT_LEVEL_ASSERT_SAFE) if (shift >= sizeof(*dst) * 8 + 7) { // Assign to '*dst' to silence the confused optimizer complaining about // 'maybe used before set' in caller. *dst = 0; return -1; // RETURN } #endif *dst = static_cast<TYPE>(tmpDst); return 0; } template <class TYPE> inline int StackTraceResolver_DwarfReader::readValue(TYPE *dst) { int rc = needBytes(sizeof(*dst)); if (rc) { // Assign to '*dst' to silence the confused optimizer complaining about // 'maybe used before set' in caller. *dst = 0; return -1; // RETURN } bsl::memcpy(dst, d_readPtr, sizeof(*dst)); d_readPtr += sizeof(*dst); return 0; } inline int StackTraceResolver_DwarfReader::skipBytes(Offset bytes) { BSLS_ASSERT(bytes >= 0); if (bytes > d_endPtr - d_readPtr) { Offset off = offset(); if (off < d_beginOffset) { return -1; // RETURN } if (off + bytes > d_endOffset) { return -1; // RETURN } // By setting 'd_readPtr == d_endPtr' we guarantee that the next read // will trigger a reload. d_offset += bytes + (d_readPtr - d_buffer_p); d_readPtr = d_buffer_p; d_endPtr = d_readPtr; } else { d_readPtr += bytes; } return 0; } inline int StackTraceResolver_DwarfReader::skipString() { do { int rc = needBytes(1); if (rc) { return -1; // RETURN } } while (*d_readPtr++); return 0; } // ACCESSORS inline int StackTraceResolver_DwarfReader::addressSize() const { return d_addressSize; } inline bool StackTraceResolver_DwarfReader::atEndOfSection() const { return d_readPtr == d_endPtr && d_endOffset - d_offset == d_readPtr - d_buffer_p; } inline StackTraceResolver_DwarfReader::Offset StackTraceResolver_DwarfReader::offset() const { return d_offset + (d_readPtr - d_buffer_p); } inline StackTraceResolver_DwarfReader::Offset StackTraceResolver_DwarfReader::offsetSize() const { return d_offsetSize; } #endif } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2016 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------