Там все несколько иначе. Подлинковываются целиком входные секции (input sections). В одной секции может быть одна функция, а может быть и несколько. Библиотека в gcc - архив объектников. Если вся библиотека - один объектник с одной секцией в которой куча функций - они будут подлинкованы все при ссылке на любую из них.
Поэтому добиться подтягивания отдельной функции можно либо размещая в одном объектнике каждую функцию в отдельную секцию при помощи -ffunction-sections, либо размещая каждую функцию в отдельный объектник. При компиляции системных библиотек gcc применяется второй вариант для совместимости со старыми формататми (a.out), которые имеют только три секции - .text, .data, .bss
При этом используются два подхода:
В libgcc исходный текст всех функций находится в двух файлах исходника - ассемблерном и сишном, при этом каждая функция обрамлена в #ifdef func_name ... #endif. При сборке библиотеки этот файл компилируется много раз, каждый раз при компиляции определяется один символ func_name. Получается, что за каждый проход из этой пары файлов компилируется по одной функции. Результат каждой компиляции складывается в отдельный объектник, потом все эти объектники пакуются в либу libgcc.a
В libc каждая функция находится в отдельном файле, каждый файл компилируется в одноименный .o и потом все *.o пакуются в либу libc.a
Использовать -ffunction-sections при компиляции системных библиотек нельзя из-за совместимости со старыми форматами объектников.
По обработчикам: обработчик такая же функция и точно также может быть выкинута линкером. Именно это и происходило в avr-gcc 2006каком-то. Для обхода этого в скрипте линкера применяется директива KEEP(секция), которая показывает, что секцию выкидывать нельзя. Этой директивой в скрипте линкера охвачены вектора прерываний. Вектора остаются, вектора содержат ссылки на обработчики => обработчики тоже остаются. Если есть обработчик, но нет ссылающегося на него вектора - такой обработчик выкидывается.