summaryrefslogtreecommitdiffstats
path: root/trunk
diff options
context:
space:
mode:
Diffstat (limited to 'trunk')
-rw-r--r--trunk/ChangeLog.cross5
-rw-r--r--trunk/src/space.c287
2 files changed, 154 insertions, 138 deletions
diff --git a/trunk/ChangeLog.cross b/trunk/ChangeLog.cross
index 23442a6..8201d0d 100644
--- a/trunk/ChangeLog.cross
+++ b/trunk/ChangeLog.cross
@@ -1,3 +1,8 @@
+2007-10-11 Richard Sandiford <richard@codesourcery.com>
+
+ * src/space.c (find_readonly_space): If a new PT_LOAD header is needed,
+ first try removing a PT_NULL header.
+
2006-12-04 Mark Hatle <mark.hatle@windriver.com>
* Merge code from:
diff --git a/trunk/src/space.c b/trunk/src/space.c
index 4fea7c7..35bcfc7 100644
--- a/trunk/src/space.c
+++ b/trunk/src/space.c
@@ -498,153 +498,164 @@ find_readonly_space (DSO *dso, GElf_Shdr *add, GElf_Ehdr *ehdr,
}
/* We have to create new PT_LOAD if at all possible. */
- addr = ehdr->e_phoff + (ehdr->e_phnum + 1) * ehdr->e_phentsize;
- for (j = 1; j < ehdr->e_shnum; ++j)
- {
- if (addr > shdr[j].sh_offset)
- {
- GElf_Addr start, addstart, endaddr, *old_addr;
- GElf_Addr minsize = ~(GElf_Addr) 0;
- int movesec = -1, last, k, e;
-
- if (ehdr->e_phoff < phdr[i].p_offset
- || ehdr->e_phoff + (ehdr->e_phnum + 1) * ehdr->e_phentsize
- > phdr[i].p_offset + phdr[i].p_filesz
- || ! readonly_is_movable (dso, ehdr, shdr, j)
- || shdr[j].sh_addr >= phdr[i].p_vaddr + phdr[i].p_filesz)
- {
- error (0, 0, "%s: No space in ELF segment table to add new ELF segment",
- dso->filename);
- return 0;
- }
-
- start = phdr[i].p_vaddr - phdr[i].p_offset + ehdr->e_phoff
- + (ehdr->e_phnum + 1) * ehdr->e_phentsize;
- for (last = 1; last < ehdr->e_shnum; ++last)
- if (! readonly_is_movable (dso, ehdr, shdr, last)
- || shdr[last].sh_addr >= phdr[i].p_vaddr + phdr[i].p_filesz)
- break;
- for (j = 1; j < last; ++j)
- {
- addstart = (start + add->sh_addralign - 1)
- & ~(add->sh_addralign - 1);
- start = (start + shdr[j].sh_addralign - 1)
- & ~(shdr[j].sh_addralign - 1);
- endaddr = -1;
- if (j + 1 < ehdr->e_shnum)
- endaddr = shdr[j + 1].sh_addr;
- if (phdr[i].p_vaddr + phdr[i].p_filesz < endaddr)
- endaddr = phdr[i].p_vaddr + phdr[i].p_filesz;
-
- switch (shdr[j].sh_type)
- {
- case SHT_HASH:
- case SHT_GNU_HASH:
- case SHT_DYNSYM:
- case SHT_STRTAB:
- case SHT_GNU_verdef:
- case SHT_GNU_verneed:
- case SHT_GNU_versym:
- case SHT_GNU_LIBLIST:
- if (endaddr >= start
- && endaddr - start < minsize)
- {
- minsize = endaddr - start;
- movesec = j;
- }
- if (endaddr > addstart
- && endaddr - addstart > add->sh_size
- && endaddr - addstart - add->sh_size
- < minsize)
- {
- minsize = endaddr - addstart - add->sh_size;
- movesec = j;
- }
- break;
- }
-
- if (start + shdr[j].sh_size <= endaddr)
- {
- movesec = j + 1;
- break;
- }
- start += shdr[j].sh_size;
- }
-
- if (movesec == -1)
- {
- error (0, 0, "%s: No space in ELF segment table to add new ELF segment",
- dso->filename);
- return 0;
- }
+ for (j = 0; j < ehdr->e_phnum; ++j)
+ if (phdr[j].p_type == PT_NULL)
+ break;
- start = phdr[i].p_vaddr - phdr[i].p_offset + ehdr->e_phoff
- + (ehdr->e_phnum + 1) * ehdr->e_phentsize;
- old_addr = (GElf_Addr *) alloca (movesec * sizeof (GElf_Addr));
- for (k = 1; k < movesec; ++k)
- {
- start = (start + shdr[k].sh_addralign - 1)
- & ~(shdr[k].sh_addralign - 1);
- old_addr[k] = shdr[k].sh_addr;
- shdr[k].sh_addr = start;
- shdr[k].sh_offset = start + phdr[i].p_offset
- - phdr[i].p_vaddr;
- start += shdr[k].sh_size;
- }
+ if (j < ehdr->e_phnum)
+ {
+ memmove (phdr, &phdr[j + 1],
+ (ehdr->e_phnum - j - 1) * sizeof (GElf_Phdr));
+ ehdr->e_phnum--;
+ }
+ else
+ {
+ addr = ehdr->e_phoff + (ehdr->e_phnum + 1) * ehdr->e_phentsize;
+ for (j = 1; j < ehdr->e_shnum; ++j)
+ if (addr > shdr[j].sh_offset)
+ {
+ GElf_Addr start, addstart, endaddr, *old_addr;
+ GElf_Addr minsize = ~(GElf_Addr) 0;
+ int movesec = -1, last, k, e;
+
+ if (ehdr->e_phoff < phdr[i].p_offset
+ || ehdr->e_phoff + (ehdr->e_phnum + 1) * ehdr->e_phentsize
+ > phdr[i].p_offset + phdr[i].p_filesz
+ || ! readonly_is_movable (dso, ehdr, shdr, j)
+ || shdr[j].sh_addr >= phdr[i].p_vaddr + phdr[i].p_filesz)
+ {
+ error (0, 0, "%s: No space in ELF segment table to add new ELF segment",
+ dso->filename);
+ return 0;
+ }
- for (e = 0; e < ehdr->e_phnum; ++e)
- if (phdr[e].p_type != PT_LOAD
- && phdr[e].p_type != PT_GNU_STACK)
- for (k = 1; k < movesec; ++k)
- if (old_addr[k] == phdr[e].p_vaddr)
+ start = phdr[i].p_vaddr - phdr[i].p_offset + ehdr->e_phoff
+ + (ehdr->e_phnum + 1) * ehdr->e_phentsize;
+ for (last = 1; last < ehdr->e_shnum; ++last)
+ if (! readonly_is_movable (dso, ehdr, shdr, last)
+ || shdr[last].sh_addr >= phdr[i].p_vaddr + phdr[i].p_filesz)
+ break;
+ for (j = 1; j < last; ++j)
+ {
+ addstart = (start + add->sh_addralign - 1)
+ & ~(add->sh_addralign - 1);
+ start = (start + shdr[j].sh_addralign - 1)
+ & ~(shdr[j].sh_addralign - 1);
+ endaddr = -1;
+ if (j + 1 < ehdr->e_shnum)
+ endaddr = shdr[j + 1].sh_addr;
+ if (phdr[i].p_vaddr + phdr[i].p_filesz < endaddr)
+ endaddr = phdr[i].p_vaddr + phdr[i].p_filesz;
+
+ switch (shdr[j].sh_type)
{
- if (phdr[e].p_filesz != shdr[k].sh_size
- || phdr[e].p_memsz != shdr[k].sh_size)
+ case SHT_HASH:
+ case SHT_GNU_HASH:
+ case SHT_DYNSYM:
+ case SHT_STRTAB:
+ case SHT_GNU_verdef:
+ case SHT_GNU_verneed:
+ case SHT_GNU_versym:
+ case SHT_GNU_LIBLIST:
+ if (endaddr >= start
+ && endaddr - start < minsize)
{
- error (0, 0, "%s: Non-PT_LOAD segment spanning more than one section",
- dso->filename);
- return 0;
+ minsize = endaddr - start;
+ movesec = j;
+ }
+ if (endaddr > addstart
+ && endaddr - addstart > add->sh_size
+ && endaddr - addstart - add->sh_size
+ < minsize)
+ {
+ minsize = endaddr - addstart - add->sh_size;
+ movesec = j;
}
- phdr[e].p_vaddr += shdr[k].sh_addr - old_addr[k];
- phdr[e].p_paddr += shdr[k].sh_addr - old_addr[k];
- phdr[e].p_offset += shdr[k].sh_addr - old_addr[k];
break;
}
- if (j < last)
- /* Now continue as if there was place for a new PT_LOAD
- in ElfW(Phdr) table initially. */
- break;
- else
- {
- GElf_Shdr moveshdr;
- int newidx, ret, movedidx, oldidx;
-
- moveshdr = shdr[movesec];
- newidx = remove_readonly_section (ehdr, shdr, movesec, adjust);
- oldidx = adjust->move->new_to_old[movesec];
- remove_section (adjust->move, movesec);
- ret = find_readonly_space (dso, add, ehdr, phdr, shdr, adjust);
- if (ret == 0)
- return 0;
- movedidx = find_readonly_space (dso, &moveshdr, ehdr, phdr,
- shdr, adjust);
- if (movedidx == 0)
+ if (start + shdr[j].sh_size <= endaddr)
+ {
+ movesec = j + 1;
+ break;
+ }
+ start += shdr[j].sh_size;
+ }
+
+ if (movesec == -1)
+ {
+ error (0, 0, "%s: No space in ELF segment table to add new ELF segment",
+ dso->filename);
return 0;
- if (newidx != -1)
- adjust->new[newidx] = movedidx;
- add_section (adjust->move, movedidx);
- if (oldidx != -1)
- {
- adjust->move->old_to_new[oldidx] = movedidx;
- adjust->move->new_to_old[movedidx] = oldidx;
- }
- if (movedidx <= ret)
- ++ret;
- return ret;
- }
- }
- }
+ }
+
+ start = phdr[i].p_vaddr - phdr[i].p_offset + ehdr->e_phoff
+ + (ehdr->e_phnum + 1) * ehdr->e_phentsize;
+ old_addr = (GElf_Addr *) alloca (movesec * sizeof (GElf_Addr));
+ for (k = 1; k < movesec; ++k)
+ {
+ start = (start + shdr[k].sh_addralign - 1)
+ & ~(shdr[k].sh_addralign - 1);
+ old_addr[k] = shdr[k].sh_addr;
+ shdr[k].sh_addr = start;
+ shdr[k].sh_offset = start + phdr[i].p_offset
+ - phdr[i].p_vaddr;
+ start += shdr[k].sh_size;
+ }
+
+ for (e = 0; e < ehdr->e_phnum; ++e)
+ if (phdr[e].p_type != PT_LOAD
+ && phdr[e].p_type != PT_GNU_STACK)
+ for (k = 1; k < movesec; ++k)
+ if (old_addr[k] == phdr[e].p_vaddr)
+ {
+ if (phdr[e].p_filesz != shdr[k].sh_size
+ || phdr[e].p_memsz != shdr[k].sh_size)
+ {
+ error (0, 0, "%s: Non-PT_LOAD segment spanning more than one section",
+ dso->filename);
+ return 0;
+ }
+ phdr[e].p_vaddr += shdr[k].sh_addr - old_addr[k];
+ phdr[e].p_paddr += shdr[k].sh_addr - old_addr[k];
+ phdr[e].p_offset += shdr[k].sh_addr - old_addr[k];
+ break;
+ }
+
+ if (j < last)
+ /* Now continue as if there was place for a new PT_LOAD
+ in ElfW(Phdr) table initially. */
+ break;
+ else
+ {
+ GElf_Shdr moveshdr;
+ int newidx, ret, movedidx, oldidx;
+
+ moveshdr = shdr[movesec];
+ newidx = remove_readonly_section (ehdr, shdr, movesec, adjust);
+ oldidx = adjust->move->new_to_old[movesec];
+ remove_section (adjust->move, movesec);
+ ret = find_readonly_space (dso, add, ehdr, phdr, shdr, adjust);
+ if (ret == 0)
+ return 0;
+ movedidx = find_readonly_space (dso, &moveshdr, ehdr, phdr,
+ shdr, adjust);
+ if (movedidx == 0)
+ return 0;
+ if (newidx != -1)
+ adjust->new[newidx] = movedidx;
+ add_section (adjust->move, movedidx);
+ if (oldidx != -1)
+ {
+ adjust->move->old_to_new[oldidx] = movedidx;
+ adjust->move->new_to_old[movedidx] = oldidx;
+ }
+ if (movedidx <= ret)
+ ++ret;
+ return ret;
+ }
+ }
+ }
for (i = 0, j = 0; i < ehdr->e_phnum; ++i)
if (phdr[i].p_type == PT_LOAD)