Path: blob/main/contrib/elftoolchain/elfcopy/segments.c
39507 views
/*-1* Copyright (c) 2007-2010,2012 Kai Wang2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/queue.h>27#include <err.h>28#include <gelf.h>29#include <stdint.h>30#include <stdio.h>31#include <stdlib.h>32#include <string.h>3334#include "elfcopy.h"3536ELFTC_VCSID("$Id: segments.c 3615 2018-05-17 04:12:24Z kaiwang27 $");3738static void insert_to_inseg_list(struct segment *seg, struct section *sec);3940/*41* elfcopy's segment handling is relatively simpler and less powerful than42* libbfd. Program headers are modified or copied from input to output objects,43* but never re-generated. As a result, if the input object has incorrect44* program headers, the output object's program headers will remain incorrect45* or become even worse.46*/4748/*49* Check whether a section is "loadable". If so, add it to the50* corresponding segment list(s) and return 1.51*/52int53add_to_inseg_list(struct elfcopy *ecp, struct section *s)54{55struct segment *seg;56int loadable;5758if (ecp->ophnum == 0)59return (0);6061/*62* Segment is a different view of an ELF object. One segment can63* contain one or more sections, and one section can be included64* in one or more segments, or not included in any segment at all.65* We call those sections which can be found in one or more segments66* "loadable" sections, and call the rest "unloadable" sections.67* We keep track of "loadable" sections in their containing68* segment(s)' v_sec queue. These information are later used to69* recalculate the extents of segments, when sections are removed,70* for example.71*/72loadable = 0;73STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {74if (s->off < seg->off || (s->vma < seg->vaddr && !s->pseudo))75continue;76if (s->off + s->sz > seg->off + seg->fsz &&77s->type != SHT_NOBITS)78continue;79if (s->vma + s->sz > seg->vaddr + seg->msz)80continue;81if (seg->type == PT_TLS && ((s->flags & SHF_TLS) == 0))82continue;8384insert_to_inseg_list(seg, s);85if (seg->type == PT_LOAD)86s->seg = seg;87else if (seg->type == PT_TLS)88s->seg_tls = seg;89if (s->pseudo)90s->vma = seg->vaddr + (s->off - seg->off);91if (seg->paddr > 0)92s->lma = seg->paddr + (s->off - seg->off);93else94s->lma = 0;95loadable = 1;96}9798return (loadable);99}100101void102adjust_addr(struct elfcopy *ecp)103{104struct section *s, *s0;105struct segment *seg;106struct sec_action *sac;107uint64_t dl, vma, lma, start, end;108int found, i;109110/*111* Apply VMA and global LMA changes in the first iteration.112*/113TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {114115/* Only adjust loadable section's address. */116if (!s->loadable)117continue;118119/* Apply global VMA adjustment. */120if (ecp->change_addr != 0)121s->vma += ecp->change_addr;122123/* Apply global LMA adjustment. */124if (ecp->change_addr != 0 && s->seg != NULL &&125s->seg->paddr > 0)126s->lma += ecp->change_addr;127}128129/*130* Apply sections VMA change in the second iteration.131*/132TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {133134if (!s->loadable)135continue;136137/*138* Check if there is a VMA change request for this139* section.140*/141sac = lookup_sec_act(ecp, s->name, 0);142if (sac == NULL)143continue;144vma = s->vma;145if (sac->setvma)146vma = sac->vma;147if (sac->vma_adjust != 0)148vma += sac->vma_adjust;149if (vma == s->vma)150continue;151152/*153* No need to make segment adjustment if the section doesn't154* belong to any segment.155*/156if (s->seg == NULL) {157s->vma = vma;158continue;159}160161/*162* Check if the VMA change is viable.163*164* 1. Check if the new VMA is properly aligned accroding to165* section alignment.166*167* 2. Compute the new extent of segment that contains this168* section, make sure it doesn't overlap with other169* segments.170*/171#ifdef DEBUG172printf("VMA for section %s: %#jx\n", s->name, vma);173#endif174175if (vma % s->align != 0)176errx(EXIT_FAILURE, "The VMA %#jx for "177"section %s is not aligned to %ju",178(uintmax_t) vma, s->name, (uintmax_t) s->align);179180if (vma < s->vma) {181/* Move section to lower address. */182if (vma < s->vma - s->seg->vaddr)183errx(EXIT_FAILURE, "Not enough space to move "184"section %s VMA to %#jx", s->name,185(uintmax_t) vma);186start = vma - (s->vma - s->seg->vaddr);187if (s == s->seg->v_sec[s->seg->nsec - 1])188end = start + s->seg->msz;189else190end = s->seg->vaddr + s->seg->msz;191} else {192/* Move section to upper address. */193if (s == s->seg->v_sec[0])194start = vma;195else196start = s->seg->vaddr;197end = vma + (s->seg->vaddr + s->seg->msz - s->vma);198if (end < start)199errx(EXIT_FAILURE, "Not enough space to move "200"section %s VMA to %#jx", s->name,201(uintmax_t) vma);202}203204#ifdef DEBUG205printf("new extent for segment containing %s: (%#jx,%#jx)\n",206s->name, start, end);207#endif208209STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {210if (seg == s->seg || seg->type != PT_LOAD)211continue;212if (start > seg->vaddr + seg->msz)213continue;214if (end < seg->vaddr)215continue;216errx(EXIT_FAILURE, "The extent of segment containing "217"section %s overlaps with segment(%#jx,%#jx)",218s->name, (uintmax_t) seg->vaddr,219(uintmax_t) (seg->vaddr + seg->msz));220}221222/*223* Update section VMA and file offset.224*/225226if (vma < s->vma) {227/*228* To move a section to lower VMA, we decrease229* the VMA of the section and all the sections that230* are before it, and we increase the file offsets231* of all the sections that are after it.232*/233dl = s->vma - vma;234for (i = 0; i < s->seg->nsec; i++) {235s0 = s->seg->v_sec[i];236s0->vma -= dl;237#ifdef DEBUG238printf("section %s VMA set to %#jx\n",239s0->name, (uintmax_t) s0->vma);240#endif241if (s0 == s)242break;243}244for (i = i + 1; i < s->seg->nsec; i++) {245s0 = s->seg->v_sec[i];246s0->off += dl;247#ifdef DEBUG248printf("section %s offset set to %#jx\n",249s0->name, (uintmax_t) s0->off);250#endif251}252} else {253/*254* To move a section to upper VMA, we increase255* the VMA of the section and all the sections that256* are after it, and we increase the their file257* offsets too unless the section in question258* is the first in its containing segment.259*/260dl = vma - s->vma;261for (i = 0; i < s->seg->nsec; i++)262if (s->seg->v_sec[i] == s)263break;264if (i >= s->seg->nsec)265errx(EXIT_FAILURE, "Internal: section `%s' not"266" found in its containing segement",267s->name);268for (; i < s->seg->nsec; i++) {269s0 = s->seg->v_sec[i];270s0->vma += dl;271#ifdef DEBUG272printf("section %s VMA set to %#jx\n",273s0->name, (uintmax_t) s0->lma);274#endif275if (s != s->seg->v_sec[0]) {276s0->off += dl;277#ifdef DEBUG278printf("section %s offset set to %#jx\n",279s0->name, (uintmax_t) s0->off);280#endif281}282}283}284}285286/*287* Apply load address padding.288*/289290if (ecp->pad_to != 0) {291292/*293* Find the section with highest VMA.294*/295s = NULL;296STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {297if (seg->type != PT_LOAD)298continue;299for (i = seg->nsec - 1; i >= 0; i--)300if (seg->v_sec[i]->type != SHT_NOBITS)301break;302if (i < 0)303continue;304if (s == NULL)305s = seg->v_sec[i];306else {307s0 = seg->v_sec[i];308if (s0->vma > s->vma)309s = s0;310}311}312313if (s == NULL)314goto adjust_lma;315316/* No need to pad if the pad_to address is lower. */317if (ecp->pad_to <= s->vma + s->sz)318goto adjust_lma;319320s->pad_sz = ecp->pad_to - (s->vma + s->sz);321#ifdef DEBUG322printf("pad section %s VMA to address %#jx by %#jx\n", s->name,323(uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);324#endif325}326327328adjust_lma:329330/*331* Apply sections LMA change in the third iteration.332*/333TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {334335/*336* Only loadable section that's inside a segment can have337* LMA adjusted. Also, if LMA of the containing segment is338* set to 0, it probably means we should ignore the LMA.339*/340if (!s->loadable || s->seg == NULL || s->seg->paddr == 0)341continue;342343/*344* Check if there is a LMA change request for this345* section.346*/347sac = lookup_sec_act(ecp, s->name, 0);348if (sac == NULL)349continue;350if (!sac->setlma && sac->lma_adjust == 0)351continue;352lma = s->lma;353if (sac->setlma)354lma = sac->lma;355if (sac->lma_adjust != 0)356lma += sac->lma_adjust;357if (lma == s->lma)358continue;359360#ifdef DEBUG361printf("LMA for section %s: %#jx\n", s->name, lma);362#endif363364/* Check alignment. */365if (lma % s->align != 0)366errx(EXIT_FAILURE, "The LMA %#jx for "367"section %s is not aligned to %ju",368(uintmax_t) lma, s->name, (uintmax_t) s->align);369370/*371* Update section LMA.372*/373374if (lma < s->lma) {375/*376* To move a section to lower LMA, we decrease377* the LMA of the section and all the sections that378* are before it.379*/380dl = s->lma - lma;381for (i = 0; i < s->seg->nsec; i++) {382s0 = s->seg->v_sec[i];383s0->lma -= dl;384#ifdef DEBUG385printf("section %s LMA set to %#jx\n",386s0->name, (uintmax_t) s0->lma);387#endif388if (s0 == s)389break;390}391} else {392/*393* To move a section to upper LMA, we increase394* the LMA of the section and all the sections that395* are after it.396*/397dl = lma - s->lma;398for (i = 0; i < s->seg->nsec; i++)399if (s->seg->v_sec[i] == s)400break;401if (i >= s->seg->nsec)402errx(EXIT_FAILURE, "Internal: section `%s' not"403" found in its containing segement",404s->name);405for (; i < s->seg->nsec; i++) {406s0 = s->seg->v_sec[i];407s0->lma += dl;408#ifdef DEBUG409printf("section %s LMA set to %#jx\n",410s0->name, (uintmax_t) s0->lma);411#endif412}413}414}415416/*417* Issue a warning if there are VMA/LMA adjust requests for418* some nonexistent sections.419*/420if ((ecp->flags & NO_CHANGE_WARN) == 0) {421STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) {422if (!sac->setvma && !sac->setlma &&423!sac->vma_adjust && !sac->lma_adjust)424continue;425found = 0;426TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {427if (s->pseudo || s->name == NULL)428continue;429if (!strcmp(s->name, sac->name)) {430found = 1;431break;432}433}434if (!found)435warnx("cannot find section `%s'", sac->name);436}437}438}439440static void441insert_to_inseg_list(struct segment *seg, struct section *sec)442{443struct section *s;444int i;445446seg->nsec++;447seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec));448if (seg->v_sec == NULL)449err(EXIT_FAILURE, "realloc failed");450451/*452* Sort the section in order of offset.453*/454455for (i = seg->nsec - 1; i > 0; i--) {456s = seg->v_sec[i - 1];457if (sec->off >= s->off) {458seg->v_sec[i] = sec;459break;460} else461seg->v_sec[i] = s;462}463if (i == 0)464seg->v_sec[0] = sec;465}466467void468setup_phdr(struct elfcopy *ecp)469{470struct segment *seg;471GElf_Phdr iphdr;472size_t iphnum, i;473474if (elf_getphnum(ecp->ein, &iphnum) == 0)475errx(EXIT_FAILURE, "elf_getphnum failed: %s",476elf_errmsg(-1));477478ecp->ophnum = ecp->iphnum = iphnum;479if (iphnum == 0)480return;481482/* If --only-keep-debug is specified, discard all program headers. */483if (ecp->strip == STRIP_NONDEBUG) {484ecp->ophnum = 0;485return;486}487488for (i = 0; i < iphnum; i++) {489if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)490errx(EXIT_FAILURE, "gelf_getphdr failed: %s",491elf_errmsg(-1));492if ((seg = calloc(1, sizeof(*seg))) == NULL)493err(EXIT_FAILURE, "calloc failed");494seg->vaddr = iphdr.p_vaddr;495seg->paddr = iphdr.p_paddr;496seg->off = iphdr.p_offset;497seg->fsz = iphdr.p_filesz;498seg->msz = iphdr.p_memsz;499seg->type = iphdr.p_type;500STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list);501}502}503504void505copy_phdr(struct elfcopy *ecp)506{507struct segment *seg;508struct section *s;509GElf_Phdr iphdr, ophdr;510int i;511512STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {513if (seg->type == PT_PHDR) {514if (!TAILQ_EMPTY(&ecp->v_sec)) {515s = TAILQ_FIRST(&ecp->v_sec);516if (s->pseudo) {517seg->vaddr = s->vma +518gelf_fsize(ecp->eout, ELF_T_EHDR,5191, EV_CURRENT);520seg->paddr = s->lma +521gelf_fsize(ecp->eout, ELF_T_EHDR,5221, EV_CURRENT);523}524}525seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,526ecp->ophnum, EV_CURRENT);527continue;528}529530if (seg->nsec > 0) {531s = seg->v_sec[0];532seg->vaddr = s->vma;533seg->paddr = s->lma;534}535536seg->fsz = seg->msz = 0;537for (i = 0; i < seg->nsec; i++) {538s = seg->v_sec[i];539seg->msz = s->vma + s->sz - seg->vaddr;540if (s->type != SHT_NOBITS)541seg->fsz = s->off + s->sz - seg->off;542}543}544545/*546* Allocate space for program headers, note that libelf keep547* track of the number in internal variable, and a call to548* elf_update is needed to update e_phnum of ehdr.549*/550if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)551errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",552elf_errmsg(-1));553554/*555* This elf_update() call is to update the e_phnum field in556* ehdr. It's necessary because later we will call gelf_getphdr(),557* which does sanity check by comparing ndx argument with e_phnum.558*/559if (elf_update(ecp->eout, ELF_C_NULL) < 0)560errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));561562/*563* iphnum == ophnum, since we don't remove program headers even if564* they no longer contain sections.565*/566i = 0;567STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {568if (i >= ecp->iphnum)569break;570if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)571errx(EXIT_FAILURE, "gelf_getphdr failed: %s",572elf_errmsg(-1));573if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)574errx(EXIT_FAILURE, "gelf_getphdr failed: %s",575elf_errmsg(-1));576577ophdr.p_type = iphdr.p_type;578ophdr.p_vaddr = seg->vaddr;579ophdr.p_paddr = seg->paddr;580ophdr.p_flags = iphdr.p_flags;581ophdr.p_align = iphdr.p_align;582ophdr.p_offset = seg->off;583ophdr.p_filesz = seg->fsz;584ophdr.p_memsz = seg->msz;585if (!gelf_update_phdr(ecp->eout, i, &ophdr))586errx(EXIT_FAILURE, "gelf_update_phdr failed: %s",587elf_errmsg(-1));588589i++;590}591}592593594