Чтение с адреса 0x0 в Аде - PullRequest
       79

Чтение с адреса 0x0 в Аде

2 голосов
/ 30 октября 2019

Я работаю в автономном режиме, и чтение данных с нулевого адреса является допустимым вариантом использования в моем программном обеспечении. Однако среда выполнения обрабатывает адрес 0x0 как null и создает исключение в следующем коде при компиляции с -O2. Код ведет себя как ожидалось при компиляции с -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;

Есть ли способ обойти эту проблему?


Подробности на моей платформе:

  • Среда разработки Ravenscar, занимающая небольшую площадь, перенесена на Arm Cortex M1
  • Работа на программном процессоре, созданном в FPGA Microsemi RTG4

Подробнеепо ошибке:

Предыдущий код умирает во время выполнения в функции memcpy, реализованной s-memcop.adb. Код скопирован ниже с комментарием, сообщающим о неудачной строке. Я не уверен в действительном исключении, которое выдается. Все, что я могу видеть, это то, что обработчик последнего шанса вызывается с информацией 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;

Есть ли способ увидеть, как выдается фактическое исключение?

Ответы [ 2 ]

4 голосов
/ 06 ноября 2019

Я связался с AdaCore и получил решение этой проблемы:

Если вы компилируете при -O2 или выше, то вам также нужно передать компилятору ключ -fno-delete-null-pointer-check, потому что -fdelete-null-pointer-check автоматически включается при -O2 для архитектуры ARM.

В соответствии с docs флагом -fdelete-null-pointer-checks:

Предположим, что программы не могут безопасно разыменовать нулевые указатели, и что никакой код или элемент данных не находится по адресу нуля.

Поскольку это НЕ верно для моего приложения, иМне нужно получить доступ к данным по нулевому адресу, мне нужно было отключить этот ключ, передав компилятору параметр -fno-delete-null-pointer-checks.

Примечание: У меня были проблемы, когда у меня были флаги оптимизации моего кода ивремя выполнения не было последовательным. В этом случае компиляция моего кода и среды выполнения с опциями -O2 и -fno-delete-null-pointer-checks позволили мне свободно читать / записывать адрес 0x0.

2 голосов
/ 30 октября 2019

Обновлено

Хотя это и не 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;

сеанс отладки (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)

выход (полухостинг, запуск в отдельном терминале)

$ nc localhost 4444
 136 8 0 32

Примечание по использованию System.Null_Address.

Обратите внимание, что ранее я использовал System.Null_Address для ссылки на адрес 0. В этом случае (и большую часть времени) это будет работать. Однако System.Null_Address не представляет ячейку памяти 0 по определению. Следовательно, To_Address (16#0#) действительно может быть более безопасным (см. Также ARM 34/2 и ARM 37.c ).


Подсказки, которыеможет идентифицировать причину.

На основании предоставленной информации (неожиданный вызов обработчика исключений SVCall и другое поведение в зависимости от уровня оптимизации) я подозреваю некоторое повреждение памяти (например, из-за записи в неправильную памятьместоположения; в C это часто вызвано висячими указателями; в Ada это может быть связано с использованием «небезопасных» языковых функций, таких как Unchecked_Access, Unrestricted_Access и атрибут Address).

Отслеживание повреждения памяти может быть очень сложным, поскольку место (исходный код), где причинен ущерб, может даже не быть близко к тому месту, где вы действительно наблюдаете проблему.

Первое, что я бы начал исследовать, - это вызов обработчика исключений SVCall. Я не ожидал бы, что инструкция ARM svc будет выполнена в вашем случае. Некоторые шаги, которые я бы попробовал (нет гарантии, что это приблизит вас к решению):

  • Разберите части соответствующего кода вокруг проблемы (в вашем случае memcpy), используя arm-eabi-objdump -d -S иоставьте его в качестве ссылки.
  • Подключите отладчик gdb, загрузите программу и проверьте, сопоставима ли разборка рядом с Other_Bytes := Bytes; и memcpy с тем, что вернул objdump. Используя мой собственный пример:
(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
[...]

и

(gdb) info address memcpy
Symbol "memcpy" is at 0x300 in a file compiled without debugging.
(gdb) x/100i 0x300
[...]
  • Установите опцию, чтобы показать разборку для каждой строки и установить точку останова непосредственно перед чтением памяти (то есть Other_bytes := Bytes;). Опять же, используя мой собственный пример:
(gdb) set disassemble-next-line on
(gdb) b 17
  • Запустите программу (используя c) и, как только остановитесь в точке останова, снова проверьте разобранные инструкции. Продолжайте шаг за шагом (используя stepi и затем последовательно нажимая <return>, чтобы повторить последнюю команду) и внимательно следите за инструкциями процессора, показанными внимательно. Проверьте, соответствуют ли они ожиданиям (вывод objdump). Я ожидаю, что в какой-то момент фактический и ожидаемый поток программ начнет отклоняться. Попробуйте посмотреть, если / где это происходит на уровне инструкций процессора.
...