summaryrefslogtreecommitdiffstats
path: root/libelf/elf32_updatefile.c
diff options
context:
space:
mode:
authorUlrich Drepper <[email protected]>2009-01-22 16:32:15 -0800
committerUlrich Drepper <[email protected]>2009-01-22 16:32:15 -0800
commit5ece80960dac84eae27270f73ecbb6fba17a666c (patch)
tree0666b9c49189db6cd1df7ea5ab6dfb5bd323f1ae /libelf/elf32_updatefile.c
parent23fba7582ad1e9f383dc0519635ac1bfe2acba48 (diff)
When writing ELF files in libelf, fill the gap between sections even if only
the section at the start of the gap has been changed.
Diffstat (limited to 'libelf/elf32_updatefile.c')
-rw-r--r--libelf/elf32_updatefile.c139
1 files changed, 95 insertions, 44 deletions
diff --git a/libelf/elf32_updatefile.c b/libelf/elf32_updatefile.c
index f23035a5..def4c09a 100644
--- a/libelf/elf32_updatefile.c
+++ b/libelf/elf32_updatefile.c
@@ -126,11 +126,10 @@ int
internal_function
__elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
{
- ElfW2(LIBELFBITS,Ehdr) *ehdr;
- char *last_position;
+ bool previous_scn_changed = false;
/* We need the ELF header several times. */
- ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
+ ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
/* Write out the ELF header. */
if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
@@ -160,6 +159,10 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
sizeof (ElfW2(LIBELFBITS,Ehdr)));
elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
+
+ /* We start writing sections after the ELF header only if there is
+ no program header. */
+ previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
}
/* Write out the program header table. */
@@ -200,14 +203,19 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
sizeof (ElfW2(LIBELFBITS,Phdr)) * ehdr->e_phnum);
elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
+
+ /* We modified the program header. Maybe this created a gap so
+ we have to write fill bytes, if necessary. */
+ previous_scn_changed = true;
}
/* From now on we have to keep track of the last position to eventually
fill the gaps with the prescribed fill byte. */
- last_position = ((char *) elf->map_address + elf->start_offset
- + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
- ehdr->e_phoff)
- + elf_typesize (LIBELFBITS, ELF_T_PHDR, ehdr->e_phnum));
+ char *last_position = ((char *) elf->map_address + elf->start_offset
+ + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
+ ehdr->e_phoff)
+ + elf_typesize (LIBELFBITS, ELF_T_PHDR,
+ ehdr->e_phnum));
/* Write all the sections. Well, only those which are modified. */
if (shnum > 0)
@@ -286,7 +294,7 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
if (scn->index == 0)
{
/* The dummy section header entry. It should not be
- possible to make this "section" as dirty. */
+ possible to mark this "section" as dirty. */
assert ((scn->flags & ELF_F_DIRTY) == 0);
continue;
}
@@ -298,6 +306,25 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
char *scn_start = ((char *) elf->map_address
+ elf->start_offset + shdr->sh_offset);
Elf_Data_List *dl = &scn->data_list;
+ bool scn_changed = false;
+
+ void fill_mmap (size_t offset)
+ {
+ size_t written = 0;
+
+ if (last_position < shdr_start)
+ {
+ written = MIN (scn_start + offset - last_position,
+ shdr_start - last_position);
+
+ memset (last_position, __libelf_fill_byte, written);
+ }
+
+ if (last_position + written != scn_start + offset
+ && shdr_end < scn_start + offset)
+ memset (shdr_end, __libelf_fill_byte,
+ scn_start + offset - shdr_end);
+ }
if (scn->data_list_rear != NULL)
do
@@ -307,31 +334,15 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
assert (dl->data.d.d_size <= (shdr->sh_size
- (GElf_Off) dl->data.d.d_off));
+ /* If there is a gap, fill it. */
+ if (scn_start + dl->data.d.d_off > last_position
+ && ((previous_scn_changed && dl->data.d.d_off == 0)
+ || ((scn->flags | dl->flags | elf->flags)
+ & ELF_F_DIRTY) != 0))
+ fill_mmap (dl->data.d.d_off);
+
if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
{
- if (scn_start + dl->data.d.d_off > last_position)
- {
- /* This code assumes that the data blocks for
- a section are ordered by offset. */
- size_t written = 0;
-
- if (last_position < shdr_start)
- {
- written = MIN (scn_start + dl->data.d.d_off
- - last_position,
- shdr_start - last_position);
-
- memset (last_position, __libelf_fill_byte,
- written);
- }
-
- if (last_position + written
- != scn_start + dl->data.d.d_off
- && shdr_end < scn_start + dl->data.d.d_off)
- memset (shdr_end, __libelf_fill_byte,
- scn_start + dl->data.d.d_off - shdr_end);
- }
-
/* Let it go backward if the sections use a bogus
layout with overlaps. We'll overwrite the stupid
user's section data with the latest one, rather than
@@ -359,6 +370,8 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
last_position = mempcpy (last_position,
dl->data.d.d_buf,
dl->data.d.d_size);
+
+ scn_changed = true;
}
else
last_position += dl->data.d.d_size;
@@ -372,9 +385,18 @@ __elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
}
while (dl != NULL);
else
- /* We have to trust the existing section header information. */
- last_position = scn_start + shdr->sh_size;
+ {
+ /* If the previous section (or the ELF/program
+ header) changed we might have to fill the gap. */
+ if (scn_start > last_position && previous_scn_changed)
+ fill_mmap (0);
+
+ /* We have to trust the existing section header information. */
+ last_position = scn_start + shdr->sh_size;
+ }
+
+ previous_scn_changed = scn_changed;
next:
scn->flags &= ~ELF_F_DIRTY;
}
@@ -480,6 +502,7 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
{
char fillbuf[FILLBUFSIZE];
size_t filled = 0;
+ bool previous_scn_changed = false;
/* We need the ELF header several times. */
ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;
@@ -523,6 +546,10 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
}
elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;
+
+ /* We start writing sections after the ELF header only if there is
+ no program header. */
+ previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
}
/* If the type sizes should be different at some time we have to
@@ -588,6 +615,10 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
free (tmp_phdr);
elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;
+
+ /* We modified the program header. Maybe this created a gap so
+ we have to write fill bytes, if necessary. */
+ previous_scn_changed = true;
}
/* From now on we have to keep track of the last position to eventually
@@ -629,7 +660,7 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
if (scn->index == 0)
{
/* The dummy section header entry. It should not be
- possible to make this "section" as dirty. */
+ possible to mark this "section" as dirty. */
assert ((scn->flags & ELF_F_DIRTY) == 0);
continue;
}
@@ -640,24 +671,29 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
off_t scn_start = elf->start_offset + shdr->sh_offset;
Elf_Data_List *dl = &scn->data_list;
+ bool scn_changed = false;
if (scn->data_list_rear != NULL)
do
{
+ /* If there is a gap, fill it. */
+ if (scn_start + dl->data.d.d_off > last_offset
+ && ((previous_scn_changed && dl->data.d.d_off == 0)
+ || ((scn->flags | dl->flags | elf->flags)
+ & ELF_F_DIRTY) != 0))
+ {
+ if (unlikely (fill (elf->fildes, last_offset,
+ (scn_start + dl->data.d.d_off)
+ - last_offset, fillbuf,
+ &filled) != 0))
+ return 1;
+ }
+
if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
{
char tmpbuf[MAX_TMPBUF];
void *buf = dl->data.d.d_buf;
- if (scn_start + dl->data.d.d_off > last_offset)
- {
- if (unlikely (fill (elf->fildes, last_offset,
- (scn_start + dl->data.d.d_off)
- - last_offset, fillbuf,
- &filled) != 0))
- return 1;
- }
-
/* Let it go backward if the sections use a bogus
layout with overlaps. We'll overwrite the stupid
user's section data with the latest one, rather than
@@ -704,6 +740,8 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
if (buf != dl->data.d.d_buf && buf != tmpbuf)
free (buf);
+
+ scn_changed = true;
}
last_offset += dl->data.d.d_size;
@@ -714,8 +752,21 @@ __elfw2(LIBELFBITS,updatefile) (Elf *elf, int change_bo, size_t shnum)
}
while (dl != NULL);
else
- last_offset = scn_start + shdr->sh_size;
+ {
+ /* If the previous section (or the ELF/program
+ header) changed we might have to fill the gap. */
+ if (scn_start > last_offset && previous_scn_changed)
+ {
+ if (unlikely (fill (elf->fildes, last_offset,
+ scn_start - last_offset, fillbuf,
+ &filled) != 0))
+ return 1;
+ }
+
+ last_offset = scn_start + shdr->sh_size;
+ }
+ previous_scn_changed = scn_changed;
next:
/* Collect the section header table information. */
if (unlikely (change_bo))