Interrupt
In this post, we demonstrate how to use the GNU Build ID to uniquely identify a build. We explain what the GNU build ID is, how it is enabled, and how it is used in a firmware context.
Much has been written on how to craft a firmware version. From Embedded Artistry’s excellent blog post, to Wolfram Rösler’s how to.
Versions are a great way to identify a release: a set of interfaces and capabilities bundled together.
Versions do not — however — identify a specific binary all that well. For example, you could have multiple binaries for a given version in order to accommodate different variants of your hardware in the field.
For this, we need something else. This is where the GNU build ID comes in.
Why would we want to identify a specific binary? A few cases:
- To match a set of debug symbols to a given binary when trying to debug a device
- To verify that two binaries are in fact the same build
What is the GNU Build ID
Firmware engineers are not alone in wanting to uniquely identify a build for debugging purposes. In fact, Linux developers have long wanted to match a coredump to a specific build.
Roland McGrath of glibc fame came up with the GNU build ID 15 years ago, and contributed the implementation to various build tools.
The build ID is a 160-bit SHA1 string computed over the elf header bits and section contents in the file. It is bundled in the elf file as an entry in the notes section.
Each note section entry has the following layout:
In the case of the GNU build ID:
- name is «GNU\0» , which gives us namesz = 4
- desc is our 160-bit SHA1 value, which gives us descsz = 20
- type is 3
Adding the GNU build ID to your builds
Note: all of our example are based on the minimal program from our Zero to main() series.
In GCC, you can enable build IDs with the -Wl,—build-id which passes the —build-id flag to the linker. You can then read it back by dumping the notes section of the resulting elf file with readelf -n .
By default, this is not enabled:
But a small change to the CFLAGS is all it takes:
As you can see, our binary now contains the build id bab6b09f86b3c3017499d8e386447a610c559bd5 .
Bundling the GNU build ID in firmware bin files
Getting the build ID in your executables on Linux is easy: they are ELF files! Firmware on the other hand typically deals with binaries which are assembled by copying relevant sections of the elf at the right offset in a file.
This is typically accomplished with objcopy:
This takes every elf section earmarked to be loaded and places them at the correct offset in the bin file. In the process, most debug sections are stripped out.
Dumping the elf sections of the resulting minimal.elf gives us:
As you can see, the .text , .bss , .data , .stack sections each have the LOAD attribute, all others (including our .note.gnu.build-id ) do not and will be discarded.
To add a section to our binary, we must specify an address for it in our linker script. Assuming your linker script declares the following memory layout:
You can add the build ID to your flash memory with:
This instructs the linker to append the contents of .note.gnu.build-id to the rom region of memory and create a symbol ( g_note_build_id ) to point to it.
Let’s check our elf sections now:
As you can see, our build ID now has an address assigned to it and is marked with the LOAD attribute.
Note: Make sure to declare the .gnu_build_id section after the .text section, otherwise the build ID will be set to address 0x0 and the firmware will not boot.
Reading the build ID in firmware
Once the build ID is in our binary, we need to modify our firmware to read it and, at the very least, print it over serial at boot.
From the linker script, we know that we will find the build ID section at &g_note_build_id . From the spec, we know the structure of the section and can write down a typedef:
We can now simply index into the data field and print the build ID data.
Calling this code on boot, we get:
Update: A prior version of the above code was incorrect: g_note_build_id was declared as a pointer which would lead to random data being read in the best case, and a crash in the worst case. Thanks to Simon Doppler for reporting the problem.
References
François Baldassari has worked on the embedded software teams at Sun, Pebble, and Oculus. He is currently the CEO of Memfault.
Как мне узнать свой Build ID Android?
Как мне найти номер сборки на моем Android-устройстве?
Номер сборки на вашем телефоне Android находится в разных местах на каждом телефоне, но его достаточно легко найти.
- Google Pixel: Настройки> Система> О телефоне> Номер сборки.
- Samsung Galaxy S8 и новее: «Настройки»> «О телефоне»> «Информация о программном обеспечении»> «Номер сборки».
Какой номер сборки у моего телефона?
В меню «О телефоне / планшете» вы должны найти версию Android вашего устройства, указанную примерно на 7 или 8 записей. Прокрутите вниз до конца этого же меню, и номер сборки будет указан в качестве последней записи — довольно просто, не так ли?
Номер сборки совпадает с номером модели?
Нет, номер сборки и версия программного обеспечения одинаковы для всех телефонов этой модели, на которых установлен этот уровень обновления.
Что встроено в Android?
Система сборки Android компилирует ресурсы приложения и исходный код и упаковывает их в APK-файлы, которые вы можете тестировать, развертывать, подписывать и распространять. … Результат сборки будет одинаковым независимо от того, создаете ли вы проект из командной строки, на удаленном компьютере или с помощью Android Studio.
Какая польза от номера сборки на Android?
2 ответа. Первая буква — это кодовое название семейства релизов, например F — Froyo. Вторая буква — это код ветки, который позволяет Google определить точную ветвь кода, из которой была сделана сборка, а R по соглашению является основной ветвью выпуска. Следующая буква и две цифры — это код даты.
Как мне найти номер сборки Samsung?
На большинстве устройств Android меню параметров разработчика по умолчанию скрыто. Чтобы показать меню параметров разработчика: 1 Перейдите в «Настройки», затем нажмите «Об устройстве» или «О телефоне». 2 Прокрутите вниз и семь раз коснитесь «Номер сборки».
Как мне найти версию сборки моего Android?
Если вы используете плагин Gradle / Android Studio, начиная с версии 0.7. 0, код версии и название версии доступны статически в BuildConfig. Убедитесь, что вы импортируете пакет своего приложения, а не другой BuildConfig: import com.
Как включить на моем телефоне функции разработчика?
Как получить доступ к меню параметров разработчика. Чтобы включить параметры разработчика, откройте экран настроек, прокрутите вниз и нажмите «О телефоне» или «О планшете». Прокрутите вниз до нижней части экрана «О программе» и найдите номер сборки. Коснитесь поля номера сборки семь раз, чтобы включить параметры разработчика.
Как включить параметры разработчика, не вводя число?
На Android 4.0 и новее это в «Настройки»> «Параметры разработчика». Примечание. В Android 4.2 и новее параметры разработчика по умолчанию скрыты. Чтобы сделать его доступным, перейдите в «Настройки»> «О телефоне» и нажмите «Номер сборки» семь раз. Вернитесь к предыдущему экрану, чтобы найти параметры разработчика.
Где номер сборки?
Откройте «Настройки»> «Система»> «О телефоне». Нажмите Информация о программном обеспечении> Номер сборки. Коснитесь номера сборки семь раз. После первых нескольких нажатий вы должны увидеть, как идет обратный отсчет шагов, пока вы не разблокируете параметры разработчика.
Что такое версия и номер сборки?
Следующее число — это дополнительный номер версии. Он может представлять некоторые новые функции, исправления ошибок или небольшие изменения архитектуры. Компоненты одного продукта, которые различаются второстепенным номером версии, могут или не могут работать вместе, и, вероятно, не должны. Следующее обычно называется номером сборки.
Как вы пишете номера версий?
Номера версий обычно состоят из трех чисел, разделенных точками. Например: 1.2. 3 У этих номеров есть имена. Крайний левый номер (1) называется основной версией.
.
Чтение номеров версий
- Если основная версия выше, ваша версия новее. …
- Если младшая версия старше, ваша версия новее.
Что такое Dex в Android?
Файл Dex содержит код, который в конечном итоге выполняется средой выполнения Android. … Dex-файл, который ссылается на любые классы или методы, используемые в приложении. По сути, любое действие, объект или фрагмент, используемые в вашей кодовой базе, будут преобразованы в байты в файле Dex, который можно запустить как приложение для Android.
Как мне узнать марку моего устройства Android?
Получите имя текущего устройства: String deviceName = DeviceName. getDeviceName (); Приведенный выше код получит правильное имя для 600 лучших устройств Android.
Что такое активность в Android?
Действие представляет собой один экран с пользовательским интерфейсом, как окно или фрейм Java. Активность Android является подклассом класса ContextThemeWrapper. Если вы работали с языком программирования C, C ++ или Java, то вы должны были заметить, что ваша программа запускается с функции main ().
Основы Make
GNU Make 4.2.1 Built for x86_64-pc-linux-gnu Copyright (C) 1988-2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
Для чего используются Makefiles
Make-файлы используются, чтобы помочь решить, какие части большой программы должны быть перекомпилированы.
В подавляющем большинстве случаев компилируются файлы C или C++ .
Другие языки обычно имеют свои собственные инструменты, которые служат той же цели, что и Make.
Его можно использовать и за пределами программ, когда вам нужна серия инструкций для запуска в зависимости от того, какие файлы изменились.
В этой статье вы узнаете про использование компиляции C/C++.
Вот пример графика зависимостей, который вы можете построить с помощью Make.
Если какие-либо зависимости файла изменятся, то файл будет перекомпилирован:

Граф зависимостей
wikipedia.org
Формат
Makefile состоит из правил (rules). Первым указывается название цели (target), затем зависимости (prerequisites) и действие (recipe — набор действий/команд), которое нужно выполнить.
Зависимости нужны не всегда и указываются по необходимости. Для простоты на первом этапе можно думать о зависимостях как о файлах, которые нужно проверить: если ни один не изменился — заново компилировать не нужно.
Отступы по умолчанию нужно ставить табуляцией. Если хотите поменять на другой символ — задайте .RECIPEPREFIX
target: prerequisites recipe
На русский обычно переводят так
цель: зависимости команды
Типичное применение: какая-то зависимость изменилась → выполнятеся действие в результате которого создаётся таргет файл.
output: main.o message.o g++ main.o message.o -o output clean: rm *.o output
Как и в статье Configure, make, install в примере выше используются стандартные цели (target)
Про опции -o и -c читайте статью «Компиляция в C++
Если файл вам не нужен, например, вы просто хотите выполнить какие-то команды — можно использовать .PHONY
.PHONY
Рассмотрим следующий Makefile
.PHONY: site site: echo «HeiHei.ru»
Если теперь выполнить
Удалите site из первой строки, а всё остальное не трогайте
Вроде бы ничего не изменилось, но теперь создайте файл site рядом с Makefile
touch site
make site
make: 'site' is up to date.
Так как таргет теперь реальный — make не нашёл изменений и ничего не сделал. Из-за такого простого совпадения имени цели (target) и какого-то файла в директории может перестать работать скрипт.
Для защиты от таких неприятностей и применяют PHONY
Также PHONY удобен тем, что можно перечислить все цели в самом начале файла.
Если не злоупотреблять этой возможностью — можно улучшить читаемость кода, особенно в небольших файлах.
Посмотреть цели Make-файла
Если вы создали Make-файл с большим количеством PHONY целей и забыли название нужно — не обязательно продираться через весь файл
Чтобы получить списко всех целей воспользуйтесь grep и выполните
cat GNUmakefile | grep PHONY:
Пример из C++
Functions.cpp Functions.h Main.cpp
#include <iostream> #include «Functions.h» int main()
Functions.cpp
double add( double x, double y)
#pragma once double add(double x, double y);
Если один из этих файлов изменился — нужно перекомпилировать проект. Для начала будем пользоваться командой
g++ -o output Main.cpp Functions.cpp
Эта команда сначала вызывает компиляцию, затем линковку
Создайте Makefile и откройте его в текстовом редакторе. Например, в Vim
touch Makefile
vi Makefile
Makefile будет выглядеть следующим образом
output: Main.cpp Functions.cpp Functions.h g++ -o output Main.cpp Functions.cpp
Теперь для компиляции достаточно выполнить
В результате появится исполняемый файл output
В этот пример можно добавить ещё два шага: отдельно следить за компиляцией и убираться после работы.
Если вам не понятно что происходит в этом файле — изучите статью «Компиляция в C++
.PHONY: clean output: Main.o Functions.o g++ Main.o Functions.o -o output Main.o: Main.cpp g++ -c Main.cpp Functions.o: Functions.cpp g++ -c Functions.cpp clean: rm *.o output
To запустить скрипт, достаточно выполнить
g++ -c Main.cpp
g++ -c Functions.cpp
g++ -o output Main.o Functions.o
Если нужно скомпилировать Main execute
Появится файл Main.o но не появятся остальные (Functions.o, output)
Functions.cpp Functions.h Main.cpp Main.o Makefile
На примере команды make Main.o можно понять почему в Make-файлах используется термин цели (target)
make Main.o говорит — создай файл Main.o а инструкция в Makefile определяет правило по которому это нужно сделать.
Если теперь выполнить make Main.o не будет перекомпилироваться. Будут выполнены только последние два шага.
g++ -c Functions.cpp
g++ -o output Main.o Functions.o
Выполните make если ещё не выполняли и не делайте после этого clean
Добавим ещё одну функцию в наш проект. Нужно указать её в файлах Functions.*
Вызывать пока не будет, поэтому Main.cpp остаётся без изменений
Functions.cpp
bool test( bool x)
bool test(bool x);
g++ -c Functions.cpp g++ -o output Main.o Functions.o
Обратите внимание: Main.cpp не был перекомпилирован так как в нём нет изменений.
Таже посмотрите на время изменения файла output оно должно измениться.
Не вносите никаких изменений в файлы и execute
make: 'output' is up to date.
Перекомпиляция не нужна и поэтому не выполнена
Переменные
Подробнее про переменные в Makefile читайте в статье Работа с переменными в GNUmakefile
В этом примере вы можете увидеть как названия файлов сохранены в переменную для сокращения кода.
.PHONY: clean objects = Main.o Functions.o output: $( objects ) g++ -o output $( objects ) Main.o: Main.cpp g++ -c Main.cpp Functions.o: Functions.cpp g++ -c Functions.cpp clean: rm *.o output
Запустить Docker container из Makefile
Читайте также статью
.PHONY: docker docker: docker-compose -f docker/dev/docker-compose.yml build
Параметризация Make
?= позволяет переменным быть перезаписанными на существующие переменные окружения
:= перезаписывает значение переменной
PROJECT_NAME ?= myproject ORG_NAME ?= heihei REPO_NAME ?= myproject #Filenames DEV_COMPOSE_FILE := docker/dev/docker-compose.yml REL_COMPOSE_FILE := docker/release/docker-compose.yml .PHONY: test release test: docker-compose -f $(DEV_COMPOSE_FILE) build docker-compose -f $(DEV_COMPOSE_FILE) up agent docker-compose -f $(DEV_COMPOSE_FILE) up test release: docker-compose -f $(REL_COMPOSE_FILE) build docker-compose -f $(REL_COMPOSE_FILE) up agent docker-compose -f $(REL_COMPOSE_FILE) run —rm app manage.py collectstatic —noinput docker-compose -f $(REL_COMPOSE_FILE) run —rm app manage.py migrate —noinput docker-compose -f $(REL_COMPOSE_FILE) up test clean: docker-compose -f $(DEV_COMPOSE_FILE) kill docker-compose -f $(DEV_COMPOSE_FILE) rm -f docker-compose -f $(REL_COMPOSE_FILE) kill docker-compose -f $(DEV_COMPOSE_FILE) rm -f
BUILD_ID
To добавить переменным уникальности используют BUILD_ID
# Docker Compose Project Names REL_PROJECT := $(PROJECT_NAME)$(BUILD_ID) DEV_PROJECT := $(REL_PROJECT)dev
USER_ID
To получить ID пользователя запустившего GNUmakefile
USER_ID = $ ( shell id -u $
Какие альтернативы Make существуют
Популярными альтернативными системами сборки C/C++ являются SCons, CMake, Bazel и Ninja. Некоторые редакторы кода, такие как Microsoft Visual Studio , имеют свои собственные встроенные инструменты сборки.
Для Java есть Ant, Maven и Gradle.
Другие языки, такие как Go и Rust, имеют свои собственные инструменты сборки.
Интерпретируемые языки, такие как Python , Ruby и JavaScript , не требуют аналога для создания файлов.
Цель Makefile состоит в том, чтобы скомпилировать любые файлы, которые должны быть скомпилированы, основываясь на том, какие файлы изменились.
Но когда файлы на интерпретируемых языках меняются, ничего не нужно перекомпилировать.
При запуске программы используется самая последняя версия файла.

Что означает cc -c
cc это C compiler
Существует несколько общедоступных компиляторов C
В этой статье использовался gcc
-c это опция, которую разбирали здесь
whoami
В обычном Bash скрипте достаточно написать $(whoami) и это будет равносильно подстановке вывода whoami
В Make файле это может не получиться. Есть два варианта решить проблему
Игнорировать ошибки
Если какая-то команда выполнена с ошибкой выполнение сценария прерывается.
RPM_DIR =/home/$$(whoami)/rpms/ .PHONY: clean-repo clean-repo: @ sudo rm $(RPM_DIR)release/* @ sudo rm $(RPM_DIR)master/*
Если в …release/ пусто, то удалять в …master/ make уже не будет.
Вместо этого появится ошибка:
sudo rm /home/$(whoami)/rpms/release/* rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory make: *** [clean-repo] Error 1
Избежать этой проблемы можно поставив — перед командой
RPM_DIR =/home/$$(whoami)/rpms/ .PHONY: clean-repo clean-repo: @ -sudo rm $(RPM_DIR)release/* @ -sudo rm $(RPM_DIR)master/*
rm: cannot remove ‘/home/andrei/rpms/release/*’: No such file or directory
make: [clean-repo] Error 1 (ignored)
make жалуется, но переходит ко второй команде и чистит директорию.
Цель из других целей
Если нужно запустить несколько целей сразу, можно вызывать из новой цели
all-targets: target1 target2 target3

Несколько make-файлов в одной директории
Если в одной директории находится два и более make-файлов с совпадающими целями, вызывать из нужного файла помогает опция -f
make ├── GNUmakefile.beget └── GNUmakefile.heihei
# GNUmakefile.beget .PHONY: url url: echo «https://beget.com»
# GNUmakefile.heihei .PHONY: url url: echo «https://heihei.ru»
make -f GNUmakefile.beget url
make -f GNUmakefile.heihei url
Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых
Name already in use
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Read your own .note.gnu.build-id
The .note.gnu.build-id section in an ELF binary contains a «strongly unique embedded identifier».
binutils’ ld has supported the —build-id=. option since version 2.18 (released 2007). When used, with a sha1 or md5 argument it directs ld to insert an ELF section .note.gnu.build-id into the binary containing a hash of the normative parts of the output—that is, an identifier that uniquely identifies the output file.
Its originally intended purpose (described here) is to simplify and improve debugging tools, but it is occasionally useful for a program to be able to read its own build-id. Mesa uses the build-id of the running OpenGL or Vulkan driver as a way of identifying its on-disk cache of pre-compiled shader programs.
I spent a good amount of time researching possible ways of uniquely identifying the running OpenGL or Vulkan driver, and I saw that others had similar questions.
Mesa, the software project providing OpenGL and Vulkan on Linux, needs to identify its on-disk cache of compiled shader programs. Shader programs compiled by one version of Mesa may not work (or worse: cause GPU hangs) with another version, so how can we know whether the running version of Mesa generated those cached files?
I found the —build-id=. flag but struggled to find a way to access the identifier it generates from within a running process.
The dl_iterate_phdr function is the critical piece of the puzzle, allowing the application to inspect the shared objects it has loaded. A callback function searches the program headers of each object loaded and finds the appropriate .note.gnu.build-id section. Mesa’s build-id is included in the data that’s hashed to provide the key to look up a shader in the on-disk cache. This ensures that Mesa will only load shader programs that were produced by an identical build.
With the problem now solved and the code in successful use in Mesa since 2017, my hope is to make the technique more widely known and in doing so to save others time. The code is very small and MIT licensed, so feel free to include the two source files in your project.
Retrieve an opaque pointer to the .note.gnu.build-id ELF segment in the process’s address space using either the filename of a loaded ELF binary or a symbol address. From the returned pointer, access the build-id and its length in bytes.
The API consists of only four functions and an opaque struct data type.
Find the .note.gnu.build-id section given the filename of the ELF binary
Returns NULL on failure.
Find the .note.gnu.build-id section given a symbol in the ELF binary
Returns NULL on failure.
Return the length (in bytes) of the build-id
Return a pointer to the build-id
Some demonstrations of the API are provided:
-
— Retrieves its own build-id — Retrieves the build-id of a linked shared object — Retrieves the build-id of a dlopen ‘d shared object
Separately, the file command can retrieve the build-ids:
A simple Makefile builds the example programs with -Wl,—build-id=sha1 (and -fPIC ; see Caveats)
make check runs the example programs and verifies that they output the same build-id as reported by file .
Looking up a build-id given a symbol name (with build_id_find_nhdr_by_symbol ) requires a call to the dladdr function. Quoting from the dladdr(3) man page:
Sometimes, the function pointers you pass to dladdr() may surprise you. On some architectures (notably i386 and x86-64), dli_fname and dli_fbase may end up pointing back at the object from which you called dladdr(), even if the function used as an argument should come from a dynamically linked library.
The problem is that the function pointer will still be resolved at compile time, but merely point to the plt (Procedure Linkage Table) section of the original object (which dispatches the call after asking the dynamic linker to resolve the symbol). To work around this, you can try to compile the code to be position-independent: then, the compiler cannot prepare the pointer at compile time any more and gcc(1) will generate code that just loads the final symbol address from the got (Global Offset Table) at run time before passing it to dladdr().
As a result, build code with -fPIC to ensure that build_id_find_nhdr_by_symbol works as expected. In practice, I found that compiling the so-build-id program without -fPIC with clang caused the program to fail.
build-id dependent on compiler flags
Any change that affects the code or data of the ELF binary will also result in a different build-id. It’s obvious but important to note that debug and release builds will have different build-ids.
For Mesa’s usage this is entirely acceptable because we expect that the vast majority of users are using distribution-provided builds of Mesa.