
{"id":2235,"date":"2026-09-03T09:27:52","date_gmt":"2026-09-03T08:27:52","guid":{"rendered":"https:\/\/www.kanda.com\/blog\/?p=2235"},"modified":"2026-09-03T09:27:54","modified_gmt":"2026-09-03T08:27:54","slug":"elf-files-explained-part-3","status":"publish","type":"post","link":"https:\/\/www.kanda.com\/blog\/microcontrollers\/elf-files-explained-part-3\/","title":{"rendered":"ELF Files Explained Part 3"},"content":{"rendered":"\n<style data-wp-block-html=\"css\">\n\/* =========================================================\n   ELF Files Explained\n   Plain technical article styling for WordPress\n   ========================================================= *\/\n\n\/* ---------- Base article ---------- *\/\n\n#headerContainer {\n    max-width: 900px;\n    margin: 0 auto;\n    padding: 0 20px 60px;\n    color: #222;\n    background: #fff;\n    font-family: Arial, Helvetica, sans-serif;\n    font-size: 17px;\n    line-height: 1.7;\n}\n\n\/* ---------- Headings ---------- *\/\n\n#headerContainer h1,\n#headerContainer h2,\n#headerContainer h3 {\n    color: #222;\n    font-family: Arial, Helvetica, sans-serif;\n    font-weight: 600;\n    line-height: 1.3;\n}\n\n#headerContainer h1 {\n    margin: 24px 0 28px;\n    font-size: 2.1rem;\n}\n\n#headerContainer h2 {\n    margin: 48px 0 18px;\n    padding-bottom: 8px;\n    border-bottom: 1px solid #ddd;\n    font-size: 1.65rem;\n}\n\n#headerContainer h3 {\n    margin: 34px 0 12px;\n    font-size: 1.25rem;\n}\n\n#headerContainer p {\n    margin: 0 0 18px;\n}\n\n#headerContainer strong {\n    font-weight: 700;\n}\n\n#headerContainer em {\n    color: #444;\n}\n\n\/* ---------- Horizontal rules ---------- *\/\n\n#headerContainer hr {\n    margin: 42px 0;\n    border: 0;\n    border-top: 1px solid #ddd;\n}\n\n\/* ---------- Hero image and figures ---------- *\/\n\n#headerContainer > p > img {\n    display: block;\n    max-width: 100%;\n    height: auto;\n    margin: 0 auto;\n}\n\n#headerContainer figure {\n    margin: 30px 0;\n}\n\n#headerContainer figure img {\n    display: block;\n    max-width: 100%;\n    height: auto;\n    margin: 0 auto;\n}\n\n#headerContainer p:has(> img) {\n    margin: 30px 0 8px;\n}\n\n#headerContainer p:has(> img) + p {\n    margin-bottom: 4px;\n    font-size: 0.95rem;\n    line-height: 1.5;\n}\n\n#headerContainer p:has(> img) + p + p {\n    margin-bottom: 30px;\n    color: #555;\n    font-size: 0.95rem;\n    line-height: 1.55;\n}\n\n\/* Make the opening\/hero image slightly more prominent *\/\n#headerContainer > p:first-child {\n    margin-bottom: 28px;\n}\n\n\/* ---------- Intro \/ block quotations ---------- *\/\n\n#headerContainer blockquote {\n    margin: 28px 0;\n    padding: 20px 24px;\n    border-left: 4px solid #888;\n    background: #f5f5f5;\n    color: #333;\n    font-size: 0.95rem;\n}\n\n#headerContainer blockquote p:last-child {\n    margin-bottom: 0;\n}\n\n\/* ---------- Notes ---------- *\/\n\n\/*\n   The current HTML uses <blockquote> for the build-pipeline note.\n   This selector identifies the note from its \"Note:\" label without\n   requiring a change to the HTML.\n*\/\n#headerContainer blockquote:has(p > strong:first-child) {\n    border-left-color: #666;\n    background: #f7f7f7;\n}\n\n#headerContainer blockquote p:first-child {\n    margin-bottom: 12px;\n}\n\n#headerContainer blockquote p:first-child strong:first-child {\n    font-weight: 700;\n}\n\n\/* ---------- Lists ---------- *\/\n\n#headerContainer ul,\n#headerContainer ol {\n    margin: 0 0 22px;\n    padding-left: 28px;\n}\n\n#headerContainer li {\n    margin: 5px 0;\n    padding-left: 4px;\n}\n\n\/* ---------- Inline code ---------- *\/\n\n#headerContainer code {\n    padding: 0.12em 0.35em;\n    border: 1px solid #ddd;\n    border-radius: 3px;\n    background: #f6f6f6;\n    color: #222;\n    font-family: \"Courier New\", Courier, monospace;\n    font-size: 0.9em;\n}\n\n\/* ---------- Tables ---------- *\/\n\n#headerContainer table {\n    width: 100%;\n    margin: 28px 0 34px;\n    border-collapse: collapse;\n    font-size: 0.95rem;\n    line-height: 1.5;\n}\n\n#headerContainer th,\n#headerContainer td {\n    padding: 11px 13px;\n    border: 1px solid #d5d5d5;\n    text-align: left;\n    vertical-align: top;\n}\n\n#headerContainer th {\n    background: #f1f1f1;\n    color: #222;\n    font-weight: 600;\n}\n\n#headerContainer tbody tr:nth-child(even) {\n    background: #fafafa;\n}\n\n\/* ---------- Links ---------- *\/\n\n#headerContainer a {\n    color: #333;\n    text-decoration: underline;\n    text-underline-offset: 2px;\n}\n\n#headerContainer a:hover {\n    color: #000;\n}\n\n\/* ---------- Responsive behaviour ---------- *\/\n\n@media (max-width: 700px) {\n    #headerContainer {\n        padding: 0 15px 45px;\n        font-size: 16px;\n        line-height: 1.65;\n    }\n\n    #headerContainer h1 {\n        font-size: 1.8rem;\n    }\n\n    #headerContainer h2 {\n        margin-top: 38px;\n        font-size: 1.45rem;\n    }\n\n    #headerContainer h3 {\n        font-size: 1.15rem;\n    }\n\n    #headerContainer blockquote {\n        margin: 24px 0;\n        padding: 16px 18px;\n    }\n\n    #headerContainer table {\n        display: block;\n        overflow-x: auto;\n        white-space: nowrap;\n    }\n}\n\n<\/style>\n\n<div id=\"headerContainer\">\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF03_Hero.jpg\" alt=\"ELF Files Explained - Part 3 Hero\"><\/p>\n    <h1 id=\"elf-files-explained-part-3-avr-elf-production-files-the-complete-firmware-image\">ELF Files Explained \u2013 Part 3: AVR ELF Production Files \u2013 The Complete Firmware Image<\/h1>\n    <hr>\n    <blockquote>\n    <p><strong>Part 3 of the &quot;ELF Files Explained&quot; Series<\/strong><\/p>\n    <p>In Part 1, we explored the history of executable file formats and saw how ELF became the industry standard for modern operating systems and GCC-based development toolchains. In Part 2, we looked inside the format itself, examining the headers, sections, symbol tables and debugging information that let compilers, linkers and debuggers work together using a common file format.<\/p>\n    <p>Understanding the internal structure of an ELF file is valuable, but it naturally leads to another question: what does all of this mean for an embedded firmware project?<\/p>\n    <p>In this article, we turn to AVR development and examine the AVR ELF Production File generated by Atmel Studio and the AVR-GCC toolchain. We will see how it becomes the definitive output of the build process, bringing together executable code, EEPROM data, fuse settings, lock bits, User Signature memory and debugging information into a single engineering artefact that represents the complete firmware project.<\/p>\n    <\/blockquote>\n    <hr>\n<\/div>\n\n\n\n<!--more-->\n\n\n\n<style data-wp-block-html=\"css\">\n\/* =========================================================\n   ELF Files Explained\n   Plain technical article styling for WordPress\n   ========================================================= *\/\n\n\/* ---------- Base article ---------- *\/\n\n#blogContainer {\n    max-width: 900px;\n    margin: 0 auto;\n    padding: 0 20px 60px;\n    color: #222;\n    background: #fff;\n    font-family: Arial, Helvetica, sans-serif;\n    font-size: 17px;\n    line-height: 1.7;\n}\n\n\/* ---------- Headings ---------- *\/\n\n#blogContainer h1,\n#blogContainer h2,\n#blogContainer h3 {\n    color: #222;\n    font-family: Arial, Helvetica, sans-serif;\n    font-weight: 600;\n    line-height: 1.3;\n}\n\n#blogContainer h1 {\n    margin: 24px 0 28px;\n    font-size: 2.1rem;\n}\n\n#blogContainer h2 {\n    margin: 48px 0 18px;\n    padding-bottom: 8px;\n    border-bottom: 1px solid #ddd;\n    font-size: 1.65rem;\n}\n\n#blogContainer h3 {\n    margin: 34px 0 12px;\n    font-size: 1.25rem;\n}\n\n#blogContainer p {\n    margin: 0 0 18px;\n}\n\n#blogContainer strong {\n    font-weight: 700;\n}\n\n#blogContainer em {\n    color: #444;\n}\n\n\/* ---------- Horizontal rules ---------- *\/\n\n#blogContainer hr {\n    margin: 42px 0;\n    border: 0;\n    border-top: 1px solid #ddd;\n}\n\n\/* ---------- Hero image and figures ---------- *\/\n\n#blogContainer > p > img {\n    display: block;\n    max-width: 100%;\n    height: auto;\n    margin: 0 auto;\n}\n\n#blogContainer figure {\n    margin: 30px 0;\n}\n\n#blogContainer figure img {\n    display: block;\n    max-width: 100%;\n    height: auto;\n    margin: 0 auto;\n}\n\n#blogContainer p:has(> img) {\n    margin: 30px 0 8px;\n}\n\n#blogContainer p:has(> img) + p {\n    margin-bottom: 4px;\n    font-size: 0.95rem;\n    line-height: 1.5;\n}\n\n#blogContainer p:has(> img) + p + p {\n    margin-bottom: 30px;\n    color: #555;\n    font-size: 0.95rem;\n    line-height: 1.55;\n}\n\n\/* Make the opening\/hero image slightly more prominent *\/\n#blogContainer > p:first-child {\n    margin-bottom: 28px;\n}\n\n\/* ---------- Intro \/ block quotations ---------- *\/\n\n#blogContainer blockquote {\n    margin: 28px 0;\n    padding: 20px 24px;\n    border-left: 4px solid #888;\n    background: #f5f5f5;\n    color: #333;\n    font-size: 0.95rem;\n}\n\n#blogContainer blockquote p:last-child {\n    margin-bottom: 0;\n}\n\n\/* ---------- Notes ---------- *\/\n\n\/*\n   The current HTML uses <blockquote> for the build-pipeline note.\n   This selector identifies the note from its \"Note:\" label without\n   requiring a change to the HTML.\n*\/\n#blogContainer blockquote:has(p > strong:first-child) {\n    border-left-color: #666;\n    background: #f7f7f7;\n}\n\n#blogContainer blockquote p:first-child {\n    margin-bottom: 12px;\n}\n\n#blogContainer blockquote p:first-child strong:first-child {\n    font-weight: 700;\n}\n\n\/* ---------- Lists ---------- *\/\n\n#blogContainer ul,\n#blogContainer ol {\n    margin: 0 0 22px;\n    padding-left: 28px;\n}\n\n#blogContainer li {\n    margin: 5px 0;\n    padding-left: 4px;\n}\n\n\/* ---------- Inline code ---------- *\/\n\n#blogContainer code {\n    padding: 0.12em 0.35em;\n    border: 1px solid #ddd;\n    border-radius: 3px;\n    background: #f6f6f6;\n    color: #222;\n    font-family: \"Courier New\", Courier, monospace;\n    font-size: 0.9em;\n}\n\n\/* ---------- Tables ---------- *\/\n\n#blogContainer table {\n    width: 100%;\n    margin: 28px 0 34px;\n    border-collapse: collapse;\n    font-size: 0.95rem;\n    line-height: 1.5;\n}\n\n#blogContainer th,\n#blogContainer td {\n    padding: 11px 13px;\n    border: 1px solid #d5d5d5;\n    text-align: left;\n    vertical-align: top;\n}\n\n#blogContainer th {\n    background: #f1f1f1;\n    color: #222;\n    font-weight: 600;\n}\n\n#blogContainer tbody tr:nth-child(even) {\n    background: #fafafa;\n}\n\n\/* ---------- Links ---------- *\/\n\n#blogContainer a {\n    color: #333;\n    text-decoration: underline;\n    text-underline-offset: 2px;\n}\n\n#blogContainer a:hover {\n    color: #000;\n}\n\n\/* ---------- Responsive behaviour ---------- *\/\n\n@media (max-width: 700px) {\n    #blogContainer {\n        padding: 0 15px 45px;\n        font-size: 16px;\n        line-height: 1.65;\n    }\n\n    #blogContainer h1 {\n        font-size: 1.8rem;\n    }\n\n    #blogContainer h2 {\n        margin-top: 38px;\n        font-size: 1.45rem;\n    }\n\n    #blogContainer h3 {\n        font-size: 1.15rem;\n    }\n\n    #blogContainer blockquote {\n        margin: 24px 0;\n        padding: 16px 18px;\n    }\n\n    #blogContainer table {\n        display: block;\n        overflow-x: auto;\n        white-space: nowrap;\n    }\n}\n\n<\/style>\n\n<div id=\"blogContainer\">\n    <h2 id=\"from-source-code-to-elf\">From Source Code to ELF<\/h2>\n    <p>Every AVR application begins life as source code \u2014 whether that is a few hundred lines written for a hobby project or hundreds of source files forming a complex commercial product. The build process follows the same fundamental sequence regardless of scale: source files are compiled, object files are produced, those object files are linked together, and a complete firmware image is created. Although this process is largely hidden behind the <em>Build<\/em> button in modern development environments, each stage contributes something essential to the finished application.<\/p>\n    <p>Compilation is the first step. Each C or C++ source file is processed independently by the compiler, translating the high-level language into AVR machine instructions. The output is an object file, conventionally identified by the <code>.o<\/code> extension. These object files already contain executable code, but they do not yet represent a complete application: functions defined in one source file may still reference functions in another, global variables have not yet been assigned their final addresses, and no overall memory layout has been established. At this stage the project exists only as a collection of individual modules.<\/p>\n    <p>Transforming those modules into a complete firmware image is the job of the linker. It is sometimes described simply as &quot;the program that joins object files together&quot;, but its role is considerably more sophisticated than that. The linker resolves references between modules, incorporates the startup code and runtime libraries the application requires, allocates memory for code and data, positions interrupt vectors, and determines where every function and variable will reside within the finished firmware image. By the time this process completes, the project has ceased to be a collection of independently compiled source files and has become a single, coherent application ready for further processing.<\/p>\n    <p>The output of this stage is the project&#39;s <strong>ELF Production File<\/strong>.<\/p>\n    <p>This point is often misunderstood. The linker does not generate an Intel HEX file as its primary output \u2014 it produces a complete ELF file describing the firmware in its entirety. If additional programming files are required by the development environment or programming software, they are derived <em>from<\/em> this production file. The ELF therefore occupies a unique position in the development workflow: it is the first file that represents the finished firmware as a complete engineering project, rather than a collection of individual software modules.<\/p>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF03_Fig01.jpg\" alt=\"Figure 1. The AVR Build Pipeline\"><\/p>\n    <p><strong>Figure 1. The AVR Build Pipeline.<\/strong><\/p>\n    <p><em>The AVR development process from source files through AVR-GCC compilation and linking to the ELF Production File. The ELF represents the completed firmware project before any programming-file conversion takes place.<\/em><\/p>\n    <p>Seen in this way, the build process is less about producing a programming file and more about constructing an increasingly complete description of the application. Each stage adds information unavailable at the previous stage until, after linking, the project has become a fully defined firmware image \u2014 captured within the ELF Production File, which records not only the executable code but also the information needed by development and analysis tools to understand precisely how the application has been constructed.<\/p>\n    <hr>\n    <h2 id=\"more-than-an-executable\">More Than an Executable<\/h2>\n    <p>Developers whose experience lies primarily in desktop software often think of an executable file as something that exists solely to be loaded into memory and run by an operating system. Embedded systems have rather different requirements. An AVR microcontroller has no operating system to load applications, resolve shared libraries or manage virtual memory. Instead, the firmware image must describe exactly what should be programmed into the device and how that device should be configured before the application can execute correctly.<\/p>\n    <p>This is where the flexibility of the ELF format becomes particularly valuable. Rather than storing nothing more than executable instructions, an AVR ELF Production File provides a structured framework capable of describing every significant aspect of the firmware project. Some parts of the file describe the application&#39;s executable code; others record initialised data, symbolic information and debugging metadata; and the format is also flexible enough to accommodate information unique to the AVR architecture itself, allowing the production file to represent far more than simply the contents of Flash memory.<\/p>\n    <p>It is this ability to bring together multiple aspects of the firmware within a single engineering artefact that distinguishes the ELF Production File from simpler programming formats. Rather than a collection of unrelated files and settings, the project becomes one complete description of the firmware, generated directly from the source code and project configuration.<\/p>\n    <p>The next section looks at how this information is organised \u2014 first the standard ELF sections inherited from the generic format, then the AVR-specific additions that allow executable code, EEPROM data, fuse settings, lock bits and other device configuration information to coexist within a single, unified firmware image.<\/p>\n    <hr>\n    <h2 id=\"inside-an-avr-elf-production-file\">Inside an AVR ELF Production File<\/h2>\n    <p>By the time the linker has finished its work, the AVR project has been transformed from a collection of independently compiled modules into a single ELF Production File. Although we now refer to it as a single file, it is worth remembering that an ELF file is not simply a block of machine code. As we saw in Part 2, it is a carefully organised container made up of numerous sections, each serving a specific purpose within the development process.<\/p>\n    <p>Some of these sections are common to almost every ELF file, regardless of processor architecture \u2014 they describe the executable program itself, store initialised data, record symbolic information, or provide the debugging data required by source-level development tools. Others are unique to particular processor families and reflect the specific requirements of those architectures. This extensibility is central to ELF&#39;s success: rather than inventing a new file format for every processor family, the specification provides a common framework that can be extended where necessary while remaining compatible with the existing toolchain.<\/p>\n    <p>The AVR architecture takes full advantage of this flexibility. An AVR ELF Production File contains all the standard information expected by the compiler, linker and debugger, but it can also describe elements unique to AVR microcontrollers \u2014 resulting in a file capable of representing not only the application itself but also the additional information needed to configure the target device correctly.<\/p>\n    <h3 id=\"the-standard-elf-sections\">The Standard ELF Sections<\/h3>\n    <p>Every AVR ELF Production File begins with the familiar sections found in almost every ELF file generated by GCC. These were introduced in Part 2, but it is worth briefly revisiting them, since they form the foundation on which the AVR-specific additions are built.<\/p>\n    <p>The most recognisable section is <strong><code>.text<\/code><\/strong>, containing the application&#39;s executable machine instructions. When the firmware is programmed into the device, it is this section that ultimately occupies Flash program memory \u2014 every compiled function, interrupt service routine and startup routine eventually finds its place here.<\/p>\n    <p>Alongside the executable code sit the sections that describe the application&#39;s data. Initialised global and static variables are placed in <strong><code>.data<\/code><\/strong>, while uninitialised variables occupy <strong><code>.bss<\/code><\/strong>. Although these variables reside in the AVR&#39;s SRAM while the application is running, their initial values are recorded within the ELF so that the startup code can initialise memory correctly on every reset. Read-only constants are typically placed into <strong><code>.rodata<\/code><\/strong>, keeping lookup tables, fixed strings and other constant data separate from executable instructions.<\/p>\n    <p>Symbol tables record the names and addresses of functions and variables, letting development tools relate machine code back to the original source. Debugging sections, including the DWARF information discussed in Part 2, preserve line numbers, variable types and other metadata that make source-level debugging possible.<\/p>\n    <p>Together, these sections provide a complete description of the executable application \u2014 not only <em>what<\/em> instructions should execute, but <em>how<\/em> the application is organised, where its data resides, and how development tools should interpret the finished firmware. For a desktop application, that may be sufficient. For an AVR microcontroller, it is only part of the story.<\/p>\n    <hr>\n    <h2 id=\"extending-elf-for-avr-microcontrollers\">Extending ELF for AVR Microcontrollers<\/h2>\n    <p>Unlike a desktop computer, an AVR microcontroller contains several distinct areas of non-volatile memory, each serving a different purpose. Programming the application code is only one stage of configuring the device. Depending on the project, the firmware may also require predefined EEPROM contents, fuse settings that determine how the microcontroller starts and operates, lock bits that control code protection, and \u2014 on supported devices \u2014 information stored in User Signature memory.<\/p>\n    <p>These are all fundamental parts of the finished firmware, yet very different in nature from executable instructions. Rather than forcing developers to manage them independently, the AVR toolchain extends the standard ELF format so that they can be represented within the same production file.<\/p>\n    <p>The result is an ELF Production File that describes the firmware as a whole, not merely its executable code. This is an important distinction from an engineering perspective: instead of treating configuration information as separate documentation to be remembered during production, the project itself becomes the authoritative source of that information. Rather than asking &quot;which files are needed to program this device?&quot;, it becomes more natural to ask: &quot;what information has been defined within the project&#39;s ELF Production File?&quot;<\/p>\n    <h3 id=\"from-elf-sections-to-device-memory\">From ELF Sections to Device Memory<\/h3>\n    <p>One of the easiest ways to understand an AVR ELF Production File is to compare its internal organisation with the physical memory architecture of the target microcontroller. Although the ELF exists as a single file on disk, it describes several distinct regions within the AVR itself, allowing the firmware to represent far more than just the contents of program memory.<\/p>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF03_Fig02.jpg\" alt=\"Figure 2. Mapping an AVR ELF Production File to Device Memory\"><\/p>\n    <p><strong>Figure 2. Mapping an AVR ELF Production File to Device Memory.<\/strong><\/p>\n    <p><em>An AVR ELF Production File can describe multiple regions of the target device, including Flash program memory, SRAM, EEPROM, fuse bytes, lock bits and User Signature memory where supported.<\/em><\/p>\n    <p>This mapping highlights an important characteristic of the AVR toolchain: the ELF Production File does not blur the distinction between these memory regions. It records each one separately while preserving the relationship between them \u2014 Flash remains distinct from EEPROM, fuse bytes remain separate from executable code, and User Signature memory retains its own identity within the project. Yet all of these elements coexist within a single structured file, allowing the firmware to be treated as one coherent engineering artefact.<\/p>\n    <p>The practical advantages become increasingly apparent as projects grow in complexity. A production firmware release is no longer defined solely by the machine instructions programmed into Flash \u2014 it also includes the configuration that determines how the microcontroller behaves after reset, the non-volatile data required when the application first starts, and the information needed by debugging tools throughout development. By bringing these elements together within the ELF, the toolchain ensures that every significant aspect of the firmware remains associated with the build that created it.<\/p>\n    <hr>\n    <h2 id=\"defining-the-complete-firmware\">Defining the Complete Firmware<\/h2>\n    <p>If the ELF Production File can describe executable code, EEPROM contents, fuse settings and other device-specific information, where does that information actually come from? Is it added manually after the build, or generated automatically as part of the normal development process?<\/p>\n    <p>It originates from the project itself. The same source code and project configuration that define the application&#39;s behaviour also define the additional information the target device requires. During the build, the compiler and linker recognise these declarations and place them into the appropriate sections of the ELF Production File alongside the executable application.<\/p>\n    <p>The executable code is easy enough to understand: every function written in C or C++ is compiled into machine instructions before being placed into the executable sections of the ELF by the linker, and symbol tables and debugging information are generated automatically as part of the normal build process. The AVR-specific information \u2014 EEPROM data, fuse settings, lock bits and User Signature contents \u2014 deserves a closer look, since it can be defined directly within the project rather than relying on external configuration files or programming scripts.<\/p>\n    <p>This approach offers an important practical advantage. Instead of relying on separate documentation to describe how a microcontroller should be configured, the firmware project itself becomes the authoritative source of that information. When the project is rebuilt, every element of the firmware is regenerated together, keeping the executable code and the device configuration synchronised.<\/p>\n    <h2 id=\"defining-the-complete-firmware\">Defining the Complete Firmware<\/h2>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF03_Fig03.jpg\" alt=\"Figure 3. Building a Complete AVR ELF Production File\"><\/p>\n    <p><strong>Figure 3. Building a Complete AVR ELF Production File.<\/strong><\/p>\n    <p><em>Information defined by the AVR firmware project is brought together during compilation and linking to produce a single ELF Production File containing executable code, EEPROM data, fuse settings, lock bit configuration and User Signature information where supported.<\/em><\/p>\n    <h3 id=\"defining-eeprom-data\">Defining EEPROM Data<\/h3>\n    <p>Although many embedded applications use EEPROM to store values generated while the system is running, it is equally common for a project to require predefined EEPROM contents from the moment the device is first programmed. Calibration constants, default configuration values, serial number templates and lookup tables are all examples of information that may need to be present before the firmware executes for the first time.<\/p>\n    <p>AVR-GCC makes this straightforward through the <code>EEMEM<\/code> attribute. Rather than requiring a separate EEPROM programming file to be created manually, the developer simply declares the required data within the application source code:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include<\/span> <span class=\"hljs-meta-string\">&lt;avr\/eeprom.h&gt;<\/span><\/span>\n\n    <span class=\"hljs-keyword\">uint16_t<\/span> EEMEM calibrationValue = <span class=\"hljs-number\">1234<\/span>;\n    <\/code><\/pre>\n    <p>Although this declaration resembles an ordinary global variable, its purpose is quite different. Instead of allocating storage in SRAM, the compiler recognises the <code>EEMEM<\/code> attribute and records the variable within the EEPROM section of the finished ELF Production File. The data becomes part of the firmware project itself, rather than a separate programming resource \u2014 the source code is not merely describing how the application behaves after power-up, but also defining how the device should be prepared before the application ever begins to execute.<\/p>\n    <h3 id=\"defining-fuse-settings\">Defining Fuse Settings<\/h3>\n    <p>If executable code determines what an AVR does, the fuse bytes determine how it starts. Fuse settings configure fundamental characteristics of the microcontroller \u2014 clock selection, startup timing, brown-out detection and, depending on the device family, a variety of other hardware options. Incorrect fuse settings can prevent an otherwise perfect application from operating correctly, making them just as important as the application code itself.<\/p>\n    <p>Rather than treating these settings as values to be remembered during production, AVR-GCC allows them to be defined as part of the firmware project:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-comment\">#include &lt;avr\/io.h&gt;<\/span>\n\n    <span class=\"hljs-attr\">FUSES<\/span> =\n    {\n        .<span class=\"hljs-attr\">low<\/span> = LFUSE_DEFAULT,\n        .<span class=\"hljs-attr\">high<\/span> = HFUSE_DEFAULT,\n        .<span class=\"hljs-attr\">extended<\/span> = EFUSE_DEFAULT\n    };\n    <\/code><\/pre>\n    <p>During compilation, these declarations are recognised by the toolchain and incorporated into the appropriate sections of the ELF Production File, becoming part of the project&#39;s engineering definition rather than existing solely as notes in a production document or entries in a programmer&#39;s configuration dialogue. This significantly improves the reproducibility of a firmware build: anyone rebuilding the project has access not only to the application source code but also to the intended device configuration used when the firmware was originally created.<\/p>\n    <h3 id=\"lock-bits-and-device-protection\">Lock Bits and Device Protection<\/h3>\n    <p>Many commercial products require some degree of firmware protection once manufacturing is complete. AVR devices provide lock bits that can restrict access to program memory, preventing accidental overwriting or unauthorised extraction of application code. As with fuse bytes, these settings can be defined directly within the project:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include<\/span> <span class=\"hljs-meta-string\">&lt;avr\/io.h&gt;<\/span><\/span>\n\n    LOCKBITS =\n    {\n        .lockbits = LB_MODE_3\n    }<span class=\"hljs-comment\">;<\/span>\n    <\/code><\/pre>\n    <p>By incorporating these settings into the ELF Production File, the project records not only the firmware itself but also the intended protection policy for the finished product. The security configuration becomes part of the firmware definition, rather than a separate manufacturing instruction that must be applied manually \u2014 reflecting an important engineering principle: every piece of information required to produce a correctly configured device should, wherever possible, originate from the same controlled source.<\/p>\n    <h3 id=\"user-signature-memory\">User Signature Memory<\/h3>\n    <p>Many modern AVR devices provide an additional non-volatile memory region known as the <strong>User Signature<\/strong> or <strong>User Row<\/strong>. Unlike Flash memory or EEPROM, this area is intended for application-specific information that remains associated with the device throughout its lifetime \u2014 manufacturing identifiers, hardware revision information, calibration constants and other product-specific data that does not naturally belong within the executable application.<\/p>\n    <p>Where supported by the target device, the AVR toolchain provides mechanisms for defining this information within the firmware project, so that it too becomes part of the completed ELF Production File. Not every AVR family implements User Signature memory in exactly the same way, but the underlying philosophy remains consistent: information that forms part of the finished firmware package should be represented within the project&#39;s production file wherever possible.<\/p>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF03_Fig04.jpg\" alt=\"Figure 4. AVR-Specific Information Within the ELF\"><\/p>\n    <p><strong>Figure 4. AVR-Specific Information Within the ELF.<\/strong><\/p>\n    <p><em>AVR-specific declarations such as EEPROM data, fuse settings, lock bits and User Signature information are incorporated into dedicated sections of the ELF Production File, keeping device configuration associated with the firmware project.<\/em><\/p>\n    <hr>\n    <h2 id=\"one-project-one-engineering-record\">One Project, One Engineering Record<\/h2>\n    <p>Stepping back from the implementation details, a clear pattern emerges. Each of the examples above starts life as an ordinary declaration within the project source code. During compilation and linking, those declarations are recognised by the AVR toolchain and placed into their appropriate sections within the ELF Production File. The result is a single file that captures far more than executable instructions alone.<\/p>\n    <p><strong>Figure 3 \u2013 Building a Complete AVR ELF Production File<\/strong><\/p>\n    <pre><code class=\"lang-text\"><span class=\"hljs-symbol\">Application<\/span> Source <span class=\"hljs-meta\">Code<\/span>\n            \u2502\n            \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba Executable <span class=\"hljs-meta\">Code<\/span>\n            \u2502\n            \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba EEPROM <span class=\"hljs-meta\">Data<\/span>\n            \u2502\n            \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba Fuse Settings\n            \u2502\n            \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba Lock <span class=\"hljs-keyword\">Bit <\/span>Configuration\n            \u2502\n            \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba User Signature Information\n                            \u2502\n                            \u25bc\n                    Project.elf\n    <\/code><\/pre>\n    <p>Seen in this light, the ELF Production File becomes much more than the output of the linker \u2014 it is the complete engineering record of the firmware project. Every significant aspect of the application, from its executable code and memory initialisation to its device configuration and supporting metadata, is brought together into a single, structured artefact generated directly from the project&#39;s controlled source.<\/p>\n    <p>This marks an important shift in perspective. Rather than thinking in terms of separate firmware files and independent device settings, it becomes more natural to think in terms of a single production file that represents the firmware in its entirety \u2014 a distinction fundamental to understanding modern AVR development workflows.<\/p>\n    <hr>\n    <h2 id=\"conclusion\">Conclusion<\/h2>\n    <p>Throughout this article we have looked beyond the executable code of an AVR project and examined the production file that sits at the heart of the entire build process. Although many developers interact primarily with Intel HEX files day to day, it is the ELF Production File that represents the complete output of the toolchain. Created by the linker, it brings together every significant aspect of the firmware project into a single, structured engineering artefact \u2014 machine instructions, EEPROM initialisation data, fuse settings, lock bit configuration and, where supported, User Signature memory, combined with the symbol tables and debugging information required during development.<\/p>\n    <p>Viewing the project in this way changes the role of the ELF file. Rather than an intermediate build artefact used solely by the debugger, it becomes the authoritative representation of the firmware itself \u2014 the single source from which every other stage of the development process can proceed.<\/p>\n    <hr>\n    <h2 id=\"coming-next\">Coming Next<\/h2>\n    <p><strong>Part 4 \u2013 Programming AVR Microcontrollers Using ELF Production Files<\/strong><\/p>\n    <p>In the final article of this series, we follow the AVR ELF Production File beyond the build system and into the programming process. We begin by looking at why Intel HEX became the traditional firmware programming format, before exploring how modern programming software can work directly with an ELF Production File instead \u2014 mapping executable code, EEPROM data, fuse settings, lock bits and other device configuration to their appropriate regions within the target microcontroller.<\/p>\n    <p>Finally, we see how Kanda&#39;s AVR programming software simplifies this entire workflow by automatically using the GNU Binutils behind the scenes, allowing developers to work with a single authoritative firmware image throughout development, testing and production.<\/p>\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>ELF Files Explained \u2013 Part 3: AVR ELF Production Files \u2013 The Complete Firmware Image Part 3 of the &quot;ELF Files Explained&quot; Series In Part 1, we explored the history of executable file formats and saw how ELF became the industry standard for modern operating systems and GCC-based development toolchains. In Part 2, we looked &hellip; <a href=\"https:\/\/www.kanda.com\/blog\/microcontrollers\/elf-files-explained-part-3\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">ELF Files Explained Part 3<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[3],"tags":[70],"class_list":["post-2235","post","type-post","status-publish","format-standard","placeholder-for-hentry","category-microcontrollers","tag-avr-handheld"],"_links":{"self":[{"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/posts\/2235","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/comments?post=2235"}],"version-history":[{"count":1,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/posts\/2235\/revisions"}],"predecessor-version":[{"id":2236,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/posts\/2235\/revisions\/2236"}],"wp:attachment":[{"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/media?parent=2235"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/categories?post=2235"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/tags?post=2235"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}