2

I am writing a custom programming language. I am generating LLVM IR as an intermediate (via LLVMlite), and I want to expose variables to GDB. This is an example of the generated IR:

; ModuleID = "tests/debuginfo.xan"
source_filename = "debuginfo.xan"
target triple = "x86_64-unknown-linux-gnu"
target datalayout = ""

define void @"main"(i32 %".1", i8** %".2") !dbg !10
{
entry:
  %"$argc" = alloca i32
  store i32 %".1", i32* %"$argc"
  %"$argv" = alloca i8**
  store i8** %".2", i8*** %"$argv"
  %"$a" = alloca i32
  call void @"llvm.dbg.declare"(metadata i32* %"$a", metadata !12, metadata !13), !dbg !14
  call void @"llvm.dbg.value"(metadata i32 0, metadata !12, metadata !13), !dbg !15
  store i32 0, i32* %"$a"
  ret void
}

declare i32 @"printf"(i8* %".1", ...) 

declare void @"llvm.dbg.declare"(metadata %".1", metadata %".2", metadata %".3") nounwind readnone

declare void @"llvm.dbg.value"(metadata %".1", metadata %".2", metadata %".3") nounwind readnone

@"NULL" = internal constant i8* inttoptr (i32 0 to i8*)
!llvm.dbg.cu = !{ !2 }
!llvm.module.flags = !{ !3, !4, !5 }
!llvm.ident = !{ !6 }
!gen-by = !{ !16 }
!0 = !DIFile(directory: "/home/proc-daemon/Dropbox/Xanathar/xanathar/tests", filename: "debuginfo.xan")
!1 = !{  }
!2 = distinct !DICompileUnit(emissionKind: FullDebug, enums: !1, file: !0, isOptimized: false, language: DW_LANG_Python, producer: "Xanathar v. a0.0.1", runtimeVersion: 0)
!3 = !{ i32 2, !"Dwarf Version", i32 4 }
!4 = !{ i32 2, !"Debug Info Version", i32 3 }
!5 = !{ i32 1, !"wchar_size", i32 4 }
!6 = !{ !"Xanathar a0.0.1" }
!7 = !DIDerivedType(baseType: null, size: 64, tag: DW_TAG_pointer_type)
!8 = !{ !7 }
!9 = !DISubroutineType(types: !8)
!10 = distinct !DISubprogram(file: !0, isDefinition: true, isLocal: false, isOptimized: false, name: "main", scope: !0, scopeLine: 1, type: !9, unit: !2, variables: !1)
!11 = !DIBasicType(encoding: DW_ATE_signed, name: "int", size: 4)
!12 = !DILocalVariable(file: !0, line: 1, name: "a", scope: !10, type: !11)
!13 = !DIExpression()
!14 = !DILocation(column: 1, line: 1, scope: !10)
!15 = !DILocation(column: 1, line: 2, scope: !10)
!16 = !{ !"Xanathar" }

I removed the previous edits. The generation code can be found here. As you can see, I have a variable $a that I am trying to declare with llvm.dbg.declare. However, although objdump --sym lists debug info (pastebin), gdb gives No locals. upon running info locals. What is the correct way to export variables? How could I generate that with LLVMlite?


Here is the compilation code:

 def compile(self, name, so):
        sys.stderr.write('---COMPILE---\n')
        if not so:
            command = 'clang {0} -g -fstandalone-debug -O0 ' + name + '.ll '
        else:
            command = 'clang {0} -g -fstandalone-debug -O0 -shared -undefined dynamic_lookup ' + name + '.ll '

        command = command.format(self.flags)

        for i in self.LOADED_MODULES:
            if i["type"] == 'LINKED_SO':
                command += os.path.abspath(i["FILE"]) + ' '
        command = command + '-o ' + name + ('.so' if so else '.o')
        # print(command)
        os.system(command)

Here is the LLVM confirmation code:

def _compile_ir(self, builder):
    """
    Compile the LLVM IR string with the given engine.
    The compiled module object is returned.
    """
    # Create a LLVM module object from the IR
    self.builder = builder
    self.builder.ret_void()
    self.module.add_named_metadata("gen-by", ["Xanathar"])
    llvm_ir = str(self.module)
    try:
        mod = self.binding.parse_assembly(llvm_ir)
    except RuntimeError as e:
        sys.stderr.write("[ERR] LLVM parsing error!\n")
        sys.stderr.write(str(e))
        if "expected instruction opcode" in str(e):
            sys.stderr.write("\nDid you forget to return from a function?")
        exit(1)

        mod = 0  # Otherwise PyCharm complains about mod's usage below

    mod.verify()
    # Now add the module and make sure it is ready for execution
    self.engine.add_module(mod)
    self.engine.finalize_object()
    self.engine.run_static_constructors()
    return mod

(After this, the module is written to a file)

EDIT 5 (or 6, idk): As Chirag Patel suggested, I added a new debug statement to the ret instruction. Then, this happened.

(gdb) r
Starting program: /home/proc-daemon/Dropbox/Xanathar/xanathar/tests/debuginfo.xan.o 

Breakpoint 1, main () at debuginfo.xan:1
1       declare $a as int32;
(gdb) info locals
Segmentation fault (core dumped)
NoOneIsHere
  • 1,054
  • 16
  • 28
  • 1
    The IR looks correct to me, it contains debug info. I don't know how llvmlite works. What does it do with the IR? If it calls clang, maybe it's missing an option to produce debug info. [Note: The only reason why I don't upvote is because you didn't post the code where you configure what llvmlite does with the IR.] – Andreas Haferburg Nov 28 '18 at 16:55
  • It calls `clang -g -O0 ` – NoOneIsHere Nov 28 '18 at 17:11
  • can you try and add scope to return instruction? "ret void, !dbg !15" – Chirag Patel Nov 28 '18 at 19:42
  • @ChiragPatel I did that and gdb segfaulted – NoOneIsHere Nov 28 '18 at 20:01
  • few more issues, DIBasicType size needs tobe in bits, DIDerivedType base type needs tobe qualified also DISubprogram line is missing. this are on the fly i could see. there is a nice document that covers this topic https://llvm.org/docs/SourceLevelDebugging.html. also https://llvm.org/docs/LangRef.html – Chirag Patel Nov 28 '18 at 20:04
  • @ChiragPatel Thank you. Fixing those two things solved the problem. If you post that as an answer, I will accept and upvote. – NoOneIsHere Nov 28 '18 at 20:07
  • Please file a gdb bug. It's fine to attach the executable that makes gdb crash. Thanks. – Tom Tromey Nov 30 '18 at 16:48
  • @TomTromey I don't have a copy of the executable that caused the issue because I solved the problem. It was a malformed debug statement. – NoOneIsHere Nov 30 '18 at 17:19

0 Answers0