elfcopy: overhaul of LMA handling
Merge ELF Tool Chain r3434: Previously, elfcopy defines: VMA: section virtual address LMA: PHDR(p_vaddr) While binutils(libbfd) defines: VMA: section virtual address and PHDR(p_vaddr). LMA: PHDR(p_paddr) For elfcopy, p_paddr is considered not meaningful and is always set to the same value as p_vaddr. elfcopy was implemented that way because I thought p_paddr is not used/meaningful according to the ELF ABI. However it turned out p_paddr is at least used in some ELF files, e.g. the FreeBSD kernel. This change made elfcopy treat p_paddr as LMA, same as libbfd. (However, some VMA/LMA related command line option still need tweaking to make them compatible with binutils objcopy. This will be improved later) Ticket: #524 And typo fixes in r3435 and r3436. This fixes the Xen kernel build. Submitted by: kaiw Tested by: royger
This commit is contained in:
parent
c8f59118be
commit
676e25ab0f
@ -139,7 +139,8 @@ struct section {
|
||||
|
||||
/* Internal data structure for segments. */
|
||||
struct segment {
|
||||
uint64_t addr; /* load addr */
|
||||
uint64_t vaddr; /* virtual addr (VMA) */
|
||||
uint64_t paddr; /* physical addr (LMA) */
|
||||
uint64_t off; /* file offset */
|
||||
uint64_t fsz; /* file size */
|
||||
uint64_t msz; /* memory size */
|
||||
|
@ -72,12 +72,12 @@ add_to_inseg_list(struct elfcopy *ecp, struct section *s)
|
||||
*/
|
||||
loadable = 0;
|
||||
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
|
||||
if (s->off < seg->off || (s->vma < seg->addr && !s->pseudo))
|
||||
if (s->off < seg->off || (s->vma < seg->vaddr && !s->pseudo))
|
||||
continue;
|
||||
if (s->off + s->sz > seg->off + seg->fsz &&
|
||||
s->type != SHT_NOBITS)
|
||||
continue;
|
||||
if (s->vma + s->sz > seg->addr + seg->msz)
|
||||
if (s->vma + s->sz > seg->vaddr + seg->msz)
|
||||
continue;
|
||||
|
||||
insert_to_inseg_list(seg, s);
|
||||
@ -85,7 +85,12 @@ add_to_inseg_list(struct elfcopy *ecp, struct section *s)
|
||||
s->seg = seg;
|
||||
else if (seg->type == PT_TLS)
|
||||
s->seg_tls = seg;
|
||||
s->lma = seg->addr + (s->off - seg->off);
|
||||
if (s->pseudo)
|
||||
s->vma = seg->vaddr + (s->off - seg->off);
|
||||
if (seg->paddr > 0)
|
||||
s->lma = seg->paddr + (s->off - seg->off);
|
||||
else
|
||||
s->lma = 0;
|
||||
loadable = 1;
|
||||
}
|
||||
|
||||
@ -98,7 +103,7 @@ adjust_addr(struct elfcopy *ecp)
|
||||
struct section *s, *s0;
|
||||
struct segment *seg;
|
||||
struct sec_action *sac;
|
||||
uint64_t dl, lma, start, end;
|
||||
uint64_t dl, vma, lma, start, end;
|
||||
int found, i;
|
||||
|
||||
/*
|
||||
@ -110,59 +115,52 @@ adjust_addr(struct elfcopy *ecp)
|
||||
if (!s->loadable)
|
||||
continue;
|
||||
|
||||
/* Apply global VMA adjustment. */
|
||||
if (ecp->change_addr != 0)
|
||||
s->vma += ecp->change_addr;
|
||||
|
||||
/* Apply global LMA adjustment. */
|
||||
if (ecp->change_addr != 0 && s->seg != NULL)
|
||||
if (ecp->change_addr != 0 && s->seg != NULL &&
|
||||
s->seg->paddr > 0)
|
||||
s->lma += ecp->change_addr;
|
||||
|
||||
if (!s->pseudo) {
|
||||
/* Apply global VMA adjustment. */
|
||||
if (ecp->change_addr != 0)
|
||||
s->vma += ecp->change_addr;
|
||||
|
||||
/* Apply section VMA adjustment. */
|
||||
sac = lookup_sec_act(ecp, s->name, 0);
|
||||
if (sac == NULL)
|
||||
continue;
|
||||
if (sac->setvma)
|
||||
s->vma = sac->vma;
|
||||
if (sac->vma_adjust != 0)
|
||||
s->vma += sac->vma_adjust;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply sections LMA change in the second iteration.
|
||||
* Apply sections VMA change in the second iteration.
|
||||
*/
|
||||
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
|
||||
|
||||
/*
|
||||
* Only loadable section that's inside a segment can have
|
||||
* LMA adjusted.
|
||||
*/
|
||||
if (!s->loadable || s->seg == NULL)
|
||||
if (!s->loadable)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check if there is a LMA change request for this
|
||||
* Check if there is a VMA change request for this
|
||||
* section.
|
||||
*/
|
||||
sac = lookup_sec_act(ecp, s->name, 0);
|
||||
if (sac == NULL)
|
||||
continue;
|
||||
if (!sac->setlma && sac->lma_adjust == 0)
|
||||
continue;
|
||||
lma = s->lma;
|
||||
if (sac->setlma)
|
||||
lma = sac->lma;
|
||||
if (sac->lma_adjust != 0)
|
||||
lma += sac->lma_adjust;
|
||||
if (lma == s->lma)
|
||||
vma = s->vma;
|
||||
if (sac->setvma)
|
||||
vma = sac->vma;
|
||||
if (sac->vma_adjust != 0)
|
||||
vma += sac->vma_adjust;
|
||||
if (vma == s->vma)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check if the LMA change is viable.
|
||||
* No need to make segment adjustment if the section doesn't
|
||||
* belong to any segment.
|
||||
*/
|
||||
if (s->seg == NULL) {
|
||||
s->vma = vma;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the VMA change is viable.
|
||||
*
|
||||
* 1. Check if the new LMA is properly aligned accroding to
|
||||
* 1. Check if the new VMA is properly aligned accroding to
|
||||
* section alignment.
|
||||
*
|
||||
* 2. Compute the new extent of segment that contains this
|
||||
@ -170,37 +168,36 @@ adjust_addr(struct elfcopy *ecp)
|
||||
* segments.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
printf("LMA for section %s: %#jx\n", s->name, lma);
|
||||
printf("VMA for section %s: %#jx\n", s->name, vma);
|
||||
#endif
|
||||
|
||||
if (lma % s->align != 0)
|
||||
errx(EXIT_FAILURE, "The load address %#jx for "
|
||||
if (vma % s->align != 0)
|
||||
errx(EXIT_FAILURE, "The VMA %#jx for "
|
||||
"section %s is not aligned to %ju",
|
||||
(uintmax_t) lma, s->name, (uintmax_t) s->align);
|
||||
(uintmax_t) vma, s->name, (uintmax_t) s->align);
|
||||
|
||||
if (lma < s->lma) {
|
||||
if (vma < s->vma) {
|
||||
/* Move section to lower address. */
|
||||
if (lma < s->lma - s->seg->addr)
|
||||
if (vma < s->vma - s->seg->vaddr)
|
||||
errx(EXIT_FAILURE, "Not enough space to move "
|
||||
"section %s load address to %#jx", s->name,
|
||||
(uintmax_t) lma);
|
||||
start = lma - (s->lma - s->seg->addr);
|
||||
"section %s VMA to %#jx", s->name,
|
||||
(uintmax_t) vma);
|
||||
start = vma - (s->vma - s->seg->vaddr);
|
||||
if (s == s->seg->v_sec[s->seg->nsec - 1])
|
||||
end = start + s->seg->msz;
|
||||
else
|
||||
end = s->seg->addr + s->seg->msz;
|
||||
|
||||
end = s->seg->vaddr + s->seg->msz;
|
||||
} else {
|
||||
/* Move section to upper address. */
|
||||
if (s == s->seg->v_sec[0])
|
||||
start = lma;
|
||||
start = vma;
|
||||
else
|
||||
start = s->seg->addr;
|
||||
end = lma + (s->seg->addr + s->seg->msz - s->lma);
|
||||
start = s->seg->vaddr;
|
||||
end = vma + (s->seg->vaddr + s->seg->msz - s->vma);
|
||||
if (end < start)
|
||||
errx(EXIT_FAILURE, "Not enough space to move "
|
||||
"section %s load address to %#jx", s->name,
|
||||
(uintmax_t) lma);
|
||||
"section %s VMA to %#jx", s->name,
|
||||
(uintmax_t) vma);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -211,34 +208,34 @@ adjust_addr(struct elfcopy *ecp)
|
||||
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
|
||||
if (seg == s->seg || seg->type != PT_LOAD)
|
||||
continue;
|
||||
if (start > seg->addr + seg->msz)
|
||||
if (start > seg->vaddr + seg->msz)
|
||||
continue;
|
||||
if (end < seg->addr)
|
||||
if (end < seg->vaddr)
|
||||
continue;
|
||||
errx(EXIT_FAILURE, "The extent of segment containing "
|
||||
"section %s overlaps with segment(%#jx,%#jx)",
|
||||
s->name, (uintmax_t) seg->addr,
|
||||
(uintmax_t) (seg->addr + seg->msz));
|
||||
s->name, (uintmax_t) seg->vaddr,
|
||||
(uintmax_t) (seg->vaddr + seg->msz));
|
||||
}
|
||||
|
||||
/*
|
||||
* Update section LMA and file offset.
|
||||
* Update section VMA and file offset.
|
||||
*/
|
||||
|
||||
if (lma < s->lma) {
|
||||
if (vma < s->vma) {
|
||||
/*
|
||||
* To move a section to lower load address, we decrease
|
||||
* the load addresses of the section and all the
|
||||
* sections that are before it, and we increase the
|
||||
* file offsets of all the sections that are after it.
|
||||
* To move a section to lower VMA, we decrease
|
||||
* the VMA of the section and all the sections that
|
||||
* are before it, and we increase the file offsets
|
||||
* of all the sections that are after it.
|
||||
*/
|
||||
dl = s->lma - lma;
|
||||
dl = s->vma - vma;
|
||||
for (i = 0; i < s->seg->nsec; i++) {
|
||||
s0 = s->seg->v_sec[i];
|
||||
s0->lma -= dl;
|
||||
s0->vma -= dl;
|
||||
#ifdef DEBUG
|
||||
printf("section %s LMA set to %#jx\n",
|
||||
s0->name, (uintmax_t) s0->lma);
|
||||
printf("section %s VMA set to %#jx\n",
|
||||
s0->name, (uintmax_t) s0->vma);
|
||||
#endif
|
||||
if (s0 == s)
|
||||
break;
|
||||
@ -253,13 +250,13 @@ adjust_addr(struct elfcopy *ecp)
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* To move a section to upper load address, we increase
|
||||
* the load addresses of the section and all the
|
||||
* sections that are after it, and we increase the
|
||||
* their file offsets too unless the section in question
|
||||
* To move a section to upper VMA, we increase
|
||||
* the VMA of the section and all the sections that
|
||||
* are after it, and we increase the their file
|
||||
* offsets too unless the section in question
|
||||
* is the first in its containing segment.
|
||||
*/
|
||||
dl = lma - s->lma;
|
||||
dl = vma - s->vma;
|
||||
for (i = 0; i < s->seg->nsec; i++)
|
||||
if (s->seg->v_sec[i] == s)
|
||||
break;
|
||||
@ -269,9 +266,9 @@ adjust_addr(struct elfcopy *ecp)
|
||||
s->name);
|
||||
for (; i < s->seg->nsec; i++) {
|
||||
s0 = s->seg->v_sec[i];
|
||||
s0->lma += dl;
|
||||
s0->vma += dl;
|
||||
#ifdef DEBUG
|
||||
printf("section %s LMA set to %#jx\n",
|
||||
printf("section %s VMA set to %#jx\n",
|
||||
s0->name, (uintmax_t) s0->lma);
|
||||
#endif
|
||||
if (s != s->seg->v_sec[0]) {
|
||||
@ -292,9 +289,8 @@ adjust_addr(struct elfcopy *ecp)
|
||||
if (ecp->pad_to != 0) {
|
||||
|
||||
/*
|
||||
* Find the section with highest load address.
|
||||
* Find the section with highest VMA.
|
||||
*/
|
||||
|
||||
s = NULL;
|
||||
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
|
||||
if (seg->type != PT_LOAD)
|
||||
@ -308,26 +304,113 @@ adjust_addr(struct elfcopy *ecp)
|
||||
s = seg->v_sec[i];
|
||||
else {
|
||||
s0 = seg->v_sec[i];
|
||||
if (s0->lma > s->lma)
|
||||
if (s0->vma > s->vma)
|
||||
s = s0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s == NULL)
|
||||
goto issue_warn;
|
||||
goto adjust_lma;
|
||||
|
||||
/* No need to pad if the pad_to address is lower. */
|
||||
if (ecp->pad_to <= s->lma + s->sz)
|
||||
goto issue_warn;
|
||||
if (ecp->pad_to <= s->vma + s->sz)
|
||||
goto adjust_lma;
|
||||
|
||||
s->pad_sz = ecp->pad_to - (s->lma + s->sz);
|
||||
s->pad_sz = ecp->pad_to - (s->vma + s->sz);
|
||||
#ifdef DEBUG
|
||||
printf("pad section %s load to address %#jx by %#jx\n", s->name,
|
||||
printf("pad section %s VMA to address %#jx by %#jx\n", s->name,
|
||||
(uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
issue_warn:
|
||||
|
||||
adjust_lma:
|
||||
|
||||
/*
|
||||
* Apply sections LMA change in the third iteration.
|
||||
*/
|
||||
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
|
||||
|
||||
/*
|
||||
* Only loadable section that's inside a segment can have
|
||||
* LMA adjusted. Also, if LMA of the containing segment is
|
||||
* set to 0, it probably means we should ignore the LMA.
|
||||
*/
|
||||
if (!s->loadable || s->seg == NULL || s->seg->paddr == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check if there is a LMA change request for this
|
||||
* section.
|
||||
*/
|
||||
sac = lookup_sec_act(ecp, s->name, 0);
|
||||
if (sac == NULL)
|
||||
continue;
|
||||
if (!sac->setlma && sac->lma_adjust == 0)
|
||||
continue;
|
||||
lma = s->lma;
|
||||
if (sac->setlma)
|
||||
lma = sac->lma;
|
||||
if (sac->lma_adjust != 0)
|
||||
lma += sac->lma_adjust;
|
||||
if (lma == s->lma)
|
||||
continue;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("LMA for section %s: %#jx\n", s->name, lma);
|
||||
#endif
|
||||
|
||||
/* Check alignment. */
|
||||
if (lma % s->align != 0)
|
||||
errx(EXIT_FAILURE, "The LMA %#jx for "
|
||||
"section %s is not aligned to %ju",
|
||||
(uintmax_t) lma, s->name, (uintmax_t) s->align);
|
||||
|
||||
/*
|
||||
* Update section LMA.
|
||||
*/
|
||||
|
||||
if (lma < s->lma) {
|
||||
/*
|
||||
* To move a section to lower LMA, we decrease
|
||||
* the LMA of the section and all the sections that
|
||||
* are before it.
|
||||
*/
|
||||
dl = s->lma - lma;
|
||||
for (i = 0; i < s->seg->nsec; i++) {
|
||||
s0 = s->seg->v_sec[i];
|
||||
s0->lma -= dl;
|
||||
#ifdef DEBUG
|
||||
printf("section %s LMA set to %#jx\n",
|
||||
s0->name, (uintmax_t) s0->lma);
|
||||
#endif
|
||||
if (s0 == s)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* To move a section to upper LMA, we increase
|
||||
* the LMA of the section and all the sections that
|
||||
* are after it.
|
||||
*/
|
||||
dl = lma - s->lma;
|
||||
for (i = 0; i < s->seg->nsec; i++)
|
||||
if (s->seg->v_sec[i] == s)
|
||||
break;
|
||||
if (i >= s->seg->nsec)
|
||||
errx(EXIT_FAILURE, "Internal: section `%s' not"
|
||||
" found in its containing segement",
|
||||
s->name);
|
||||
for (; i < s->seg->nsec; i++) {
|
||||
s0 = s->seg->v_sec[i];
|
||||
s0->lma += dl;
|
||||
#ifdef DEBUG
|
||||
printf("section %s LMA set to %#jx\n",
|
||||
s0->name, (uintmax_t) s0->lma);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue a warning if there are VMA/LMA adjust requests for
|
||||
@ -408,7 +491,8 @@ setup_phdr(struct elfcopy *ecp)
|
||||
elf_errmsg(-1));
|
||||
if ((seg = calloc(1, sizeof(*seg))) == NULL)
|
||||
err(EXIT_FAILURE, "calloc failed");
|
||||
seg->addr = iphdr.p_vaddr;
|
||||
seg->vaddr = iphdr.p_vaddr;
|
||||
seg->paddr = iphdr.p_paddr;
|
||||
seg->off = iphdr.p_offset;
|
||||
seg->fsz = iphdr.p_filesz;
|
||||
seg->msz = iphdr.p_memsz;
|
||||
@ -429,20 +513,30 @@ copy_phdr(struct elfcopy *ecp)
|
||||
if (seg->type == PT_PHDR) {
|
||||
if (!TAILQ_EMPTY(&ecp->v_sec)) {
|
||||
s = TAILQ_FIRST(&ecp->v_sec);
|
||||
if (s->pseudo)
|
||||
seg->addr = s->lma +
|
||||
if (s->pseudo) {
|
||||
seg->vaddr = s->vma +
|
||||
gelf_fsize(ecp->eout, ELF_T_EHDR,
|
||||
1, EV_CURRENT);
|
||||
seg->paddr = s->lma +
|
||||
gelf_fsize(ecp->eout, ELF_T_EHDR,
|
||||
1, EV_CURRENT);
|
||||
}
|
||||
}
|
||||
seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
|
||||
ecp->ophnum, EV_CURRENT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seg->nsec > 0) {
|
||||
s = seg->v_sec[0];
|
||||
seg->vaddr = s->vma;
|
||||
seg->paddr = s->lma;
|
||||
}
|
||||
|
||||
seg->fsz = seg->msz = 0;
|
||||
for (i = 0; i < seg->nsec; i++) {
|
||||
s = seg->v_sec[i];
|
||||
seg->msz = s->vma + s->sz - seg->addr;
|
||||
seg->msz = s->vma + s->sz - seg->vaddr;
|
||||
if (s->type != SHT_NOBITS)
|
||||
seg->fsz = s->off + s->sz - seg->off;
|
||||
}
|
||||
@ -481,8 +575,8 @@ copy_phdr(struct elfcopy *ecp)
|
||||
elf_errmsg(-1));
|
||||
|
||||
ophdr.p_type = iphdr.p_type;
|
||||
ophdr.p_vaddr = seg->addr;
|
||||
ophdr.p_paddr = seg->addr;
|
||||
ophdr.p_vaddr = seg->vaddr;
|
||||
ophdr.p_paddr = seg->paddr;
|
||||
ophdr.p_flags = iphdr.p_flags;
|
||||
ophdr.p_align = iphdr.p_align;
|
||||
ophdr.p_offset = seg->off;
|
||||
|
Loading…
x
Reference in New Issue
Block a user