section .DATA
является прямой причиной аварии. Нижний регистр section .data
является специальным и связан как отображение (чтение) (частное) отображения исполняемого файла.
Верхний регистр .DATA
не является специальным для компоновщика и заканчивается как часть текстового сегмента, отображенного для чтения + exec без разрешения на запись. ( В чем отличие раздела и сегмент в формате файла ELF ).
Верхний регистр .TEXT
также странен: по умолчанию objdump -drwC -Mintel
только разбирает секцию .text
(чтобы не разбирать данные, как если бы это был код), поэтому он показывает пустой вывод для вашего исполняемого файла.
После запуска программы в GDB (gdb ./foo
, starti
) я посмотрел карту памяти процесса из другой оболочки.
$ cat /proc/11343/maps
00400000-00401000 r-xp 00000000 00:31 110651257 /tmp/foo
7ffff7ffa000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0 [stack]
Как видите, кроме специальных сопоставлений VDSO и стека, существует только одно сопоставление с файловой поддержкой, и у него есть только разрешение на чтение + exec.
Один шаг внутри GDB, загрузка mov eax,DWORD PTR ds:0x400086
завершается успешно, но mov DWORD PTR ds:0x400086,eax
сохраняет ошибки . (См. Нижнюю часть x86 вики для получения советов по GDB asm.)
Начиная с readelf -a foo
, мы можем видеть заголовки программ ELF, которые сообщают загрузчику программ ОС, как отобразить его в память:
$ readelf -a foo # broken version
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000bf 0x00000000000000bf R 0x200000
Section to Segment mapping:
Segment Sections...
00 .DATA .TEXT
Обратите внимание, как .DATA
и .TEXT
находятся в одном сегменте. Это то, что вам нужно для section .rodata
(стандартное имя раздела, в которое вы должны помещать постоянные данные только для чтения, например вашу строку), но оно не будет работать для изменяемых глобальных переменных.
После исправления asm для использования section .data
и .text
, readelf показывает нам:
$ readelf -a foo # fixed version
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000e7 0x00000000000000e7 R E 0x200000
LOAD 0x00000000000000e8 0x00000000006000e8 0x00000000006000e8
0x0000000000000010 0x0000000000000010 RW 0x200000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
Обратите внимание, что сегмент 00 - это R + E без W, и там есть раздел .text
. Сегмент 01 - это RW (чтение + запись) без exec, и там есть раздел .data
.
Тег LOAD
означает, что они отображаются в виртуальном адресном пространстве процесса. Некоторые разделы (например, отладочная информация) не являются и являются просто метаданными для других инструментов. Но NASM помечает неизвестные имена разделов как прогбиты, то есть загруженные, поэтому он мог связываться и иметь нагрузку, а не segfault.
После исправления для использования section .data
ваша программа запускается без ошибок segfaulting .
Цикл выполняется в течение одной итерации, поскольку 2 байта, следующие за step: dw 1
, не равны нулю. После загрузки меча RAX = 0x2c0001
в моей системе. (cmp
между 0x002c0002 и 0xa
делает условие LE ложным, потому что оно не меньше или не равно.)
dw
означает «слово данных» или «определить слово». Используйте dd
для меча данных .
Кстати, нет необходимости хранить счетчик циклов в памяти . Вы ни для чего не используете RDI, RSI, RBP или R8..R15, так что вы можете просто сохранить это в реестре. Как mov edi, limit
перед циклом и dec edi / jnz
внизу.
Но на самом деле вы должны использовать 64-битный syscall
ABI, если вы хотите создать 64-битный код, а не 32-битный int 0x80
ABI. Что произойдет, если вы используете 32-битный int 0x80 Linux ABI в 64-битном коде? . Или создайте 32-битные исполняемые файлы, если вы следуете руководству или учебнику, написанному для этого.
В любом случае, в этом случае вы сможете использовать ebx
в качестве счетчика циклов, потому что системный вызов ABI использует разные аргументы для регистров.