├── Changelog.txt ├── README.md ├── LICENSE_1_0.txt └── librepr.hpp /Changelog.txt: -------------------------------------------------------------------------------- 1 | 2022-04-11 v0.3 2 | 3 | - Fix loading ET_DYN executables 4 | - Handle dwarf line_strp form 5 | - Handle resolving typedefs on enums 6 | - Handle executables position independent code 7 | 8 | 2021-08-29 v0.2 9 | 10 | - Ability to print structs 11 | - Improvements to dwarf parsing 12 | 13 | 2021-08-08 v0.1 14 | 15 | - Initial release with enum class support 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # librepr 2 | 3 | C++ library that uses dwarf data to pretty print complex values. 4 | 5 | Example: 6 | 7 | ```cpp 8 | #include 9 | 10 | #include 11 | 12 | using librepr::repr; 13 | 14 | enum class Suit { Clubs, Diamonds, Hearts, Spades }; 15 | enum class Rank { Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King }; 16 | 17 | struct CardBase 18 | { 19 | int foo = 5; 20 | }; 21 | 22 | struct Card : CardBase 23 | { 24 | Suit suit; 25 | Rank rank; 26 | double bar = 3.14; 27 | }; 28 | 29 | int main() 30 | { 31 | Card c; 32 | c.suit = Suit::Spades; 33 | c.rank = Rank::Ace; 34 | 35 | // prints "{.foo=5, .suit=Suit::Spades, .rank=Rank::Ace, .bar=3.14}" 36 | std::cout << repr(c) << "\n"; 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /librepr.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 Mustafa Serdar Sanli 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | 8 | 9 | 10 | #ifndef LIBREPR_HPP_ 11 | #define LIBREPR_HPP_ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | namespace librepr::_internal_v3 { 37 | 38 | 39 | enum class DwarfTag : uint16_t 40 | { 41 | None = 0, 42 | ArrayType = 1, 43 | ClassType = 2, 44 | EntryPoint = 3, 45 | EnumerationType = 4, 46 | FormalParameter = 5, 47 | ImportedDeclaration = 8, 48 | Label = 10, 49 | LexicalBlock = 11, 50 | Member = 13, 51 | PointerType = 15, 52 | ReferenceType = 16, 53 | CompileUnit = 17, 54 | StringType = 18, 55 | StructureType = 19, 56 | SubroutineType = 21, 57 | Typedef = 22, 58 | UnionType = 23, 59 | UnspecifiedParameters = 24, 60 | Variant = 25, 61 | CommonBlock = 26, 62 | CommonInclusion = 27, 63 | Inheritance = 28, 64 | InlinedSubroutine = 29, 65 | Module = 30, 66 | PtrToMemberType = 31, 67 | SetType = 32, 68 | SubrangeType = 33, 69 | WithStmt = 34, 70 | AccessDeclaration = 35, 71 | BaseType = 36, 72 | CatchBlock = 37, 73 | ConstType = 38, 74 | Constant = 39, 75 | Enumerator = 40, 76 | FileType = 41, 77 | Friend = 42, 78 | Namelist = 43, 79 | NamelistItem = 44, 80 | PackedType = 45, 81 | Subprogram = 46, 82 | TemplateTypeParameter = 47, 83 | TemplateValueParameter = 48, 84 | ThrownType = 49, 85 | TryBlock = 50, 86 | VariantPart = 51, 87 | Variable = 52, 88 | VolatileType = 53, 89 | DwarfProcedure = 54, 90 | RestrictType = 55, 91 | InterfaceType = 56, 92 | Namespace = 57, 93 | ImportedModule = 58, 94 | UnspecifiedType = 59, 95 | PartialUnit = 60, 96 | ImportedUnit = 61, 97 | Condition = 63, 98 | SharedType = 64, 99 | TypeUnit = 65, 100 | RvalueReferenceType = 66, 101 | TemplateAlias = 67, 102 | LoUser = 16512, 103 | HiUser = 65535, 104 | }; 105 | 106 | inline 107 | std::ostream& operator<<(std::ostream &out, DwarfTag v) 108 | { 109 | switch (v) 110 | { 111 | case DwarfTag::None: out << "None"; return out; 112 | case DwarfTag::ArrayType: out << "ArrayType"; return out; 113 | case DwarfTag::ClassType: out << "ClassType"; return out; 114 | case DwarfTag::EntryPoint: out << "EntryPoint"; return out; 115 | case DwarfTag::EnumerationType: out << "EnumerationType"; return out; 116 | case DwarfTag::FormalParameter: out << "FormalParameter"; return out; 117 | case DwarfTag::ImportedDeclaration: out << "ImportedDeclaration"; return out; 118 | case DwarfTag::Label: out << "Label"; return out; 119 | case DwarfTag::LexicalBlock: out << "LexicalBlock"; return out; 120 | case DwarfTag::Member: out << "Member"; return out; 121 | case DwarfTag::PointerType: out << "PointerType"; return out; 122 | case DwarfTag::ReferenceType: out << "ReferenceType"; return out; 123 | case DwarfTag::CompileUnit: out << "CompileUnit"; return out; 124 | case DwarfTag::StringType: out << "StringType"; return out; 125 | case DwarfTag::StructureType: out << "StructureType"; return out; 126 | case DwarfTag::SubroutineType: out << "SubroutineType"; return out; 127 | case DwarfTag::Typedef: out << "Typedef"; return out; 128 | case DwarfTag::UnionType: out << "UnionType"; return out; 129 | case DwarfTag::UnspecifiedParameters: out << "UnspecifiedParameters"; return out; 130 | case DwarfTag::Variant: out << "Variant"; return out; 131 | case DwarfTag::CommonBlock: out << "CommonBlock"; return out; 132 | case DwarfTag::CommonInclusion: out << "CommonInclusion"; return out; 133 | case DwarfTag::Inheritance: out << "Inheritance"; return out; 134 | case DwarfTag::InlinedSubroutine: out << "InlinedSubroutine"; return out; 135 | case DwarfTag::Module: out << "Module"; return out; 136 | case DwarfTag::PtrToMemberType: out << "PtrToMemberType"; return out; 137 | case DwarfTag::SetType: out << "SetType"; return out; 138 | case DwarfTag::SubrangeType: out << "SubrangeType"; return out; 139 | case DwarfTag::WithStmt: out << "WithStmt"; return out; 140 | case DwarfTag::AccessDeclaration: out << "AccessDeclaration"; return out; 141 | case DwarfTag::BaseType: out << "BaseType"; return out; 142 | case DwarfTag::CatchBlock: out << "CatchBlock"; return out; 143 | case DwarfTag::ConstType: out << "ConstType"; return out; 144 | case DwarfTag::Constant: out << "Constant"; return out; 145 | case DwarfTag::Enumerator: out << "Enumerator"; return out; 146 | case DwarfTag::FileType: out << "FileType"; return out; 147 | case DwarfTag::Friend: out << "Friend"; return out; 148 | case DwarfTag::Namelist: out << "Namelist"; return out; 149 | case DwarfTag::NamelistItem: out << "NamelistItem"; return out; 150 | case DwarfTag::PackedType: out << "PackedType"; return out; 151 | case DwarfTag::Subprogram: out << "Subprogram"; return out; 152 | case DwarfTag::TemplateTypeParameter: out << "TemplateTypeParameter"; return out; 153 | case DwarfTag::TemplateValueParameter: out << "TemplateValueParameter"; return out; 154 | case DwarfTag::ThrownType: out << "ThrownType"; return out; 155 | case DwarfTag::TryBlock: out << "TryBlock"; return out; 156 | case DwarfTag::VariantPart: out << "VariantPart"; return out; 157 | case DwarfTag::Variable: out << "Variable"; return out; 158 | case DwarfTag::VolatileType: out << "VolatileType"; return out; 159 | case DwarfTag::DwarfProcedure: out << "DwarfProcedure"; return out; 160 | case DwarfTag::RestrictType: out << "RestrictType"; return out; 161 | case DwarfTag::InterfaceType: out << "InterfaceType"; return out; 162 | case DwarfTag::Namespace: out << "Namespace"; return out; 163 | case DwarfTag::ImportedModule: out << "ImportedModule"; return out; 164 | case DwarfTag::UnspecifiedType: out << "UnspecifiedType"; return out; 165 | case DwarfTag::PartialUnit: out << "PartialUnit"; return out; 166 | case DwarfTag::ImportedUnit: out << "ImportedUnit"; return out; 167 | case DwarfTag::Condition: out << "Condition"; return out; 168 | case DwarfTag::SharedType: out << "SharedType"; return out; 169 | case DwarfTag::TypeUnit: out << "TypeUnit"; return out; 170 | case DwarfTag::RvalueReferenceType: out << "RvalueReferenceType"; return out; 171 | case DwarfTag::TemplateAlias: out << "TemplateAlias"; return out; 172 | case DwarfTag::LoUser: out << "LoUser"; return out; 173 | case DwarfTag::HiUser: out << "HiUser"; return out; 174 | default: out << "Unknown(" << static_cast(v) << ")"; return out; 175 | } 176 | } 177 | 178 | enum class DwarfAttr : uint16_t 179 | { 180 | None = 0, 181 | Sibling = 1, 182 | Location = 2, 183 | Name = 3, 184 | Ordering = 9, 185 | ByteSize = 11, 186 | BitOffset = 12, 187 | BitSize = 13, 188 | StmtList = 16, 189 | LowPc = 17, 190 | HighPc = 18, 191 | Language = 19, 192 | Discr = 21, 193 | DiscrValue = 22, 194 | Visibility = 23, 195 | Import = 24, 196 | StringLength = 25, 197 | CommonReference = 26, 198 | CompDir = 27, 199 | ConstValue = 28, 200 | ContainingType = 29, 201 | DefaultValue = 30, 202 | Inline = 32, 203 | IsOptional = 33, 204 | LowerBound = 34, 205 | Producer = 37, 206 | Prototyped = 39, 207 | ReturnAddr = 42, 208 | StartScope = 44, 209 | BitStride = 46, 210 | UpperBound = 47, 211 | AbstractOrigin = 49, 212 | Accessibility = 50, 213 | AddressClass = 51, 214 | Artificial = 52, 215 | BaseTypes = 53, 216 | CallingConvention = 54, 217 | Count = 55, 218 | DataMemberLocation = 56, 219 | DeclColumn = 57, 220 | DeclFile = 58, 221 | DeclLine = 59, 222 | Declaration = 60, 223 | DiscrList = 61, 224 | Encoding = 62, 225 | External = 63, 226 | FrameBase = 64, 227 | Friend = 65, 228 | IdentifierCase = 66, 229 | MacroInfo = 67, 230 | NamelistItem = 68, 231 | Priority = 69, 232 | Segment = 70, 233 | Specification = 71, 234 | StaticLink = 72, 235 | Type = 73, 236 | UseLocation = 74, 237 | VariableParameter = 75, 238 | Virtuality = 76, 239 | VtableElemLocation = 77, 240 | Allocated = 78, 241 | Associated = 79, 242 | DataLocation = 80, 243 | ByteStride = 81, 244 | EntryPc = 82, 245 | Use_UTF8 = 83, 246 | Extension = 84, 247 | Ranges = 85, 248 | Trampoline = 86, 249 | CallColumn = 87, 250 | CallFile = 88, 251 | CallLine = 89, 252 | Description = 90, 253 | BinaryScale = 91, 254 | DecimalScale = 92, 255 | Small = 93, 256 | DecimalSign = 94, 257 | DigitCount = 95, 258 | PictureString = 96, 259 | Mutable = 97, 260 | ThreadsScaled = 98, 261 | Explicit = 99, 262 | ObjectPointer = 100, 263 | Endianity = 101, 264 | Elemental = 102, 265 | Pure = 103, 266 | Recursive = 104, 267 | Signature = 105, 268 | MainSubprogram = 106, 269 | DataBitOffset = 107, 270 | ConstExpr = 108, 271 | EnumClass = 109, 272 | LinkageName = 110, 273 | StringLengthBitSize = 111, 274 | StringLengthByteSize = 112, 275 | Rank = 113, 276 | StrOffsetsBase = 114, 277 | AddrBase = 115, 278 | RnglistsBase = 116, 279 | DwoName = 118, 280 | Reference = 119, 281 | RvalueReference = 120, 282 | Macros = 121, 283 | CallAllCalls = 122, 284 | CallAllSourceCalls = 123, 285 | CallAllTailCalls = 124, 286 | CallReturnPc = 125, 287 | CallValue = 126, 288 | CallOrigin = 127, 289 | CallParameter = 128, 290 | CallPc = 129, 291 | CallTailCall = 130, 292 | CallTarget = 131, 293 | CallTargetClobbered = 132, 294 | CallDataLocation = 133, 295 | CallDataValue = 134, 296 | Noreturn = 135, 297 | Alignment = 136, 298 | ExportSymbols = 137, 299 | Deleted = 138, 300 | Defaulted = 139, 301 | LoclistsBase = 140, 302 | LoUser = 8192, 303 | HiUser = 16383, 304 | }; 305 | 306 | inline 307 | std::ostream& operator<<(std::ostream &out, DwarfAttr v) 308 | { 309 | switch (v) 310 | { 311 | case DwarfAttr::None: out << "None"; return out; 312 | case DwarfAttr::Sibling: out << "Sibling"; return out; 313 | case DwarfAttr::Location: out << "Location"; return out; 314 | case DwarfAttr::Name: out << "Name"; return out; 315 | case DwarfAttr::Ordering: out << "Ordering"; return out; 316 | case DwarfAttr::ByteSize: out << "ByteSize"; return out; 317 | case DwarfAttr::BitOffset: out << "BitOffset"; return out; 318 | case DwarfAttr::BitSize: out << "BitSize"; return out; 319 | case DwarfAttr::StmtList: out << "StmtList"; return out; 320 | case DwarfAttr::LowPc: out << "LowPc"; return out; 321 | case DwarfAttr::HighPc: out << "HighPc"; return out; 322 | case DwarfAttr::Language: out << "Language"; return out; 323 | case DwarfAttr::Discr: out << "Discr"; return out; 324 | case DwarfAttr::DiscrValue: out << "DiscrValue"; return out; 325 | case DwarfAttr::Visibility: out << "Visibility"; return out; 326 | case DwarfAttr::Import: out << "Import"; return out; 327 | case DwarfAttr::StringLength: out << "StringLength"; return out; 328 | case DwarfAttr::CommonReference: out << "CommonReference"; return out; 329 | case DwarfAttr::CompDir: out << "CompDir"; return out; 330 | case DwarfAttr::ConstValue: out << "ConstValue"; return out; 331 | case DwarfAttr::ContainingType: out << "ContainingType"; return out; 332 | case DwarfAttr::DefaultValue: out << "DefaultValue"; return out; 333 | case DwarfAttr::Inline: out << "Inline"; return out; 334 | case DwarfAttr::IsOptional: out << "IsOptional"; return out; 335 | case DwarfAttr::LowerBound: out << "LowerBound"; return out; 336 | case DwarfAttr::Producer: out << "Producer"; return out; 337 | case DwarfAttr::Prototyped: out << "Prototyped"; return out; 338 | case DwarfAttr::ReturnAddr: out << "ReturnAddr"; return out; 339 | case DwarfAttr::StartScope: out << "StartScope"; return out; 340 | case DwarfAttr::BitStride: out << "BitStride"; return out; 341 | case DwarfAttr::UpperBound: out << "UpperBound"; return out; 342 | case DwarfAttr::AbstractOrigin: out << "AbstractOrigin"; return out; 343 | case DwarfAttr::Accessibility: out << "Accessibility"; return out; 344 | case DwarfAttr::AddressClass: out << "AddressClass"; return out; 345 | case DwarfAttr::Artificial: out << "Artificial"; return out; 346 | case DwarfAttr::BaseTypes: out << "BaseTypes"; return out; 347 | case DwarfAttr::CallingConvention: out << "CallingConvention"; return out; 348 | case DwarfAttr::Count: out << "Count"; return out; 349 | case DwarfAttr::DataMemberLocation: out << "DataMemberLocation"; return out; 350 | case DwarfAttr::DeclColumn: out << "DeclColumn"; return out; 351 | case DwarfAttr::DeclFile: out << "DeclFile"; return out; 352 | case DwarfAttr::DeclLine: out << "DeclLine"; return out; 353 | case DwarfAttr::Declaration: out << "Declaration"; return out; 354 | case DwarfAttr::DiscrList: out << "DiscrList"; return out; 355 | case DwarfAttr::Encoding: out << "Encoding"; return out; 356 | case DwarfAttr::External: out << "External"; return out; 357 | case DwarfAttr::FrameBase: out << "FrameBase"; return out; 358 | case DwarfAttr::Friend: out << "Friend"; return out; 359 | case DwarfAttr::IdentifierCase: out << "IdentifierCase"; return out; 360 | case DwarfAttr::MacroInfo: out << "MacroInfo"; return out; 361 | case DwarfAttr::NamelistItem: out << "NamelistItem"; return out; 362 | case DwarfAttr::Priority: out << "Priority"; return out; 363 | case DwarfAttr::Segment: out << "Segment"; return out; 364 | case DwarfAttr::Specification: out << "Specification"; return out; 365 | case DwarfAttr::StaticLink: out << "StaticLink"; return out; 366 | case DwarfAttr::Type: out << "Type"; return out; 367 | case DwarfAttr::UseLocation: out << "UseLocation"; return out; 368 | case DwarfAttr::VariableParameter: out << "VariableParameter"; return out; 369 | case DwarfAttr::Virtuality: out << "Virtuality"; return out; 370 | case DwarfAttr::VtableElemLocation: out << "VtableElemLocation"; return out; 371 | case DwarfAttr::Allocated: out << "Allocated"; return out; 372 | case DwarfAttr::Associated: out << "Associated"; return out; 373 | case DwarfAttr::DataLocation: out << "DataLocation"; return out; 374 | case DwarfAttr::ByteStride: out << "ByteStride"; return out; 375 | case DwarfAttr::EntryPc: out << "EntryPc"; return out; 376 | case DwarfAttr::Use_UTF8: out << "Use_UTF8"; return out; 377 | case DwarfAttr::Extension: out << "Extension"; return out; 378 | case DwarfAttr::Ranges: out << "Ranges"; return out; 379 | case DwarfAttr::Trampoline: out << "Trampoline"; return out; 380 | case DwarfAttr::CallColumn: out << "CallColumn"; return out; 381 | case DwarfAttr::CallFile: out << "CallFile"; return out; 382 | case DwarfAttr::CallLine: out << "CallLine"; return out; 383 | case DwarfAttr::Description: out << "Description"; return out; 384 | case DwarfAttr::BinaryScale: out << "BinaryScale"; return out; 385 | case DwarfAttr::DecimalScale: out << "DecimalScale"; return out; 386 | case DwarfAttr::Small: out << "Small"; return out; 387 | case DwarfAttr::DecimalSign: out << "DecimalSign"; return out; 388 | case DwarfAttr::DigitCount: out << "DigitCount"; return out; 389 | case DwarfAttr::PictureString: out << "PictureString"; return out; 390 | case DwarfAttr::Mutable: out << "Mutable"; return out; 391 | case DwarfAttr::ThreadsScaled: out << "ThreadsScaled"; return out; 392 | case DwarfAttr::Explicit: out << "Explicit"; return out; 393 | case DwarfAttr::ObjectPointer: out << "ObjectPointer"; return out; 394 | case DwarfAttr::Endianity: out << "Endianity"; return out; 395 | case DwarfAttr::Elemental: out << "Elemental"; return out; 396 | case DwarfAttr::Pure: out << "Pure"; return out; 397 | case DwarfAttr::Recursive: out << "Recursive"; return out; 398 | case DwarfAttr::Signature: out << "Signature"; return out; 399 | case DwarfAttr::MainSubprogram: out << "MainSubprogram"; return out; 400 | case DwarfAttr::DataBitOffset: out << "DataBitOffset"; return out; 401 | case DwarfAttr::ConstExpr: out << "ConstExpr"; return out; 402 | case DwarfAttr::EnumClass: out << "EnumClass"; return out; 403 | case DwarfAttr::LinkageName: out << "LinkageName"; return out; 404 | case DwarfAttr::StringLengthBitSize: out << "StringLengthBitSize"; return out; 405 | case DwarfAttr::StringLengthByteSize: out << "StringLengthByteSize"; return out; 406 | case DwarfAttr::Rank: out << "Rank"; return out; 407 | case DwarfAttr::StrOffsetsBase: out << "StrOffsetsBase"; return out; 408 | case DwarfAttr::AddrBase: out << "AddrBase"; return out; 409 | case DwarfAttr::RnglistsBase: out << "RnglistsBase"; return out; 410 | case DwarfAttr::DwoName: out << "DwoName"; return out; 411 | case DwarfAttr::Reference: out << "Reference"; return out; 412 | case DwarfAttr::RvalueReference: out << "RvalueReference"; return out; 413 | case DwarfAttr::Macros: out << "Macros"; return out; 414 | case DwarfAttr::CallAllCalls: out << "CallAllCalls"; return out; 415 | case DwarfAttr::CallAllSourceCalls: out << "CallAllSourceCalls"; return out; 416 | case DwarfAttr::CallAllTailCalls: out << "CallAllTailCalls"; return out; 417 | case DwarfAttr::CallReturnPc: out << "CallReturnPc"; return out; 418 | case DwarfAttr::CallValue: out << "CallValue"; return out; 419 | case DwarfAttr::CallOrigin: out << "CallOrigin"; return out; 420 | case DwarfAttr::CallParameter: out << "CallParameter"; return out; 421 | case DwarfAttr::CallPc: out << "CallPc"; return out; 422 | case DwarfAttr::CallTailCall: out << "CallTailCall"; return out; 423 | case DwarfAttr::CallTarget: out << "CallTarget"; return out; 424 | case DwarfAttr::CallTargetClobbered: out << "CallTargetClobbered"; return out; 425 | case DwarfAttr::CallDataLocation: out << "CallDataLocation"; return out; 426 | case DwarfAttr::CallDataValue: out << "CallDataValue"; return out; 427 | case DwarfAttr::Noreturn: out << "Noreturn"; return out; 428 | case DwarfAttr::Alignment: out << "Alignment"; return out; 429 | case DwarfAttr::ExportSymbols: out << "ExportSymbols"; return out; 430 | case DwarfAttr::Deleted: out << "Deleted"; return out; 431 | case DwarfAttr::Defaulted: out << "Defaulted"; return out; 432 | case DwarfAttr::LoclistsBase: out << "LoclistsBase"; return out; 433 | case DwarfAttr::LoUser: out << "LoUser"; return out; 434 | case DwarfAttr::HiUser: out << "HiUser"; return out; 435 | default: out << "Unknown(" << static_cast(v) << ")"; return out; 436 | } 437 | } 438 | 439 | enum class DwarfForm : uint16_t 440 | { 441 | None = 0, 442 | Addr = 1, 443 | Block2 = 3, 444 | Block4 = 4, 445 | Data2 = 5, 446 | Data4 = 6, 447 | Data8 = 7, 448 | String = 8, 449 | Block = 9, 450 | Block1 = 10, 451 | Data1 = 11, 452 | Flag = 12, 453 | Sdata = 13, 454 | Strp = 14, 455 | Udata = 15, 456 | RefAddr = 16, 457 | Ref1 = 17, 458 | Ref2 = 18, 459 | Ref4 = 19, 460 | Ref8 = 20, 461 | RefUdata = 21, 462 | Indirect = 22, 463 | SecOffset = 23, 464 | Exprloc = 24, 465 | FlagPresent = 25, 466 | Strx = 26, 467 | Addrx = 27, 468 | RefSup4 = 28, 469 | StrpSup = 29, 470 | Data16 = 30, 471 | LineStrp = 31, 472 | RefSig8 = 32, 473 | ImplicitConst = 33, 474 | Loclistx = 34, 475 | Rnglistx = 35, 476 | RefSup8 = 36, 477 | Strx1 = 37, 478 | Strx2 = 38, 479 | Strx3 = 39, 480 | Strx4 = 40, 481 | Addrx1 = 41, 482 | Addrx2 = 42, 483 | Addrx3 = 43, 484 | Addrx4 = 44, 485 | }; 486 | 487 | inline 488 | std::ostream& operator<<(std::ostream &out, DwarfForm v) 489 | { 490 | switch (v) 491 | { 492 | case DwarfForm::None: out << "None"; return out; 493 | case DwarfForm::Addr: out << "Addr"; return out; 494 | case DwarfForm::Block2: out << "Block2"; return out; 495 | case DwarfForm::Block4: out << "Block4"; return out; 496 | case DwarfForm::Data2: out << "Data2"; return out; 497 | case DwarfForm::Data4: out << "Data4"; return out; 498 | case DwarfForm::Data8: out << "Data8"; return out; 499 | case DwarfForm::String: out << "String"; return out; 500 | case DwarfForm::Block: out << "Block"; return out; 501 | case DwarfForm::Block1: out << "Block1"; return out; 502 | case DwarfForm::Data1: out << "Data1"; return out; 503 | case DwarfForm::Flag: out << "Flag"; return out; 504 | case DwarfForm::Sdata: out << "Sdata"; return out; 505 | case DwarfForm::Strp: out << "Strp"; return out; 506 | case DwarfForm::Udata: out << "Udata"; return out; 507 | case DwarfForm::RefAddr: out << "RefAddr"; return out; 508 | case DwarfForm::Ref1: out << "Ref1"; return out; 509 | case DwarfForm::Ref2: out << "Ref2"; return out; 510 | case DwarfForm::Ref4: out << "Ref4"; return out; 511 | case DwarfForm::Ref8: out << "Ref8"; return out; 512 | case DwarfForm::RefUdata: out << "RefUdata"; return out; 513 | case DwarfForm::Indirect: out << "Indirect"; return out; 514 | case DwarfForm::SecOffset: out << "SecOffset"; return out; 515 | case DwarfForm::Exprloc: out << "Exprloc"; return out; 516 | case DwarfForm::FlagPresent: out << "FlagPresent"; return out; 517 | case DwarfForm::Strx: out << "Strx"; return out; 518 | case DwarfForm::Addrx: out << "Addrx"; return out; 519 | case DwarfForm::RefSup4: out << "RefSup4"; return out; 520 | case DwarfForm::StrpSup: out << "StrpSup"; return out; 521 | case DwarfForm::Data16: out << "Data16"; return out; 522 | case DwarfForm::LineStrp: out << "LineStrp"; return out; 523 | case DwarfForm::RefSig8: out << "RefSig8"; return out; 524 | case DwarfForm::ImplicitConst: out << "ImplicitConst"; return out; 525 | case DwarfForm::Loclistx: out << "Loclistx"; return out; 526 | case DwarfForm::Rnglistx: out << "Rnglistx"; return out; 527 | case DwarfForm::RefSup8: out << "RefSup8"; return out; 528 | case DwarfForm::Strx1: out << "Strx1"; return out; 529 | case DwarfForm::Strx2: out << "Strx2"; return out; 530 | case DwarfForm::Strx3: out << "Strx3"; return out; 531 | case DwarfForm::Strx4: out << "Strx4"; return out; 532 | case DwarfForm::Addrx1: out << "Addrx1"; return out; 533 | case DwarfForm::Addrx2: out << "Addrx2"; return out; 534 | case DwarfForm::Addrx3: out << "Addrx3"; return out; 535 | case DwarfForm::Addrx4: out << "Addrx4"; return out; 536 | default: out << "Unknown(" << static_cast(v) << ")"; return out; 537 | } 538 | } 539 | 540 | using Buffer = std::basic_string_view; 541 | 542 | // DWARF processing lib 543 | struct DebugDataLoader; 544 | 545 | struct Reader 546 | { 547 | Reader(const uint8_t *data) 548 | : _it(data) 549 | { 550 | } 551 | 552 | static int64_t DecodeLEB128Unsigned(const uint8_t *it, const uint8_t **it_out = nullptr) 553 | { 554 | const uint8_t *begin = it; 555 | while (*it & 0b1000'0000) 556 | { 557 | ++it; 558 | } 559 | if (it_out) 560 | { 561 | *it_out = it + 1; 562 | } 563 | 564 | uint64_t result = 0; 565 | 566 | while (true) 567 | { 568 | result <<= 7; 569 | result |= (*it & 0x7f); 570 | if (it == begin) break; 571 | --it; 572 | } 573 | return result; 574 | } 575 | 576 | static int64_t DecodeLEB128Signed(const uint8_t *it, const uint8_t **it_out = nullptr) 577 | { 578 | const uint8_t *begin = it; 579 | while (*it & 0b1000'0000) 580 | { 581 | ++it; 582 | } 583 | if (it_out) 584 | { 585 | *it_out = it + 1; 586 | } 587 | 588 | uint64_t result = 0; 589 | if (*it & 0b0100'0000) 590 | { 591 | result = -1; 592 | } 593 | 594 | while (true) 595 | { 596 | result <<= 7; 597 | result |= (*it & 0x7f); 598 | if (it == begin) break; 599 | --it; 600 | } 601 | return result; 602 | } 603 | 604 | 605 | uint64_t u8() { uint8_t res = *_it; _it += 1; return res; } 606 | uint64_t u16() { uint16_t res = *(uint16_t*)_it; _it += 2; return res; } 607 | uint64_t u32() { uint32_t res = *(uint32_t*)_it; _it += 4; return res; } 608 | uint64_t u64() { uint64_t res = *(uint64_t*)_it; _it += 8; return res; } 609 | 610 | uint64_t leb128() { return DecodeLEB128Unsigned(_it, &_it); } 611 | uint64_t leb128s() { return DecodeLEB128Signed(_it, &_it); } 612 | 613 | void skip(size_t len) { _it += len; } 614 | Buffer buffer(size_t len) { Buffer res(_it, len); _it += len; return res; } 615 | 616 | const char* str() { const char *res = (const char*)_it; _it += strlen(res) + 1; return res; } 617 | 618 | const uint8_t *_it; 619 | }; 620 | 621 | struct AttrNameAndForm 622 | { 623 | DwarfAttr name; 624 | DwarfForm form; 625 | int64_t implicit_const; // TODO rarely used, inefficient to keep inline 626 | }; 627 | 628 | struct AbbrevEntry 629 | { 630 | DwarfTag tag; 631 | uint8_t has_children; 632 | uint16_t num_attrs; 633 | AttrNameAndForm attrs[0]; 634 | 635 | size_t findAttrIdxByName(DwarfAttr name) const 636 | { 637 | for (uint16_t i = 0; i < num_attrs; ++i) 638 | { 639 | if (attrs[i].name == name) 640 | { 641 | return i; 642 | } 643 | } 644 | return -1; 645 | } 646 | }; 647 | 648 | struct DwarfCompilationUnit 649 | { 650 | void parse_debug_abbrev(Reader it) 651 | { 652 | { 653 | _abbrev_offsets.push_back(0); 654 | _abbrev_data_unpacked.resize(sizeof(AbbrevEntry)); 655 | AbbrevEntry *entry = reinterpret_cast(_abbrev_data_unpacked.data()); 656 | entry->tag = DwarfTag::None; 657 | entry->has_children = 0; 658 | entry->num_attrs = 0; 659 | } 660 | 661 | while (true) { 662 | uint64_t abbrev_code = it.leb128(); 663 | if (abbrev_code == 0) break; 664 | 665 | if (abbrev_code != _abbrev_offsets.size()) throw std::runtime_error("Unexpected abbrev code"); 666 | 667 | uint32_t entry_offset = _abbrev_data_unpacked.size(); 668 | _abbrev_offsets.push_back(entry_offset); 669 | _abbrev_data_unpacked.resize(_abbrev_data_unpacked.size() + sizeof(AbbrevEntry)); 670 | AbbrevEntry *entry = reinterpret_cast(_abbrev_data_unpacked.data() + entry_offset); 671 | 672 | entry->tag = static_cast(it.leb128()); 673 | entry->has_children = (it.u8() == 1); // DW_CHILDREN_yes 674 | entry->num_attrs = 0; 675 | 676 | while (true) { 677 | DwarfAttr attr_name = static_cast(it.leb128()); 678 | DwarfForm attr_form = static_cast(it.leb128()); 679 | int64_t implicit_const_val = 0; 680 | 681 | if (attr_name == DwarfAttr::None && attr_form == DwarfForm::None) { 682 | break; 683 | } 684 | 685 | if (attr_form == DwarfForm::ImplicitConst) 686 | { 687 | implicit_const_val = it.leb128s(); 688 | } 689 | 690 | _abbrev_data_unpacked.resize(_abbrev_data_unpacked.size() + sizeof(AttrNameAndForm)); 691 | entry = reinterpret_cast(_abbrev_data_unpacked.data() + entry_offset); 692 | entry->attrs[entry->num_attrs].name = attr_name; 693 | entry->attrs[entry->num_attrs].form = attr_form; 694 | entry->attrs[entry->num_attrs].implicit_const = implicit_const_val; 695 | entry->num_attrs++; 696 | } 697 | 698 | static uint16_t max_num = 0; 699 | if (entry->num_attrs > max_num) max_num = entry->num_attrs; 700 | } 701 | } 702 | 703 | const AbbrevEntry& get_abbrev(uint32_t abbrev_code) 704 | { 705 | return *reinterpret_cast(_abbrev_data_unpacked.data() + _abbrev_offsets[abbrev_code]); 706 | } 707 | 708 | size_t _size; 709 | size_t _offset; 710 | size_t _root_die_offset; 711 | std::vector _abbrev_data_unpacked; 712 | std::vector _abbrev_offsets; 713 | }; 714 | 715 | struct RawDwarfData 716 | { 717 | Buffer debug_info; 718 | Buffer debug_abbrev; 719 | Buffer debug_str; 720 | 721 | RawDwarfData() = default; 722 | 723 | RawDwarfData(RawDwarfData &&ot) 724 | : debug_info(ot.debug_info) 725 | , debug_abbrev(ot.debug_abbrev) 726 | , debug_str(ot.debug_str) 727 | { 728 | // TODO steal destructor 729 | } 730 | 731 | 732 | RawDwarfData& operator=(RawDwarfData &&ot) 733 | { 734 | debug_info = ot.debug_info; 735 | debug_abbrev = ot.debug_abbrev; 736 | debug_str = ot.debug_str; 737 | return *this; 738 | } 739 | 740 | static RawDwarfData LoadELF(const char *path) 741 | { 742 | int fd __attribute__((__cleanup__(CleanupFD))) = open(path, O_RDONLY); 743 | if (fd == -1) 744 | { 745 | throw std::runtime_error("open failed"); 746 | } 747 | 748 | struct stat sb; 749 | if (fstat(fd, &sb) == -1) 750 | { 751 | throw std::runtime_error("fstat failed"); 752 | } 753 | 754 | void *_file_data = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 755 | if (_file_data == MAP_FAILED) 756 | { 757 | throw std::runtime_error("mmap failed"); 758 | } 759 | 760 | uint8_t *file_begin = (uint8_t*)_file_data; 761 | 762 | Elf64_Ehdr *elf = reinterpret_cast(_file_data); 763 | if (*(uint32_t*)elf != 0x464C457f) 764 | { 765 | throw std::runtime_error("Not an ELF file"); 766 | } 767 | 768 | if (elf->e_ident[4] != ELFCLASS64) 769 | { 770 | throw std::runtime_error("Not 64 bits"); 771 | } 772 | if (elf->e_ident[5] != ELFDATA2LSB) 773 | { 774 | throw std::runtime_error("Not little endian"); 775 | } 776 | 777 | if (elf->e_type != ET_EXEC && elf->e_type != ET_DYN) 778 | { 779 | throw std::runtime_error("Not an executable file"); 780 | } 781 | if (elf->e_machine != EM_X86_64) 782 | { 783 | throw std::runtime_error("Not AMD64"); 784 | } 785 | 786 | Elf64_Shdr *sec_shstr = reinterpret_cast(file_begin + elf->e_shoff + elf->e_shentsize * elf->e_shstrndx); 787 | uint8_t *shstr = file_begin + sec_shstr->sh_offset; 788 | 789 | Buffer debug_info, debug_abbrev, debug_str; 790 | const char *debug_link = nullptr; 791 | for (int i = 0; i < elf->e_shnum; ++i) 792 | { 793 | Elf64_Shdr *shdr = reinterpret_cast(file_begin + elf->e_shoff + elf->e_shentsize * i); 794 | const char *sname = reinterpret_cast(shstr + shdr->sh_name); 795 | 796 | if (strcmp(sname, ".debug_info") == 0) 797 | { 798 | debug_info = Buffer(file_begin + shdr->sh_offset, shdr->sh_size); 799 | } 800 | else if (strcmp(sname, ".debug_abbrev") == 0) 801 | { 802 | debug_abbrev = Buffer(file_begin + shdr->sh_offset, shdr->sh_size); 803 | } 804 | else if (strcmp(sname, ".debug_str") == 0) 805 | { 806 | debug_str = Buffer(file_begin + shdr->sh_offset, shdr->sh_size); 807 | } 808 | else if (strcmp(sname, ".gnu_debuglink") == 0) 809 | { 810 | debug_link = reinterpret_cast(file_begin + shdr->sh_offset); 811 | } 812 | } 813 | 814 | if (!debug_info.empty() && !debug_abbrev.empty() && !debug_str.empty()) 815 | { 816 | return RawDwarfData(debug_info, debug_abbrev, debug_str); 817 | } 818 | 819 | if (debug_link) 820 | { 821 | char buf[1024]; 822 | ssize_t len; 823 | if (strcmp(path, "/proc/self/exe") == 0) 824 | { 825 | len = readlink("/proc/self/exe", buf, 1024); 826 | if (len == -1 || len == 1024) throw std::runtime_error("readlink"); 827 | buf[len] = 0; 828 | } 829 | else 830 | { 831 | strcpy(buf, path); 832 | len = strlen(buf); 833 | } 834 | 835 | while (true) 836 | { 837 | --len; 838 | if (buf[len] == '/') 839 | { 840 | break; 841 | } 842 | buf[len] = 0; 843 | } 844 | 845 | strcat(buf, debug_link); // TODO unsafe 846 | return RawDwarfData::LoadELF(buf); // TODO this doesn't work when debug_link file is not in the same dir 847 | } 848 | 849 | throw std::runtime_error("No debug info found"); 850 | } 851 | 852 | private: 853 | 854 | static void CleanupFD(int *fd) 855 | { 856 | if (*fd != -1) 857 | { 858 | close(*fd); 859 | *fd = -1; 860 | } 861 | } 862 | 863 | RawDwarfData(Buffer debug_info_, Buffer debug_abbrev_, Buffer debug_str_) 864 | : debug_info(debug_info_) 865 | , debug_abbrev(debug_abbrev_) 866 | , debug_str(debug_str_) 867 | { 868 | // TODO cleanup mmap.. 869 | } 870 | }; 871 | 872 | struct DIEAccessor 873 | { 874 | DebugDataLoader *_loader; 875 | DwarfCompilationUnit *_cu; 876 | const AbbrevEntry *_abbrev; 877 | const uint8_t *_nextDieBegin; 878 | const uint8_t *_rangeEnd; 879 | const uint8_t *_attrData[26]; // TODO make this a small_vector 880 | uint64_t _offset; 881 | 882 | void operator++(); 883 | 884 | operator bool() const 885 | { 886 | return _abbrev != nullptr; 887 | } 888 | 889 | DwarfTag tag() 890 | { 891 | return _abbrev->tag; 892 | } 893 | 894 | bool has_children() 895 | { 896 | return _abbrev->has_children; 897 | } 898 | 899 | 900 | RawDwarfData& getRawDwarfData(); 901 | 902 | std::optional getCStringView(DwarfAttr attr) 903 | { 904 | RawDwarfData &rdd = getRawDwarfData(); 905 | 906 | size_t idx = _abbrev->findAttrIdxByName(attr); 907 | if (idx == (size_t)-1) 908 | { 909 | return std::nullopt; 910 | } 911 | 912 | switch (_abbrev->attrs[idx].form) 913 | { 914 | case DwarfForm::Strp: return (const char*)rdd.debug_str.data() + *(const uint32_t*)(_attrData[idx]); 915 | case DwarfForm::String: return (const char*)_attrData[idx]; 916 | default: return std::nullopt; 917 | } 918 | } 919 | 920 | std::optional getBuffer(DwarfAttr attr) 921 | { 922 | size_t idx = _abbrev->findAttrIdxByName(attr); 923 | if (idx == (size_t)-1) 924 | { 925 | return std::nullopt; 926 | } 927 | 928 | switch (_abbrev->attrs[idx].form) 929 | { 930 | case DwarfForm::Block1: 931 | { 932 | uint64_t size = *(const uint8_t*)(_attrData[idx]); 933 | return Buffer(_attrData[idx] + 1, size); 934 | } 935 | case DwarfForm::Exprloc: 936 | { 937 | Reader it(_attrData[idx]); 938 | uint64_t len = it.leb128(); 939 | return it.buffer(len); 940 | } 941 | default: return std::nullopt; 942 | } 943 | } 944 | 945 | std::optional getOffset(DwarfAttr attr) 946 | { 947 | size_t idx = _abbrev->findAttrIdxByName(attr); 948 | if (idx == (size_t)-1) 949 | { 950 | return std::nullopt; 951 | } 952 | 953 | switch (_abbrev->attrs[idx].form) 954 | { 955 | case DwarfForm::SecOffset: return *(const uint32_t*)(_attrData[idx]); 956 | case DwarfForm::Ref4: return *(const uint32_t*)(_attrData[idx]); 957 | case DwarfForm::Addr: return *(const uint64_t*)(_attrData[idx]); 958 | case DwarfForm::Exprloc: 959 | { 960 | Reader it(_attrData[idx]); 961 | if (it.leb128() == 9 && it.u8() == 3) // When exprloc program is a single `DW_OP_addr` 962 | { 963 | return it.u64(); 964 | } 965 | return std::nullopt; 966 | } 967 | default: return std::nullopt; 968 | } 969 | } 970 | 971 | std::optional getUnsigned(DwarfAttr attr) 972 | { 973 | size_t idx = _abbrev->findAttrIdxByName(attr); 974 | if (idx == (size_t)-1) 975 | { 976 | return std::nullopt; 977 | } 978 | 979 | switch (_abbrev->attrs[idx].form) 980 | { 981 | case DwarfForm::Data1: return *(const uint8_t* )(_attrData[idx]); 982 | case DwarfForm::Data2: return *(const uint16_t*)(_attrData[idx]); 983 | case DwarfForm::Data4: return *(const uint32_t*)(_attrData[idx]); 984 | case DwarfForm::Data8: return *(const uint64_t*)(_attrData[idx]); 985 | case DwarfForm::Udata: return Reader::DecodeLEB128Unsigned(_attrData[idx]); 986 | case DwarfForm::Sdata: return Reader::DecodeLEB128Signed(_attrData[idx]); 987 | case DwarfForm::ImplicitConst: return _abbrev->attrs[idx].implicit_const; 988 | default: return std::nullopt; 989 | } 990 | } 991 | 992 | std::optional getSigned(DwarfAttr attr) 993 | { 994 | size_t idx = _abbrev->findAttrIdxByName(attr); 995 | if (idx == (size_t)-1) 996 | { 997 | return std::nullopt; 998 | } 999 | 1000 | switch (_abbrev->attrs[idx].form) 1001 | { 1002 | case DwarfForm::Data1: return (uint64_t)*(const uint8_t* )(_attrData[idx]); 1003 | case DwarfForm::Data2: return (uint64_t)*(const uint16_t*)(_attrData[idx]); 1004 | case DwarfForm::Data4: return (uint64_t)*(const uint32_t*)(_attrData[idx]); 1005 | case DwarfForm::Data8: return (uint64_t)*(const uint64_t*)(_attrData[idx]); 1006 | case DwarfForm::Sdata: return Reader::DecodeLEB128Signed(_attrData[idx]); 1007 | default: return std::nullopt; 1008 | } 1009 | } 1010 | }; 1011 | 1012 | struct DebugDataLoader 1013 | { 1014 | void loadFile(const char *path) 1015 | { 1016 | try 1017 | { 1018 | loadFileImpl(path); 1019 | } 1020 | catch (const std::runtime_error &err) 1021 | { 1022 | std::cerr << err.what() << "\n"; 1023 | std::cerr << "librepr: Error loading file: " << path << ". Values will not be pretty printed.\n"; 1024 | _compilation_units.clear(); 1025 | } 1026 | } 1027 | 1028 | size_t num_compilation_units() 1029 | { 1030 | return _compilation_units.size(); 1031 | } 1032 | 1033 | DIEAccessor loadDie(DwarfCompilationUnit *cu, const uint8_t *die) 1034 | { 1035 | Reader it(die); 1036 | 1037 | uint64_t die_abbrev_code = it.leb128(); 1038 | 1039 | const auto &abbrev = cu->get_abbrev(die_abbrev_code); 1040 | if (abbrev.tag == DwarfTag::None) { 1041 | DIEAccessor curDie; 1042 | curDie._offset = die - rdd.debug_info.data(); 1043 | curDie._loader = this; 1044 | curDie._cu = cu; 1045 | curDie._rangeEnd = rdd.debug_info.data() + cu->_offset + cu->_size; 1046 | curDie._abbrev = &abbrev; 1047 | curDie._nextDieBegin = it._it; 1048 | return curDie; 1049 | } 1050 | 1051 | DIEAccessor curDie; 1052 | curDie._offset = die - rdd.debug_info.data(); 1053 | curDie._loader = this; 1054 | curDie._cu = cu; 1055 | curDie._rangeEnd = rdd.debug_info.data() + cu->_offset + cu->_size; 1056 | curDie._abbrev = &abbrev; 1057 | if (abbrev.num_attrs > 26) throw std::runtime_error("TODO larger than small_vector size"); 1058 | 1059 | auto skipLEB128 = [](const uint8_t *it) -> const uint8_t* { 1060 | while (true) { 1061 | uint8_t byte = *it; ++it; 1062 | if ((byte & 0x80) == 0) 1063 | break; 1064 | } 1065 | return it; 1066 | }; 1067 | 1068 | for (int i = 0; i < abbrev.num_attrs; ++i) 1069 | { 1070 | curDie._attrData[i] = it._it; 1071 | switch (abbrev.attrs[i].form) 1072 | { 1073 | case DwarfForm::FlagPresent: break; // maybe use bool? 1074 | case DwarfForm::Strp: it._it += 4; break; 1075 | case DwarfForm::LineStrp: it._it += 4; break; 1076 | case DwarfForm::Data1: it._it += 1; break; 1077 | case DwarfForm::Data2: it._it += 2; break; 1078 | case DwarfForm::Data4: it._it += 4; break; 1079 | case DwarfForm::Data8: it._it += 8; break; 1080 | case DwarfForm::Sdata: it._it = skipLEB128(it._it); break; 1081 | case DwarfForm::Udata: it._it = skipLEB128(it._it); break; 1082 | case DwarfForm::Addrx: it._it = skipLEB128(it._it); break; 1083 | case DwarfForm::Rnglistx: it._it = skipLEB128(it._it); break; 1084 | case DwarfForm::Loclistx: it._it = skipLEB128(it._it); break; 1085 | case DwarfForm::SecOffset: it._it += 4; break; 1086 | case DwarfForm::Addr: it._it += 8; break; 1087 | case DwarfForm::Ref4: it._it += 4; break; 1088 | case DwarfForm::String: it._it += strlen((const char*)it._it) + 1; break; 1089 | case DwarfForm::Block1: it._it += 1 + it._it[0]; break; 1090 | case DwarfForm::Strx1: it._it += 1; break; 1091 | case DwarfForm::Strx2: it._it += 2; break; 1092 | case DwarfForm::Exprloc: 1093 | { 1094 | uint64_t len = it.leb128(); 1095 | it.skip(len); 1096 | break; 1097 | } 1098 | case DwarfForm::ImplicitConst: break; 1099 | default: 1100 | { 1101 | std::stringstream ss; 1102 | ss << "Unknown form " << static_cast(abbrev.attrs[i].form); 1103 | throw std::runtime_error(ss.str()); 1104 | } 1105 | } 1106 | } 1107 | 1108 | curDie._nextDieBegin = it._it; 1109 | 1110 | return curDie; 1111 | } 1112 | 1113 | DIEAccessor loadCompilationUnitRootDie(size_t cu_idx) 1114 | { 1115 | return loadCompilationUnitDie(cu_idx, _compilation_units[cu_idx]._root_die_offset); 1116 | } 1117 | 1118 | DIEAccessor loadCompilationUnitDie(size_t cu_idx, uint64_t cu_die_offset) 1119 | { 1120 | auto &cu = _compilation_units[cu_idx]; 1121 | return loadDie(&cu, rdd.debug_info.data() + cu._offset + cu_die_offset); 1122 | } 1123 | 1124 | void *_file_data; 1125 | RawDwarfData rdd; 1126 | std::vector _compilation_units; 1127 | 1128 | private: 1129 | void loadFileImpl(const char *path) 1130 | { 1131 | rdd = RawDwarfData::LoadELF(path); 1132 | 1133 | Reader it(rdd.debug_info.data()); 1134 | 1135 | for (int i = 0; ; ++i) 1136 | { 1137 | uint64_t unit_offset = (it._it - rdd.debug_info.data()); 1138 | if (unit_offset >= rdd.debug_info.size()) break; 1139 | 1140 | uint64_t unit_len = it.u32(); 1141 | if (unit_len == 0xFFFFFFFFu) 1142 | { 1143 | throw std::runtime_error("64 bit dwarf format"); // Below sizes must differ too.. 1144 | } 1145 | 1146 | uint16_t dwarf_ver = it.u16(); 1147 | switch (dwarf_ver) 1148 | { 1149 | case 4: 1150 | { 1151 | uint64_t debug_abbrev_offset = it.u32(); 1152 | uint64_t address_size = it.u8(); 1153 | if (address_size != 8) 1154 | { 1155 | throw std::runtime_error("Not 8-byte addressing"); 1156 | } 1157 | 1158 | it._it = rdd.debug_info.data() + unit_offset + 4 + unit_len; 1159 | 1160 | 1161 | auto &cu = _compilation_units.emplace_back(); 1162 | cu._offset = unit_offset; 1163 | cu._size = 4 + unit_len; 1164 | cu._root_die_offset = 11; 1165 | cu.parse_debug_abbrev(Reader(rdd.debug_abbrev.data() + debug_abbrev_offset)); 1166 | break; 1167 | } 1168 | case 5: 1169 | { 1170 | uint64_t unit_type = it.u8(); 1171 | if (unit_type != 1) 1172 | { 1173 | throw std::runtime_error("Unknown unit type"); 1174 | } 1175 | 1176 | uint64_t address_size = it.u8(); 1177 | if (address_size != 8) 1178 | { 1179 | throw std::runtime_error("Not 8-byte addressing"); 1180 | } 1181 | 1182 | uint64_t debug_abbrev_offset = it.u32(); 1183 | 1184 | it._it = rdd.debug_info.data() + unit_offset + 4 + unit_len; 1185 | 1186 | 1187 | auto &cu = _compilation_units.emplace_back(); 1188 | cu._offset = unit_offset; 1189 | cu._size = 4 + unit_len; 1190 | cu._root_die_offset = 12; 1191 | cu.parse_debug_abbrev(Reader(rdd.debug_abbrev.data() + debug_abbrev_offset)); 1192 | break; 1193 | } 1194 | default: 1195 | throw std::runtime_error("Unsupported dwarf version"); 1196 | } 1197 | } 1198 | } 1199 | }; 1200 | 1201 | inline void DIEAccessor::operator++() 1202 | { 1203 | if (_nextDieBegin >= _rangeEnd) 1204 | { 1205 | _abbrev = nullptr; // Mark end 1206 | _nextDieBegin = nullptr; 1207 | _rangeEnd = nullptr; 1208 | return; 1209 | } 1210 | *this = this->_loader->loadDie(this->_cu, this->_nextDieBegin); 1211 | } 1212 | 1213 | inline RawDwarfData& DIEAccessor::getRawDwarfData() 1214 | { 1215 | return _loader->rdd; 1216 | } 1217 | 1218 | 1219 | using StringifyFunc = void(*)(std::ostream &out, void *type_info, const void *obj); 1220 | struct StringifyFuncAndTypeInfo 1221 | { 1222 | StringifyFunc func; 1223 | void *type_info; 1224 | }; 1225 | 1226 | struct DwarfStringify2 1227 | { 1228 | template 1229 | struct EnumClassTypeInfo 1230 | { 1231 | const char *enum_name; 1232 | std::unordered_map valueToNameMap; 1233 | }; 1234 | 1235 | struct StructTypeInfo 1236 | { 1237 | struct MemberInfo 1238 | { 1239 | const char *name; 1240 | size_t offset; 1241 | StringifyFuncAndTypeInfo stringifier; 1242 | }; 1243 | std::vector members; 1244 | }; 1245 | 1246 | template 1247 | static void EnumClass(std::ostream &out, void *type_info_, const void *val_) 1248 | { 1249 | const EnumClassTypeInfo *type_info = reinterpret_cast*>(type_info_); 1250 | 1251 | UnderlyingT val = *(const UnderlyingT*)val_; 1252 | 1253 | constexpr bool IsSigned = std::is_signed_v; 1254 | 1255 | auto it = type_info->valueToNameMap.find(static_cast(val)); 1256 | if (it != type_info->valueToNameMap.end()) 1257 | { 1258 | out << type_info->enum_name << "::" << it->second; 1259 | return; 1260 | } 1261 | 1262 | if constexpr (IsSigned) 1263 | { 1264 | if (val == (-9223372036854775807-1)) 1265 | { 1266 | out << "static_cast<" << type_info->enum_name << ">(-9223372036854775807-1)"; 1267 | } 1268 | else 1269 | { 1270 | out << "static_cast<" << type_info->enum_name << ">(" << (int64_t)val << ")"; 1271 | } 1272 | } 1273 | else 1274 | { 1275 | if (val > 9223372036854775807) 1276 | { 1277 | out << "static_cast<" << type_info->enum_name << ">(" << (uint64_t)val << "ull)"; 1278 | } 1279 | else 1280 | { 1281 | out << "static_cast<" << type_info->enum_name << ">(" << (uint64_t)val << ")"; 1282 | } 1283 | } 1284 | } 1285 | 1286 | static void Struct(std::ostream &out, void *type_info_, const void *val_) 1287 | { 1288 | const StructTypeInfo *type_info = reinterpret_cast(type_info_); 1289 | 1290 | (void)val_; 1291 | out << "{"; 1292 | bool need_comma = false; 1293 | for (const auto &m : type_info->members) 1294 | { 1295 | if (need_comma) out << ", "; 1296 | out << "." << m.name << "="; 1297 | 1298 | m.stringifier.func(out, m.stringifier.type_info, (const void*)((const char *)val_ + m.offset)); 1299 | 1300 | need_comma = true; 1301 | } 1302 | out << "}"; 1303 | } 1304 | }; 1305 | 1306 | // Manages mapping of dwarf type refs to their relevant stringify functions and data 1307 | struct LibReprGlobalCache 1308 | { 1309 | using DwarfLocation = std::pair; // cu_idx, cu_die_offset 1310 | 1311 | std::map stringifiers; 1312 | 1313 | template 1314 | StringifyFuncAndTypeInfo loadEnumStringify(DIEAccessor die) 1315 | { 1316 | constexpr bool IsSigned = std::is_signed_v; 1317 | 1318 | auto type_info = std::make_unique>(); 1319 | 1320 | type_info->enum_name = die.getCStringView(DwarfAttr::Name).value().data(); 1321 | if (die.has_children()) 1322 | { 1323 | ++die; 1324 | for (; die.tag() != DwarfTag::None; ++die) 1325 | { 1326 | if (die.tag() != DwarfTag::Enumerator) throw std::runtime_error("Invalid dwarf"); 1327 | 1328 | UnderlyingT value; 1329 | if constexpr (IsSigned) 1330 | { 1331 | value = die.getSigned(DwarfAttr::ConstValue).value(); 1332 | } 1333 | else 1334 | { 1335 | value = die.getUnsigned(DwarfAttr::ConstValue).value(); 1336 | } 1337 | 1338 | type_info->valueToNameMap[value] = die.getCStringView(DwarfAttr::Name).value().data(); 1339 | } 1340 | } 1341 | 1342 | StringifyFuncAndTypeInfo res; 1343 | res.func = DwarfStringify2::EnumClass; 1344 | res.type_info = static_cast(type_info.release()); 1345 | return res; 1346 | } 1347 | 1348 | void loadStructStringifyAppendMembers(DwarfStringify2::StructTypeInfo &type_info, DebugDataLoader &loader, size_t cu_idx, DIEAccessor die, size_t offset_base) 1349 | { 1350 | int depth = 0; 1351 | if (die.has_children()) 1352 | { 1353 | ++depth; 1354 | ++die; 1355 | for (; ; ++die) 1356 | { 1357 | if (depth == 1 && die.tag() == DwarfTag::Inheritance) 1358 | { 1359 | DIEAccessor baseClassDie = loader.loadCompilationUnitDie(cu_idx, die.getOffset(DwarfAttr::Type).value()); 1360 | loadStructStringifyAppendMembers(type_info, loader, cu_idx, baseClassDie, offset_base + die.getUnsigned(DwarfAttr::DataMemberLocation).value()); 1361 | } 1362 | else if (depth == 1 && die.tag() == DwarfTag::Member) 1363 | { 1364 | auto &member = type_info.members.emplace_back(); 1365 | member.name = die.getCStringView(DwarfAttr::Name)->data(); 1366 | member.offset = offset_base + die.getUnsigned(DwarfAttr::DataMemberLocation).value(); 1367 | member.stringifier = loadStringify(loader, cu_idx, die.getOffset(DwarfAttr::Type).value()); 1368 | } 1369 | 1370 | if (die.has_children()) 1371 | { 1372 | ++depth; 1373 | } 1374 | else if (die.tag() == DwarfTag::None) 1375 | { 1376 | --depth; 1377 | if (depth == 0) 1378 | { 1379 | break; 1380 | } 1381 | } 1382 | } 1383 | } 1384 | } 1385 | 1386 | StringifyFuncAndTypeInfo loadStructStringify(DebugDataLoader &loader, size_t cu_idx, DIEAccessor die) 1387 | { 1388 | auto type_info = std::make_unique(); 1389 | loadStructStringifyAppendMembers(*type_info, loader, cu_idx, die, 0); 1390 | 1391 | StringifyFuncAndTypeInfo res; 1392 | res.func = DwarfStringify2::Struct; 1393 | res.type_info = static_cast(type_info.release()); 1394 | return res; 1395 | } 1396 | 1397 | StringifyFuncAndTypeInfo loadBaseStringify(DIEAccessor die) 1398 | { 1399 | StringifyFuncAndTypeInfo res = {}; 1400 | 1401 | uint64_t encoding = die.getUnsigned(DwarfAttr::Encoding).value(); 1402 | uint64_t byteSize = die.getUnsigned(DwarfAttr::ByteSize).value(); 1403 | 1404 | switch (encoding) 1405 | { 1406 | case 4: // float 1407 | if (byteSize == 4) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const float*)val; }; return res; } 1408 | if (byteSize == 8) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const double*)val; }; return res; } 1409 | if (byteSize == 16) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const long double*)val; }; return res; } 1410 | break; 1411 | case 5: // signed 1412 | case 6: // signed char 1413 | if (byteSize == 1) { res.func = [](std::ostream &out, void *, const void *val) { out << static_cast(*(const int8_t*)val); }; return res; } 1414 | if (byteSize == 2) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const int16_t*)val; }; return res; } 1415 | if (byteSize == 4) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const int32_t*)val; }; return res; } 1416 | if (byteSize == 8) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const int64_t*)val; }; return res; } 1417 | break; 1418 | case 7: // unsigned 1419 | case 8: // unsigned char 1420 | if (byteSize == 1) { res.func = [](std::ostream &out, void *, const void *val) { out << static_cast(*(const uint8_t*)val); }; return res; } 1421 | if (byteSize == 2) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const uint16_t*)val; }; return res; } 1422 | if (byteSize == 4) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const uint32_t*)val; }; return res; } 1423 | if (byteSize == 8) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const uint64_t*)val; }; return res; } 1424 | break; 1425 | case 16: // DW_ATE_UTF 1426 | if (byteSize == 1) { res.func = [](std::ostream &out, void *, const void *val) { out << static_cast(*(const uint8_t*)val); }; return res; } 1427 | if (byteSize == 2) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const uint16_t*)val; }; return res; } 1428 | if (byteSize == 4) { res.func = [](std::ostream &out, void *, const void *val) { out << *(const uint32_t*)val; }; return res; } 1429 | break; 1430 | } 1431 | 1432 | std::cerr << "encoding=" << encoding << ", byteSize=" << byteSize << " type=" << die.getCStringView(DwarfAttr::Name).value() << "\n"; 1433 | 1434 | res.func = [](std::ostream &out, void *, const void *) 1435 | { 1436 | out << "???"; 1437 | }; 1438 | res.type_info = nullptr; 1439 | return res; 1440 | } 1441 | 1442 | StringifyFuncAndTypeInfo loadStringify(DebugDataLoader &loader, size_t cu_idx, uint64_t typeDieOffset) 1443 | { 1444 | DwarfLocation loc(cu_idx, typeDieOffset); 1445 | if (auto it = stringifiers.find(loc); it != stringifiers.end()) { 1446 | return it->second; 1447 | } 1448 | 1449 | DIEAccessor acc = loader.loadCompilationUnitDie(cu_idx, typeDieOffset); 1450 | std::optional res; 1451 | switch (acc.tag()) 1452 | { 1453 | case DwarfTag::EnumerationType: 1454 | { 1455 | // GCC has encoding/byteSize in enum type, but clang only has them on linked primitive 1456 | DIEAccessor primitiveDie = loader.loadCompilationUnitDie(cu_idx, acc.getOffset(DwarfAttr::Type).value()); 1457 | 1458 | // TODO extract this typedef following logic to a function 1459 | while (primitiveDie.tag() == DwarfTag::Typedef) 1460 | { 1461 | primitiveDie = loader.loadCompilationUnitDie(cu_idx, primitiveDie.getOffset(DwarfAttr::Type).value()); 1462 | } 1463 | 1464 | uint64_t encoding = primitiveDie.getUnsigned(DwarfAttr::Encoding).value(); 1465 | uint64_t byteSize = primitiveDie.getUnsigned(DwarfAttr::ByteSize).value(); 1466 | 1467 | // GCC uses encoding=5,7, clang uses 6,8 1468 | bool signed_ = (encoding == 5 || encoding == 6); 1469 | bool unsigned_ = (encoding == 7 || encoding == 8); 1470 | if (signed_ && byteSize == 1) { res = loadEnumStringify(acc); break; } 1471 | if (signed_ && byteSize == 2) { res = loadEnumStringify(acc); break; } 1472 | if (signed_ && byteSize == 4) { res = loadEnumStringify(acc); break; } 1473 | if (signed_ && byteSize == 8) { res = loadEnumStringify(acc); break; } 1474 | if (unsigned_ && byteSize == 1) { res = loadEnumStringify(acc); break; } 1475 | if (unsigned_ && byteSize == 2) { res = loadEnumStringify(acc); break; } 1476 | if (unsigned_ && byteSize == 4) { res = loadEnumStringify(acc); break; } 1477 | if (unsigned_ && byteSize == 8) { res = loadEnumStringify(acc); break; } 1478 | break; 1479 | } 1480 | case DwarfTag::StructureType: 1481 | case DwarfTag::ClassType: 1482 | { 1483 | res = loadStructStringify(loader, cu_idx, acc); 1484 | break; 1485 | } 1486 | case DwarfTag::BaseType: 1487 | { 1488 | res = loadBaseStringify(acc); 1489 | break; 1490 | } 1491 | case DwarfTag::Typedef: 1492 | { 1493 | res = loadStringify(loader, cu_idx, acc.getOffset(DwarfAttr::Type).value()); 1494 | break; 1495 | } 1496 | case DwarfTag::PointerType: 1497 | { 1498 | // TODO function ptrs? 1499 | // TODO maybe make char* etc to print the string etc 1500 | res.emplace(); 1501 | res->func = [](std::ostream &out, void *, const void *val) 1502 | { 1503 | uint64_t addr = *(const uint64_t*)val; 1504 | if (addr == 0) 1505 | { 1506 | out << "nullptr"; 1507 | } 1508 | else 1509 | { 1510 | std::stringstream ss; 1511 | ss << "0x" << std::hex << std::setw(16) << std::setfill('0') << addr; 1512 | out << ss.str(); 1513 | } 1514 | }; 1515 | res->type_info = nullptr; 1516 | break; 1517 | } 1518 | default: 1519 | break; 1520 | } 1521 | 1522 | if (!res) { 1523 | std::cerr << "Can't stringify type at 0x" << std::hex << typeDieOffset << std::dec << " " << acc.tag() << "\n"; 1524 | res.emplace(); 1525 | res->func = [](std::ostream &out, void *, const void *) 1526 | { 1527 | out << "???"; 1528 | }; 1529 | res->type_info = nullptr; 1530 | } 1531 | 1532 | stringifiers[loc] = *res; 1533 | return *res; 1534 | } 1535 | 1536 | uint64_t findGlobalOffset(DebugDataLoader &loader) 1537 | { 1538 | // Find a position of a well known global variable and compare it to its debug data 1539 | // Use that offset for any global variables later, to make this work with PIE. 1540 | static volatile bool librepr_global_offset_marker__; 1541 | uint64_t dwarfLocation = -1; 1542 | uint64_t realLocation = reinterpret_cast(&librepr_global_offset_marker__); 1543 | 1544 | for (size_t i = 0; i < loader.num_compilation_units(); ++i) 1545 | { 1546 | for (DIEAccessor acc = loader.loadCompilationUnitRootDie(i); acc; ++acc) 1547 | { 1548 | if (acc.tag() == DwarfTag::Variable) 1549 | { 1550 | if (acc.getCStringView(DwarfAttr::Name) == "librepr_global_offset_marker__") 1551 | { 1552 | dwarfLocation = acc.getOffset(DwarfAttr::Location).value(); 1553 | return realLocation - dwarfLocation; 1554 | } 1555 | } 1556 | } 1557 | } 1558 | 1559 | // TODO fallback, not fail 1560 | throw std::runtime_error("Unable to find librepr_global_offset_marker__, did you enable debug data?"); 1561 | } 1562 | 1563 | void run(DebugDataLoader &loader) 1564 | { 1565 | uint64_t globalOffset = findGlobalOffset(loader); 1566 | 1567 | for (size_t i = 0; i < loader.num_compilation_units(); ++i) 1568 | { 1569 | std::optional ttypeDie, fnVarDie; 1570 | auto check = [&]() 1571 | { 1572 | if (ttypeDie && fnVarDie) 1573 | { 1574 | uint64_t typeDieOffset = ttypeDie->getOffset(DwarfAttr::Type).value(); 1575 | 1576 | uint64_t dwarfOffset = fnVarDie->getOffset(DwarfAttr::Location).value(); 1577 | StringifyFuncAndTypeInfo *fnti = (StringifyFuncAndTypeInfo*)(globalOffset + dwarfOffset); 1578 | *fnti = loadStringify(loader, i, typeDieOffset); 1579 | 1580 | ttypeDie.reset(); 1581 | fnVarDie.reset(); 1582 | } 1583 | }; 1584 | 1585 | for (DIEAccessor acc = loader.loadCompilationUnitRootDie(i); acc; ++acc) 1586 | { 1587 | switch (acc.tag()) 1588 | { 1589 | case DwarfTag::Subprogram: 1590 | ttypeDie.reset(); 1591 | fnVarDie.reset(); 1592 | break; 1593 | case DwarfTag::TemplateTypeParameter: 1594 | if (acc.getCStringView(DwarfAttr::Name) == "librepr_T__") 1595 | { 1596 | ttypeDie = acc; 1597 | check(); 1598 | } 1599 | break; 1600 | case DwarfTag::Variable: 1601 | { 1602 | if (acc.getCStringView(DwarfAttr::Name) == "librepr_stringify_fnti__") 1603 | { 1604 | fnVarDie = acc; 1605 | check(); 1606 | } 1607 | break; 1608 | } 1609 | default: 1610 | break; 1611 | } 1612 | } 1613 | } 1614 | } 1615 | 1616 | static 1617 | void InitializeAll(std::ostream &out, void *type_info, const void *obj) 1618 | { 1619 | StringifyFuncAndTypeInfo *fnti = reinterpret_cast(type_info); 1620 | 1621 | static std::mutex gMut; 1622 | static std::shared_ptr gLoader; 1623 | static std::shared_ptr gCache; 1624 | 1625 | std::lock_guard guard(gMut); 1626 | if (!gLoader) 1627 | { 1628 | gLoader = std::make_shared(); 1629 | gLoader->loadFile("/proc/self/exe"); 1630 | gCache = std::make_shared(); 1631 | gCache->run(*gLoader); 1632 | } 1633 | 1634 | if (fnti->func == InitializeAll) 1635 | { 1636 | // TODO implement fallback printers? 1637 | fnti->func = [](std::ostream &out, void *, const void *) 1638 | { 1639 | out << "???"; 1640 | }; 1641 | fnti->type_info = nullptr; 1642 | } 1643 | 1644 | fnti->func(out, fnti->type_info, obj); 1645 | } 1646 | }; 1647 | 1648 | 1649 | 1650 | } // namespace librepr::_internal_v3 1651 | 1652 | 1653 | 1654 | 1655 | namespace librepr { 1656 | 1657 | 1658 | 1659 | template 1660 | inline 1661 | std::string repr(const librepr_T__ &val) 1662 | { 1663 | using namespace _internal_v3; 1664 | 1665 | static StringifyFuncAndTypeInfo librepr_stringify_fnti__ = { 1666 | LibReprGlobalCache::InitializeAll, 1667 | reinterpret_cast(&librepr_stringify_fnti__) 1668 | }; 1669 | 1670 | std::stringstream ss; 1671 | librepr_stringify_fnti__.func(ss, librepr_stringify_fnti__.type_info, reinterpret_cast(&val)); 1672 | return ss.str(); 1673 | } 1674 | 1675 | 1676 | } // namespace librepr 1677 | 1678 | 1679 | #endif // LIBREPR_HPP_ 1680 | --------------------------------------------------------------------------------