title: Is accessing the "value" of a linker script variable undefined behavior in C?
body: <p>The GNU ld (linker script) manual Section <a href="https://sourceware.org/binutils/docs-2.32/ld/Source-Code-Reference.html#Source-Code-Reference" rel="nofollow noreferrer">3.5.5 Source Code Reference</a> has some really important information on how to access linker script &quot;variables&quot; (which are actually just integer addresses) in C source code. I used this info. to extensively use linker script variables, and I wrote this answer here: <a href="https://stackoverflow.com/questions/48561217/how-to-get-value-of-variable-defined-in-ld-linker-script-from-c/54728097#54728097">How to get value of variable defined in ld linker script from C</a>.</p>
  <p>However, it is easy to do it wrong and make the mistake of trying to access a linker script variable's <strong>value</strong> (mistakenly) instead of its address, since this is a bit esoteric. The manual (link above) says:</p>
  <blockquote>
  <p>This means that you cannot access the
  <em>value</em> of a linker script defined symbol - it has no value - all you can do is access the <em>address</em> of a linker script defined symbol.</p>
  </blockquote>
  <blockquote>
  <p>Hence when you are using a linker script defined symbol in source code you should always take the address of the symbol, <strong>and never attempt to use its value</strong>.</p>
  </blockquote>
  <p><strong>The question:</strong> So, if you <em>do</em> attempt to access a linker script variable's <em>value</em>, is this &quot;undefined behavior&quot;?</p>
  <h2>Quick refresher:</h2>
  <p>Imagine in linker script (ex: <strong>STM32F103RBTx_FLASH.ld</strong>) you have:</p>
  <pre><code>/* Specify the memory areas */
  MEMORY
  {
      FLASH (rx)      : ORIGIN = 0x8000000,  LENGTH = 128K
      RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 20K
  }
  
  /* Some custom variables (addresses) I intend to access from my C source code */
  __flash_start__ = ORIGIN(FLASH);
  __flash_end__ = ORIGIN(FLASH) + LENGTH(FLASH);
  __ram_start__ = ORIGIN(RAM);
  __ram_end__ = ORIGIN(RAM) + LENGTH(RAM);
  </code></pre>
  <p>And in your C source code you do:</p>
  <pre><code>// 1. correct way A:
  extern uint32_t __flash_start__;
  printf(&quot;__flash_start__ addr = 0x%lX\n&quot;, (uint32_t)&amp;__flash_start__);
  
  // OR 2. correct way B (my preferred approach):
  extern uint32_t __flash_start__[]; // not a true array; [] is required to access linker script variables (addresses) as though they were normal variables
  printf(&quot;__flash_start__ addr = 0x%lX\n&quot;, (uint32_t)__flash_start__);
  
  // OR 3. COMPLETELY WRONG WAY TO DO IT!
  // - IS THIS UNDEFINED BEHAVIOR?
  extern uint32_t __flash_start__;
  printf(&quot;__flash_start__ addr = 0x%lX\n&quot;, __flash_start__);
  </code></pre>
  <h2>Sample printed output</h2>
  <p>(this is real output: it was actually compiled, run, and printed by an STM32 mcu):</p>
  <ol>
  <li><code>__flash_start__ addr = 0x8000000</code></li>
  <li><code>__flash_start__ addr = 0x8000000</code></li>
  <li><code>__flash_start__ addr = 0x20080000</code> &lt;== NOTICE LIKE I SAID ABOVE: this one is <em>completely wrong</em> (even though it compiles and runs)! <em>&lt;== Update Mar. 2020: actually, see my answer, this is just fine and right too, it just does something different is all.</em></li>
  </ol>
  <hr />
  <h2>Update:</h2>
  <p>Response to @Eric Postpischil's 1st comment:</p>
  <blockquote>
  <p>The C standard does not define anything at all about linker script symbols. Any specification of behavior is up to the GNU tools. That said, if a linker script symbol identifies a place in memory where some valid object is stored, I would expect accessing the value of that object to work, if it were accessed with its proper type. Supposing <strong>flash_start</strong> is normally accessible memory, and except for any requirements of your system about what is at <strong>flash_start</strong>, you could, in theory, put a uint32_t (using appropriate input to the linker) and then access it via <strong>flash_start</strong>.</p>
  </blockquote>
  <p>Yes, but that's not my question. I'm not sure if you're picking up the subtlety of my question. Take a look at the examples I provide. It is true you can access this location just fine, but make sure you understand <em>how</em> you do so, and then my question will become apparent. Look especially at example 3 above, which is <em>wrong</em> even though to a C programmer it <em>looks right</em>. To read a <code>uint32_t</code>, for ex, at <code>__flash_start__</code>, you'd do this:</p>
  <pre><code>extern uint32_t __flash_start__;
  uint32_t u32 = *((uint32_t *)&amp;__flash_start__); // correct, even though it *looks like* you're taking the address (&amp;) of an address (__flash_start__)
  </code></pre>
  <p>OR this:</p>
  <pre><code>extern uint32_t __flash_start__[];
  uint32_t u32 = *((uint32_t *)__flash_start__); // also correct, and my preferred way of doing it because it looks more correct to the trained &quot;C-programmer&quot; eye
  </code></pre>
  <p>But most definitely NOT this:</p>
  <pre><code>extern uint32_t __flash_start__;
  uint32_t u32 = __flash_start__; // incorrect; &lt;==UPDATE: THIS IS ALSO CORRECT! (and more straight-forward too, actually; see comment discussion under this question)
  </code></pre>
  <p>and NOT this:</p>
  <pre><code>extern uint32_t __flash_start__;
  uint32_t u32 = *((uint32_t *)__flash_start__); // incorrect, but *looks* right
  </code></pre>
  <h2>Related:</h2>
  <ol>
  <li><a href="https://stackoverflow.com/questions/55621981/why-do-stm32-gcc-linker-scripts-automatically-discard-all-input-sections-from-th">Why do STM32 gcc linker scripts automatically discard all input sections from these standard libraries: libc.a, libm.a, libgcc.a?</a></li>
  <li>[My answer] <a href="https://stackoverflow.com/questions/48561217/how-to-get-value-of-variable-defined-in-ld-linker-script-from-c/54728097#54728097">How to get value of variable defined in ld linker script from C</a></li>
  </ol>