使用 LLDB 调试内联 ASM - 将指令视为步骤命令的单独语句?

问题描述 投票:0回答:1

在 LLDB 中,

step
命令作为单个“语句”跨过整个
asm{}
块。 有没有办法让它单独处理每条指令,这样你就不必使用
si
来单步执行指令,并切换到反汇编模式来查看当前位置?


作为我正在参加的课程的一部分,我们正在用 C 语言编写内联汇编。他们在 Windows 上使用 Visual Studio 来编写和调试 MSVS 程序集。我想在我的 M2 Macbook 上安装并运行它(我可以运行虚拟机,但这很无聊)

编译这个东西非常容易。我使用

clang
并像这样复制:

clang -fasm-blocks -target x86_64-apple-macos main.c -g -O0

这是一个示例代码片段:

#include <inttypes.h>
#include <stdio.h>

uint8_t count1(uint32_t x) {
    uint8_t out;
    __asm {
        mov edx, x
        mov al, 0
next:   cmp edx, 0
        je done
        mov cl, dl
        and cl, 1
        add al, cl
        shr edx, 1
        jmp next
done:
        mov out, al
    }
    return out;
}

int main() {
    uint32_t x = 0x5789ABCD;
    uint8_t cnt = count1(x);
    printf("Number of 1s in 0x%X: %hhu\n", x, cnt);
}

我唯一需要做的不同的事情是返回值。我必须手动将其移至 C 变量并返回。如果您知道如何隐式地完成这项工作,那就太棒了,但这对我来说不是什么大问题。

问题来自于调试。我使用 neovim,并且对

nvim-dap
codelldb
有很好的调试经验。然而,当我单步执行上面截取的代码时,asm 块一步运行。 我尝试在 cli 上使用 raw
lldb
进行调试,它做了同样的事情。我可以使用
si
di -F intel
单步执行 asm,但这有点麻烦。

我想我只是在编译步骤中缺少一个标志来为 asm 块生成调试符号。有谁知道如何逐步通过

__asm
中的
LLDB
块以及 Neovim ?我对问题的理解正确吗?

c debugging inline-assembly lldb codelldb
1个回答
0
投票

所以,我找到了解决方案。事实证明,您可以手动指定位置。这就是它的样子:

uint8_t count1(uint32_t x) {
    int out;
    __asm {
        .file 1 "main.c"
        .loc 1 9
        mov edx, [x]
        .loc 1 11
        mov al, 0
        .loc 1 13
next:   cmp edx, 0
        .loc 1 15
        je done
        .loc 1 17
        mov cl, dl
        .loc 1 19
        and cl, 1
        .loc 1 21
        add al, cl
        .loc 1 23
        shr edx, 1
        .loc 1 25
        jmp next
done:
        .loc 1 28
        mov out, al
    }
    return out;
}

手动执行此操作会非常烦人,并且修改源代码会弄乱源代码在调试器中的外观(您会看到

.loc ...
声明)

我们可以做的是使用 clang 将 c 文件编译为中间 LLVM IR,使用自定义编译器通道或简单脚本(我选择了脚本)对其进行修改,然后将 IR 编译为可执行文件:

clang -S -emit-llvm -o main.ll main.c -fasm-blocks -target x86_64-apple-macos -g -O0
python3 preprocess.py main.ll
clang main.ll -o a.out -fasm-blocks -target x86_64-apple-macos -g -O0

这将创建一个包含所有调试符号和原始源代码的可执行文件,这就是我最初想要的。

这里是 Python 脚本,供感兴趣的人参考:

import sys

current_file_no = 0


def get_file_no():
    global current_file_no
    current_file_no += 1
    return current_file_no


def process_asm(asm_string: str, source_filename: str, first_line: int) -> str:
    instructions = asm_string.split("\\0A\\09")

    file_no = get_file_no()
    result = [f".file {file_no} \\22{source_filename}\\22"]

    loc_directives = [
        f".loc 1 {line}" for line in range(first_line, first_line + len(instructions))
    ]
    for loc, inst in zip(loc_directives, instructions):
        result.append(loc)
        result.append(inst)

    return "\\0A\\09".join(result)


asm_prefix = "call void asm sideeffect inteldialect "


def main():
    filename = sys.argv[1]

    with open(filename, "r") as f:
        lines = f.readlines()

    source_filename = None

    result = []
    for line in lines:
        stripped_line = line.strip()

        if stripped_line.startswith("source_filename"):
            source_filename = stripped_line.split('"')[1]
            result.append(line)
            continue

        if stripped_line.startswith("call void asm sideeffect inteldialect "):
            start = line.find(asm_prefix) + len(asm_prefix) + 1
            end = start
            while line[end] != '"':
                end += 1
            asm_string = line[start:end]

            dbg_entry = line.split("!dbg ")[1].split(",")[0]
            di_location = [ln for ln in lines if ln.startswith(dbg_entry)][0]
            line_number = int(di_location.split("line: ")[1].split(",")[0])

            assert source_filename is not None
            new_asm = process_asm(asm_string, source_filename, line_number + 1)
            result.append(line[:start] + new_asm + line[end:])
            continue

        result.append(line)

    with open(filename, "w") as f:
        f.write("".join(result))


if __name__ == "__main__":
    main()
© www.soinside.com 2019 - 2024. All rights reserved.