简体   繁体   English

从 Ada 中的地址 0x0 读取

[英]Reading from Address 0x0 in Ada

I am running on a bare-board runtime, and reading data from address zero is a valid use case in my software.我在裸板运行时运行,从地址零读取数据是我的软件中的一个有效用例。 However, the runtime treats address 0x0 as null , and throws an exception in the following code when compiled with -O2 .但是,运行时将地址 0x0 视为null ,并在使用-O2编译时在以下代码中引发异常。 The code behaves as expected when compiled with -O1 :使用-O1编译时,代码的行为符合预期:

declare
  use System.Storage_Elements;
  type Byte_Array is array (Natural range 0 .. 3) of Interfaces.Unsigned_8;
  bytes : constant Byte_Array with Import, Convention => Ada, Address => To_Address(Integer_Address(16#00000000#));
begin
  -- Copy bytes to some other byte array in memory:
  other_bytes := bytes; -- Throws exception
end;

Is there any way around this problem?有没有办法解决这个问题?


Details on my platform:我的平台上的详细信息:

  • Ravenscar Small-footprint Runtime ported to Arm Cortex M1 Ravenscar 小尺寸运行时移植到 Arm Cortex M1
  • Running on softcore processor instantiated inside a Microsemi RTG4 FPGA在 Microsemi RTG4 FPGA 内实例化的软核处理器上运行

Details on the failure:失败详情:

The previous code dies in my runtime in the memcpy function implemented by s-memcop.adb .之前的代码在我的运行时中的memcpy function 由s-memcop.adb实现。 The code is copied below, with a comment reporting the line which fails.代码复制在下面,并带有一条注释报告失败的行。 I am unsure of the actual exception that gets thrown.我不确定引发的实际异常。 All I can see is the the last chance handler gets called with the information s-memcop.adb:52 , which is commented below.我所能看到的是最后一次机会处理程序被调用的信息s-memcop.adb:52 ,在下面评论。

 40    function memcpy
 41      (Dest : Address; Src : Address; N : size_t) return Address
 42    is
 43       D : IA     := To_IA (Dest);
 44       S : IA     := To_IA (Src);
 45       C : size_t := N;
 46
 47    begin
 48       --  Try to copy per word, if alignment constraints are respected
 49
 50       if ((D or S) and (Word'Alignment - 1)) = 0 then
 51          while C >= Word_Unit loop
 52             To_Word_Ptr (D).all := To_Word_Ptr (S).all; -- Last_Chance_Handler Called here :(
 53             D := D + Word_Unit;
 54             S := S + Word_Unit;
 55             C := C - Word_Unit;
 56          end loop;
 57       end if;
 58
 59       --  Copy the remaining byte per byte
 60
 61       while C > 0 loop
 62          To_Byte_Ptr (D).all := To_Byte_Ptr (S).all;
 63          D := D + Byte_Unit;
 64          S := S + Byte_Unit;
 65          C := C - Byte_Unit;
 66       end loop;
 67
 68       return Dest;
 69    end memcpy;

Is there a way to see the actual exception being thrown?有没有办法查看抛出的实际异常?

I got in contact with AdaCore and received a solution to this problem:我与 AdaCore 取得了联系并收到了解决此问题的方法:

If you compile at -O2 or above, then you also need to pass to the compiler the switch -fno-delete-null-pointer-checks because -fdelete-null-pointer-checks is automatically enabled at -O2 for the ARM architecture.如果您在 -O2 或更高版本进行编译,那么您还需要将开关 -fno-delete-null-pointer-checks 传递给编译器,因为 -fdelete-null-pointer-checks 在 ARM 架构的 -O2 处自动启用。

According to the docs the -fdelete-null-pointer-checks flag:根据文档-fdelete-null-pointer-checks标志:

Assume that programs cannot safely dereference null pointers, and that no code or data element resides at address zero.假设程序不能安全地取消引用 null 指针,并且没有代码或数据元素驻留在地址零处。

Since this is NOT true of my application, and I need to access data at address zero, I needed to disable this switch by passing the compiler the -fno-delete-null-pointer-checks option.由于这不适用于我的应用程序,并且我需要访问地址为零的数据,因此我需要通过向编译器传递-fno-delete-null-pointer-checks选项来禁用此开关。

Note: I had issues when the optimization flags of my code and the runtime were not consistent.注意:当我的代码和运行时的优化标志不一致时,我遇到了问题。 In this case, compiling my code AND the runtime with the -O2 and -fno-delete-null-pointer-checks options allowed me to freely read/write address 0x0.在这种情况下,使用-O2-fno-delete-null-pointer-checks选项编译我的代码和运行时允许我自由地读/写地址 0x0。

Updated更新

Although not a Cortex-M1, the example below seems to work on both the micro:bit (Cortex-M0, ZFP runtime) and the STM32F407 (SFP runtime, same result, not shown).虽然不是 Cortex-M1,但下面的示例似乎适用于 micro:bit(Cortex-M0,ZFP 运行时)和 STM32F407(SFP 运行时,相同的结果,未显示)。

main.adb主文件

with System;
with System.Storage_Elements;
with Interfaces;
with Ada.Text_IO;

procedure Main is

   type Byte_Array is array (Natural range 0 .. 3) of Interfaces.Unsigned_8;

   Bytes : Byte_Array with
     Address => System.Storage_Elements.To_Address (16#0#);

   Other_Bytes  : Byte_Array;

begin

   Other_Bytes := Bytes;    --  Line 17

   --  Output via semihosting.
   for I in Other_Bytes'Range loop
      Ada.Text_IO.Put (Other_Bytes (I)'Image);
   end loop;
   Ada.Text_IO.New_Line;    --  Required to flush semihosting output buffer.

   loop
      null;                 --  Line 26
   end loop;

end Main;

debug session (Cortex-M0, using pyocd as server)调试 session (Cortex-M0,使用pyocd作为服务器)

$ arm-eabi-gdb main
GNU gdb (GDB) 8.3 for GNAT Community 2019 [rev=gdb-8.3-ref-194-g3fc1095]
Copyright (C) 2019 Free Software Foundation, Inc.
[...]
Reading symbols from main...
(gdb) target remote :3333
Remote debugging using :3333
0x0000020a in _start ()
(gdb) load
Loading section .text, size 0x4fc lma 0x0
Loading section .rodata, size 0x20 lma 0x4e8
Loading section .data, size 0x4 lma 0x4fc
Start address 0x20a, load size 1312
Transfer rate: 3 KB/sec, 437 bytes/write.
(gdb) b 26
Breakpoint 1 at 0x14a: file [...]/src/main.adb, line 26.
(gdb) c
Continuing.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at [...]/src/main.adb:26
26      null;                 --  Line 26
(gdb) p/x Other_Bytes 
$1 = (0 => 0x88, 0x8, 0x0, 0x20)
(gdb) x/4xb 0
0x0 <__vectors>:    0x88    0x08    0x00    0x20
(gdb) p/x Bytes (0)'Address
$2 = 0x0
(gdb) p/x Other_Bytes (0)'Address
$3 = 0x20000848
(gdb)

output (semihosting, run in separate terminal) output (半主机,在单独的终端中运行)

$ nc localhost 4444
 136 8 0 32

Note on the usage of System.Null_Address .注意System.Null_Address的用法。

Note that I previously used System.Null_Address to reference address 0 .请注意,我之前使用System.Null_Address来引用地址0 In this case (and most of the time) this will work.在这种情况下(并且大多数情况下),这将起作用。 However, System.Null_Address does not represent memory location 0 by definition.但是,根据定义, System.Null_Address不代表 memory 位置0 Hence, To_Address (16#0#) might indeed be more safe (see also ARM 34/2 and ARM 37.c ).因此, To_Address (16#0#)可能确实更安全(另请参见ARM 34/2ARM 37.Z4A8A08F09D37B73795649038408B5F3 )。


Hints that might identify cause.可能确定原因的提示。

Based on the provided information (unexpected invocation of the SVCall exception handler and different behavior depending on optimization level) I would suspect some memory corruption (eg because of writes to incorrect memory locations; in C often caused by dangling pointers; in Ada might be because of using "unsafe" language features like Unchecked_Access , Unrestricted_Access and the Address attribute). Based on the provided information (unexpected invocation of the SVCall exception handler and different behavior depending on optimization level) I would suspect some memory corruption (eg because of writes to incorrect memory locations; in C often caused by dangling pointers; in Ada might be because使用“不安全”的语言功能,如Unchecked_AccessUnrestricted_AccessAddress属性)。

Tracking down memory corruption can be very challenging as the (source code) location where the damage is done may not even be close to where you actually observe the problem.追踪 memory 损坏可能非常具有挑战性,因为造成损坏的(源代码)位置甚至可能与您实际观察到问题的位置不接近。

The first thing I would start to investigate is the invocation of the SVCall exception handler.我要开始调查的第一件事是调用SVCall异常处理程序。 I would not expect an ARM svc instruction to be executed in your case.我不希望在您的情况下执行 ARM svc指令。 Some steps I would try (no guarantee this will bring you closer to the solution):我会尝试一些步骤(不保证这会让您更接近解决方案):

  • Disassemble parts of the relevant code around near the problem (in your case memcpy ) using arm-eabi-objdump -d -S and keep it as reference.使用arm-eabi-objdump -d -S分解问题附近的部分相关代码(在您的情况下为memcpy )并将其作为参考。
  • Hook up the gdb debugger, load the program and inspect if the disassembly near Other_Bytes:= Bytes;连接gdb调试器,加载程序并检查是否在Other_Bytes:= Bytes; and that of memcpy is comparable with what objdump returned.并且memcpy的返回值与objdump的返回值相当。 Using my own example:用我自己的例子:
(gdb) info line main.adb:17
Line 17 of "[...]/src/main.adb" starts at address 0xca <_ada_main+10> and ends at 0xe6 <_ada_main+38>.
(gdb) x/14i 0xca
[...]

and

(gdb) info address memcpy
Symbol "memcpy" is at 0x300 in a file compiled without debugging.
(gdb) x/100i 0x300
[...]
  • Set option to show disassembly per line and set a breakpoint just before reading the memory (ie Other_bytes:= Bytes; ).设置选项以显示每行反汇编并在读取 memory 之前设置断点(即Other_bytes:= Bytes; )。 Again, using my own example:同样,使用我自己的示例:
(gdb) set disassemble-next-line on
(gdb) b 17
  • Start the program (using c ) and once halted at the breakpoint, inspect the disassembled instructions again.启动程序(使用c )并在断点处停止,再次检查反汇编指令。 Continue step-by-step (using stepi and then subsequently pressing <return> to repeat the last command) and monitor the processor instructions shown closely.继续一步一步(使用stepi然后按<return>重复上一个命令)并密切监视显示的处理器指令。 Check if they are as expected (output of objdump ).检查它们是否符合预期( objdump的输出)。 I expect that, at some point, the actual and expected program flow starts to deviate.我预计,在某些时候,实际和预期的程序流程开始偏离。 Try to see if/where this happens at the level of processor instructions.尝试在处理器指令级别查看是否/在何处发生这种情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM