Значение раздела .data ассемблера GNU повреждено после системного вызова - PullRequest
0 голосов
/ 29 мая 2018

У меня следующий код

.data
result: .byte 1
.lcomm input 1
.lcomm cha 2

.text
(some other code, syscalls)

Сначала все в порядке.Когда вызывается системный вызов (например, read), значение в метке 'result' меняется на какое-то случайное значение корзины.

Кто-нибудь знает, что не так?

PS Environment Debian x86_64 latest Работает в virtualboxИспользование в качестве -g ld emacs делает последние

----- edit -----

(continue)
.global _start
_start:
mov $3,%rax
mov $0,%rbx
mov $input,%rcx
mov $1,%rdx
int $0x80
(sys_exit)

Значение 'input' было изменено правильно, но значение 'result''также изменилось на случайное значение после

int $0x80 

1 Ответ

0 голосов
/ 29 мая 2018

Вы смотрите на 4 байта, начиная с result, который включает input в качестве 2-го или 3-го байта.(Вот почему значение увеличивается кратно 256 или 65536, оставляя младший байт = 1, если вы print (char)result).Это было бы более очевидно, если вы используете p /x для печати в шестнадцатеричном формате.

Поведение GDB по умолчанию для print result, когда не было отладочной информации, предполагало int.Теперь, из-за таких ошибок пользователя, как gdb 8.1 в Arch Linux, print result сообщает 'result' has unknown type; cast it to its declared type

GAS + ld неожиданно (во всяком случае, мне) объединить сегменты BSS и данных в одинстраница, поэтому ваши переменные являются смежными, даже если вы помещаете их в разные разделы, которые, как вы ожидаете, будут обрабатываться по-разному.(BSS поддерживается анонимными обнуленными страницами, данные - частным отображением файла для чтения и записи).

После сборки с gcc -nostdlib -no-pie test.S я получаю:

(gdb) p &result
$1 = (<data variable, no debug info> *) 0x600126
(gdb) p &input
$2 = (<data variable, no debug info> *) 0x600128 <input>

В отличие отиспользуя .section .bss и резервируя место вручную, .lcomm свободен для заполнения, если захочет.Предположительно для выравнивания, может быть, здесь, так что BSS начинается на 8-байтовой границе.Когда я строил с clang, я получил input в байте после result (по разным адресам).


Я исследовал, добавив большой массив с .lcomm arr, 888332.Как только я понял, что в исполняемом файле не хранятся буквальные нули для BSS, я использовал readelf -a a.out для дальнейшей проверки:

(по теме: В чем разница между разделом и сегментом в формате файла ELF )

...
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000126 0x0000000000000126  R E    0x200000
  LOAD           0x0000000000000126 0x0000000000600126 0x0000000000600126
                 0x0000000000000001 0x00000000000d8e1a  RW     0x200000
  NOTE           0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
                 0x0000000000000024 0x0000000000000024  R      0x4

 Section to Segment mapping:
  Segment Sections...
   00     .note.gnu.build-id .text 
   01     .data .bss 
   02     .note.gnu.build-id 

...

Так что да, секции .data и .bss оказались в одном и том же сегменте ELF.

Я думаю, что здесь происходит то, что метаданные ELF говорятдля отображения MemSize 0xd8e1a байтов (обнуленных страниц), начиная с virt addr 0x600126ЗАГРУЗИТЬ 1 байт из смещения 0x126 в файле на виртуальный адрес 0x600126.

Таким образом, вместо просто mmap, загрузчик программ ELF должен копировать данные из файла встраница с нулевым счетом, поддерживающая разделы BSS и .data.

Вероятно, это больший раздел .data, который потребуется компоновщику для принятия решения об использовании отдельных сегментов.

...