Поведение GCC для неразрешенных слабых функций

Рассмотрим простую программу ниже:

__attribute__((weak)) void weakf(void);

int main(int argc, char *argv[])
{
        weakf();
}

При компиляции с помощью gcc и запуске его на ПК с Linux это происходит с ошибками. При запуске его на ARM CM0 (arm-none-eabi-gcc) компоновщик заменяет неопределенный символ переходом к следующей инструкции и nop.

Где это поведение задокументировано? Есть ли возможные способы изменить его через параметры командной строки? Я ознакомился с документами GCC и LD, информации об этом нет.

Однако, если я проверю документацию по компилятору ARM, это будет ясно объяснено.

1 ответ

man nm

Я читал некоторые документы и случайно наткнулся на соответствующую цитату для этого:

man nm

говорит:

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

"W"
"w" Символ - это слабый символ, который не был специально помечен как символ слабого объекта. Когда слабый определенный символ связан с нормальным определенным символом, нормальный определенный символ используется без ошибок. Когда слабый неопределенный символ связан, а символ не определен, значение символа определяется системным образом без ошибок. В некоторых системах верхний регистр указывает, что задано значение по умолчанию.

nm является частью Binutils, который GCC использует под капотом, так что это должно быть достаточно каноническим.

Затем пример в вашем исходном файле:

main.c

__attribute__((weak)) void weakf(void);

int main(int argc, char *argv[])
{
        weakf();
}

мы делаем:

gcc -O0 -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
nm main.out

который содержит:

w weakf

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

v будет установлен на 0, но это используется для неопределенных переменных (которые являются объектами): Как заставить слабые ссылки работать с GCC?

Затем:

gdb -batch -ex 'disassemble/rs main' main.out

дает:

Dump of assembler code for function main:
main.c:
4       {
   0x0000000000001135 <+0>:     55      push   %rbp
   0x0000000000001136 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x0000000000001139 <+4>:     48 83 ec 10     sub    $0x10,%rsp
   0x000000000000113d <+8>:     89 7d fc        mov    %edi,-0x4(%rbp)
   0x0000000000001140 <+11>:    48 89 75 f0     mov    %rsi,-0x10(%rbp)

5               weakf();
   0x0000000000001144 <+15>:    e8 e7 fe ff ff  callq  0x1030 <weakf@plt>
   0x0000000000001149 <+20>:    b8 00 00 00 00  mov    $0x0,%eax

6       }
   0x000000000000114e <+25>:    c9      leaveq 
   0x000000000000114f <+26>:    c3      retq   
End of assembler dump.

что означает, что это решается в PLT.

Затем, так как я не полностью понимаю PLT, я экспериментально проверяю, что он разрешается по адресу 0 и segfaults:

gdb -nh -ex run -ex bt main.out

Я полагаю, что то же самое происходит с ARM, он должен просто установить его на 0.

На ARM с gcc этот код у меня не работает (тест на armv7 с gcc Debian 4.6.3-14+rpi1). Похоже, что набор инструментов arm-компилятора имеет другое поведение.

Я не нашел полезной документации для этого поведения. Кажется, что слабый равен NULL, если он не определен во время соединения.

Поэтому я рекомендую вам проверить это:

if (weakf == NULL) printf ("weakf not found\n");
else weakf();
Другие вопросы по тегам