Если у вас есть статическая библиотека (файл .lib), которая представляет собой просто набор из одного или нескольких объектных файлов (.obj), компоновщик просто добавляет этот код к вашему в одном исполняемом файле. Вы можете сказать компоновщику сделать это с помощью переключателя командной строки, параметра конфигурации IDE или, возможно, даже #pragma (особенности зависят от вашей среды и компилятора).
Когда вы ссылаетесь в DLL, вам нужно дать компоновщику код, который будет вызываться при вызове одной из функций DLL. Обычно это делается с помощью файла с тем же именем, что и у .dll, за исключением того, что он является .lib. Код в этом .lib связан с вашей программой так же, как описано выше, но когда вы вызываете его, он загружает DLL (если она еще не загружена) и затем вызывает соответствующую функцию.
Существуют и другие способы обработки ссылок DLL (например, файлы .def или операторы #using в .NET), но, похоже, это то, о чем вы говорите.
Отвечая на ваше уточнение вопроса:
Проблема в том, что .lib не является конечным продуктом. Это просто совокупность объектного кода, который будет использоваться позже, когда компоновщик соединяет все ваши вызовы функций с адресами функций.
DLL, с другой стороны, является конечным продуктом, и поэтому компоновщик требует, чтобы все функции и переменные были связаны с реальными адресами.
Я говорю немного неточно, но вы поняли.