diff --git a/ChangeLog b/ChangeLog index 37fac2da..65a04946 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2012-04-18 Joerg Wunsch + + * fileio.c (elf_get_scn): Rather than trying to just match whether + any given section maps straight to a program header segment, use a + more sophisticated decision that matches any section as long as it + fits into the segment. This is needed for situations where the + program header segment spans a larger area than the section data + provided. (This can e.g. happen in an ELF file that contains no + data at address 0, like a bootloader only.) + 2012-04-13 Joerg Wunsch bug #28744: Can't load bootloader to xmega128a1 (part 2, fix for diff --git a/fileio.c b/fileio.c index 3c5dc59e..0f859f19 100644 --- a/fileio.c +++ b/fileio.c @@ -687,6 +687,28 @@ static int srec2b(char * infile, FILE * inf, } #ifdef HAVE_LIBELF +/* + * Determine whether the ELF file section pointed to by `sh' fits + * completely into the program header segment pointed to by `ph'. + * + * Assumes the section has been checked already before to actually + * contain data (SHF_ALLOC, SHT_PROGBITS, sh_size > 0). + * + * Sometimes, program header segments might be larger than the actual + * file sections. On VM architectures, this is used to allow mmapping + * the entire ELF file "as is" (including things like the program + * header table itself). + */ +static inline +int is_section_in_segment(Elf32_Shdr *sh, Elf32_Phdr *ph) +{ + if (sh->sh_offset < ph->p_offset) + return 0; + if (sh->sh_offset + sh->sh_size > ph->p_offset + ph->p_filesz) + return 0; + return 1; +} + /* * Return the ELF section descriptor that corresponds to program * header `ph'. The program header is expected to be of p_type @@ -711,8 +733,10 @@ static Elf_Scn *elf_get_scn(Elf *e, Elf32_Phdr *ph, Elf32_Shdr **shptr) sh->sh_type != SHT_PROGBITS) /* we are only interested in PROGBITS, ALLOC sections */ continue; - if (ph->p_vaddr == sh->sh_addr && - ph->p_offset == sh->sh_offset) { + if (sh->sh_size == 0) + /* we are not interested in empty sections */ + continue; + if (is_section_in_segment(sh, ph)) { /* yeah, we found it */ *shptr = sh; return s;