⌨ Labor omnia vincit ☮

Unable to find dynamic linker breakpoint function

Posted in debugging by anaumov on 28.03.2015

В каком случае мы получаем ошибку bash: ./file_name: No such file or directory?
До всерашнего дня я был уверен, что только в том случае, если нет такого файла или каталога, либо что-то не так с inode. Вчера получилось воспроизвести ситуацию, при которой при существующем файле и без каких-либо проблем с inode, bash все равно выбрасывал эту ошибку, если я пытался запустить существующий ELF-файл.

Давайте создадим какой-нибудь простенький бинарник. Для примера возьмем самую простую программу на С:

#include <stdio.h>
int main()
{
        printf("Hello world\n");
        return 0;
}

Скомпилируем её и получим обычный ELF-файл:

> gcc main.c -o main

>./main
Hello world

> ls -la main
-rwxr-xr-x 1 alex users 12544 Mar 28 12:27 main

> ldd main
        linux-vdso.so.1 (0x00007fff4b3fe000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f003c6be000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f003ca81000)

> file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 3.0.0,
BuildID[sha1]=f3d869a53c8264f07b5696a12261845c221db420, not stripped

В сознании любого Linux-пользователя жевет убеждение в том, что о любых неполадках в core-системы его всегда оповестят, т.е. он получит как минимум корректное сообщение об ошибке. Я говорю сейчас о таких компонентак как, к примеру, shell-интерпретатор, т.е. bash. Если я попытаюсь запустить нерабочую программу, то программа завершится с ошибкой, и прежде чем bash вернет управление, я буду об этой ошибке проинформирован. Так или нет?

Вчера я игрался с shared library своей программы. Мне нужно было запустить уже собранный ELF-файл на другой системе с другой версией glibc. Естественно просто так это работать не будет, нужны соответствующие манипуляции с файлом (программа была написанна и скомпилированна на Lisp, т.е. я не мог управлять процессом сборки). В ходе работы я заметил интересную реакцию системы на эти изменения.

PATCHELF(1) позволяет производить некоторые изменения в ELF-файлах. Например, мы можем поменять адрес dynamic linking loader. Его задача: во время запуска программы (run-time) слинковать её с динамическими библиотеками (см. вывод ldd выше).

> patchelf --print-interpreter main
/lib64/ld-linux-x86-64.so.2

> ll /lib64/ld-linux-x86-64.so.2
lrwxrwxrwx 1 root root 10 Nov 18 21:24 /lib64/ld-linux-x86-64.so.2 -> ld-2.20.so

ld-2.20.so и есть наш run-time liner. Его версия совпадает с версией glibc, установленной в системе (он из пакета glibc). Я не буду сейчас углубляться в принцип его работы, давайте просто посмотрим, что произойдет, если из получившегося elf-файла удалить информацию о нахождении ld-2.20.so или просто поломать её.

> ln -s /lib64/ld-2.20.so link

> patchelf --set-interpreter link main

> patchelf --print-interpreter main
link

>./main
Hello world

> ldd ./main
        linux-vdso.so.1 (0x00007fffec1fe000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f84011c7000)
        link => /lib64/ld-linux-x86-64.so.2 (0x00007f840158a000)

Все как и прежде, просто для нахождения dynamic loader мы теперь заглядываем в link.

Теперь удалим link. Что после этого произойдет с main?

> rm -f link

> ./main
bash: ./main: No such file or directory

> ll main
-rwxr-xr-x 1 alex users 12544 Mar 28 12:27 main

> file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 3.0.0,
BuildID[sha1]=f3d869a53c8264f07b5696a12261845c221db420, not stripped

Она как бы есть… и её как бы нет🙂
Интересно реагирует strace на действительно несуществующий файл foobar, и на файл, который существут, но при этом… его как бы нет:

> ll foobar
ls: cannot access foobar: No such file or directory

> strace ./foobar
strace: Can't stat './foobar': No such file or directory

> ll main
-rwxr-xr-x 1 alex users 12992 Mar 28 12:51 main

> strace ./main
execve("./main", ["./main"], [/* 97 vars */]) = -1 ENOENT (No such file or directory)
write(2, "strace: exec: No such file or di"..., 40strace: exec: No such file or directory
) = 40
exit_group(1)                           = ?
+++ exited with 1 +++

Я считаю, что при поломке файла (умышленном или нет), bash или strace (они запускают новый процесс при помощи exec-семейство функций) должны сообщать о действительной причине невозможности запуска нового процесса. Если ссылка на dynamic linking в порядке, но в системе установленна другая версия glibc, то мы получим ошибку например
version `GLIBC_2.14′ not found.
В случае же с невозможности вообще найти ссылку на dynamic linking мы могли бы получить что-то типа Unable to find dynamic linker breakpoint function вместо No such file or directory, для файла, который существует.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: