От сложного к простому:
1. Программирование в машинных кодах. "Что такое имя переменной?". А нету его, как вы правильно заметили. На уровне машинного кода есть только циферки. Переменные могут храниться в регистрах процессора или в оперативной памяти по какому-то адресу. И программист должен везде указывать код регистра или адрес памяти к которому обращается программа. Впрочем, программирование в машинных кодах осталось далеко в прошлом (с появлением языков ассемблера, это конец 40-х).
2. Язык ассемблера. Тут можно задать псевдонимы для адресов памяти. Но это именно псевдонимы. Так как на ассемблере программист полностью контролирует память программы - он сам решает какой блок памяти как называть. В процессе сборки ассемблер заменит псевдонимы на реальные адреса памяти.
3. Языки высокого уровня (такие как C/C++, Паскаль, FORTRAN, LISP и т.п.). Компилятор сам планирует использование памяти и назначает переменным какие-то адреса (может разместить переменную в регистре, может в стеке, может даже в динамической памяти). На этапе компиляции в машинный код помещаются только адреса переменных.
Собственно на этих исторических этапах имя переменной нигде не хранится. И по коду программы можно понять что в каком-то адресе памяти размещена некая переменная, но узнать её имя (каким оно было в исходном коде) нельзя никаким образом. Впрочем, есть одно исключение - таблица отладочных символов. Это именно то соответствие "имя-адрес", чтобы программисту было проще отлаживать программу. Используя эту таблицу отладчик может "подписать" адреса в памяти, облегчая программисту понимание что происходит внутри программы. Но это касается именно отладки, для работы программы эта таблица не нужна и конечному пользователю, как правило, не предоставляется.
4. Байт-код. Это касается языков с виртуальными машинами, таких как Java, C# и подобных. Компилятор преобразует текст программы в промежуточный байт-код. И в этом байт-коде могут сохраняться имена переменных (в частности в Java сохраняются имена классов, полей классов, методов классов). Могут - потому что могут и не сохраняться, допустим не сохраняются имена аргументов функций и локальных переменных (в Java, про C# утверждать не буду), в байт-коде на них ссылаются по порядковому номеру. Нужны они в байт-коде для работы механизма рефлексии, то есть когда программа обращается "сама к себе", спрашивая: "Какие свойства есть у этого объекта?" (имеет смысл, например, если объект из какого-нибудь плагина, то есть код на момент своей компиляции об этом объекте и понятия не имел). Но когда виртуальная машина исполняет байт-код, то она преобразует его в машинный код в котором, опять же, каждой переменной сопоставлен какой-то регистр процессора или адрес памяти.
Таким образом, если мы говорим о компилируемых в машинный код языках ответом будет "нигде" или "в таблице отладочных символов". Если мы говорим о компилируемых в байт-код языках, то ответом будет "в байт-коде (в метаданных), но это не точно"