I noticed that gcc was optimizing out calls to pgm_read_word_far() that were within a while loop. Adding the volatile directive fixes the underlying problem. I've added the directive to each of the LPM and ELPM calls (20 in all).