
{"id":2232,"date":"2026-08-21T10:50:31","date_gmt":"2026-08-21T09:50:31","guid":{"rendered":"https:\/\/www.kanda.com\/blog\/?p=2232"},"modified":"2026-08-21T10:50:32","modified_gmt":"2026-08-21T09:50:32","slug":"elf-files-explained-part-2","status":"publish","type":"post","link":"https:\/\/www.kanda.com\/blog\/microcontrollers\/elf-files-explained-part-2\/","title":{"rendered":"ELF Files Explained Part 2"},"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\/ELF02_Hero.jpg\" alt=\"ELF Files Explained - Part 2 Hero\"><\/p>\n    <h1 id=\"elf-files-explained-part-2-inside-the-elf-format-headers-sections-symbols-and-debug-information\">ELF Files Explained \u2013 Part 2: Inside the ELF Format \u2013 Headers, Sections, Symbols and Debug Information<\/h1>\n    <hr>\n    <blockquote>\n    <p><strong>Part 2 of the &quot;ELF Files Explained&quot; Series<\/strong><\/p>\n    <p>In Part 1, we explored the history of executable file formats and the evolution from early UNIX formats such as a.out and COFF to the Executable and Linkable Format. We also saw how ELF became the standard executable format for Linux and a fundamental component of modern embedded development toolchains.<\/p>\n    <p>In this article, we move beyond that historical perspective and begin examining the internal structure of an ELF file. Rather than treating it as an opaque binary, we will break it down into its major components and see how linkers, loaders and debuggers each interpret the information it contains.<\/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=\"introduction\">Introduction<\/h2>\n    <p>At first glance, an ELF file appears to be little more than a collection of binary data. Open one in a text editor and the result is largely unreadable \u2014 seemingly random bytes, punctuated only by the occasional recognisable string. It would be easy to assume the file is simply compiled machine code and little else.<\/p>\n    <p>In reality, an ELF file is a carefully organised data structure. Rather than storing executable instructions in isolation, the format arranges information into well-defined regions, each serving a particular purpose during software development or execution. Some parts of the file are intended for the operating system loader, others exist solely for the linker, and others are used only by debuggers or analysis tools.<\/p>\n    <p>This separation of responsibilities is one of the reasons for ELF&#39;s longevity. Instead of creating separate file formats for different stages of software development, ELF provides a single container capable of serving multiple purposes: a compiler can produce it, a linker can modify it, an operating system can execute it, and a debugger can use it to reconstruct source-level information \u2014 all without changing the underlying format.<\/p>\n    <p>Understanding this organisation explains why executable files contain much more than machine code, why firmware images can often be converted into multiple programming formats, and why tools such as <code>readelf<\/code>, <code>objdump<\/code> and <code>nm<\/code> are able to reveal so much about a compiled program. Before examining the individual components in detail, it is worth starting with a high-level overview of how an ELF file is organised.<\/p>\n    <hr>\n    <h2 id=\"the-overall-layout-of-an-elf-file\">The Overall Layout of an ELF File<\/h2>\n    <p>Although the exact contents of an ELF file vary depending on its purpose, most follow the same general layout, consisting of four principal areas:<\/p>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF02_Fig01.jpg\" alt=\"Figure 1. Overall Layout of an ELF File\"><\/p>\n    <p><strong>Figure 1. Overall Layout of an ELF File.<\/strong><\/p>\n    <p><em>The principal structures within an ELF file, including the ELF Header, Program Header Table, sections and Section Header Table. These structures provide different views of the information contained within the file for loaders, linkers, debuggers and other development tools.<\/em><\/p>\n    <p>The <strong>ELF Header<\/strong> acts as the file&#39;s introduction, identifying it as an ELF object and providing the information required to interpret everything that follows. The <strong>Program Header Table<\/strong> describes how portions of the file should be loaded into memory when a program executes \u2014 information intended primarily for operating system loaders. The central portion of the file holds the actual program content: executable instructions, data, symbols, debugging information and much else besides. Finally, the <strong>Section Header Table<\/strong> provides an index describing each section contained within the file, intended primarily for linkers, debuggers and analysis tools.<\/p>\n    <p>Although these four components coexist within a single file, not every tool makes use of all of them \u2014 different stages of the software development process view the ELF file from different perspectives, as the following sections make clear.<\/p>\n    <hr>\n    <h2 id=\"the-elf-header\">The ELF Header<\/h2>\n    <p>Every ELF file begins with a fixed-size structure known as the <strong>ELF Header<\/strong>, occupying the very start of the file. It serves much the same purpose as the cover page of a technical document: before attempting to interpret any of the remaining data, software first examines the header to determine what kind of file it is dealing with and how the rest of the file should be read.<\/p>\n    <p>Among the information stored within the ELF Header are:<\/p>\n    <ul>\n    <li>Whether the file is actually an ELF file<\/li>\n    <li>Whether it uses the 32-bit or 64-bit format<\/li>\n    <li>The byte ordering (endianness)<\/li>\n    <li>The target processor architecture<\/li>\n    <li>The operating system ABI<\/li>\n    <li>The type of ELF file<\/li>\n    <li>The program entry point<\/li>\n    <li>The locations of the Program Header Table and Section Header Table<\/li>\n    <\/ul>\n    <p>One of the most recognisable fields is the <strong>ELF magic number<\/strong>. The first four bytes of every ELF file are:<\/p>\n    <pre><code><span class=\"hljs-number\">7<\/span>F <span class=\"hljs-number\">45<\/span> <span class=\"hljs-number\">4<\/span>C <span class=\"hljs-number\">46<\/span>\n    <\/code><\/pre><p>The final three bytes correspond to the ASCII characters <code>E<\/code>, <code>L<\/code> and <code>F<\/code>. This signature allows software to immediately recognise the file as an ELF object before any further processing occurs.<\/p>\n    <p>Following the magic number, several fields describe the format itself. The header specifies whether the file follows the 32-bit or 64-bit ELF specification \u2014 modern desktop operating systems predominantly use the 64-bit format, though many embedded systems, including AVR development, continue to use the 32-bit variant, reflecting the capabilities of their associated toolchains. The header also records the processor architecture the file was built for, preventing incompatible binaries from being loaded on unsupported hardware and allowing development tools to interpret architecture-specific information correctly.<\/p>\n    <p>Perhaps the most important values in the header are the offsets pointing to the Program Header Table and the Section Header Table. Rather than assuming these structures appear at fixed locations within the file, ELF allows them to be placed wherever appropriate, with the header acting as a directory pointing to their location. This flexibility contributes significantly to the extensibility of the format and avoids many of the limitations encountered by earlier executable formats.<\/p>\n    <hr>\n    <h2 id=\"the-program-header-table\">The Program Header Table<\/h2>\n    <p>If the ELF Header identifies the file, the <strong>Program Header Table<\/strong> describes how it should be loaded into memory. This table is intended primarily for the operating system or runtime loader, rather than for the linker.<\/p>\n    <p>When an executable program is launched, the operating system does not need every piece of information contained within the ELF file \u2014 it simply needs to know which parts of the file should be copied into memory, where they should be placed, and what permissions they should have. The Program Header Table provides exactly that information.<\/p>\n    <p>Rather than describing individual sections such as <code>.text<\/code> or <code>.data<\/code>, the Program Header Table groups related information into larger regions known as <strong>segments<\/strong>. Several sections may ultimately be loaded into a single executable memory region, for example, while others form a writable data region. The loader therefore works with segments rather than the finer-grained sections used elsewhere in the file.<\/p>\n    <p>This distinction often causes confusion for developers encountering ELF for the first time, so it is worth stating plainly:<\/p>\n    <blockquote>\n    <p><strong>Sections<\/strong> are a <em>logical<\/em> organisation used during compilation and linking.\n    <strong>Segments<\/strong> are a <em>runtime<\/em> organisation used during program loading.<\/p>\n    <\/blockquote>\n    <p>Both describe the same underlying data, but they exist for different purposes and are interpreted by different software components. Understanding this distinction is one of the key concepts required to make sense of the rest of the ELF format.<\/p>\n    <hr>\n    <h2 id=\"the-section-header-table\">The Section Header Table<\/h2>\n    <p>Where the Program Header Table serves the needs of the operating system loader, the <strong>Section Header Table<\/strong> serves the needs of software development tools. Each entry describes one section within the ELF file, recording its name, size, location, type and various attributes.<\/p>\n    <p>Where the Program Header Table groups information according to how it should be loaded into memory, the Section Header Table preserves the logical organisation created during compilation. This allows development tools to examine individual components of a program independently: a debugger may be interested in symbolic information, a linker in relocation records, a static analysis tool in compiler metadata, and another utility may simply want to list all available sections for diagnostic purposes.<\/p>\n    <p>The Section Header Table provides the roadmap that lets these tools locate the information they need without having to understand the entire file. As we shall see next, some sections contain executable instructions, others contain program data, and many exist solely to support development tools, never becoming part of the running application. Understanding these sections \u2014 and the role each plays within the build process \u2014 is key to understanding the ELF format itself.<\/p>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF02_Fig02.jpg\" alt=\"Figure 2. Program Headers and Section Headers\"><\/p>\n    <p><strong>Figure 2. Program Headers and Section Headers.<\/strong><\/p>\n    <p><em>Program Headers describe how parts of an ELF file are loaded into memory, while Section Headers describe the logical sections used by linkers, debuggers and other development tools. Both provide different views of the same underlying ELF file.<\/em><\/p>\n    <hr>\n    <h2 id=\"understanding-sections\">Understanding Sections<\/h2>\n    <p>The heart of an ELF file is its collection of <strong>sections<\/strong>. Each section stores a particular type of information generated during compilation or linking \u2014 some contain executable machine instructions, others contain program data, and many exist purely to support development tools such as linkers and debuggers.<\/p>\n    <p>One of ELF&#39;s strengths is that these different kinds of information are kept separate. Rather than placing everything into one continuous block of binary data, the compiler and linker organise related information into individual sections, making it easier for development tools to locate exactly what they need.<\/p>\n    <p>The ELF specification allows for many section types, but a handful appear so frequently that every developer should be familiar with them.<\/p>\n    <table>\n    <thead>\n    <tr>\n    <th>Section<\/th>\n    <th>Contents<\/th>\n    <th>Typical Attributes<\/th>\n    <\/tr>\n    <\/thead>\n    <tbody>\n    <tr>\n    <td><code>.text<\/code><\/td>\n    <td>Executable machine instructions<\/td>\n    <td>Read-only, executable<\/td>\n    <\/tr>\n    <tr>\n    <td><code>.data<\/code><\/td>\n    <td>Initialised global\/static variables<\/td>\n    <td>Read-write<\/td>\n    <\/tr>\n    <tr>\n    <td><code>.bss<\/code><\/td>\n    <td>Uninitialised global\/static variables<\/td>\n    <td>Read-write, occupies no file space<\/td>\n    <\/tr>\n    <tr>\n    <td><code>.rodata<\/code><\/td>\n    <td>Constants: strings, lookup tables, literals<\/td>\n    <td>Read-only<\/td>\n    <\/tr>\n    <\/tbody>\n    <\/table>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF02_Fig03.jpg\" alt=\"Figure 3. Common ELF Sections\"><\/p>\n    <p><strong>Figure 3. Common ELF Sections.<\/strong><\/p>\n    <p><em>Common ELF sections such as <code>.text<\/code>, <code>.data<\/code>, <code>.bss<\/code> and <code>.rodata<\/code> organise executable code, initialised data, uninitialised data and read-only data within a compiled program. Their exact placement and use depends on the target system and linker configuration.<\/em><\/p>\n    <h3 id=\"-text-\"><code>.text<\/code><\/h3>\n    <p>The <strong><code>.text<\/code><\/strong> section contains the executable machine instructions generated by the compiler \u2014 the code the processor actually runs. It is normally marked read-only and executable; preventing modification of executable code improves both reliability and security by ensuring program instructions cannot be accidentally overwritten during execution. In embedded systems, <code>.text<\/code> is typically programmed into Flash memory.<\/p>\n    <h3 id=\"-data-\"><code>.data<\/code><\/h3>\n    <p>The <strong><code>.data<\/code><\/strong> section stores global and static variables that have been given an initial value:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-attribute\">int counter<\/span> = 10;\n    <\/code><\/pre>\n    <p>The initial value <code>10<\/code> is stored within <code>.data<\/code>. When the program starts, the runtime system copies these initial values into writable memory before execution begins. On embedded systems, the initial values usually reside in Flash and are copied into RAM during start-up.<\/p>\n    <h3 id=\"-bss-\"><code>.bss<\/code><\/h3>\n    <p>The <strong><code>.bss<\/code><\/strong> section contains global and static variables that are <strong>not<\/strong> explicitly initialised:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-keyword\">int<\/span> counter;\n    <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">char<\/span> <span class=\"hljs-built_in\">buffer<\/span>[<span class=\"hljs-number\">256<\/span>];\n    <\/code><\/pre>\n    <p>Rather than storing hundreds or thousands of zero bytes within the executable, the ELF file simply records the size of the required memory. During start-up, the runtime initialisation code clears this region to zero before calling <code>main()<\/code>. This significantly reduces executable size while ensuring uninitialised variables begin with predictable values.<\/p>\n    <h3 id=\"-rodata-\"><code>.rodata<\/code><\/h3>\n    <p>The <strong><code>.rodata<\/code><\/strong> (read-only data) section contains constants that never change during program execution \u2014 string literals, lookup tables, mathematical constants and constant arrays:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-keyword\">char<\/span> message[] = <span class=\"hljs-string\">\"Hello World\"<\/span>;\n    <\/code><\/pre>\n    <p>Because these values never change, they can safely remain in read-only memory. On embedded systems this often means they stay permanently in Flash, avoiding unnecessary RAM usage.<\/p>\n    <h3 id=\"other-common-sections\">Other Common Sections<\/h3>\n    <p>Although <code>.text<\/code>, <code>.data<\/code>, <code>.bss<\/code> and <code>.rodata<\/code> are the sections encountered most often, a typical ELF file contains several others:<\/p>\n    <ul>\n    <li><code>.init<\/code> \u2014 start-up initialisation code<\/li>\n    <li><code>.fini<\/code> \u2014 termination routines<\/li>\n    <li><code>.comment<\/code> \u2014 compiler identification<\/li>\n    <li><code>.note<\/code> \u2014 miscellaneous metadata<\/li>\n    <li><code>.ctors<\/code> \/ <code>.dtors<\/code> \u2014 constructor and destructor information (where applicable)<\/li>\n    <\/ul>\n    <p>Not every project contains every section \u2014 the exact contents depend on the compiler, linker, operating system and optimisation settings in use. As software projects become more sophisticated, the number of sections tends to increase accordingly.<\/p>\n    <hr>\n    <h2 id=\"symbols\">Symbols<\/h2>\n    <p>If sections describe <strong>where<\/strong> information is stored, <strong>symbols<\/strong> describe <strong>what<\/strong> that information represents. Every function, global variable and many other named objects within a program may be represented by a symbol.<\/p>\n    <p>Consider the following source code:<\/p>\n    <pre><code class=\"lang-c\"><span class=\"hljs-keyword\">int<\/span> ledState;\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">initialiseHardware<\/span><span class=\"hljs-params\">(<span class=\"hljs-keyword\">void<\/span>)<\/span>\n    <\/span>{\n        ...\n    }\n    <\/code><\/pre>\n    <p>The compiler records symbolic information linking the names <code>ledState<\/code> and <code>initialiseHardware<\/code> to their corresponding locations within the program. This information is invaluable throughout development: during linking, symbols allow separately compiled object files to reference one another; during debugging, they allow development environments to display meaningful function names rather than raw memory addresses. Without symbols, a debugger could show that execution had reached address <code>0x000013A4<\/code>, but it would have no way of telling the developer that this address corresponds to <code>initialiseHardware()<\/code>.<\/p>\n    <h3 id=\"local-and-global-symbols\">Local and Global Symbols<\/h3>\n    <p>Symbols generally fall into two broad categories. <strong>Local symbols<\/strong> exist only within the object file in which they were created. <strong>Global symbols<\/strong> may be referenced by other object files during linking. This distinction allows large software projects to be divided into independent modules while still permitting carefully controlled interaction between them \u2014 the linker uses the global symbol table to resolve references between these independently compiled modules.<\/p>\n    <h3 id=\"weak-symbols\">Weak Symbols<\/h3>\n    <p>Some toolchains also support <strong>weak symbols<\/strong>: a default implementation that may be replaced if another object file provides a stronger definition. This mechanism is commonly used within embedded software libraries \u2014 a startup library might provide a default interrupt handler, and if the application supplies its own implementation, the linker automatically selects the application&#39;s version instead. This allows library developers to provide sensible defaults without preventing applications from supplying customised behaviour.<\/p>\n    <hr>\n    <h2 id=\"relocation\">Relocation<\/h2>\n    <p>One of the more remarkable aspects of modern software development is that source files can be compiled independently \u2014 often on different machines, at different times \u2014 before finally being combined into a single executable. This flexibility is made possible through <strong>relocation<\/strong>.<\/p>\n    <p>When the compiler translates an individual source file, it usually has no knowledge of where that code or its associated data will eventually reside within the finished program. Instead of inserting final memory addresses, it leaves placeholders together with relocation information describing how those addresses should later be updated. During linking, the linker determines the final memory layout of the application and revisits each relocation record, replacing the temporary placeholders with the correct addresses.<\/p>\n    <p>Without relocation, every source file would need to know the exact layout of the finished program before compilation \u2014 a practical impossibility for all but the simplest software projects. Although largely invisible to most developers, relocation is one of the key technologies enabling modular software development.<\/p>\n    <hr>\n    <h2 id=\"debug-information\">Debug Information<\/h2>\n    <p>One reason ELF has become such an effective development format is its ability to carry extensive debugging information alongside the executable program. Most modern toolchains store this information using the <strong>DWARF<\/strong> debugging standard.<\/p>\n    <p>Contrary to what many developers initially assume, this information is not intended for the processor at all \u2014 it exists solely for software development tools. When a debugger displays source code, shows the current value of local variables, allows breakpoints to be placed on function names, or reconstructs a call stack after an exception, it is drawing on information stored in the ELF file&#39;s debugging sections. Without it, developers would need to work directly with machine instructions and memory addresses, manually correlating them with the original source.<\/p>\n    <p>Many production firmware releases intentionally strip debugging information before distribution, to reduce file size and protect implementation details. Organisations almost always retain the original ELF file with its complete DWARF information, however, so that should a fault need investigating later, the exact firmware image can be analysed using the original symbols and source mappings. This ability to separate deployable firmware from development information is another example of the flexibility that has helped ELF remain the dominant executable format for more than three decades.<\/p>\n    <hr>\n    <h2 id=\"exploring-an-elf-file\">Exploring an ELF File<\/h2>\n    <p>One of the advantages of the ELF format is that its contents are easy to inspect using freely available command-line utilities. These tools let developers look inside an ELF file without specialist software, and are invaluable when investigating build problems, analysing firmware, or simply understanding how a program has been constructed. Many integrated development environments provide graphical interfaces to this information, but the command-line tools reveal the underlying data directly and are well worth becoming familiar with.<\/p>\n    <p><img decoding=\"async\" src=\"https:\/\/www.kanda.com\/blog\/wp-content\/uploads\/ELF02_Fig04.jpg\" alt=\"Figure 4. Tools for Exploring an ELF File\"><\/p>\n    <p><strong>Figure 4. Tools for Exploring an ELF File.<\/strong><\/p>\n    <p><em>GNU Binutils provides a collection of tools for examining different aspects of an ELF file. Utilities such as <code>readelf<\/code>, <code>objdump<\/code>, <code>nm<\/code>, <code>size<\/code> and <code>strings<\/code> expose its structure, machine code, symbols, memory usage and human-readable content.<\/em><\/p>\n    <h3 id=\"-readelf-\"><code>readelf<\/code><\/h3>\n    <p>As its name suggests, <code>readelf<\/code> is designed specifically to examine the contents of ELF files. Unlike some other utilities, it understands the ELF format directly and reports information without attempting to disassemble machine instructions.<\/p>\n    <pre><code class=\"lang-bash\">readelf -h <span class=\"hljs-built_in\">application<\/span>.elf\n    <\/code><\/pre>\n    <p>displays the ELF Header, with output covering the ELF class (32-bit or 64-bit), endianness, target architecture, file type, entry point address, and the locations of the Program Header Table and Section Header Table.<\/p>\n    <p>Other options inspect individual components of the file:<\/p>\n    <pre><code class=\"lang-bash\">readelf -S <span class=\"hljs-built_in\">application<\/span>.elf    <span class=\"hljs-comment\"># list every section<\/span>\n    readelf -l <span class=\"hljs-built_in\">application<\/span>.elf    <span class=\"hljs-comment\"># display the Program Header Table<\/span>\n    <\/code><\/pre>\n    <p>Together, these commands provide an excellent overview of how an ELF file has been organised.<\/p>\n    <h3 id=\"-objdump-\"><code>objdump<\/code><\/h3>\n    <p>Where <code>readelf<\/code> concentrates on structure, <code>objdump<\/code> focuses more on content. One of its most useful features is disassembly:<\/p>\n    <pre><code class=\"lang-bash\">objdump -d <span class=\"hljs-built_in\">application<\/span>.elf\n    <\/code><\/pre>\n    <p>This produces an assembly language listing of the executable code in the <code>.text<\/code> section. Developers frequently use it to examine compiler output, investigate optimisation behaviour, compare different compiler options, understand generated machine instructions, and diagnose low-level software issues. Additional options display symbol tables, relocation records and section contents, making <code>objdump<\/code> one of the most versatile utilities in the GNU Binutils package.<\/p>\n    <h3 id=\"-nm-\"><code>nm<\/code><\/h3>\n    <p>Large software projects often contain thousands of functions and variables. The <code>nm<\/code> utility provides a concise summary of the symbols contained within an ELF file:<\/p>\n    <pre><code class=\"lang-bash\">nm <span class=\"hljs-built_in\">application<\/span>.elf\n    <\/code><\/pre>\n    <p>The output lists each symbol together with its address and type \u2014 useful for confirming that expected functions have been linked, identifying duplicate definitions, investigating unresolved references, and examining exported library symbols. When working with embedded firmware, <code>nm<\/code> can also help determine whether interrupt handlers or startup routines have been linked correctly.<\/p>\n    <h3 id=\"-size-\"><code>size<\/code><\/h3>\n    <p>One of the first questions asked during embedded development is often: <em>how much Flash and RAM does my application use?<\/em> The <code>size<\/code> utility answers this almost immediately:<\/p>\n    <pre><code class=\"lang-bash\">size <span class=\"hljs-built_in\">application<\/span>.elf\n    <\/code><\/pre>\n    <p>This typically reports the sizes of the principal memory regions \u2014 executable code (<code>.text<\/code>), initialised data (<code>.data<\/code>) and uninitialised data (<code>.bss<\/code>). Embedded engineers frequently run this command after every significant code change, to monitor memory consumption and ensure the application continues to fit within the available device resources.<\/p>\n    <h3 id=\"-strings-\"><code>strings<\/code><\/h3>\n    <p>Although most of an ELF file consists of binary data, many sections contain human-readable text. The <code>strings<\/code> utility extracts these printable sequences \u2014 typical output includes compiler version information, file names, function names, build paths, embedded messages and copyright notices. This makes it useful during software analysis and reverse engineering, as it can quickly reveal surprisingly large amounts of information contained within a binary.<\/p>\n    <hr>\n    <h2 id=\"looking-at-an-elf-file-as-a-whole\">Looking at an ELF File as a Whole<\/h2>\n    <p>Having examined the major components individually, it is worth stepping back and considering the ELF file as a complete package. It is more accurate to think of it not as a single binary blob but as a carefully organised database describing a compiled program \u2014 one where different tools simply view different parts of that database.<\/p>\n    <p>The compiler creates the initial information. The linker combines multiple object files and resolves symbols. The operating system loader uses the Program Header Table to prepare a running process. A debugger reads symbol tables and DWARF information to reconstruct the original source code. Utilities such as <code>readelf<\/code> and <code>objdump<\/code> simply expose these structures in human-readable form.<\/p>\n    <p>The processor itself, however, has no interest in symbol names, debugging information or compiler metadata \u2014 once the executable code has been loaded into memory, only the machine instructions and program data are required for execution. This is why production firmware images can often be much smaller than the corresponding ELF file: much of the additional information exists purely to support software development, not execution.<\/p>\n    <hr>\n    <h2 id=\"summary\">Summary<\/h2>\n    <p>Although ELF files often appear intimidating at first, their organisation follows a clear and logical design. The ELF Header identifies the file and provides the information needed to interpret its contents. The Program Header Table describes how executable data should be loaded into memory, while the Section Header Table provides an index allowing development tools to locate the many different types of information the file contains.<\/p>\n    <p>Within those sections reside executable instructions, program data, symbols, relocation records and debugging information, each serving a distinct purpose during compilation, linking, execution or debugging. Rather than being a simple executable image, an ELF file acts as a comprehensive description of a compiled software project \u2014 and the availability of mature utilities such as <code>readelf<\/code>, <code>objdump<\/code>, <code>nm<\/code>, <code>size<\/code> and <code>strings<\/code> makes that description readily accessible, without requiring proprietary tools.<\/p>\n    <p>A solid understanding of these concepts provides valuable insight into the entire software build process, and forms the foundation for the more advanced, AVR-specific topics covered in the remainder of this series.<\/p>\n    <hr>\n    <h2 id=\"coming-next\">Coming Next<\/h2>\n    <p><strong>Part 3 \u2013 AVR ELF Production Files: The Complete Firmware Image<\/strong><\/p>\n    <p>The next article applies everything we have learned to a real embedded development workflow. Using Atmel Studio and AVR-GCC, we follow a project from source code through compilation and linking to the generation of an ELF file, and see how the AVR toolchain extends the standard ELF sections to describe EEPROM contents, fuse settings, lock bits and User Signature memory alongside the executable code.<\/p>\n    <p>By the end of the series, readers will understand not only what an ELF file contains, but also how it fits into the complete firmware development process \u2014 from writing code to programming a physical device.<\/p>\n\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>ELF Files Explained \u2013 Part 2: Inside the ELF Format \u2013 Headers, Sections, Symbols and Debug Information Part 2 of the &quot;ELF Files Explained&quot; Series In Part 1, we explored the history of executable file formats and the evolution from early UNIX formats such as a.out and COFF to the Executable and Linkable Format. We &hellip; <a href=\"https:\/\/www.kanda.com\/blog\/microcontrollers\/elf-files-explained-part-2\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">ELF Files Explained Part 2<\/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-2232","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\/2232","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=2232"}],"version-history":[{"count":2,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/posts\/2232\/revisions"}],"predecessor-version":[{"id":2234,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/posts\/2232\/revisions\/2234"}],"wp:attachment":[{"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/media?parent=2232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/categories?post=2232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kanda.com\/blog\/wp-json\/wp\/v2\/tags?post=2232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}