在微控制器目标上通过 gdb 即时执行任意代码?

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

让我试着解释一下我在找什么,因为我找不到更好的标题措辞。

假设我正在对 RP2040 微控制器进行编程,我可以使用

gdb
openocd
与它建立调试会话。 (请注意,即使我在这里通过一个具体的 MCU 平台进行讨论,我也很想知道这种方法是否可以实现一般 - 使用任何类型的“外部微控制器”,
gdb
可能能够瞄准)

现在假设我想用外部硬件做一些(相对简单的)过程:举个例子,假设我想打开一些 GPIO 引脚,等待 2000 个 CPU 周期,然后关闭相同的 GPIO。即使是这样一个简单的例子,这也需要硬件初始化,所以总而言之,在固件代码中我必须做类似的事情(C 使用 pico-sdk):

#define MY_PIN_NR 12
static inline void my_hardware_init(void) {
  gpio_init(MY_PIN_NR);
  gpio_set_dir(MY_PIN_NR, GPIO_OUT);
}

static inline void my_hardware_do_process(void) {
  // raise pin high:
  gpio_put(MY_PIN_NR, 1);
  // wait for 2000 CPU cycles
  uint16_t cycles_to_wait = 2000;
  while(cycles_to_wait--) {
    asm volatile("nop");
  }
  // set pin low:
  gpio_put(MY_PIN_NR, 0);
}

void my_hardware_full_process(void) {
  // ensure hardware is initialized
  my_hardware_init();
  // do process:
  my_hardware_do_process();
}

如果这是在固件中编译并刻录在闪存中,我可以在 GDB 会话中直接在目标微控制器上调用它,say:

(gdb) call my_hardware_full_process()

(甚至只是

p my_hardware_full_process()
);那么即使调试器让微控制器在断点处暂停,函数仍然执行,然后返回到调试器。

现在,这意味着闪存上烧录了实际代码(从

gdb
解析为符号
my_hardware_full_process
的位置开始)。

所以,我的问题是——我能以某种方式做类似的事情吗,也就是说,执行与

my_hardware_full_process
中相同的代码,但是如果微控制器闪存被完全擦除/未初始化? (这意味着微控制器没有可运行的代码,因此不会运行任何代码 - 注意
gdb
通过
openocd
仍然可以挂接到此状态)。在这种情况下,即使
gdb
从.elf文件中获取到
my_hardware_full_process
的地址,它仍然是一个不包含可运行代码的地址,因此使用
(gdb) call function-symbol()
的方法失败。

考虑到这一点,我在猜测,也许可以编译一个“二进制 blob”,它将包含

my_hardware_full_process()
函数的程序集 - 例如,
arm-none-eabi-objdump -S --disassemble=my_hardware_full_process firmware.elf
这里会给出:

Disassembly of section .text:

10000310 <my_hardware_full_process>:
  }
  // set pin low:
  gpio_put(MY_PIN_NR, 0);
}

void my_hardware_full_process(void) {
10000310:       b510            push    {r4, lr}
  gpio_init(MY_PIN_NR);
10000312:       200c            movs    r0, #12
10000314:       f003 fcf2       bl      10003cfc <gpio_init>
 * Switch all GPIOs in "mask" to output
 *
 * \param mask Bitmask of GPIO to set to output, as bits 0-29
 */
static inline void gpio_set_dir_out_masked(uint32_t mask) {
    sio_hw->gpio_oe_set = mask;
10000318:       23d0            movs    r3, #208        ; 0xd0
1000031a:       061b            lsls    r3, r3, #24
1000031c:       2280            movs    r2, #128        ; 0x80
1000031e:       0152            lsls    r2, r2, #5
10000320:       625a            str     r2, [r3, #36]   ; 0x24
    sio_hw->gpio_set = mask;
10000322:       615a            str     r2, [r3, #20]
  uint16_t cycles_to_wait = 2000;
10000324:       22fa            movs    r2, #250        ; 0xfa
10000326:       00d2            lsls    r2, r2, #3
  while(cycles_to_wait--) {
10000328:       e001            b.n     1000032e <my_hardware_full_process+0x1e>
    asm volatile("nop");
1000032a:       46c0            nop                     ; (mov r8, r8)
  while(cycles_to_wait--) {
1000032c:       001a            movs    r2, r3
1000032e:       1e53            subs    r3, r2, #1
10000330:       b29b            uxth    r3, r3
10000332:       2a00            cmp     r2, #0
10000334:       d1f9            bne.n   1000032a <my_hardware_full_process+0x1a>
    sio_hw->gpio_clr = mask;
10000336:       23d0            movs    r3, #208        ; 0xd0
10000338:       061b            lsls    r3, r3, #24
1000033a:       2280            movs    r2, #128        ; 0x80
1000033c:       0152            lsls    r2, r2, #5
1000033e:       619a            str     r2, [r3, #24]
  // ensure hardware is initialized
  my_hardware_init();
  // do process:
  my_hardware_do_process();
}
10000340:       bd10            pop     {r4, pc}

Disassembly of section .data:

所以,基本上,我需要这段代码,加上

<gpio_init>
和依赖项跳转到的地方 - 本质上,PC 上已知的“静态构建”。原则上,我可以想象一个“静态构建”blob,它“包含”运行(在这种情况下)
my_hardware_full_process
函数所需的所有要求/依赖项。

那么问题就变成了:我能否以某种方式使用

gdb
读取 PC 上的这种“静态构建二进制 blob”文件,然后以某种方式将指令及其数据“推送”到微控制器,并让 blob 的在那里执行的指令(即“即时”),因此硬件执行预期的功能(之后,控制返回到
gdb
提示符)——即使闪存已被完全擦除?

如果是这样,我怎么能创建这样一个“静态构建二进制 blob”——我怎么能指示

gdb
在目标微控制器上运行它?

c debugging gdb microcontroller openocd
© www.soinside.com 2019 - 2024. All rights reserved.