Why is this hex file different than the code programmed onto the device?
Perhaps Eclipse uses gcc to create the hex, and then modifies it en
  route to the device?


No, it's the other way round. When you compile a project in Eclipse,


individual files are compiled into .o object files
object files and libraries are linked into an .elf file
objcopy generates a .hex file from .elf
and finally the size is printed by size, but this last step is purely informational.


The debugger however works with the .elf from step 2, because it has symbols and other debugging aids in it, which are lost when converting to .hex. Therefore, I'd take a very close look at step 3, and perhaps at the .elf file too.

This is how step 3 should look like in the eclipse build console window

Finished building target: app.elf

Invoking: Cross ARM GNU Create Flash Image
arm-none-eabi-objcopy -O ihex "app.elf"  "app.hex"
Finished building: app.hex

Invoking: Cross ARM GNU Print Size


First, check if there are any warnings or other suspicious signs. Is it regenerated when you remove it, and build the project? And when you remove the .elf? Are the file timestamps correct? Try to repeat it from the command line. For that to work, you'll have to specify the full path to arm-none-eabi-objcopy in your toolchain, or include it in your PATH. Make sure that the toolchain path is properly set in eclipse, and there are no other versions of objcopy lying around, especially in your PATH.

You can examine the .elf with arm-none-eabi-objdump. List the "directory" with -h, it looks like this

app.elf:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .isr_vector   0000013c  08008000  08008000  00008000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000b4cc  08008140  08008140  00008140  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       0000b1c0  08013610  08013610  00013610  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .ARM          00000008  0801e7d0  0801e7d0  0001e7d0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .init_array   00000008  0801e7d8  0801e7d8  0001e7d8  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  5 .fini_array   00000004  0801e7e0  0801e7e0  0001e7e0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  6 .data         00000a60  20000000  0801e7e4  00020000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  7 .noinit       00000004  20000a60  0801f244  00020a60  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  8 .bss          0000cbf8  20000a68  0801f248  00020a68  2**3
                  ALLOC


Check the LMA and Size fields for each section. Is there an overlap? Dump the contents with -s and examine it around the problem address.