Пользователь
Information
- Rating
- 2,010-th
- Location
- Петропавловск, Северо-Казахстанская обл., Казахстан
- Registered
- Activity
Specialization
Software Developer, Embedded Software Engineer
Pure C
Assembler
X86 asm
Win32 API
Visual Basic
MySQL
Git
OOP
Electronics Development
Reverse development
На XP пробовали?
Неимоверно бесит слово «куар». Пожалуйста, не подливайте масла в огонь укоренения этого слова-уродца.
Есть русская традиция произношения латинских букв. В ней а, бэ, цэ и икс, игрек, зэт.
Есть калькированная из английского языка традиция называть буквы английского алфавита. В ней эй, би, си и экс, уай, зид.
НЕ НАДО ИХ СМЕШИВАТЬ!
Поэтому либо кью-ар, либо ку-эр.
Иначе получаем цэ два эйч пять оу аш, сидэ-диски (или цэди-диски?) и прочих уродцев.
Так же думали балбесы из команды разработки gmail, из-за чего при наборе писем не получается пользоваться раскладкой Бирмана.
«Идешь и покупаешь новый»
Думаю секрет разработки такой техники в доСАПРовское время — декомпозиция сложной задачи на простые. В итоге устройство разрабатывает на пара сумрачных гениев, а тысяча инженеров, из которых 95% могут быть вчерашними стулентами.
ИМХО, автор делает очень некрасиво, что спрятал имя автора материала, на Лбазе которого создан это хабрапост.
Вместо этого автор приводит в конце список аж на три источника: как будто бы автор изучил три разных источник, переработал, отобрал, систематизировал информацию, и сделал свой пост.
На самом же деле есть такой гиковский англоязычный канал — CuriousMarc. Авторы, стоящие за этим каналом, раздобыли этот агрегат (купили на каком-то аукционе, вроде бы). Аппарат был неисправен, его сперва починили, сняли обзор, а затем один из соавторов канала, Кен Шеррифф, он же «мастер Кен» — человек, про которого его напарнике отзываются как о человеке, способном разреверсинженирить всё, что угодно, разреверсил и описал принцип работы этого блока. И написал соответствующий пост.
Так вот, все три «источника» — это репост оригинального материала Кена.
По идее, этот хабрапост должен бы был быть оформлен как перевод со ссылкой на автора, хотя это и не дословный перевод. Но по сути, это пересказ работы Кена с его же фотографиями.
И ладно бы это был пост простого хабраюзера, но когда это делается от лица корпоративного блога, возникают нехорошие мысли о корыстных интересах и эксплуатации чужого труда.
По-моему, идиотизм это то, что птичка вообще взлетела. Наверное не было у меня более масштабного разочарования за последние 20 лет в человечестве, нежели тот факт, что человечество нашло идею твиттера и инстаграмма хоть сколько-нибудь стоящими и полезными.
Принципиально не пользуюсь всем тем, что вы тут как будто бы с восторгом перечисляете.
Я из тех людей, которых лаг в 100 мс уже дичайше раздражает.
>лет на 15 назад
>троллейбус из буханки
Где же ваша статья, где вы в последней версии Альтиума разрабатываете устройство на базе новейшей FPGA со встроенным DSP и нейропроцессором, в BGA-корпусн с полутора тысячами ножек, на 16-слойной плате?
Отлично, осталось только научиться быстро надевать перчатки, которые всей своей натурой сопротивляются быстрому надеванию.
Когда-то лет 11 назад довелось писать библиотеку-расширение для MySQL: когда я изучил интерфейс взаимодействия между расширениями и самой СУБД, я был подвален и разочарован — он создал ощущение на коленке придуманной внутренней архитектуры. Возможно это только интерфейс взаимодействия так одебилен в угоду плагинописателям, тогда это одно, но если и для своих внутренних нужд СУБД использует такие poor-designed структуры и способы представления даных, то это грустно.
По идее, blame (должно быть) можно настроить так, чтобы он не считал чисто-пробельные внутристрочные модификации за полноценные и при обнаружении таких продвигался глубже по графу коммитов.
Я пишу «должно быть», потому что на вкус и цвет все
фломастерыVCS разные, но вgit blame
это ключик-w
, например.Когда-нибудь автор переболеет своей категоричности и эволюционирует до m_bHasChildren или m_fInitialized.
Какого плана могла бы быть эта статья? Как статья-мнение, что в C++ хотят затащить дебильную фичу? Как статья, разбирающая внутренности объектных файлов и внутреннюю кухню процесса линковки?
Глядя на баланс лайков между моими комментариями, и комментариями оппонентов, можно представить себе мировоззрение апологетов modern-C++ — усомниться в правильности пути, выбранного комитетом, значит устроить себе кармическое самоубийство в рамках Хабра.
На протяжении всей статьи, начиная с самого заголовка, автор пытался изнасиловать нам мозг свои непониманием концептуальной разницы между дефисом и тире, и у него это получилось.
Господи, до чего убогий термин. Те, кто это придумал и укоренил, мне кажется, это люди того уровня, на котором нормально системный блок называть процессором.
Прочитайте вторую часть вот этого комментария:
https://habr.com/ru/company/yandex/blog/715358/comments/#comment_25266596
Какой смысл вы вкладываете в словосочетание «из объектного файла пропадает»? Лежит на диске объектный файл, там эта информация есть, и вдруг она внезапно пропадает оттуда — объектный файл по прежнему лежит там, где лежал, но информации о размере там уже нет. Вряд ли вы что-то подобное имеете в виду.
Тогда давайте разберёмся, что?
Она вообще не попадает в объектный файл?
Или в объектных файлах вообще нет места подобной информации, потому что формат объектных файлов не предусматривает хранение такой информации?
Или она там есть, но её никто не принимает во внимание?
Вообще, какие отношения вас связывают с форматами объектных файлах ELF и COFF? Знаете эти форматы «на зубок», или просто имеете примерное представление, что объектные файлы хранят и зачем это нужно? Надеюсь, что вы всё-таки достаточно хорошо представляете, что там.
Так вот, если говорить об объектных файлах формата ELF, сам по себе формат ELF предусматривает хранение размера ELF-символа. В структуре Elf32_Sym/Elf64_Sym есть поле st_size именно для этого.
То есть как минимум, теоретически возможность сохранять информацию о размере в ELF-формате объектных файлов предусмотрена. И она не только существует, а ещё и используется на практике: компиляторы в это поле записывают актуальный размер сущности, к которой относится символ в таблице символов.
Например GCC:
Предпоследняя колонка — это именно оно. Размер.
Так что, как видите, GCC размер массива в объектный файл кладёт.
Остаётся только взять его оттуда при линковке, но для это требуется, чтобы на уровне формата ELF поддерживался тип релоков, который бы позволил при резолвинге подставлять не адрес сущности, а размер сущности. Предусмотрен ли в формате ELF такой тип релоков? Хороший вопрос. Типы релоков специфичны для каждой аппаратной платформы. Так вот, для x86, x86_64 aka amd64 и других популярных аппаратных архитектур такой тип релоков есть.
Например, для x86 это R_386_SIZE32.
Так что на уровне линковки линкер вполне способен разрулить ситуацию, когда в первом объектном файле есть ссылка на размер массива, который живёт в другом объектном файле, а во-втором объектном файле есть этот самый массив. Линкер возмёт размер сущности из таблицы символов второго файла и подставить это значение (точнее скорректирует на величину этого значения) в заданное место в куске машинного кода или данных из первого объектного файла.
Остаётся только чтобы компилятор мог породить на выходе объектный файл, ссылающийся на размеру «внешних» сущностей.
И вот только в этом месте начинаются проблемы.
Это не объектные файлы сами по себе не способны хранить размер массивов.
Это не линкер сам по себе не способен подставлять размер массива там, где попросят (а не адрес массива, как просят обычно)
Это лишь на уровне компилятора C/C++ сделано так, что компилятор by-design отказывается компилировать sizeof, применённый
extern int foo[]
, выбрасывая ошибку, вместо того, чтобы сгенерировать объектный файл с соответствующей ссылкой на размер сущности, которую подставил бы линкер, ориентируясь на информацию из другого объектного файла.Но это ELF. А что с COFF?
В COFF в таблице символов у символа нет поля, аналогичного st_size из ELF. Но есть кое-что другое.
Во-первых, у COFF-символов есть тип символа и класс символа, а значит и два соответствующих поля, а кроме того, у символов в таблице символов есть обязательная часть постоянного формата и размера и опциональная auxilliary-часть, формат и размер которой зависит от типа/класса COFF-символа. То есть записи символов с COFF не имеют постоянной длины (но размер записей так или иначе кратен некоей гранулярности, равной размеру обязательной части символа — это к слову). Так вот, форматом COFF предусмотрен такой тип как DT_ARY, который предполагает наличие вслед за обязательной частью aux-записи, в которой хранится размер массива.
Вот первый механизм, посредством которого в объектных файлах COFF можно было бы хранить информацию о размере массива. Но этот существующие компиляторы, например компилятор Microsoft, этот путь не использует. Типы символов как таковые не используются. Потому что нет таковой надобности. Используя sizeof на сущности из других объектных файлов мы ссылаться не можем, а раз так, то зачем сохранять в COFF-OBJ-файлы размеры массивов? И вот они и не сохраняются. К тому же нет соответствующих типов релоков, которые позволили бы на этот размер массива сослаться из другого места — а это уже серьёзно.
Но во-вторых, есть принципиально иной механизм. Причём концепция, позволяющая этому механизму быть, присутствует как в ELF, так и в COFF.
По сути, и в ELF, и в COFF объектный файл представляет собой три кита: заголовок файла, таблицу секций (с атрибутами каждой секции и указанием, где в объектном фале лежат данные для этой секции) и таблицу символов (с атрибутами символов и ссылкой на секцию, которой принадлежит этот символ).
Выделенное жирным — ключевое. В ELF-символах это поле st_shndx, а в COFF-символах это поле SectionNumber (по номенклатуре Microsoft) или **n_scnum`` (в рамках оригинальной номенклатуры, берущей начало, видимо, из System V).
В норме это поле хранит номер секции (из данного объектного файла), в которой обитает сущность, описываемая данным символом (разумеется, если символ описывает сущность из данного объектного файла, а не из какого-то внешнего, на которую в данном фале всего лишь делается ссылка).
И в COFF, и в ELF предусмотрено несколько специальных значений этого поля, и одно из них: это ABS. Если в норме поле «значение» хранит смещение сущности относительно начала секции, индекс которой приведён в поле «индекс секции», то ABS это означает, что символ не «живёт» в какой-то секции, а поле «значение» означает не смещение в секции, а абсолютное значение, то есть, грубо говоря, числовую константу. В COFF за это отвечает специальная константа
IMAGE_SYM_ABSOLUTE
, в ELF — константаN_ABS
.(и там, и там она равна минус единице).Это именно то, что позволяет сохранить в COFF размеры сущностей, в частности, массивов. И вот тут как раз соответствующий тип релоков, чтобы просто взять и подставить в нужное место абсолютное значение абсолютного COFF-символа в COFF предусмотрен для всех аппаратных архитектур. Например, для x86 это IMAGE_REL_I386_ABSOLUTE.
И тут остаётся только один момент: как положить в объектный файл COFF одновременно информацию и о смещении массива в рамках секции и о размере массива, если оба таких символа будут иметь одинаковое имя, а имя — это единственный способ ссылаться на сущность из других объектных файлов?
А очень просто: как компилятор С++ помещает в объектный файл (будь то COFF или ELF) информацию символы для vftable, vbtable класса, или же символы, соответствующие разным вариантам перегруженной функции или перегруженного оператора? Он декорирует имена. Причём каждый компилятор использует свою схему декорирования. Даже компилятор C (не C++) от MS использует нечто вроде декларирования имён, когда раздаёт имена COFF-символам. Так вот, для хранения информации о массива foo, если бы стандарт предусматривал применение sizeof к extern-массивам, размер которых не очевиден из объявления, то достаточно было бы придумать схему декорирования для присвоения имени COFF-символу, несущему абсолютное значение размера.
Так что после всего это хочу сказать:
Объектные файлы прекрасно могут хранить размеры массивов — хранение такой информации прямо заложено в ELF и прекрасно доступно в COFF. Способ ссылаться из одного объектного файла на размер сущности из другого тоже предусмотрен напрямую в ELF, и как частный случай ссылок на абсолютные константные значения в COFF. Касательно COFF я также дополнительно проверил: «а поддерживает ли линкер этот механизм ссылки на ABS-символы, который на практике вроде бы как не используется?» — прекрасно поддерживате, как оказалось. Не верите: сформируйте два COFF .obj-файла вручную (в хекс-редакторе, например) и попробуйте слинковать их MS-овским линкером. Все прекрасно линкуется.
Информация из объектных файлах не пропадает: она там есть, если её туда положил компилятор, и она оттуда берётся и используется (линкером), если линкер попросят её взять и использовать (а линкер об этом просит компилятор).
Так что дело исключительно в компиляторе.
И вот, вместо того, чтобы закрыть пробел вокруг уже давно существующей части языка — вокруг применение sizeof к extern-массивам неуказанного в декларации размера (при том, что нижележащий инструментарий в виде объектных файлах и линкера со своей стороны поддерживает такую возможность), что делает комитет и горе-предлагальщики?
Вместо логичного решения чуть-чуть довести до ума существующую конструкцию языка они привносят в язык ИДИОТСКИЙ КОСТЫЛЬ в виде новой директивы препроцессора #embed.
А главное (ВЫ ТОЛЬКО ВДУМАЙТЕСЬ!), что #embed ВООБЩЕ НИКАК не решает обозначенную вами проблему. Вот вы говорите, что убрать большой прединициализированный массив в отдельный объектный файл это так себе решение, потому что хоть обращаться к нему можно из каждого файла-исходника, размер (с помощью sizeof) получить не удастся ни откуда.
А как чёртова #embed решает эту проблему? Абсолютно никак. Если вы объявите огромный массив табличных значений в foo.cpp, загнав в него полгига табличных значений с помощью #embed, то из bar.cpp, baaz.cpp, test.cpp, core.cpp, main.cpp вы по прежнему не можете получить её размер (с помощью sizeof, без промежуточной функции из того же объектного файла, что и сам массив)!
Хотя примерно даже понятно, как из этого будут выкручиваться CPP-программисты. Поскольку с введением COMDAT в объектные файлы C++-программистам разрешили объявлять одно и то же в разных компилируемых файлах (лишь бы на этапе линковки оно оказалось одинаковым во всех файлах), C++-программисты будут помещать объявление полгигабайтного массива не в .h-файл, который будут подключать везде, где только можно.
В итоге, при компиляции полгигабайтный массив будет попадать в каждый объектный файл, а потом уже линкеру придётся отбросить дубликаты. Гениальное стратегическое решение, ничего не скажешь...
Она ОБЯЗАНА знать, для того, чтобы в массиве оказались правильные значения, если только вы не согласны с мыслью, что костыль #embed настолько костыль, что годится только для инициализации байтовых массивов, но не массивов int16_t, int32_t, int64_t.
Комментатор выше написал, что основная ценность этого подхода с #embed по сравнению с прилинковыванием заранее заготовленной и упакованной в .obj/.o-файл таблицей это возможность применить оптимизацию на основе compile-time расчётов.
Допустим, у нас есть какой-то алгоритм, который полагается на таблицу простых чисел, и чтобы не вычислять последовательность простых чисел, оно вшита в программу:
В таком виде у вас в коде есть одно место, которое является авторитетным источником сведений о простых числах.
Программисту не нужно заботиться о том, как число 9973 представлено в памяти. Компилятор берёт эту заботу на себя.
Если этот код компилируют под little-endian-архитектуру, оно будет представлено как F5 B6. Если под big-endian-архитектуру — то как B6 F5.
И вот программист проникся идеей использовать #embed, ему осточертело держать таблицу простых чисел в коде, потому что ему нужны простые числа не до 10 000, а до 10 миллонов (или миллиардов). И он, потирая радостно ладошки, пишет утилиту, которая посчитает и запишет миллионы простых чисел в бинарный файл, а потом делает #embed этого бинарного файла в свой исходный код.
Вопрос номер один: он сходу идёт нафиг, потому что #embed предназначена только для инициализации байтовых массивов?
Вопрос номер два: если не только байтовых, то ему нужно один бинарный файл на случай компиляции под LE-архитектуры, и другой на случай компиляции под BE-архитектуры, переключаться между которыми нужно при помощи #if...#else...#endif?
Вопрос номер три: если эта идиотская директива #embed будет иметь дополнительные спецификаторы, которые уточнят препроцессору, что нужно поменять порядок байтов, то в каком именно порядке байтов таблица волшебных значений (простых чисел — в нашем случае) должна храниться на диске, в репозитори? Почему это должен быть именно little-endian или big-endian?
Вопрос номер три (б): а если речь идёт о хранении в таблице отрицательных значений, принимая во внимание, что в разных аппаратных архитектурах помимо разного порядка байтов (big endian vs. little endian) может практиковаться разная схема представления отрицательных чисел (дополнительный код vs. прямой код), то открывается ещё одна неопределённость? Теперь если я хочу задать большой массив 16-битных чисел с помощью бинарного файла, у меня есть целых 4 гипотетических способа того, как в этом бинарном файле может представлено число –2: как FE FF, как FF FE, как 80 02, как 02 80.
Вопрос номер четыре: а как быть, если мы пишем переносимый код, который могут компилировать под разные архитектуры, причём такие, что в одной целевой архитектуре тип int будет иметь размер 4 байта, а в другой — 2 байта (допустим, это какая-то 16-битная аппаратная архитектура). Как должен выглядеть бинарный файл, которым с помощью элемента #embed инициализируется массив int-ов?
Вопрос номер 5:
А что, если я хочу с помощью бинарного файла инициализировать массив структур, например массив 3D-векторов struct{float x; float y; float z;}? Допустим, моей программе нужно иметь огромный массив, содержащий координаты всех известных науке звёзд, и эта информация у меня есть в виде бинарного файла? А если поля в структуре гетерогенны? А если они содержат вложенные структуры? А если разные архитектуры предполагают разную ширину полей и разное выравнивание? Какую ширину должны иметь поля в бинарном файле и какие должны выдерживаться выравнивани, чтобы в массиве оказались верные значение, а не какой-то съехавший мусор? А если в массиве будет указатель?
Ну да, да, верю. Я уже выше привёл доводы, но ещё раз, с другой стороны: я пишу прошивку для стиральной машинки, и объявляю массив sndDone, в котором содержатся PCM-кодированный семпл с фразой «Стирка закончена». Каждый элемент массива — это отсчёт, который будет выставлен на ЦАП.
Раньше у меня этот звук был закодирован текстовым инициализатором ( `= {-3000, -122, 0, 7956, 29744, ... }), а теперь я решил делать #embed на базе WAV-файла, от которого отказывается WAV-заголовок. Почему-то теперь, чтобы код не превратился в тыкву, функция, проигрывающая PCM-кодированный семпл, которая раньше была написана просто с расчётом на то, что она просто принимает массив 16-битных чисел, и никаких нюансов с порядком байтов и быть не может, теперь должна заботиться о том, а совпадает ли используемый ею (и архитектурой в целом) порядок байтов с порядком байтов, который использовался в бинарном файле, который использовался при компиляции.
Короче, это такой жуткий костыль, что мне кажется тут обсужать нечего.
То что ваш комментарий обильно пролайкали, да и в целом, то что большинство народа поддерживает то, что на протяжении последних лет делают с С++, вызывает у меня только ужас и желание охарактеризовать всё это одной фразой:
Вот такой фразой
Каким вообще боком тут ABI? И вообще,я не понимаю каким образом эта цитата относится к описанному. Причём здесь строковые литералы? Когда я писал «выплюнет текстовое представление», я не имел в виду строковый литерал, я имел в виду то, что называется aggregate initializer list.
Я только что скомпилировал исходник, где объявлен и инициализирован элементами глобальный массив размером 20 Мб! 20 мегабайт, Карл! Это очень много для какой-то таблицы значений, которая должна быть известна на этапе компиляции чтобы что-то там значительно оптимизировать.
Больше не получается, потому что я использую компилятор MSVC 1998-го года выпуска, и он генерирует ошибку, что внутренняя куча, которая используется для внутренних нужд компилятора, достигла своего лимита. Потому что создателям компилятора в 1998-ом году не пришло бы и в голову, что каким-то идиотам в 2023 понадобится инициализировать массив размером в несколько десятков Мб на уровне исходного кода сишной программы.
Но давайте разберёмся: если бы #embed был 25 лет назад, #embed позволил бы внедрить в компилируемый модуль массив размером в полгига, если оставить в силе то же ограничение на размер внутренней служебной кучи компилятора? Значит проблема-то не в отсутствии директивы #embed, а в лимите размера кучи?
Значит можно решить проблему. просто соответствующим образом увеличив лимит на размер кучи без добавления #embed в язык?
Причём тут тогда ABI? По сути эти люди тащат в язык новую директиву препроцессора, потому что понимают, что Microsoft бросится имплементировать новый стандарт и им придётся так или иначе убрать этот лимит на размер кучи или применить принципиально иную архитектуру для хранения этих данных? А может просто попросить их подвинуть лимит кучи?
В каком смысле «платформа»? Как такового механизма прилинковывания произвольных файлов и не должно существовать. Линкер должен давать возможность скормить ему на вход произвольное число произвольных объектных файлов — не важно какими путями на свет порожденных. И линкеры базово эту возможность имеют. Остается только найти или написать тривиальный инструмент, который генерирует объектный файл на базе произвольного файла.
Ну, с compile-time вычислениями хоть какой-то смысл прослеживается. Но опять же, правильный подход — это написать утилиту, которая получив на вход бинарный файл с данными на выходе выплюнет текстовое представление этих данных, которое потом будет просто #includeиться. И никакое #embed не нужно будет, потому что только подобная кастомная утилита знает, как интерпретировать входной бинарник. Может там массив байтов? А может массив слов? А может массив массив 64-битных целых чисел? А может массив 64-битных чисел с плавающей точкой? А какой endianness? А может там массив структур?