Сборка AT & amp; T, ошибка seg в ret [duplicate]

Обертка вокруг GNU ld, которая не поддерживает скрипты компоновщика

. Некоторые .so-файлы на самом деле являются GNU ld ссылками-компоновщиками , например. Файл libtbb.so является текстовым файлом ASCII с этим содержимым:

INPUT (libtbb.so.2)

Некоторые более сложные сборки могут не поддерживать это. Например, если вы включаете -v в параметры компилятора, вы можете видеть, что mainwin gcc wrapper mwdip отбрасывает файлы команд сценария компоновщика в подробном списке результатов библиотек для ссылки. Простая работа вместо файла командной строки ввода сценария компоновщика вместо копии (или символической ссылки), например

cp libtbb.so.2 libtbb.so

Или вы можете заменить аргумент -l полным путем .so, например вместо -ltbb сделать /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2

1
задан nrz 14 December 2013 в 02:31
поделиться

2 ответа

Когда вы покидаете «ret», компьютер выполняет последний «move eax, ebx», а затем выполняет все, что будет дальше в памяти компьютера.

Я удивлен, что вы не получить незаконную инструкцию / доступ; это будет самый общий ответ. Как-то инструкция мусора действует как возврат, после разгрома регистров.

Его также немного непонятно, что вы подразумеваете под «возвращает 60». Вы имеете в виду значение командной строки? Понятно, что ваша программа не защищает от незаконных ловушек. Что делает Windows, когда вы получаете такую ​​ловушку без защиты, мне непонятно; Я знаю по опыту, когда я делаю, что Windows имеет тенденцию просто прекратить мой процесс, и я получаю некоторый случайный статус выхода. «0» может быть таким статусом.

Попробуйте добавить:

      mov   byte ptr[eax], 0

перед командой «ret»; это приведет к нелегальной памяти. Вы сообщаете, какой статус вы получаете. Это не удивило бы меня, если бы вы получили нулевой результат в этом случае.

5
ответ дан Ira Baxter 25 August 2018 в 06:05
поделиться

Поскольку он проваливается и запускает следующую функцию, компоновщик помещает после нее.

См. мои комментарии к ответу Ира, почему ваш код не просто сбой. Если вы не связывались с кодом запуска библиотеки времени выполнения C (т. Е. У вас только _start вместо main), выполнение ударит по нескольким некодерам, а также к ошибкам в незаконной инструкции или к попытке получить доступ к немаркированной памяти. См. Ниже.

Разберите свой последний бинарный файл, чтобы узнать, что произошло. Когда я попробовал это, я обнаружил, что компоновщик помещает main между стандартными запускаемыми функциями запуска C frame_dummy и __libc_csu_init. Это

00000000004004f6 <main>:
  4004f6:       b8 0a 00 00 00          mov    $0xa,%eax
  4004fb:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000400500 <__libc_csu_init>:
  400500:       41 57                   push   %r15
  400502:       41 56                   push   %r14
  400504:       41 89 ff                mov    %edi,%r15d
  400507:       41 55                   push   %r13
  ... a bunch more code that eventually returns.

Вы могли узнать, что происходит с отладчиком, инструкциями с одним шагом.


BTW, если вы сделали автономный двоичный файл, либо с gcc -static -nostartfiles или путем сборки (as foo.s) / linking (ld foo.o), вы получите файл размером 888 байт, содержащий одну команду, а остальные будут заголовками ELF и т. д.

$ cat > fallthrough.s <<EOF
        .globl main
main:
    .globl _start
_start:
    mov $10, %eax
    # fall through
EOF
$ gcc -g -static -nostartfiles fallthrough.s -o fallthrough
$ gdb fallthrough
(gdb) b _start   # breakpoint
(gdb) r          # run the prog
(gdb) disassemble /r _start, _start+40
Dump of assembler code from 0x4000d4 to 0x4000fc:
=> 0x00000000004000d4 <main+0>: b8 0a 00 00 00  mov    $0xa,%eax
   0x00000000004000d9:  00 00   add    %al,(%rax)
   0x00000000004000db:  00 00   add    %al,(%rax)
   0x00000000004000dd:  00 00   add    %al,(%rax)
   0x00000000004000df:  00 2c 00        add    %ch,(%rax,%rax,1)
   0x00000000004000e2:  00 00   add    %al,(%rax)
   0x00000000004000e4:  02 00   add    (%rax),%al
   0x00000000004000e6:  00 00   add    %al,(%rax)
   0x00000000004000e8:  00 00   add    %al,(%rax)
   0x00000000004000ea:  08 00   or     %al,(%rax)
   0x00000000004000ec:  00 00   add    %al,(%rax)
   0x00000000004000ee:  00 00   add    %al,(%rax)
   0x00000000004000f0:  d4      (bad)  
   0x00000000004000f1:  00 40 00        add    %al,0x0(%rax)
   ...
(gdb) layout asm  #text-window mode. layout reg is great for single-stepping, BTW.
(gdb) si   # step instruction
0x00000000004000d9 in ?? ()
(gdb) si
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000d9 in ?? ()
(gdb) c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

00 байты, которые следуют за вашим кодом в памяти, также находятся в исполняемом элементе ELF. сопоставление памяти с файлами происходит только с гранулярностью страницы, поэтому все они отображаются в виде исполняемых инструкций. (машинный код не копируется из кэша диска для исполняемых файлов, а память просто отображается с разрешением read + execute в процесс, который execve(2) является двоичным.)

$ objdump -s a.out
a.out:     file format elf64-x86-64

Contents of section .note.gnu.build-id:
 4000b0 04000000 14000000 03000000 474e5500  ............GNU.
 4000c0 db31c97d 55481b9a 57110753 1786dd1a  .1.}UH..W..S....
 4000d0 11679958                             .g.X
Contents of section .text:
 4000d4 b80a0000 00                          .....
Contents of section .debug_aranges:
 0000 2c000000 02000000 00000800 00000000  ,...............
 0010 d4004000 00000000 05000000 00000000  ..@.............
 0020 00000000 00000000 00000000 00000000  ................
 ...

$ size a.out
   text    data     bss     dec     hex filename
     41       0       0      41      29 a.out

Удаление двоичного кода все еще делает это segfault, но с другой инструкцией. Yay?

# b _start would be b *0x4000d4 without symbols.
(gdb) r
 ...
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000d9 in ?? ()
(gdb) disassemble /r $rip-5, $rip +15
Dump of assembler code from 0x4000d4 to 0x4000e8:
   0x00000000004000d4:  b8 0a 00 00 00  mov    $0xa,%eax
=> 0x00000000004000d9:  00 2e   add    %ch,(%rsi)
   0x00000000004000db:  73 68   jae    0x400145
   0x00000000004000dd:  73 74   jae    0x400153
   0x00000000004000df:  72 74   jb     0x400155
   0x00000000004000e1:  61      (bad)  
   0x00000000004000e2:  62      (bad)  
   0x00000000004000e3:  00 2e   add    %ch,(%rsi)
   0x00000000004000e5:  6e      outsb  %ds:(%rsi),(%dx)
   0x00000000004000e6:  6f      outsl  %ds:(%rsi),(%dx)
   0x00000000004000e7:  74 65   je     0x40014e

$ hexdump -C a.out
 ...
000000d0  11 67 99 58 b8 0a 00 00  00 00 2e 73 68 73 74 72  |.g.X.......shstr|
000000e0  74 61 62 00 2e 6e 6f 74  65 2e 67 6e 75 2e 62 75  |tab..note.gnu.bu|
000000f0  69 6c 64 2d 69 64 00 2e  74 65 78 74 00 00 00 00  |ild-id..text....|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Наша команда mov - это b8 0a 00 00 00 в первой строке, включенной в hexdump. Я думаю, что следующая 00 2e ... - это структура данных ELF, вероятно, индекс разделов или что-то еще. Как инструкция x86, это add %ch,(%rsi), который segfaults, потому что %rsi не указывает на записываемую память. (ABI говорит, что регистры, отличные от указателя стека, не определены в записи процесса, но Linux решает их обнулить в загрузчике ELF, чтобы избежать утечки данных ядра. %rsi не указывает на записываемую память, и процесс, вероятно, не есть.)


Итак, что, если вы добавили сюда возврат? Нет, нет ничего, чтобы вернуть в . Стек содержит указатели на переменные среды arg процесса. Вы должны сделать системный вызов exit.

.section .text
.globl _start
_start:
        xor %edi, %edi
        mov $231, %eax  #  exit(0)
        syscall

#       movl $1, %eax    # The 32bit ABI works even for processes in long mode, BTW.
#       int $0x80        # exit(edx)
2
ответ дан Peter Cordes 25 August 2018 в 06:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: