LINUX.ORG.RU

Запретить неразрешённые «слабые» символы

 , ,


0

4

Как запретить линкеру создавать бинарники с «weak» символами, неразрезолвленными ни одной из зависимостей?

Upd: судя по отписавшимся, да и по ощущениям, я плаваю в теме и не понял, что произошло. Поэтому опишу ситуацию.

  • Плюсовый проект
  • Собираем исполняемый файл
  • В двух разных единицах трансляции, есть два класса с одинаковым именем, реализующих один интерфейс (базовый класс с vtable)
    • Оба класса так же имеют статические методы и статические данные
    • Оба обьектника линкуются в результирующий бинарь

Upd2: Однако, написал тестец. Скачать можно тут(и да, я не виртуал xaizek, просто он заботливо сохранил код, который профукал предыдущий файлообменник).

Код для воспроизведения проблемы:

tar xf test.tgz && cd test/; g++ -o test *.cpp \
&& nm --demangle test | grep Realization:: && ./test

Тобишь, код одной из реализаций в бинарь не попадает, что в принципе и логично, но почему линкер молча всё переписал, вместо того, чтобы крепко ругнуться, для меня загадка.

★★★★★

Последнее исправление: pon4ik (всего исправлений: 4)

Нафига в таком случае вообще нужны слабые символы? Не проще ли тогда лишь объявлять прототипы функций, не объявляя слабых реализаций?

KivApple ★★★★★
()
Ответ на: комментарий от ilammy

Вот я сам не понял как так вышло, если удастся выкроить минутку, попробую сварить минимальный компилябельный пример.

Но суть в том, что есть два класса со статическими методами и одним именем.

Так вот линкер, для одно из них залил код в бинарник и разрезолвил символы, а для второго, почему-то оставил символы с пометкой «W», как так вышло, и почему он сделал это молча, для меня загадка.

Виндовый линкер кстати в этой ситуации поступил примерно так же, если верить коллеге и отладчику.

А хочется, что бы на такие символы - была ругань.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Скорее всего я просто плаваю в вопросе, см комментарий выше.

Нужно понять что произошло, и как от этого отгородиться инструментальными средствами.

pon4ik ★★★★★
() автор топика

Upd: судя по отписавшимся, да и по ощущениям, я плаваю в теме и не понял, что произошло. Поэтому опишу ситуацию.

  • Плюсовый проект
  • Собираем исполняемый файл
  • В двух разных единицах трансляции, есть два класса с одинаковым именем, реализующих один интерфейс (базовый класс с vtable)
    • Оба класса так же имеют статические методы и статические данные
    • Оба обьектника линкуются в результирующий бинарь
pon4ik ★★★★★
() автор топика

Upd2: Однако, написал тестец. Скачать можно тут(где кстати нынче можно пошарить файл без каптчи и wgetable и без регистрации и sms ?).

Код для воспроизведения проблемы:

tar xf test.tgz && cd test/; g++ -o test *.cpp \ 
&& nm --demangle test | grep Realization:: && ./test

Тобишь, код одной из реализаций в бинарь не попадает, что в принципе и логично, но почему линкер молча всё переписал, вместо того, чтобы крепко ругнуться, для меня загадка.

pon4ik ★★★★★
() автор топика
Последнее исправление: pon4ik (всего исправлений: 2)
Ответ на: комментарий от pon4ik

но почему линкер молча всё переписал

Это же, вроде, стандартное поведения для weak-символов в отсутствие сильного символа, т.е. берётся первый из них (в порядке объектных файлов при линковке).

А вообще получился хороший пример на тему «Зачем нужны анонимные пространства имён» :-)

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Это да, как решить то понятно. А вот как дать по рукам неопытным товарищам или пропалить в легаси - непонятно.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Потомучто так работает линковщик (и он обязан так работать). Например вы используете std::cout в one.cpp и в two.cpp, это приводит к тому что при компиляции вы получите бинарный код реализующий std::cout и в one.obj и в two.obj далее линковщик при линковке уберет лишние копии std::count и оставит только одну. Чтобы было более понятно поправим ваш пример:

Добавим новый файл impl.hpp:

struct Realization : Interface {
    void run() {
        r1();
    }

    static void r1() {
        std::cout << R_ID << std::endl;
    }
};

Поправим one.cpp:

#include "one.h"
#include "interface.h"
#include <iostream>

#define R_ID "r1"

#include "impl.hpp"

void OneRunner::run() {
    Realization r;
    r.run();
}

two.cpp:

#include "two.h"
#include "interface.h"
#include <iostream>

#define R_ID "r2"

#include "impl.hpp"

void TwoRunner::run() {
    Realization r;
    r.run();
}

zaz ★★★★
()
Последнее исправление: zaz (всего исправлений: 1)
Ответ на: комментарий от zaz

Что происходит, мне в принципе понятно. Обходиться оно проще - обьявляем оба класса в анонимных неймспейсах и не знаем проблем.

Непонятно, почему сия перезапись происходит молча и как заставить линкер петь. Я точно видел ругань на такую ситуацию, но не могу вспомнить где.

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Я всеже думаю вам не совсем понятно что происходит :)

Попробуйте скомпилировать one.cpp и two.cpp (g++ -c one.cpp two.cpp) и посмотреть список всех weak символов в one.o и в two.o (objdump) - вы увидете что помимо «Realization» там еще куча дубликатов (а именно весь набор для Interface) И заставить «ругатся» на дублирующие weak символы не логично, ведь вы будете совсем не в восторге что линковщик вам откажится линковать one.o и two.o по причине того что и там и там присутствует VTABLE, RTTI, конструктор и деструктор для Interface.

Скорее всего вы видели «ругань» на strong символы. Для того чтобы Realization стала «strong» ее следует переписать вот так:

struct Realization : Interface {
    void run();
    static void r1();
};

void Realization::run() {
   r1();
}

void Realization::r1() {
   std::cout << R_ID << std::endl;
}

zaz ★★★★
()
Ответ на: комментарий от zaz

Не. Я желаю, что бы он ругался, если второй раз символ видит тот же символ обьектнике. В данном случае, хватило бы деструктора.

Либо я действительно не понял, что происходит.

Хорошо, допустим, реализация стала strong, где моя ругань ?:)

И где бы сжато и понятно почитать про сии материи?

pon4ik ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Не. Я желаю, что бы он ругался, если второй раз символ видит тот же символ обьектнике. В данном случае, хватило бы деструктора.

Тогда вы не сможете использовать STL RTTI шаблоны и виртуальные методы

Либо я действительно не понял, что происходит.

именно

Хорошо, допустим, реализация стала strong, где моя ругань ?:)

$ g++ -o test *.cpp
/tmp/ccWHvCmu.o: In function `Realization::run()':
two.cpp:(.text+0x0): multiple definition of `Realization::run()'
/tmp/ccLkz9HX.o:one.cpp:(.text+0x0): first defined here
/tmp/ccWHvCmu.o: In function `Realization::r1()':
two.cpp:(.text+0x14): multiple definition of `Realization::r1()'
/tmp/ccLkz9HX.o:one.cpp:(.text+0x14): first defined here
collect2: error: ld returned 1 exit status
zaz ★★★★
()
Ответ на: комментарий от zaz

Ага я не внимательно прочитал сниппет, и сделал реализацию только статической функции аутлайн.

Непонятно, почему не ругнулось в моей недопопытке, хм. Ну и детально понимать, что и как тут работает, тоже хотелось бы.

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

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

pon4ik ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.