Используем 32битные библиотеки в 64битных программах

    Можно ли использовать 32битные библиотеки в 64битных программах? Скорее нет. А если очень нужно? Тогда скорее да!

    Для одного проекта мне нужно работать с 32битными проприетарными библиотеками. Под виндой нет проблем — компилируем все в 32 бита и вперед. Но под линуксом дела обстоят хуже. Собирать все в 32 бита и цеплять в RPM 32битные версии свободных библиотек некрасиво, более того у меня есть API для плагинов. Совсем не хочется заставлять юзеров ставить ради несчастного плагина 32битный компилятор. Поэтому я решил сделать переходник из кота в мышь, который будет вызывать функции из 32битных библиотеке в отдельном процессе. Сначала я хотел сделать его на питоне, но скомпилировать питон в бинарник у меня не получилось. Потом добрые люди на IRC канале PyPy подсказали мне, что можно использовать libffi прямо из C! Дальше дело за малым.

    В итоге получилась программа runso32. Она работает в двух режимах. В первом режиме в аргументах командной строки задается библиотека, функция, строка описания параметров и собственно сами параметры. На данный момент поддерживаются параметры типа int, unsigned int, double, указатель и последовательность байтов (буфер). Программа вызывает функцию, печатает возвратное значение и завершается. Второй вариант — интерактивный. Программа читает команды из стандартного ввода или файла. Сначала регистрируются все интересные нам функции. Потом каждая запись, начинающаяся с «CALL», вызывает функцию с заданным номером. в интерактивном режиме перед тем как отправить буфер необходимо указать его длину. В интерактивном режиме все все величины отсылаются в бинарном формате Little Endian

    После того как мы определились какие функции нам нужны, можно сделать 64бит библиотеку. Вызов каждой функции будет отсылать в runso32 идентификатор нужной функции и параметры и получать из runso32 результат. Таким образом, мы как бы вызываем функции из 32бит библиотеки в 64бит приложении. Это происходит хоть и с задержкой, но практически незаметно для остальной программы.

    Вроде бы все готово, но программный интерфейс получился слишком громоздким. Тогда я написал 64битную библиотеку, которая регистрирует функции и позволяет их вызывать несколько проще. Теперь нам нужна только одна функция — versatile_call. Однако, прежде чем ее использовать, необходимо заполнить в глобальной структуре, имя библиотеки, имена функций и строки описания параметров. Эта структура должна быть заполнена при компиляции. Дальше необходимо всего лишь вызвать функцию versatile_call(n, ...), где n — номер нужной функции из 32бит и дальше список параметров, не забываем про длину буфера, а последний аргумент это указатель на переменную, в которой будет записано возвращаемое значение.

    Исходники и RPM лежат здесь: sourceforge.net/projects/runso32
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 17

      +2
      Извращение какое-то. Чем protobuf для RPC не устроил?
        –1
        Мне нужна была совместимость формата с командной строкой и минимальный объем метаданных. Получился еще один убогий формат RPC-запросов, зато без излишеств. В данном случае бутылочное горлышко — скорость протокола. В то же время у protobuf больше оверхед.
          0
          Издержки сериализации в protobuf велики в сравнении с оверхедом чтения/записи в stdin/stdout? Вы, верно, шутите? Если что и оптимизировать, так именно канал передачи данных, тот же /dev/shm для этой цели куда лучше подходит.
            +2
            Надо будет попробовать передавать через /dev/shm. ivlis советовал мне так поступить, но я наткнулся на обсуждение, где утверждали что общая память для 32-бит и 64-бит приложений содержит подводные камни. Я почитал еще статей, судя по всему там ошибались.
              0
              Так ведь stdin/stdout в нормальной работе этой программы будут перенаправлены, и будут представлять из себя анонимные пайпы. Какие там издержки то? Это ж просто передача буфера между процессами, с чего бы /dev/shm быстрее будет?
                0
                Пайп в общем случае медленнее чем преаллоцированная через /dev/shm область памяти.
                  0
                  И где именно он будет медленнее? Больше всего времени будет потрачено на read и write, в обоих случаях. А внутри пайпа просто небольшой буфер.

                  Я, кстати, проверил скорость работы пайпа простейшим способом.
                  $ dd if=/dev/zero bs=1M count=1024 | dd of=/dev/null
                  1024+0 records in
                  1024+0 records out
                  1073741824 bytes (1.1 GB) copied2097152+0 records in
                  2097152+0 records out
                  1073741824 bytes (1.1 GB) copied, 1.64809 s, 652 MB/s
                  , 1.64816 s, 651 MB/s

                  Медленно? Но это большими блоками. Если по 10 байт писать, то всё резко упирается в сами системные вызовы:
                  $ dd if=/dev/zero bs=10 count=102400 | dd of=/dev/null
                  102400+0 records in
                  102400+0 records out
                  390+63096 records in
                  2000+0 records out
                  1024000 bytes (1.0 MB) copied, 0.0913079 s, 11.2 MB/s
                  1024000 bytes (1.0 MB) copied, 0.0908237 s, 11.3 MB/s

                  Для /dev/shm:
                  $ dd if=/dev/zero bs=10 count=102400 of=/dev/shm/tmp
                  102400+0 records in
                  102400+0 records out
                  1024000 bytes (1.0 MB) copied, 0.0767044 s, 13.3 MB/s

                  Отличается даже не в разы. Но тут не совсем честно получается, в случае с пайпом 2 раза читается-пишется, а без пайпа только 1 раз.
                    +1
                    Есть маленький ньюанс. В shm никто не пишет через write, и никто не читает оттуда через read. Делают shm_open, а потом mmap и получают отображение одного участка физической памяти на адресное пространство двух процессов, через который потом и идёт обмен данными.
                      –1
                      там не получится прямо один и тот же участок памяти, потому что адресное пространство у 32бит и 64ббит несколько разное
                        +2
                        Не понимаю вас. Какая разница MMU, куда его маппить? Или вы имеете ввиду, что виртуальный адрес будет в каждом процессе свой? Так это нормальное поведение, никто не может гарантировать, что найдётся такой участок адресного пространства, который свободен во всех процессах.
                        +1
                        Вы тогда не путайте /dev/shm (который обычно является tmpfs) и шареную память. shm_open замечательнейше работает без наличия /dev/shm в системе.

                        В случае использования общей памяти придётся как-то реализовывать очередь команд и синхронизировать её, на это тоже требуется время. Если учесть обычный размер передаваемых аргументов (десятки байт) — именно синхронизация будет занимать всё время. С пайпом это делать намного проще.
                          0
                          Да, с пайпом проще. В случае с разделяемой памятью можно использовать семафор.
                  0
                  Я сделал через shm,mmap — результат оказался посредственным. Для проверки я взял гипотетическую функцию, которая заполняет буфер размером 1кбайт. Получилось, что shm дает выигрыш всего в 1,5 (полтора) раза. Это тормозят синхронизирующие семафоры.
              +2
              Вот еще бы rundll через wine :D
                0
                К сожалению rundll такое не умеет
                  0
                  Ну mingw, winelib, LoadLibrary и GetProcAddress в помощь.
                    0
                    Однако Rundll и Rundll32 позволяют вызывать только некоторые функции из некоторых библиотек DLL. Например, с помощью данных программ нельзя вызывать функции Win32 API (Application Programming Interface), экспортируемые системными библиотеками DLL. Эти программы позволяют вызывать функции только из тех библиотек DLL, при разработке которых была реализована подобная возможность.

                    support.microsoft.com/kb/164787

              Only users with full accounts can post comments. Log in, please.