Думаю, остальная часть должна быть двухслойной. По-крайней мере, фреймворки Java устроены именно так, и я не вижу причин, по которым .NET может отличаться от данной модели
В такой схеме существует нативный код (скорее всего, на C++, хотя совершенно не обязательно), выполняющий основные действия, и .NET-обёртка для него. Все действия над конкретной системой выносятся в нативный код. Управление временем жизни объектов тоже сюда относится: нижележащая виртуальная машина должна иметь доступ ко всем объектам, чтобы вести подсчёт ссылок и осуществлять сборку мусора.
Соответственно, байткод, получаемый при компиляции, не включает нативного кода - в него входит только набор команд виртуальной машины: операторы и вызовы методов. При исполнении его на конкретной реализации фреймворка происходят обращение к соответствующей прослойке, под которой лежит код для виртуальной машины.
Кстати, для Linux есть (несовершенная) сторонняя реализация .NET под названием Mono.