diff options
Diffstat (limited to 'bfd/mmo.c')
-rw-r--r-- | bfd/mmo.c | 58 |
1 files changed, 43 insertions, 15 deletions
@@ -88,7 +88,11 @@ SUBSECTION directive, setting the location for the next data to the next 32-bit word (for @math{Z = 1}) or 64-bit word (for @math{Z = 2}), plus @math{Y * 2^56}. Normally @samp{Y} is 0 for the text segment - and 2 for the data segment. + and 2 for the data segment. Beware that the low bits of non- + tetrabyte-aligned values are silently discarded when being + automatically incremented and when storing contents (in contrast + to e.g. its use as current location when followed by lop_fixo + et al before the next possibly-quoted tetrabyte contents). @item lop_skip 0x9802YYZZ. Increase the current location by @samp{YZ} bytes. @@ -822,6 +826,9 @@ mmo_write_chunk (bfd *abfd, const bfd_byte *loc, unsigned int len) if (len) { + /* We must have flushed a previous remainder if we get one from + this chunk too. */ + BFD_ASSERT (mmop->byte_no == 0); memcpy (mmop->buf, loc, len); mmop->byte_no = len; } @@ -869,25 +876,27 @@ static bfd_boolean mmo_write_loc_chunk (bfd *abfd, bfd_vma vma, const bfd_byte *loc, unsigned int len, bfd_vma *last_vmap) { - /* Find an initial and trailing section of zero tetras; we don't need to - write out zeros. FIXME: When we do this, we should emit section size - and address specifiers, else objcopy can't always perform an identity - translation. Only do this if we *don't* have left-over data from a - previous write or the vma of this chunk is *not* the next address, - because then data isn't tetrabyte-aligned and we're concatenating to - that left-over data. */ - - if (abfd->tdata.mmo_data->byte_no == 0 || vma != *last_vmap) + /* Find an initial and trailing section of zero (aligned) tetras; we don't + need to write out zeros. FIXME: When we do this, we should emit + section size and address specifiers, else objcopy can't always perform + an identity translation. Only do this if we *don't* have left-over + data from a previous write (and will not add any) or else the vma of + this chunk is *not* the next address, because then data isn't + tetrabyte-aligned and we're concatenating to that left-over data. */ + + if ((vma & 3) == 0 + && (abfd->tdata.mmo_data->byte_no == 0 || vma != *last_vmap)) { - while (len >= 4 && bfd_get_32 (abfd, loc) == 0) + while (len > 4 && bfd_get_32 (abfd, loc) == 0) { vma += 4; len -= 4; loc += 4; } - while (len >= 4 && bfd_get_32 (abfd, loc + len - 4) == 0) - len -= 4; + if ((len & 3) == 0) + while (len > 4 && bfd_get_32 (abfd, loc + len - 4) == 0) + len -= 4; } /* Only write out the location if it's different than the one the caller @@ -897,6 +906,22 @@ mmo_write_loc_chunk (bfd *abfd, bfd_vma vma, const bfd_byte *loc, /* We might be in the middle of a sequence. */ mmo_flush_chunk (abfd); + /* This should not happen during normal usage, but can presumably + happen with an erroneous linker-script, so handle gracefully. + Avoid Knuth-specific terms in the message, such as "tetrabyte". + Note that this function will get non-4-multiple lengths and + unaligned vmas but those come in tuples (mostly pairs) and are + continuous (i.e. the if-condition above false) and they are + group-wise aligned. */ + if ((vma & 3) != 0) + { + (*_bfd_error_handler) + (_("%s: attempt to emit contents at non-multiple-of-4 address 0x%lx\n"), + bfd_get_filename (abfd), (unsigned long) vma); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + /* We always write the location as 64 bits; no use saving bytes here. */ mmo_write_tetra_raw (abfd, (LOP << 24) | (LOP_LOC << 16) | 2); @@ -1583,9 +1608,9 @@ mmo_scan (bfd *abfd) if (bfd_bread (buf, 4, abfd) != 4) goto error_return; + vma &= ~3; mmo_xore_32 (sec, vma, bfd_get_32 (abfd, buf)); vma += 4; - vma &= ~3; lineno++; break; @@ -1617,7 +1642,10 @@ mmo_scan (bfd *abfd) goto error_return; } - sec = mmo_decide_section (abfd, vma); + /* When we decide which section the data goes into, we might + create the section. If that happens, make sure the VMA at + creation time is tetra-aligned. */ + sec = mmo_decide_section (abfd, vma & ~3); if (sec == NULL) goto error_return; break; |