blob: 1a32a59407ffcd5101645ee641717b4bdb8e2a44 [file] [log] [blame]
#include <stdio.h>
#include <common.h>
#include <debug.h>
#include <hash.h>
#include <libelf.h>
#include <libebl.h>
#include <libebl_arm.h>
#include <elf.h>
#include <gelf.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef DEBUG
#include <rangesort.h>
#endif
/* static void print_shdr_array(shdr_info_t *, int); */
#include <elfcopy.h>
#define COPY_SECTION_DATA_BUFFER (0)
/* When this macro is set to a nonzero value, we replace calls to elf_strptr()
on the target ELF handle with code that extracts the strings directly from
the data buffers of that ELF handle. In this case, elf_strptr() does not
work as expected, as it tries to read the data buffer of the associated
string section directly from the file, and that buffer does not exist yet
in the file, since we haven't committed our changes yet.
*/
#define ELF_STRPTR_IS_BROKEN (1)
static void update_relocations_section_symbol_references(Elf *newelf, Elf *elf,
shdr_info_t *info, int info_len,
shdr_info_t *relsect_info,
Elf32_Word *newsymidx);
static void update_relocations_section_offsets(Elf *newelf, Elf *elf, Ebl *ebl,
shdr_info_t *info,
int info_len,
shdr_info_t *relsect_info,
Elf_Data *data,
range_list_t *old_section_ranges);
static void update_hash_table(Elf *newelf, Elf *elf,
Elf32_Word hash_scn_idx,
shdr_info_t *symtab_info);
static inline
Elf_Data *create_section_data(shdr_info_t *, Elf_Scn *);
static Elf64_Off section_to_header_mapping(Elf *elf,
int phdr_idx,
shdr_info_t *shdr_info,
int num_shdr_info,
Elf64_Off *file_end,
Elf64_Off *mem_end);
static void build_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
int dynidx, /* index of .dynamic section */
int symtabidx, /* index of symbol table section */
shdr_info_t *shdr_info,
int shdr_info_len);
#ifdef DEBUG
static void print_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
int dynidx, /* index of .dynamic section */
int symtabidx, /* index of symbol table section */
shdr_info_t *shdr_info,
int shdr_info_len);
#endif
static void adjust_dynamic_segment_offsets(Elf *elf, Ebl *oldebl,
Elf *newelf,
int idx, /* index of .dynamic section */
shdr_info_t *shdr_info,
int shdr_info_len);
static void update_symbol_values(Elf *elf, GElf_Ehdr *ehdr,
Elf *newelf,
shdr_info_t *shdr_info,
int num_shdr_info,
int shady,
int dynamic_idx);
static bool section_belongs_to_header(GElf_Shdr *shdr, GElf_Phdr *phdr);
static range_list_t *
update_section_offsets(Elf *elf,
Elf *newelf,
GElf_Phdr *phdr_info,
shdr_info_t *shdr_info,
int num_shdr_info,
range_list_t *section_ranges,
bool adjust_alloc_section_offsets);
void handle_range_error(range_error_t err, range_t *left, range_t *right);
#ifdef DEBUG
static void
verify_elf(GElf_Ehdr *ehdr, struct shdr_info_t *shdr_info, int shdr_info_len,
GElf_Phdr *phdr_info);
#endif
void adjust_elf(Elf *elf, const char *elf_name,
Elf *newelf, const char *newelf_name __attribute__((unused)),
Ebl *ebl,
GElf_Ehdr *ehdr, /* store ELF header of original library */
bool *sym_filter, int num_symbols,
struct shdr_info_t *shdr_info, int shdr_info_len,
GElf_Phdr *phdr_info,
size_t highest_scn_num,
size_t shnum,
size_t shstrndx,
struct Ebl_Strtab *shst,
bool sections_dropped_or_rearranged,
int dynamic_idx, /* index in shdr_info[] of .dynamic section */
int dynsym_idx, /* index in shdr_info[] of dynamic symbol table */
int shady,
Elf_Data **shstrtab_data,
bool adjust_alloc_section_offsets,
bool rebuild_shstrtab)
{
int cnt; /* general-purpose counter */
Elf_Scn *scn; /* general-purpose section */
*shstrtab_data = NULL;
/* When this flag is true, we have dropped some symbols, which caused
a change in the order of symbols in the symbol table (all symbols after
the removed symbol have shifted forward), and a change in its size as
well. When the symbol table changes this way, we need to modify the
relocation entries that relocate symbols in this symbol table, and we
also need to rebuild the hash table (the hash is outdated).
Note that it is possible to change the symbols in the symbol table
without changing their position (that is, without cutting any symbols
out). If a section that a symbol refers to changes (i.e., moves), we
need to update that section's index in the symbol entry in the symbol
table. Therefore, there are symbol-table changes that can be made and
still have symtab_size_changed == false!
*/
bool symtab_size_changed = false;
/* We allow adjusting of offsets only for files that are shared libraries.
We cannot mess with the relative positions of sections for executable
files, because we do not have enough information to adjust them. The
text section is already linked to fixed addresses.
*/
ASSERT(!adjust_alloc_section_offsets || ehdr->e_type == ET_DYN);
if (!sections_dropped_or_rearranged)
INFO("Note: we aren't dropping or rearranging any sections.\n");
/* Index of the section header table in the shdr_info array. This is
an important variable because it denotes the last section of the old
file, as well as the location of the section-strings section of the
new one.
Note: we use this variable only when we are re-creating the section-
header-strings table. Otherwise, we keep it as zero.
*/
size_t shdridx = shstrndx;
if (rebuild_shstrtab) {
INFO("Creating new section-strings section...\n");
shdridx = shnum;
/* Create the new section-name-strings section */
{
INFO("\tNew index will be %d (was %d).\n", highest_scn_num, shstrndx);
/* Add the section header string table section name. */
shdr_info[shdridx] = shdr_info[shstrndx];
ASSERT(!strcmp(shdr_info[shdridx].name, ".shstrtab"));
shdr_info[shdridx].se = ebl_strtabadd (shst, ".shstrtab", 10);
ASSERT(shdr_info[shdridx].se != NULL);
shdr_info[shdridx].idx = highest_scn_num;
/* Create the section header. */
shdr_info[shdridx].shdr.sh_type = SHT_STRTAB;
shdr_info[shdridx].shdr.sh_flags = 0;
shdr_info[shdridx].shdr.sh_addr = 0;
shdr_info[shdridx].shdr.sh_link = SHN_UNDEF;
shdr_info[shdridx].shdr.sh_info = SHN_UNDEF;
shdr_info[shdridx].shdr.sh_entsize = 0;
shdr_info[shdridx].shdr.sh_offset = shdr_info[shdridx].old_shdr.sh_offset;
shdr_info[shdridx].shdr.sh_addralign = 1;
/* Create the section. */
FAILIF_LIBELF((shdr_info[shdridx].newscn = elf_newscn(newelf)) == NULL,
elf_newscn);
ASSERT(elf_ndxscn (shdr_info[shdridx].newscn) == highest_scn_num);
{
/* Finalize the string table and fill in the correct indices in
the section headers. */
FAILIF_LIBELF((*shstrtab_data =
elf_newdata (shdr_info[shdridx].newscn)) == NULL,
elf_newdata);
ebl_strtabfinalize (shst, *shstrtab_data);
/* We have to set the section size. */
INFO("\tNew size will be %d.\n", (*shstrtab_data)->d_size);
shdr_info[shdridx].shdr.sh_size = (*shstrtab_data)->d_size;
/* Setting the data pointer tells the update loop below not to
copy the information from the original section. */
shdr_info[shdridx].data = *shstrtab_data;
#if COPY_SECTION_DATA_BUFFER
shdr_info[shdridx].data->d_buf = MALLOC(shdr_info[shdridx].data->d_size);
ASSERT((*shstrtab_data)->d_buf);
memcpy(shdr_info[shdridx].data->d_buf, (*shstrtab_data)->d_buf, (*shstrtab_data)->d_size);
#endif
}
}
} /* if (rebuild_shstrtab) */
else {
/* When we are not rebuilding shstrtab, we expect the input parameter
shstrndx to be the index of .shstrtab BOTH in shdr_info[] and in
as a section index in the ELF file.
*/
ASSERT(!strcmp(shdr_info[shdridx].name, ".shstrtab"));
}
INFO("Updating section information...\n");
/* Update the section information. */
#ifdef DEBUG
/* We use this flag to ASSERT that the symbol tables comes
before the .dynamic section in the file. See comments
further below.
*/
bool visited_dynsym = false;
#endif
for (cnt = 1; cnt < shdr_info_len; ++cnt) {
if (shdr_info[cnt].idx > 0) {
Elf_Data *newdata;
INFO("\t%03d: Updating section %s (index %d, address %lld offset %lld, size %lld, alignment %d)...\n",
cnt,
(shdr_info[cnt].name ?: "(no name)"),
shdr_info[cnt].idx,
shdr_info[cnt].shdr.sh_addr,
shdr_info[cnt].shdr.sh_offset,
shdr_info[cnt].shdr.sh_size,
shdr_info[cnt].shdr.sh_addralign);
scn = shdr_info[cnt].newscn;
ASSERT(scn != NULL);
ASSERT(scn == elf_getscn(newelf, shdr_info[cnt].idx));
/* Update the name. */
if (rebuild_shstrtab) {
Elf64_Word new_sh_name = ebl_strtaboffset(shdr_info[cnt].se);
INFO("\t\tname offset %d (was %d).\n",
new_sh_name,
shdr_info[cnt].shdr.sh_name);
shdr_info[cnt].shdr.sh_name = new_sh_name;
}
/* Update the section header from the input file. Some fields
might be section indices which now have to be adjusted. */
if (shdr_info[cnt].shdr.sh_link != 0) {
INFO("\t\tsh_link %d (was %d).\n",
shdr_info[shdr_info[cnt].shdr.sh_link].idx,
shdr_info[cnt].shdr.sh_link);
shdr_info[cnt].shdr.sh_link =
shdr_info[shdr_info[cnt].shdr.sh_link].idx;
}
/* Handle the SHT_REL, SHT_RELA, and SHF_INFO_LINK flag. */
if (SH_INFO_LINK_P (&shdr_info[cnt].shdr)) {
INFO("\t\tsh_info %d (was %d).\n",
shdr_info[shdr_info[cnt].shdr.sh_info].idx,
shdr_info[cnt].shdr.sh_info);
shdr_info[cnt].shdr.sh_info =
shdr_info[shdr_info[cnt].shdr.sh_info].idx;
}
/* Get the data from the old file if necessary. We already
created the data for the section header string table, which
has a section number equal to shnum--hence the ASSERT().
*/
ASSERT(!rebuild_shstrtab || shdr_info[cnt].data || cnt < shnum);
newdata = create_section_data(shdr_info + cnt, scn);
/* We know the size. */
shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size;
/* We have to adjust symbol tables. Each symbol contains
a reference to the section it belongs to. Since we have
renumbered the sections (and dropped some), we need to adjust
the symbols' section indices as well. Also, if we do not want
to keep a symbol, we drop it from the symbol table in this loop.
When we drop symbols from the dynamic-symbol table, we need to
remove the names of the sybmols from the dynamic-symbol-strings
table. Changing the dynamic-symbol-strings table means that we
also have to rebuild the strings that go into the .dynamic
section (such as the DT_NEEDED strings, which lists the libraries
that the file depends on), since those strings are kept in the
same dynamic-symbol-strings table. That latter statement
is an assumption (which we ASSERT against, read on below).
Note: we process the symbol-table sections only when the user
specifies a symbol filter AND that leads to a change in the
symbol table, or when section indices change.
*/
/* The .dynamic section's strings need not be contained in the
same section as the strings of the dynamic symbol table,
but we assume that they are (I haven't seen it be otherwise).
We assert the validity of our assumption here.
If this assertion fails, then we *may* need to reorganize
this code as follows: we will need to call function
build_dynamic_segment_strings() even when sections numbers
don't change and there is no filter. Also, if string section
containing the .dynamic section strings changes, then we'd
need to update the sh_link of the .dynamic section to point
to the new section.
*/
ASSERT(shdr_info[dynamic_idx].shdr.sh_link ==
shdr_info[dynsym_idx].shdr.sh_link);
if (sections_dropped_or_rearranged || (sym_filter != NULL))
{
if(shdr_info[cnt].shdr.sh_type == SHT_DYNSYM)
{
INFO("\t\tupdating a symbol table.\n");
/* Calculate the size of the external representation of a
symbol. */
size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
/* Check the length of the dynamic-symbol filter. (This is the
second of two identical checks, the first one being in
the loop that checks for exceptions.)
NOTE: We narrow this assertion down to the dynamic-symbol
table only. Since we expect the symbol filter to
be parallel to .dynsym, and .dynsym in general
contains fewer symbols than .strtab, we cannot
make this assertion for .strtab.
*/
FAILIF(sym_filter != NULL &&
num_symbols != shdr_info[cnt].data->d_size / elsize,
"Length of dynsym filter (%d) must equal the number"
" of dynamic symbols (%d) in section [%s]!\n",
num_symbols,
shdr_info[cnt].data->d_size / elsize,
shdr_info[cnt].name);
shdr_info[cnt].symse =
(struct Ebl_Strent **)MALLOC(
(shdr_info[cnt].data->d_size/elsize) *
sizeof(struct Ebl_Strent *));
shdr_info[cnt].dynsymst = ebl_strtabinit(1);
FAILIF_LIBELF(NULL == shdr_info[cnt].dynsymst, ebl_strtabinit);
/* Allocate an array of Elf32_Word, one for each symbol. This
array will hold the new symbol indices.
*/
shdr_info[cnt].newsymidx =
(Elf32_Word *)CALLOC(shdr_info[cnt].data->d_size / elsize,
sizeof (Elf32_Word));
bool last_was_local = true;
size_t destidx, // index of the symbol in the new symbol table
inner, // index of the symbol in the old table
last_local_idx = 0;
int num_kept_undefined_and_special = 0;
int num_kept_global_or_weak = 0;
int num_thrown_away = 0;
unsigned long num_symbols = shdr_info[cnt].data->d_size / elsize;
INFO("\t\tsymbol table has %ld symbols.\n", num_symbols);
/* In the loop below, determine whether to remove or not each
symbol.
*/
for (destidx = inner = 1; inner < num_symbols; ++inner)
{
Elf32_Word sec; /* index of section a symbol refers to */
Elf32_Word xshndx; /* extended-section index of symbol */
/* Retrieve symbol information and separate section index
from the symbol table at the given index. */
GElf_Sym sym_mem; /* holds the symbol */
/* Retrieve symbol information and separate section index
from the symbol table at the given index. */
GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data,
NULL, inner,
&sym_mem, &xshndx);
ASSERT(sym != NULL);
FAILIF(sym->st_shndx == SHN_XINDEX,
"Can't handle symbol's st_shndx == SHN_XINDEX!\n");
/* Do not automatically strip the symbol if:
-- the symbol filter is NULL or
-- the symbol is marked to keep or
-- the symbol is neither of:
-- imported or refers to a nonstandard section
-- global
-- weak
We do not want to strip imported symbols, because then
we won't be able to link against them. We do not want
to strip global or weak symbols, because then someone
else will fail to link against them. Finally, we do
not want to strip nonstandard symbols, because we're
not sure what they are doing there.
*/
char *symname = elf_strptr(elf,
shdr_info[cnt].old_shdr.sh_link,
sym->st_name);
if (NULL == sym_filter || /* no symfilter */
sym_filter[inner] || /* keep the symbol! */
/* don't keep the symbol, but the symbol is undefined
or refers to a specific section */
sym->st_shndx == SHN_UNDEF || sym->st_shndx >= shnum ||
/* don't keep the symbol, which defined and refers to
a normal section, but the symbol is neither global
nor weak. */
(ELF32_ST_BIND(sym->st_info) != STB_GLOBAL &&
ELF32_ST_BIND(sym->st_info) != STB_WEAK))
{
/* Do not remove the symbol. */
if (sym->st_shndx == SHN_UNDEF ||
sym->st_shndx >= shnum)
{
/* This symbol has no section index (it is
absolute). Leave the symbol alone unless it is
moved. */
FAILIF_LIBELF(!(destidx == inner ||
gelf_update_symshndx(
shdr_info[cnt].data,
NULL,
destidx,
sym,
xshndx)),
gelf_update_symshndx);
shdr_info[cnt].newsymidx[inner] = destidx;
INFO("\t\t\tkeeping %s symbol %d (new index %d), name [%s]\n",
(sym->st_shndx == SHN_UNDEF ? "undefined" : "special"),
inner,
destidx,
symname);
/* mark the symbol as kept */
if (sym_filter) sym_filter[inner] = 1;
shdr_info[cnt].symse[destidx] =
ebl_strtabadd (shdr_info[cnt].dynsymst,
symname, 0);
ASSERT(shdr_info[cnt].symse[destidx] != NULL);
num_kept_undefined_and_special++;
if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
last_local_idx = destidx;
destidx++;
} else {
/* Get the full section index. */
sec = shdr_info[sym->st_shndx].idx;
if (sec) {
Elf32_Word nxshndx;
ASSERT (sec < SHN_LORESERVE);
nxshndx = 0;
/* Update the symbol only if something changed,
that is, if either the symbol's position in
the symbol table changed (because we deleted
some symbols), or because its section moved!
NOTE: We don't update the symbol's section
index, sym->st_shndx here, but in function
update_symbol_values() instead. The reason
is that if we update the symbol-section index,
now, it won't refer anymore to the shdr_info[]
entry, which we will need in
update_symbol_values().
*/
if (inner != destidx)
{
FAILIF_LIBELF(0 ==
gelf_update_symshndx(
shdr_info[cnt].data,
NULL,
destidx, sym,
nxshndx),
gelf_update_symshndx);
}
shdr_info[cnt].newsymidx[inner] = destidx;
/* If we are not filtering out some symbols,
there's no point to printing this message
for every single symbol. */
if (sym_filter) {
INFO("\t\t\tkeeping symbol %d (new index %d), name (index %d) [%s]\n",
inner,
destidx,
sym->st_name,
symname);
/* mark the symbol as kept */
sym_filter[inner] = 1;
}
shdr_info[cnt].symse[destidx] =
ebl_strtabadd(shdr_info[cnt].dynsymst,
symname, 0);
ASSERT(shdr_info[cnt].symse[destidx] != NULL);
num_kept_global_or_weak++;
if (GELF_ST_BIND(sym->st_info) == STB_LOCAL)
last_local_idx = destidx;
destidx++;
} else {
/* I am not sure, there might be other types of
symbols that do not refer to any section, but
I will handle them case by case when this
assertion fails--I want to know if each of them
is safe to remove!
*/
ASSERT(GELF_ST_TYPE (sym->st_info) == STT_SECTION ||
GELF_ST_TYPE (sym->st_info) == STT_NOTYPE);
INFO("\t\t\tignoring %s symbol [%s]"
" at index %d refering to section %d\n",
(GELF_ST_TYPE(sym->st_info) == STT_SECTION
? "STT_SECTION" : "STT_NOTYPE"),
symname,
inner,
sym->st_shndx);
num_thrown_away++;
/* mark the symbol as thrown away */
if (sym_filter) sym_filter[inner] = 0;
}
}
} /* to strip or not to strip? */
else {
INFO("\t\t\tremoving symbol [%s]\n", symname);
shdr_info[cnt].newsymidx[inner] = (Elf32_Word)-1;
num_thrown_away++;
/* mark the symbol as thrown away */
if (sym_filter) sym_filter[inner] = 0;
}
/* For symbol-table sections, sh_info is one greater than the
symbol table index of the last local symbol. This is why,
when we find the last local symbol, we update the sh_info
field.
*/
if (last_was_local) {
if (GELF_ST_BIND (sym->st_info) != STB_LOCAL) {
last_was_local = false;
if (last_local_idx) {
INFO("\t\t\tMARKING ONE PAST LAST LOCAL INDEX %d\n",
last_local_idx + 1);
shdr_info[cnt].shdr.sh_info =
last_local_idx + 1;
}
else shdr_info[cnt].shdr.sh_info = 0;
}
} else FAILIF(0 && GELF_ST_BIND (sym->st_info) == STB_LOCAL,
"Internal error in ELF file: symbol table has"
" local symbols after first global"
" symbol!\n");
} /* for each symbol */
INFO("\t\t%d undefined or special symbols were kept.\n",
num_kept_undefined_and_special);
INFO("\t\t%d global or weak symbols were kept.\n",
num_kept_global_or_weak);
INFO("\t\t%d symbols were thrown away.\n",
num_thrown_away);
if (destidx != inner) {
/* The symbol table changed. */
INFO("\t\t\tthe symbol table has changed.\n");
INFO("\t\t\tdestidx = %d, inner = %d.\n", destidx, inner);
INFO("\t\t\tnew size %d (was %lld).\n",
destidx * elsize,
shdr_info[cnt].shdr.sh_size);
shdr_info[cnt].shdr.sh_size = newdata->d_size = destidx * elsize;
symtab_size_changed = true;
} else {
/* The symbol table didn't really change. */
INFO("\t\t\tthe symbol table did not change.\n");
FREE (shdr_info[cnt].newsymidx);
shdr_info[cnt].newsymidx = NULL;
}
#ifdef DEBUG
visited_dynsym = shdr_info[cnt].shdr.sh_type == SHT_DYNSYM;
#endif
} /* if it's a symbol table... */
else if (shdr_info[cnt].shdr.sh_type == SHT_DYNAMIC) {
/* We get here either when we drop some sections, or
when we are dropping symbols. If we are not dropping
symbols, then the dynamic-symbol-table and its strings
section won't change, so we won't need to rebuild the
symbols for the SHT_DYNAMIC section either.
NOTE: If ever in the future we add the ability in
adjust_elf() to change the strings in the SHT_DYNAMIC
section, then we would need to find a way to rebuild
the dynamic-symbol-table-strings section.
*/
/* symtab_size_changed has a meaningful value only after
we've processed the symbol table. If this assertion
is ever violated, it will be because the .dynamic section
came before the symbol table in the list of section in
a file. If that happens, then we have to break up the
loop into two: one that finds and processes the symbol
tables, and another, after the first one, that finds
and handles the .dynamic sectio.
*/
ASSERT(visited_dynsym == true);
if (sym_filter != NULL && symtab_size_changed) {
/* Walk the old dynamic segment. For each tag that represents
a string, build an entry into the dynamic-symbol-table's
strings table. */
INFO("\t\tbuilding strings for the dynamic section.\n");
ASSERT(cnt == dynamic_idx);
/* NOTE: By passing the the index (in shdr_info[]) of the
dynamic-symbol table to build_dynamic_segment_strings(),
we are making the assumption that those strings will be
kept in that table. While this does not seem to be
mandated by the ELF spec, it seems to be always the case.
Where else would you put these strings? You already have
the dynamic-symbol table and its strings table, and that's
guaranteed to be in the file, so why not put it there?
*/
build_dynamic_segment_strings(elf, ebl,
dynamic_idx,
dynsym_idx,
shdr_info,
shdr_info_len);
}
else {
INFO("\t\tThe dynamic-symbol table is not changing, so no "
"need to rebuild strings for the dynamic section.\n");
#ifdef DEBUG
print_dynamic_segment_strings(elf, ebl,
dynamic_idx,
dynsym_idx,
shdr_info,
shdr_info_len);
#endif
}
}
}
/* Set the section header in the new file. There cannot be any
overflows. */
INFO("\t\tupdating section header (size %lld)\n",
shdr_info[cnt].shdr.sh_size);
FAILIF(!gelf_update_shdr (scn, &shdr_info[cnt].shdr),
"Could not update section header for section %s!\n",
shdr_info[cnt].name);
} /* if (shdr_info[cnt].idx > 0) */
else INFO("\t%03d: not updating section %s, it will be discarded.\n",
cnt,
shdr_info[cnt].name);
} /* for (cnt = 1; cnt < shdr_info_len; ++cnt) */
/* Now, if we removed some symbols and thus modified the symbol table,
we need to update the hash table, the relocation sections that use these
symbols, and the symbol-strings table to cut out the unused symbols.
*/
if (symtab_size_changed) {
for (cnt = 1; cnt < shnum; ++cnt) {
if (shdr_info[cnt].idx == 0) {
/* Ignore sections which are discarded, unless these sections
are relocation sections. This case is for use by the
prelinker. */
if (shdr_info[cnt].shdr.sh_type != SHT_REL &&
shdr_info[cnt].shdr.sh_type != SHT_RELA) {
continue;
}
}
if (shdr_info[cnt].shdr.sh_type == SHT_REL ||
shdr_info[cnt].shdr.sh_type == SHT_RELA) {
/* shdr_info[cnt].old_shdr.sh_link is index of old symbol-table
section that this relocation-table section was relative to.
We can access shdr_info[] at that index to get to the
symbol-table section.
*/
Elf32_Word *newsymidx =
shdr_info[shdr_info[cnt].old_shdr.sh_link].newsymidx;
/* The referred-to-section must be a symbol table! Note that
alrhough shdr_info[cnt].shdr refers to the updated section
header, this assertion is still valid, since when updating
the section header we never modify the sh_type field.
*/
{
Elf64_Word sh_type =
shdr_info[shdr_info[cnt].shdr.sh_link].shdr.sh_type;
FAILIF(sh_type != SHT_DYNSYM,
"Section refered to from relocation section is not"
" a dynamic symbol table (sh_type=%d)!\n",
sh_type);
}
/* If that symbol table hasn't changed, then its newsymidx
field is NULL (see comments to shdr_info_t), so we
don't have to update this relocation-table section
*/
if (newsymidx == NULL) continue;
update_relocations_section_symbol_references(newelf, elf,
shdr_info, shnum,
shdr_info + cnt,
newsymidx);
} else if (shdr_info[cnt].shdr.sh_type == SHT_HASH) {
/* We have to recompute the hash table. A hash table's
sh_link field refers to the symbol table for which the hash
table is generated.
*/
Elf32_Word symtabidx = shdr_info[cnt].old_shdr.sh_link;
/* We do not have to recompute the hash table if the symbol
table was not changed. */
if (shdr_info[symtabidx].newsymidx == NULL)
continue;
FAILIF(shdr_info[cnt].shdr.sh_entsize != sizeof (Elf32_Word),
"Can't handle 64-bit ELF files!\n");
update_hash_table(newelf, /* new ELF */
elf, /* old ELF */
shdr_info[cnt].idx, /* hash table index */
shdr_info + symtabidx);
} /* if SHT_REL else if SHT_HASH ... */
else if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM)
{
/* The symbol table's sh_link field contains the index of the
strings table for this symbol table. We want to find the
index of the section in the shdr_info[] array. That index
corresponds to the index of the section in the original ELF file,
which is why we look at shdr_info[cnt].old_shdr and not
shdr_info[cnt].shdr.
*/
int symstrndx = shdr_info[cnt].old_shdr.sh_link;
INFO("Updating [%s] (symbol-strings-section data for [%s]).\n",
shdr_info[symstrndx].name,
shdr_info[cnt].name);
ASSERT(shdr_info[symstrndx].newscn);
size_t new_symstrndx = elf_ndxscn(shdr_info[symstrndx].newscn);
Elf_Data *newdata = elf_getdata(shdr_info[symstrndx].newscn, NULL);
ASSERT(NULL != newdata);
INFO("\tbefore update:\n"
"\t\tbuffer: %p\n"
"\t\tsize: %d\n",
newdata->d_buf,
newdata->d_size);
ASSERT(shdr_info[cnt].dynsymst);
ebl_strtabfinalize (shdr_info[cnt].dynsymst, newdata);
INFO("\tafter update:\n"
"\t\tbuffer: %p\n"
"\t\tsize: %d\n",
newdata->d_buf,
newdata->d_size);
FAILIF(new_symstrndx != shdr_info[cnt].shdr.sh_link,
"The index of the symbol-strings table according to elf_ndxscn() is %d, "
"according to shdr_info[] is %d!\n",
new_symstrndx,
shdr_info[cnt].shdr.sh_link);
INFO("%d nonprintable\n",
dump_hex_buffer(stdout, newdata->d_buf, newdata->d_size, 0));
shdr_info[symstrndx].shdr.sh_size = newdata->d_size;
FAILIF(!gelf_update_shdr(shdr_info[symstrndx].newscn,
&shdr_info[symstrndx].shdr),
"Could not update section header for section %s!\n",
shdr_info[symstrndx].name);
/* Now, update the symbol-name offsets. */
{
size_t i;
size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
for (i = 1; i < shdr_info[cnt].shdr.sh_size / elsize; ++i) {
Elf32_Word xshndx;
GElf_Sym sym_mem;
/* retrieve the symbol information; */
GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data,
NULL, i,
&sym_mem, &xshndx);
ASSERT(sym != NULL);
ASSERT(NULL != shdr_info[cnt].symse[i]);
/* calculate the new name offset; */
size_t new_st_name =
ebl_strtaboffset(shdr_info[cnt].symse[i]);
#if 1
ASSERT(!strcmp(newdata->d_buf + new_st_name,
elf_strptr(elf, shdr_info[cnt].old_shdr.sh_link,
sym->st_name)));
#endif
if (sym_filter && (sym->st_name != new_st_name)) {
/* FIXME: For some reason, elf_strptr() does not return the updated
string value here. It looks like ebl_strtabfinalize() doesn't
update libelf's internal structures well enough for elf_strptr()
to work on an ELF file that's being compose.
*/
INFO("Symbol [%s]'s name (index %d, old value %llx) changes offset: %d -> %d\n",
#if 0
newdata->d_buf + new_st_name,
#else
elf_strptr(elf, shdr_info[cnt].old_shdr.sh_link,
sym->st_name),
#endif
i,
sym->st_value,
sym->st_name,
new_st_name);
}
sym->st_name = new_st_name;
/* update the symbol info; */
FAILIF_LIBELF(0 ==
gelf_update_symshndx(
shdr_info[cnt].data,
NULL,
i, sym,
xshndx),
gelf_update_symshndx);
} /* for each symbol... */
}
}
FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_versym,
"Can't handle SHT_GNU_versym!\n");
FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GROUP,
"Can't handle section groups!\n");
} /* for (cnt = 1; cnt < shnum; ++cnt) */
} /* if (symtab_size_changed) */
range_list_t *old_section_ranges = init_range_list();
range_list_t *section_ranges = NULL;
/* Analyze gaps in the ranges before we compact the sections. */
INFO("Analyzing gaps in ranges before compacting sections...\n");
{
size_t scnidx;
/* Gather the ranges */
for (scnidx = 1; scnidx < shdr_info_len; scnidx++) {
if (shdr_info[scnidx].idx > 0) {
if (/*shdr_info[scnidx].old_shdr.sh_type != SHT_NOBITS &&*/
shdr_info[scnidx].old_shdr.sh_flags & SHF_ALLOC) {
add_unique_range_nosort(
old_section_ranges,
shdr_info[scnidx].old_shdr.sh_addr,
shdr_info[scnidx].old_shdr.sh_size,
shdr_info + scnidx,
handle_range_error,
NULL);
}
}
}
sort_ranges(old_section_ranges);
#ifdef DEBUG
int num_ranges;
/* Analyze gaps in the ranges before we compact the sections. */
range_t *ranges = get_sorted_ranges(old_section_ranges, &num_ranges);
if (ranges) {
GElf_Off last_end = ranges->start;
int i;
for (i = 0; i < num_ranges; i++) {
shdr_info_t *curr = (shdr_info_t *)ranges[i].user;
ASSERT(ranges[i].start >= last_end);
int col_before, col_after;
INFO("[%016lld, %016lld] %n[%s]%n",
ranges[i].start,
ranges[i].start + ranges[i].length,
&col_before,
curr->name,
&col_after);
if (ranges[i].start > last_end) {
shdr_info_t *prev = (shdr_info_t *)ranges[i-1].user;
ASSERT(prev && curr);
while (col_after++ - col_before < 20) INFO(" ");
INFO(" [GAP: %lld bytes with %s]\n",
(ranges[i].start - last_end),
prev->name);
}
else INFO("\n");
last_end = ranges[i].start + ranges[i].length;
}
}
#endif/*DEBUG*/
}
/* Calculate the final section offsets */
INFO("Calculating new section offsets...\n");
section_ranges = update_section_offsets(elf,
newelf,
phdr_info,
shdr_info,
shdr_info_len,
init_range_list(),
adjust_alloc_section_offsets);
#ifdef DEBUG
{
/* Analyze gaps in the ranges after we've compacted the sections. */
int num_ranges;
range_t *ranges = get_sorted_ranges(section_ranges, &num_ranges);
if (ranges) {
int last_end = ranges->start;
int i;
for (i = 0; i < num_ranges; i++) {
shdr_info_t *curr = (shdr_info_t *)ranges[i].user;
ASSERT(ranges[i].start >= last_end);
int col_before, col_after;
INFO("[%016lld, %016lld] %n[%s]%n",
ranges[i].start,
ranges[i].start + ranges[i].length,
&col_before,
curr->name,
&col_after);
if (ranges[i].start > last_end) {
shdr_info_t *prev = (shdr_info_t *)ranges[i-1].user;
ASSERT(prev && curr);
while (col_after++ - col_before < 20) INFO(" ");
INFO(" [GAP: %lld bytes with %s]\n",
(ranges[i].start - last_end),
prev->name);
}
else INFO("\n");
last_end = ranges[i].start + ranges[i].length;
}
}
}
#endif
{
/* Now that we have modified the section offsets, we need to scan the
symbol tables once again and update their st_value fields. A symbol's
st_value field (in a shared library) contains the virtual address of the
symbol. For each symbol we encounter, we look up the section it was in.
If that section's virtual address has changed, then we calculate the
delta and update the symbol.
*/
#if 0
{
/* for debugging: Print out all sections and their data pointers and
sizes. */
int i = 1;
for (; i < shdr_info_len; i++) {
PRINT("%8d: %-15s: %2lld %8lld %08lx (%08lx:%8d) %08lx (%08lx:%8d)\n",
i,
shdr_info[i].name,
shdr_info[i].shdr.sh_entsize,
shdr_info[i].shdr.sh_addralign,
(long)shdr_info[i].data,
(long)(shdr_info[i].data ? shdr_info[i].data->d_buf : 0),
(shdr_info[i].data ? shdr_info[i].data->d_size : 0),
(long)shdr_info[i].newdata,
(long)(shdr_info[i].newdata ? shdr_info[i].newdata->d_buf : 0),
(shdr_info[i].newdata ? shdr_info[i].newdata->d_size : 0));
if (!strcmp(shdr_info[i].name, ".got") /* ||
!strcmp(shdr_info[i].name, ".plt") */) {
dump_hex_buffer(stdout,
shdr_info[i].newdata->d_buf,
shdr_info[i].newdata->d_size,
shdr_info[i].shdr.sh_entsize);
}
}
}
#endif
INFO("Updating symbol values...\n");
update_symbol_values(elf, ehdr, newelf, shdr_info, shdr_info_len,
shady,
dynamic_idx);
/* If we are not stripping the debug sections, then we need to adjust
* them accordingly, so that the new ELF file is actually debuggable.
* For that glorios reason, we call update_dwarf(). Note that
* update_dwarf() won't do anything if there, in fact, no debug
* sections to speak of.
*/
INFO("Updating DWARF records...\n");
int num_total_dwarf_patches = 0, num_failed_dwarf_patches = 0;
update_dwarf_if_necessary(
elf, ehdr, newelf,
shdr_info, shdr_info_len,
&num_total_dwarf_patches, &num_failed_dwarf_patches);
INFO("DWARF: %-15s: total %8d failed %8d.\n", elf_name, num_total_dwarf_patches, num_failed_dwarf_patches);
/* Adjust the program-header table. Since the file offsets of the various
sections may have changed, the file offsets of their containing segments
must change as well. We update those offsets in the loop below.
*/
{
INFO("Adjusting program-header table...\n");
int pi; /* program-header index */
for (pi = 0; pi < ehdr->e_phnum; ++pi) {
/* Print the segment number. */
INFO("\t%2.2zu\t", pi);
INFO("PT_ header type: %d", phdr_info[pi].p_type);
if (phdr_info[pi].p_type == PT_NULL) {
INFO(" PT_NULL (skip)\n");
}
else if (phdr_info[pi].p_type == PT_PHDR) {
INFO(" PT_PHDR\n");
ASSERT(phdr_info[pi].p_memsz == phdr_info[pi].p_filesz);
/* Although adjust_elf() does not remove program-header entries,
we perform this update here because I've seen object files
whose PHDR table is bigger by one element than it should be.
Here we check and correct the size, if necessary.
*/
if (phdr_info[pi].p_memsz != ehdr->e_phentsize * ehdr->e_phnum) {
ASSERT(phdr_info[pi].p_memsz > ehdr->e_phentsize * ehdr->e_phnum);
INFO("WARNING: PT_PHDR file and memory sizes are incorrect (%ld instead of %ld). Correcting.\n",
(long)phdr_info[pi].p_memsz,
(long)(ehdr->e_phentsize * ehdr->e_phnum));
phdr_info[pi].p_memsz = ehdr->e_phentsize * ehdr->e_phnum;
phdr_info[pi].p_filesz = phdr_info[pi].p_memsz;
}
}
else {
/* Go over the section array and find which section's offset
field matches this program header's, and update the program
header's offset to reflect the new value.
*/
Elf64_Off file_end, mem_end;
Elf64_Off new_phdr_offset =
section_to_header_mapping(elf, pi,
shdr_info, shdr_info_len,
&file_end,
&mem_end);
if (new_phdr_offset == (Elf64_Off)-1) {
INFO("PT_ header type: %d does not contain any sections.\n",
phdr_info[pi].p_type);
/* Move to the next program header. */
continue;
}
/* Alignments of 0 and 1 mean nothing. Higher alignments are
interpreted as powers of 2. */
if (phdr_info[pi].p_align > 1) {
INFO("\t\tapplying alignment of 0x%llx to new offset %lld\n",
phdr_info[pi].p_align,
new_phdr_offset);
new_phdr_offset &= ~(phdr_info[pi].p_align - 1);
}
Elf32_Sxword delta = new_phdr_offset - phdr_info[pi].p_offset;
INFO("\t\tnew offset %lld (was %lld)\n",
new_phdr_offset,
phdr_info[pi].p_offset);
phdr_info[pi].p_offset = new_phdr_offset;
INFO("\t\tnew vaddr 0x%llx (was 0x%llx)\n",
phdr_info[pi].p_vaddr + delta,
phdr_info[pi].p_vaddr);
phdr_info[pi].p_vaddr += delta;
INFO("\t\tnew paddr 0x%llx (was 0x%llx)\n",
phdr_info[pi].p_paddr + delta,
phdr_info[pi].p_paddr);
phdr_info[pi].p_paddr += delta;
INFO("\t\tnew mem size %lld (was %lld)\n",
mem_end - new_phdr_offset,
phdr_info[pi].p_memsz);
//phdr_info[pi].p_memsz = mem_end - new_phdr_offset;
phdr_info[pi].p_memsz = mem_end - phdr_info[pi].p_vaddr;
INFO("\t\tnew file size %lld (was %lld)\n",
file_end - new_phdr_offset,
phdr_info[pi].p_filesz);
//phdr_info[pi].p_filesz = file_end - new_phdr_offset;
phdr_info[pi].p_filesz = file_end - phdr_info[pi].p_offset;
}
FAILIF_LIBELF(gelf_update_phdr (newelf, pi, &phdr_info[pi]) == 0,
gelf_update_phdr);
}
}
if (dynamic_idx >= 0) {
/* NOTE: dynamic_idx is the index of .dynamic section in the shdr_info[] array, NOT the
index of the section in the ELF file!
*/
adjust_dynamic_segment_offsets(elf, ebl,
newelf,
dynamic_idx,
shdr_info,
shdr_info_len);
}
else INFO("There is no dynamic section in this file.\n");
/* Walk the relocation sections (again). This time, update offsets of the
relocation entries. Note that there is an implication here that the
offsets are virual addresses, because we are handling a shared library!
*/
for (cnt = 1; cnt < shdr_info_len; cnt++) {
/* Note here that we process even those relocation sections that are
* marked for removal. Normally, we wouldn't need to do this, but
* in the case where we run adjust_elf() after a dry run of
* prelink() (see apriori), we still want to update the relocation
* offsets because those will be picked up by the second run of
* prelink(). If this all seems too cryptic, go yell at Iliyan
* Malchev.
*/
if (/* shdr_info[cnt].idx > 0 && */
(shdr_info[cnt].shdr.sh_type == SHT_REL ||
shdr_info[cnt].shdr.sh_type == SHT_RELA))
{
int hacked = shdr_info[cnt].idx == 0;
Elf_Data *data;
if (hacked) {
/* This doesn't work! elf_ndxscn(shdr_info[cnt].scn) will return the section number
of the new sectin that has moved into this slot. */
shdr_info[cnt].idx = elf_ndxscn(shdr_info[cnt].scn);
data = elf_getdata (elf_getscn (elf, shdr_info[cnt].idx), NULL);
INFO("PRELINKER HACK: Temporarily restoring index of to-be-removed section [%s] to %d.\n",
shdr_info[cnt].name,
shdr_info[cnt].idx);
}
else
data = elf_getdata (elf_getscn (newelf, shdr_info[cnt].idx), NULL);
update_relocations_section_offsets(newelf, elf, ebl,
shdr_info, shdr_info_len,
shdr_info + cnt,
data,
old_section_ranges);
if (hacked) {
INFO("PRELINKER HACK: Done with hack, marking section [%s] for removal again.\n",
shdr_info[cnt].name);
shdr_info[cnt].idx = 0;
}
}
}
}
/* Finally finish the ELF header. Fill in the fields not handled by
libelf from the old file. */
{
GElf_Ehdr *newehdr, newehdr_mem;
newehdr = gelf_getehdr (newelf, &newehdr_mem);
FAILIF_LIBELF(newehdr == NULL, gelf_getehdr);
INFO("Updating ELF header.\n");
memcpy (newehdr->e_ident, ehdr->e_ident, EI_NIDENT);
newehdr->e_type = ehdr->e_type;
newehdr->e_machine = ehdr->e_machine;
newehdr->e_version = ehdr->e_version;
newehdr->e_entry = ehdr->e_entry;
newehdr->e_flags = ehdr->e_flags;
newehdr->e_phoff = ehdr->e_phoff;
/* We need to position the section header table. */
{
const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
newehdr->e_shoff = get_last_address(section_ranges);
newehdr->e_shoff += offsize - 1;
newehdr->e_shoff &= ~((GElf_Off) (offsize - 1));
newehdr->e_shentsize = gelf_fsize (elf, ELF_T_SHDR, 1, EV_CURRENT);
INFO("\tsetting section-header-table offset to %lld\n",
newehdr->e_shoff);
}
if (rebuild_shstrtab) {
/* If we are rebuilding the section-headers string table, then
the new index must not be zero. This is to guard against
code breakage resulting from rebuild_shstrtab and shdridx
somehow getting out of sync. */
ASSERT(shdridx);
/* The new section header string table index. */
FAILIF(!(shdr_info[shdridx].idx < SHN_HIRESERVE) &&
likely (shdr_info[shdridx].idx != SHN_XINDEX),
"Can't handle extended section indices!\n");
}
INFO("Index of shstrtab is now %d (was %d).\n",
shdr_info[shdridx].idx,
ehdr->e_shstrndx);
newehdr->e_shstrndx = shdr_info[shdridx].idx;
FAILIF_LIBELF(gelf_update_ehdr(newelf, newehdr) == 0, gelf_update_ehdr);
}
if (section_ranges != NULL) destroy_range_list(section_ranges);
destroy_range_list(old_section_ranges);
#ifdef DEBUG
verify_elf (ehdr, shdr_info, shdr_info_len, phdr_info);
#endif
}
static void update_hash_table(Elf *newelf, Elf *elf,
Elf32_Word hash_scn_idx,
shdr_info_t *symtab_info) {
GElf_Shdr shdr_mem, *shdr = NULL;
Elf32_Word *chain;
Elf32_Word nbucket;
/* The hash table section and data in the new file. */
Elf_Scn *hashscn = elf_getscn (newelf, hash_scn_idx);
ASSERT(hashscn != NULL);
Elf_Data *hashd = elf_getdata (hashscn, NULL);
ASSERT (hashd != NULL);
Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf; /* Sane arches first. */
/* The symbol table data. */
Elf_Data *symd = elf_getdata (elf_getscn (newelf, symtab_info->idx), NULL);
ASSERT (symd != NULL);
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
size_t strshndx = symtab_info->old_shdr.sh_link;
size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1,
ehdr->e_version);
/* Convert to the correct byte order. */
FAILIF_LIBELF(gelf_xlatetom (newelf, hashd, hashd,
BYTE_ORDER == LITTLE_ENDIAN
? ELFDATA2LSB : ELFDATA2MSB) == NULL,
gelf_xlatetom);
/* Adjust the nchain value. The symbol table size changed. We keep the
same size for the bucket array. */
INFO("hash table: buckets: %d (no change).\n", bucket[0]);
INFO("hash table: chains: %d (was %d).\n",
symd->d_size / elsize,
bucket[1]);
bucket[1] = symd->d_size / elsize;
nbucket = bucket[0];
bucket += 2;
chain = bucket + nbucket;
/* New size of the section. */
shdr = gelf_getshdr (hashscn, &shdr_mem);
ASSERT(shdr->sh_type == SHT_HASH);
shdr->sh_size = (2 + symd->d_size / elsize + nbucket) * sizeof (Elf32_Word);
INFO("hash table: size %lld (was %d) bytes.\n",
shdr->sh_size,
hashd->d_size);
hashd->d_size = shdr->sh_size;
(void)gelf_update_shdr (hashscn, shdr);
/* Clear the arrays. */
memset (bucket, '\0',
(symd->d_size / elsize + nbucket)
* sizeof (Elf32_Word));
size_t inner;
for (inner = symtab_info->shdr.sh_info;
inner < symd->d_size / elsize;
++inner) {
const char *name;
GElf_Sym sym_mem;
GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem);
ASSERT (sym != NULL);
name = elf_strptr (elf, strshndx, sym->st_name);
ASSERT (name != NULL);
size_t hidx = elf_hash (name) % nbucket;
if (bucket[hidx] == 0)
bucket[hidx] = inner;
else {
hidx = bucket[hidx];
while (chain[hidx] != 0)
hidx = chain[hidx];
chain[hidx] = inner;
}
}
/* Convert back to the file byte order. */
FAILIF_LIBELF(gelf_xlatetof (newelf, hashd, hashd,
BYTE_ORDER == LITTLE_ENDIAN
? ELFDATA2LSB : ELFDATA2MSB) == NULL,
gelf_xlatetof);
}
/* This function updates the symbol indices of relocation entries. It does not
update the section offsets of those entries.
*/
static void update_relocations_section_symbol_references(
Elf *newelf, Elf *elf __attribute__((unused)),
shdr_info_t *info,
int info_len __attribute__((unused)),
shdr_info_t *relsect_info,
Elf32_Word *newsymidx)
{
/* Get this relocation section's data */
Elf_Data *d = elf_getdata (elf_getscn (newelf, relsect_info->idx), NULL);
ASSERT (d != NULL);
ASSERT (d->d_size == relsect_info->shdr.sh_size);
size_t old_nrels =
relsect_info->old_shdr.sh_size / relsect_info->old_shdr.sh_entsize;
size_t new_nrels =
relsect_info->shdr.sh_size / relsect_info->shdr.sh_entsize;
size_t nrels = new_nrels;
if (relsect_info->use_old_shdr_for_relocation_calculations) {
nrels = old_nrels;
/* Now, we update d->d_size to point to the old size in order to
prevent gelf_update_rel() and gelf_update_rela() from returning
an error. We restore the value at the end of the function.
*/
d->d_size = old_nrels * relsect_info->shdr.sh_entsize;
}
/* Now, walk the relocations one by one. For each relocation,
check to see whether the symbol it refers to has a new
index in the symbol table, and if so--update it. We know
if a symbol's index has changed when we look up that
the newsymidx[] array at the old index. If the value at that
location is different from the array index, then the
symbol's index has changed; otherwise, it remained the same.
*/
INFO("Scanning %d relocation entries in section [%s] (taken from %s section header (old %d, new %d))...\n",
nrels,
relsect_info->name,
(relsect_info->use_old_shdr_for_relocation_calculations ? "old" : "new"),
old_nrels, new_nrels);
size_t relidx, newidx;
if (relsect_info->shdr.sh_type == SHT_REL) {
for (newidx = relidx = 0; relidx < nrels; ++relidx) {
GElf_Rel rel_mem;
FAILIF_LIBELF(gelf_getrel (d, relidx, &rel_mem) == NULL,
gelf_getrel);
size_t symidx = GELF_R_SYM (rel_mem.r_info);
if (newsymidx[symidx] != (Elf32_Word)-1)
{
rel_mem.r_info = GELF_R_INFO (newsymidx[symidx],
GELF_R_TYPE (rel_mem.r_info));
FAILIF_LIBELF(gelf_update_rel (d, newidx, &rel_mem) == 0,
gelf_update_rel);
newidx++;
}
else {
INFO("Discarding REL entry for symbol [%d], section [%d]\n",
symidx,
relsect_info->shdr.sh_info);
}
} /* for each rel entry... */
} else {
for (newidx = relidx = 0; relidx < nrels; ++relidx) {
GElf_Rela rel_mem;
FAILIF_LIBELF(gelf_getrela (d, relidx, &rel_mem) == NULL,
gelf_getrela);
size_t symidx = GELF_R_SYM (rel_mem.r_info);
if (newsymidx[symidx] != (Elf32_Word)-1)
{
rel_mem.r_info
= GELF_R_INFO (newsymidx[symidx],
GELF_R_TYPE (rel_mem.r_info));
FAILIF_LIBELF(gelf_update_rela (d, newidx, &rel_mem) == 0,
gelf_update_rela);
newidx++;
}
else {
INFO("Discarding RELA entry for symbol [%d], section [%d]\n",
symidx,
relsect_info->shdr.sh_info);
}
} /* for each rela entry... */
} /* if rel else rela */
if (newidx != relidx)
{
INFO("Shrinking relocation section from %lld to %lld bytes (%d -> %d "
"entries).\n",
relsect_info->shdr.sh_size,
relsect_info->shdr.sh_entsize * newidx,
relidx,
newidx);
d->d_size = relsect_info->shdr.sh_size =
relsect_info->shdr.sh_entsize * newidx;
} else INFO("Relocation section [%s]'s size (relocates: %s(%d), "
"symab: %s(%d)) does not change.\n",
relsect_info->name,
info[relsect_info->shdr.sh_info].name,
relsect_info->shdr.sh_info,
info[relsect_info->shdr.sh_link].name,
relsect_info->shdr.sh_link);
/* Restore d->d_size if necessary. */
if (relsect_info->use_old_shdr_for_relocation_calculations)
d->d_size = new_nrels * relsect_info->shdr.sh_entsize;
}
static void update_relocations_section_offsets(Elf *newelf __attribute((unused)), Elf *elf,
Ebl *ebl __attribute__((unused)),
shdr_info_t *info,
int info_len __attribute__((unused)),
shdr_info_t *relsect_info,
Elf_Data *d,
range_list_t *old_section_ranges)
{
/* Get this relocation section's data */
ASSERT (d != NULL);
if (d->d_size != relsect_info->shdr.sh_size) {
/* This is not necessarily a fatal error. In the case where we call adjust_elf() from apriori
(the prelinker), we may call this function for a relocation section that is marked for
removal. We still want to process this relocation section because, even though it is marked
for removal, its relocatin entries will be used by the prelinker to know what to prelink.
Once the prelinker is done, it will call adjust_elf() one more time to actually eliminate the
relocation section. */
PRINT("WARNING: section size according to section [%s]'s header is %lld, but according to data buffer is %ld.\n",
relsect_info->name,
relsect_info->shdr.sh_size,
d->d_size);
ASSERT((relsect_info->shdr.sh_type == SHT_REL || relsect_info->shdr.sh_type == SHT_RELA) &&
relsect_info->use_old_shdr_for_relocation_calculations);
}
size_t old_nrels =
relsect_info->old_shdr.sh_size / relsect_info->old_shdr.sh_entsize;
size_t new_nrels =
relsect_info->shdr.sh_size / relsect_info->shdr.sh_entsize;
size_t nrels = new_nrels;
if (relsect_info->use_old_shdr_for_relocation_calculations) {
nrels = old_nrels;
/* Now, we update d->d_size to point to the old size in order to
prevent gelf_update_rel() and gelf_update_rela() from returning
an error. We restore the value at the end of the function.
*/
d->d_size = old_nrels * relsect_info->shdr.sh_entsize;
}
/* Now, walk the relocations one by one. For each relocation,
check to see whether the symbol it refers to has a new
index in the symbol table, and if so--update it. We know
if a symbol's index has changed when we look up that
the newsymidx[] array at the old index. If the value at that
location is different from the array index, then the
symbol's index has changed; otherwise, it remained the same.
*/
INFO("Scanning %d relocation entries in section [%s] (taken from %s section header (old %d, new %d))...\n",
nrels,
relsect_info->name,
(relsect_info->use_old_shdr_for_relocation_calculations ? "old" : "new"),
old_nrels, new_nrels);
if (relsect_info->old_shdr.sh_info == 0) {
PRINT("WARNING: Relocation section [%s] relocates the NULL section.\n",
relsect_info->name);
}
else {
FAILIF(info[relsect_info->old_shdr.sh_info].idx == 0,
"Section [%s] relocates section [%s] (index %d), which is being "
"removed!\n",
relsect_info->name,
info[relsect_info->old_shdr.sh_info].name,
relsect_info->old_shdr.sh_info);
}
size_t relidx;
FAILIF(relsect_info->shdr.sh_type == SHT_RELA,
"Can't handle SHT_RELA relocation entries.\n");
if (relsect_info->shdr.sh_type == SHT_REL) {
for (relidx = 0; relidx < nrels; ++relidx) {
GElf_Rel rel_mem;
FAILIF_LIBELF(gelf_getrel (d, relidx, &rel_mem) == NULL,
gelf_getrel);
if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_NONE)
continue;
range_t *old_range = find_range(old_section_ranges,
rel_mem.r_offset);
#if 1
if (NULL == old_range) {
GElf_Sym *sym, sym_mem;
unsigned sym_idx = GELF_R_SYM(rel_mem.r_info);
/* relsect_info->shdr.sh_link is the index of the associated
symbol table. */
sym = gelf_getsymshndx(info[relsect_info->shdr.sh_link].data,
NULL,
sym_idx,
&sym_mem,
NULL);
/* info[relsect_info->shdr.sh_link].shdr.sh_link is the index
of the string table associated with the symbol table
associated with the relocation section rel_sect. */
const char *symname = elf_strptr(elf,
info[relsect_info->shdr.sh_link].shdr.sh_link,
sym->st_name);
{
int i = 0;
INFO("ABOUT TO FAIL for symbol [%s]: old section ranges:\n", symname);
int num_ranges;
range_t *ranges = get_sorted_ranges(old_section_ranges, &num_ranges);
for (; i < num_ranges; i++) {
shdr_info_t *inf = (shdr_info_t *)ranges[i].user;
INFO("\t[%8lld, %8lld] (%8lld bytes) [%8lld, %8lld] (%8lld bytes) [%-15s]\n",
ranges[i].start,
ranges[i].start + ranges[i].length,
ranges[i].length,
inf->old_shdr.sh_addr,
inf->old_shdr.sh_addr + inf->old_shdr.sh_size,
inf->old_shdr.sh_size,
inf->name);
}
INFO("\n");
}
FAILIF(1,
"No range matches relocation entry value 0x%llx (%d) [%s]!\n",
rel_mem.r_offset,
rel_mem.r_offset,
symname);
}
#else
FAILIF(NULL == old_range,
"No range matches relocation entry value 0x%llx!\n",
rel_mem.r_offset);
#endif
ASSERT(old_range->start <= rel_mem.r_offset &&
rel_mem.r_offset < old_range->start + old_range->length);
ASSERT(old_range->user);
shdr_info_t *old_range_info = (shdr_info_t *)old_range->user;
ASSERT(old_range_info->idx > 0);
if (relsect_info->old_shdr.sh_info &&
old_range_info->idx != relsect_info->old_shdr.sh_info) {
PRINT("Relocation offset 0x%llx does not match section [%s] "
"but section [%s]!\n",
rel_mem.r_offset,
info[relsect_info->old_shdr.sh_info].name,
old_range_info->name);
}
#if 0 /* This is true only for shared libraries, but not for executables */
ASSERT(old_range_info->shdr.sh_addr == old_range_info->shdr.sh_offset);
ASSERT(old_range_info->old_shdr.sh_addr == old_range_info->old_shdr.sh_offset);
#endif
Elf64_Sxword delta =
old_range_info->shdr.sh_addr - old_range_info->old_shdr.sh_addr;
if (delta) {
extern int verbose_flag;
/* Print out some info about the relocation entry we are
modifying. */
if (unlikely(verbose_flag)) {
/* Get associated (new) symbol table. */
Elf64_Word symtab = relsect_info->shdr.sh_link;
/* Get the symbol that is being relocated. */
size_t symidx = GELF_R_SYM (rel_mem.r_info);
GElf_Sym sym_mem, *sym;
/* Since by now we've already updated the symbol index,
we need to retrieve the symbol from the new symbol table.
*/
sym = gelf_getsymshndx (elf_getdata(info[symtab].newscn, NULL),
NULL,
symidx, &sym_mem, NULL);
FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
char buf[64];
INFO("\t%02d (%-15s) off 0x%llx -> 0x%llx (%lld) (relocates [%s:(%d)%s])\n",
(unsigned)GELF_R_TYPE(rel_mem.r_info),
ebl_reloc_type_name(ebl,
GELF_R_TYPE(rel_mem.r_info),
buf,
sizeof(buf)),
rel_mem.r_offset, rel_mem.r_offset + delta, delta,
old_range_info->name,
symidx,
#if ELF_STRPTR_IS_BROKEN
/* libelf does not keep track of changes very well.
Looks like, if you use elf_strptr() on a file that
has not been updated yet, you get bogus results. */
((char *)info[info[symtab].old_shdr.sh_link].
newdata->d_buf) + sym->st_name
#else
elf_strptr(newelf,
info[symtab].shdr.sh_link,
sym->st_name)
#endif
);
} /* if (verbose_flag) */
rel_mem.r_offset += delta;
FAILIF_LIBELF(gelf_update_rel (d, relidx, &rel_mem) == 0,
gelf_update_rel);
#ifdef ARM_SPECIFIC_HACKS
if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_RELATIVE) {
FAILIF(GELF_R_SYM(rel_mem.r_info) != 0,
"Can't handle relocation!\n");
/* From the ARM documentation: "when the symbol is zero,
the R_ARM_RELATIVE entry resolves to the difference
between the address at which the segment being
relocated was loaded and the address at which it
was linked."
*/
int *ptr =
(int *)(((char *)old_range_info->newdata->d_buf) +
(rel_mem.r_offset -
old_range_info->shdr.sh_addr));
*ptr += (int)delta;
}
#endif
} /* if (delta) */
} /* for each rel entry... */
}
/* Restore d->d_size if necessary. */
if (relsect_info->use_old_shdr_for_relocation_calculations)
d->d_size = new_nrels * relsect_info->shdr.sh_entsize;
}
static inline
Elf_Data *create_section_data(shdr_info_t *info, Elf_Scn *scn)
{
Elf_Data *newdata = NULL;
if (info->data == NULL) {
info->data = elf_getdata (info->scn, NULL);
FAILIF_LIBELF(NULL == info->data, elf_getdata);
INFO("\t\tcopying data from original section (%d bytes).\n",
info->data->d_size);
/* Set the data. This is done by copying from the old file. */
newdata = elf_newdata (scn);
FAILIF_LIBELF(newdata == NULL, elf_newdata);
/* Copy the structure. Note that the data buffer pointer gets
copied, but the buffer itself does not. */
*newdata = *info->data;
#if COPY_SECTION_DATA_BUFFER
if (info->data->d_buf != NULL) {
newdata->d_buf = MALLOC(newdata->d_size);
memcpy(newdata->d_buf, info->data->d_buf, newdata->d_size);
}
#endif
} else {
INFO("\t\tassigning new data to section (%d bytes).\n",
info->data->d_size);
newdata = info->data;
}
info->newdata = newdata;
return newdata;
}
#if 0
static void print_shdr_array(shdr_info_t *info, int num_entries) {
extern int verbose_flag;
if (verbose_flag) {
int i;
for (i = 0; i < num_entries; i++) {
INFO("%03d:"
"\tname [%s]\n"
"\tidx [%d]\n",
i, info[i].name, info[i].idx);
}
} /* if (verbose_flag) */
}
#endif
static size_t do_update_dyn_entry_address(Elf *elf,
GElf_Dyn *dyn,
shdr_info_t *shdr_info,
int shdr_info_len,
int newline)
{
size_t scnidx = 0;
INFO("%#0*llx",
gelf_getclass (elf) == ELFCLASS32 ? 10 : 18,
dyn->d_un.d_val);
for (scnidx = 1; scnidx < shdr_info_len; scnidx++) {
if (shdr_info[scnidx].old_shdr.sh_addr == dyn->d_un.d_ptr) {
if (shdr_info[scnidx].idx > 0) {
INFO(" (updating to 0x%08llx per section %d (shdr_info[] index %d): [%s])",
shdr_info[scnidx].shdr.sh_addr,
shdr_info[scnidx].idx,
scnidx,
shdr_info[scnidx].name);
dyn->d_un.d_ptr = shdr_info[scnidx].shdr.sh_addr;
break;
}
else {
/* FIXME: This should be more intelligent. What if there is more than one section that fits the
dynamic entry, and just the first such is being removed? We should keep on searching here.
*/
INFO(" (Setting to ZERO per section (shdr_info[] index %d) [%s], which is being removed)",
scnidx,
shdr_info[scnidx].name);
dyn->d_un.d_ptr = 0;
break;
}
}
}
if (newline) INFO("\n");
return scnidx == shdr_info_len ? 0 : scnidx;
}
static inline size_t update_dyn_entry_address(Elf *elf,
GElf_Dyn *dyn,
shdr_info_t *shdr_info,
int shdr_info_len)
{
return do_update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len, 1);
}
static void update_dyn_entry_address_and_size(Elf *elf, Ebl *oldebl,
GElf_Dyn *dyn,
shdr_info_t *shdr_info,
int shdr_info_len,
Elf_Data *dyn_data,
size_t *dyn_size_entries,
int dyn_entry_idx)
{
size_t scnidx = do_update_dyn_entry_address(elf, dyn,
shdr_info, shdr_info_len,
0);
if (scnidx) {
char buf[64];
INFO(" (affects tag %s)",
ebl_dynamic_tag_name(oldebl, dyn_entry_idx,
buf, sizeof (buf)));
if (dyn_size_entries[dyn_entry_idx]) {
/* We previously encountered this size entry, and because
we did not know which section would affect it, we saved its
index in the dyn_size_entries[] array so that we can update
the entry when we do know. Now we know that the field
shdr_info[scnidx].shdr.sh_size contains that new value.
*/
GElf_Dyn *szdyn, szdyn_mem;
szdyn = gelf_getdyn (dyn_data,
dyn_size_entries[dyn_entry_idx],
&szdyn_mem);
FAILIF_LIBELF(NULL == szdyn, gelf_getdyn);
ASSERT(szdyn->d_tag == dyn_entry_idx);
INFO("\n (!)\t%-17s completing deferred update (%lld -> %lld bytes)"
" per section %d [%s]",
ebl_dynamic_tag_name (oldebl, szdyn->d_tag,
buf, sizeof (buf)),
szdyn->d_un.d_val,
shdr_info[scnidx].shdr.sh_size,
shdr_info[scnidx].idx,
shdr_info[scnidx].name);
szdyn->d_un.d_val = shdr_info[scnidx].shdr.sh_size;
FAILIF_LIBELF(0 == gelf_update_dyn(dyn_data,
dyn_size_entries[dyn_entry_idx],
szdyn),
gelf_update_dyn);
#ifdef DEBUG
dyn_size_entries[dyn_entry_idx] = -1;
#endif
}
else dyn_size_entries[dyn_entry_idx] = scnidx;
} /* if (scnidx) */
INFO("\n");
}
static void do_build_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
int dynidx, /* index of .dynamic section */
int symtabidx, /* index of symbol table section */
shdr_info_t *shdr_info,
int shdr_info_len __attribute__((unused)),
bool print_strings_only)
{
Elf_Scn *dynscn = elf_getscn(elf, dynidx);
FAILIF_LIBELF(NULL == dynscn, elf_getscn);
Elf_Data *data = elf_getdata (dynscn, NULL);
ASSERT(data != NULL);
size_t cnt;
if (!print_strings_only) {
/* Allocate an array of string-offset structures. */
shdr_info[dynidx].symse =
(struct Ebl_Strent **)CALLOC(
shdr_info[dynidx].shdr.sh_size/shdr_info[dynidx].shdr.sh_entsize,
sizeof(struct Ebl_Strent *));
}
for (cnt = 0;
cnt < shdr_info[dynidx].shdr.sh_size/shdr_info[dynidx].shdr.sh_entsize;
++cnt)
{
char buf[64];
GElf_Dyn dynmem;
GElf_Dyn *dyn;
dyn = gelf_getdyn (data, cnt, &dynmem);
FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
switch (dyn->d_tag) {
case DT_NEEDED:
case DT_SONAME:
case DT_RPATH:
case DT_RUNPATH:
{
const char *str =
elf_strptr (elf,
shdr_info[dynidx].shdr.sh_link,
dyn->d_un.d_val);
ASSERT(str != NULL);
INFO("\t\t\t%-17s: ",
ebl_dynamic_tag_name (oldebl,
dyn->d_tag,
buf, sizeof (buf)));
INFO("[%s] (offset %ld)\n", str, dyn->d_un.d_val);
if (!print_strings_only) {
/* We append the strings to the string table belonging to the
dynamic-symbol-table section. We keep the dynsymst handle
for the strings section in the shdr_info[] entry for the
dynamic-sybmol table. Confusing, I know.
*/
ASSERT(shdr_info[symtabidx].dynsymst);
/* The string tables for the symbol table and the .dynamic
section must be the same.
*/
ASSERT(shdr_info[symtabidx].shdr.sh_link ==
shdr_info[dynidx].shdr.sh_link);
shdr_info[dynidx].symse[cnt] =
ebl_strtabadd(shdr_info[symtabidx].dynsymst, str?:"", 0);
ASSERT(shdr_info[dynidx].symse[cnt] != NULL);
}
}
break;
default:
break;
}
} /* for (...) */
} /* build_dynamic_segment_strings() */
static void build_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
int dynidx, /* index of .dynamic section */
int symtabidx, /* index of symbol table section */
shdr_info_t *shdr_info,
int shdr_info_len __attribute__((unused)))
{
INFO("\t\tbuilding string offsets for dynamic section [%s], index %d\n",
shdr_info[dynidx].name,
dynidx);
do_build_dynamic_segment_strings(elf, oldebl, dynidx, symtabidx,
shdr_info, shdr_info_len, false);
}
#ifdef DEBUG
static void print_dynamic_segment_strings(Elf *elf, Ebl *oldebl,
int dynidx, /* index of .dynamic section */
int symtabidx, /* index of symbol table section */
shdr_info_t *shdr_info,
int shdr_info_len __attribute__((unused)))
{
INFO("\t\tprinting string offsets for dynamic section [%s], index %d\n",
shdr_info[dynidx].name,
dynidx);
do_build_dynamic_segment_strings(elf, oldebl, dynidx, symtabidx,
shdr_info, shdr_info_len, true);
}
#endif
static void adjust_dynamic_segment_offsets(Elf *elf, Ebl *oldebl,
Elf *newelf __attribute__((unused)),
int dynidx, /* index of .dynamic section in shdr_info[] */
shdr_info_t *shdr_info,
int shdr_info_len)
{
Elf_Scn *scn = shdr_info[dynidx].newscn;
FAILIF_LIBELF(NULL == scn, elf_getscn);
Elf_Data *data = elf_getdata (scn, NULL);
ASSERT(data != NULL);
size_t cnt;
INFO("Updating dynamic section [%s], index %d\n",
shdr_info[dynidx].name,
dynidx);
size_t *dyn_size_entries = (size_t *)CALLOC(DT_NUM, sizeof(size_t));
ASSERT(data->d_type == ELF_T_DYN);
for (cnt = 0; cnt < shdr_info[dynidx].shdr.sh_size / shdr_info[dynidx].shdr.sh_entsize; ++cnt) {
char buf[64];
GElf_Dyn dynmem;
GElf_Dyn *dyn;
dyn = gelf_getdyn (data, cnt, &dynmem);
FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
INFO("\t%-17s ",
ebl_dynamic_tag_name (oldebl, dyn->d_tag, buf, sizeof (buf)));
switch (dyn->d_tag) {
/* Updates to addresses */
/* We assume that the address entries come before the size entries.
*/
case DT_PLTGOT:
case DT_HASH:
case DT_SYMTAB:
(void)update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len);
break;
case DT_STRTAB:
/* Defer-update DT_STRSZ as well, if not already updated. */
update_dyn_entry_address_and_size(elf, oldebl, dyn,
shdr_info, shdr_info_len,
data,
dyn_size_entries,
DT_STRSZ);
break;
case DT_RELA:
/* Defer-update DT_RELASZ as well, if not already updated. */
update_dyn_entry_address_and_size(elf, oldebl, dyn,
shdr_info, shdr_info_len,
data,
dyn_size_entries,
DT_RELASZ);
break;
case DT_REL:
/* Defer-update DT_RELSZ as well, if not already updated. */
update_dyn_entry_address_and_size(elf, oldebl, dyn,
shdr_info, shdr_info_len,
data,
dyn_size_entries,
DT_RELSZ);
break;
case DT_JMPREL:
/* Defer-update DT_PLTRELSZ as well, if not already updated. */
update_dyn_entry_address_and_size(elf, oldebl, dyn,
shdr_info, shdr_info_len,
data,
dyn_size_entries,
DT_PLTRELSZ);
break;
case DT_INIT_ARRAY:
case DT_FINI_ARRAY:
case DT_PREINIT_ARRAY:
case DT_INIT:
case DT_FINI:
(void)update_dyn_entry_address(elf, dyn, shdr_info, shdr_info_len);
break;
/* Updates to sizes */
case DT_PLTRELSZ: /* DT_JMPREL or DT_PLTGOT */
case DT_STRSZ: /* DT_STRTAB */
case DT_RELSZ: /* DT_REL */
case DT_RELASZ: /* DR_RELA */
if (dyn_size_entries[dyn->d_tag] == 0) {
/* We have not yet found the new size for this entry, so we
save the index of the dynamic entry in the dyn_size_entries[]
array. When we find the section affecting this field (in
code above), we will update the entry.
*/
INFO("(!) (deferring update: new value not known yet)\n");
dyn_size_entries[dyn->d_tag] = cnt;
}
else {
ASSERT(dyn_size_entries[dyn->d_tag] < shdr_info_len);
INFO("%lld (bytes) (updating to %lld bytes "
"per section %d [%s])\n",
dyn->d_un.d_val,
shdr_info[dyn_size_entries[dyn->d_tag]].shdr.sh_size,
shdr_info[dyn_size_entries[dyn->d_tag]].idx,
shdr_info[dyn_size_entries[dyn->d_tag]].name);
dyn->d_un.d_val =
shdr_info[dyn_size_entries[dyn->d_tag]].shdr.sh_size;
#ifdef DEBUG
/* Clear the array so that we know we are done with it. */
dyn_size_entries[dyn->d_tag] = (size_t)-1;
#endif
}
break;
/* End of updates. */
case DT_NULL:
case DT_DEBUG:
case DT_BIND_NOW:
case DT_TEXTREL:
/* No further output. */
INFO("\n");
break;
/* String-entry updates. */
case DT_NEEDED:
case DT_SONAME:
case DT_RPATH:
case DT_RUNPATH:
if (shdr_info[dynidx].symse != NULL)
{
Elf64_Xword new_offset =
ebl_strtaboffset(shdr_info[dynidx].symse[cnt]);
INFO("string [%s] offset changes: %lld -> %lld\n",
elf_strptr (elf,
shdr_info[dynidx].shdr.sh_link,
dyn->d_un.d_val),
dyn->d_un.d_val,
new_offset);
dyn->d_un.d_val = new_offset;
FAILIF_LIBELF(0 == gelf_update_dyn(data, cnt, dyn),
gelf_update_dyn);
}
else
INFO("string [%s] offset has not changed from %lld, not updating\n",
elf_strptr (elf,
shdr_info[dynidx].shdr.sh_link,
dyn->d_un.d_val),
dyn->d_un.d_val);
break;
case DT_RELAENT:
case DT_SYMENT:
case DT_RELENT:
case DT_PLTPADSZ:
case DT_MOVEENT:
case DT_MOVESZ:
case DT_INIT_ARRAYSZ:
case DT_FINI_ARRAYSZ:
case DT_SYMINSZ:
case DT_SYMINENT:
case DT_GNU_CONFLICTSZ:
case DT_GNU_LIBLISTSZ:
INFO("%lld (bytes)\n", dyn->d_un.d_val);
break;
case DT_VERDEFNUM:
case DT_VERNEEDNUM:
case DT_RELACOUNT:
case DT_RELCOUNT:
INFO("%lld\n", dyn->d_un.d_val);
break;
case DT_PLTREL: /* Specifies whether PLTREL (same as JMPREL) has REL or RELA entries */
INFO("%s (%d)\n", ebl_dynamic_tag_name (oldebl, dyn->d_un.d_val, NULL, 0), dyn->d_un.d_val);
break;
default:
INFO("%#0*llx\n",
gelf_getclass (elf) == ELFCLASS32 ? 10 : 18,
dyn->d_un.d_val);
break;
}
FAILIF_LIBELF(0 == gelf_update_dyn(data, cnt, dyn),
gelf_update_dyn);
} /* for (...) */
#ifdef DEBUG
if (1) {
int i;
for (i = 0; i < DT_NUM; i++)
ASSERT((ssize_t)dyn_size_entries[i] <= 0);
}
#endif
FREE(dyn_size_entries);
} /* adjust_dynamic_segment_offsets() */
static bool section_belongs_to_header(GElf_Shdr *shdr, GElf_Phdr *phdr)
{
if (shdr->sh_size) {
/* Compare allocated sections by VMA, unallocated
sections by file offset. */
if(shdr->sh_flags & SHF_ALLOC) {
if(shdr->sh_addr >= phdr->p_vaddr
&& (shdr->sh_addr + shdr->sh_size
<= phdr->p_vaddr + phdr->p_memsz))
{
return true;
}
}
else {
if (shdr->sh_offset >= phdr->p_offset
&& (shdr->sh_offset + shdr->sh_size
<= phdr->p_offset + phdr->p_filesz))
{
return true;
}
}
}
return false;
}
static Elf64_Off section_to_header_mapping(Elf *elf,
int phdr_idx,
shdr_info_t *shdr_info,
int num_shdr_info,
Elf64_Off *file_end,
Elf64_Off *mem_end)
{
Elf64_Off start;
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (elf, phdr_idx, &phdr_mem);
FAILIF_LIBELF(NULL == phdr, gelf_getphdr);
size_t inner;
FAILIF(phdr->p_type == PT_GNU_RELRO,
"Can't handle segments of type PT_GNU_RELRO!\n");
/* Iterate over the sections. */
start = (Elf64_Off)-1;
*file_end = *mem_end = 0;
INFO("\n\t\t");
for (inner = 1; inner < num_shdr_info; ++inner)
{
if (shdr_info[inner].idx > 0) {
/* Check to see the section is in the segment. We use the old
header because that header contains the old offset and length
information about a section.
*/
if (section_belongs_to_header(&shdr_info[inner].old_shdr, phdr))
{
INFO("%-17s", shdr_info[inner].name);
#define SECT_MEM_END(s) ((s).sh_addr + (s).sh_size)
if ((shdr_info[inner].shdr.sh_flags & SHF_ALLOC)) {
if (SECT_MEM_END(shdr_info[inner].shdr) > *mem_end) {
INFO("(mem_end 0x%llx --> 0x%llx) ", *mem_end, SECT_MEM_END(shdr_info[inner].shdr));
*mem_end = SECT_MEM_END(shdr_info[inner].shdr);
}
#undef SECT_MEM_END
#define SECT_FILE_END(s) ((s).sh_offset + (s).sh_size)
if (shdr_info[inner].shdr.sh_type != SHT_NOBITS) {
if (SECT_FILE_END(shdr_info[inner].shdr) > *file_end) {
INFO("(file_end 0x%llx --> 0x%llx) ", *file_end, SECT_FILE_END(shdr_info[inner].shdr));
*file_end = SECT_FILE_END(shdr_info[inner].shdr);
}
}
#undef SECT_FILE_END
if (shdr_info[inner].shdr.sh_offset < start) {
start = shdr_info[inner].shdr.sh_offset;
}
} /* if section takes space */
INFO("\n\t\t");
}
else
INFO("(!) %-17s does not belong\n\t\t", shdr_info[inner].name);
}
else
INFO("(!) %-17s is not considered, it is being removed\n\t\t", shdr_info[inner].name);
}
/* Finish the line. */
INFO("start: %lld\n", start);
INFO("\t\tends: %lld file, %lld mem\n", *file_end, *mem_end);
return start;
}
static void
update_symbol_values(Elf *elf, GElf_Ehdr *ehdr,
Elf *newelf __attribute__((unused)),
shdr_info_t *shdr_info,
int num_shdr_info,
int shady,
int dynamic_idx)
{
/* Scan the sections, looking for the symbol table. */
size_t i;
for (i = 1; i < num_shdr_info; i++) {
if (shdr_info[i].idx > 0 &&
(shdr_info[i].shdr.sh_type == SHT_SYMTAB ||
shdr_info[i].shdr.sh_type == SHT_DYNSYM))
{
size_t inner;
size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
Elf_Data *symdata = shdr_info[i].newdata;
/* shdr_info[i].old_shdr.sh_link is the index of the strings table
in the old ELF file. This index still points to the same section
in the shdr_info[] array. The idx field of that entry is that
section's new index. That index must, therefore, be equal to
the new value of sh_link. */
ASSERT(shdr_info[shdr_info[i].old_shdr.sh_link].idx ==
shdr_info[i].shdr.sh_link);
ASSERT(shdr_info[shdr_info[i].old_shdr.sh_link].data);
INFO("\tupdating symbol values for section [%s]...\n",
shdr_info[i].name);
#if 1 /* DEBUG */
{
Elf_Scn *symstrscn = elf_getscn(newelf, shdr_info[i].shdr.sh_link);
ASSERT(symstrscn);
Elf_Data *symstrdata = elf_getdata(symstrscn, NULL);
ASSERT(symstrdata);
INFO("%d nonprintable\n",
dump_hex_buffer(stdout, symstrdata->d_buf, symstrdata->d_size, 0));
}
#endif
INFO("\tnumber of symbols to update: %d (%d bytes)\n",
symdata->d_size / elsize, symdata->d_size);
for (inner = 0; inner < symdata->d_size / elsize; ++inner)
{
GElf_Sym sym_mem;
GElf_Sym *sym;
size_t shnum;
FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
sym = gelf_getsymshndx (symdata, NULL,
inner, &sym_mem, NULL);
FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
#if 0 /* DEBUG */
if (shdr_info[i].shdr.sh_type == SHT_SYMTAB) {
PRINT("%8d: name %d info %02x other %02x shndx %d size %lld value %lld\n",
inner,
sym->st_info,
sym->st_name,
sym->st_other,
sym->st_shndx,
sym->st_size,
sym->st_value);
}
#endif
size_t scnidx = sym->st_shndx;
FAILIF(scnidx == SHN_XINDEX,
"Can't handle SHN_XINDEX!\n");
char *symname = NULL;
{
#if ELF_STRPTR_IS_BROKEN
Elf_Scn *symstrscn = elf_getscn(newelf, shdr_info[i].shdr.sh_link);
ASSERT(symstrscn);
Elf_Data *symstrdata = elf_getdata(symstrscn, NULL);
ASSERT(symstrdata);
symname = symstrdata->d_buf + sym->st_name;
#else
symname = elf_strptr(newelf,
shdr_info[i].shdr.sh_link,
sym->st_name);
#endif
}
extern int verbose_flag;
if (unlikely(verbose_flag))
{
int c, max = 40;
INFO("%-8d [", inner);
for (c=0; c<max-1; c++) {
if (symname[c]) {
INFO("%c", symname[c]);
}
else break;
}
if (c < max-1) {
while (c++ < max) INFO(" ");
}
else INFO("<");
INFO("]");
} /* if (unlikely(verbose_flag)) */
/* Notice that shdr_info[] is an array whose indices correspond
to the section indices in the original ELF file. Of those
sections, some have been discarded, and one is moved to the
end of the file--this is section .shstrtab. Of course, no
symbol refers to this section, so it is safe for us to
address sections by their original indices in the
shdr_info[] array directly.
*/
/* Note that we do not skip over the STT_SECTION symbols. Since
they contain the addresses of sections, we update their
values as well.
*/
if (scnidx == SHN_UNDEF) {
INFO(" undefined\n");
continue;
}
if (scnidx >= shnum ||
(scnidx >= SHN_LORESERVE &&
scnidx <= SHN_HIRESERVE))
{
INFO(" special (scn %d, value 0x%llx, size %lld)\n",
scnidx,
sym->st_value,
sym->st_size);
/* We shouldn't be messing with these symbols, but they are
often absolute symbols that encode the starting address
or the ending address of some section. As a heuristic,
we will check to see if the value of the symbol matches
the start or the end of any section, and if so, we will
update it, but only if --shady is enabled.
*/
if (shady && sym->st_value) {
size_t scnidx;
/* Is it the special symbol _DYNAMIC? */
if (!strcmp(symname, "_DYNAMIC")) {
/* The _DYNAMIC symbol points to the DYNAMIC
segment. It is used by linker to bootstrap
itself. */
ASSERT(dynamic_idx >= 0);
PRINT("*** SHADY *** symbol %s: "
"new st_value = %lld (was %lld), "
"st_size = %lld (was %lld)\n",
symname,
shdr_info[dynamic_idx].shdr.sh_addr,
sym->st_value,
shdr_info[dynamic_idx].shdr.sh_size,
sym->st_size);
sym->st_value =
shdr_info[dynamic_idx].shdr.sh_addr;
sym->st_size =
shdr_info[dynamic_idx].shdr.sh_size;
/* NOTE: We don't update st_shndx, because this is a special
symbol. I am not sure if it's necessary though.
*/
FAILIF_LIBELF(gelf_update_symshndx(symdata,
NULL,
inner,
sym,
0) == 0,
gelf_update_symshndx);
}
else {
for (scnidx = 1; scnidx < num_shdr_info; scnidx++) {
if (sym->st_value ==
shdr_info[scnidx].old_shdr.sh_addr) {
if (shdr_info[scnidx].shdr.sh_addr !=
sym->st_value) {
PRINT("*** SHADY *** symbol %s matches old "
"start %lld of section %s, updating "
"to %lld.\n",
symname,
shdr_info[scnidx].old_shdr.sh_addr,
shdr_info[scnidx].name,
shdr_info[scnidx].shdr.sh_addr);
sym->st_value = shdr_info[scnidx].shdr.sh_addr;
}
break;
}
else {
Elf64_Addr oldaddr =
shdr_info[scnidx].old_shdr.sh_addr +
shdr_info[scnidx].old_shdr.sh_size;
if (sym->st_value == oldaddr) {
Elf64_Addr newaddr =
shdr_info[scnidx].shdr.sh_addr +
shdr_info[scnidx].shdr.sh_size;
if (newaddr != sym->st_value) {
PRINT("*** SHADY *** symbol %s matches old "
"end %lld of section %s, updating "
"to %lld.\n",
symname,
oldaddr,
shdr_info[scnidx].name,
newaddr);
sym->st_value = newaddr;
}
break;
}
}
} /* for each section... */
/* NOTE: We don't update st_shndx, because this is a special
symbol. I am not sure if it's necessary though.
*/
if (scnidx < num_shdr_info) {
FAILIF_LIBELF(gelf_update_symshndx(symdata,
NULL,
inner,
sym,
0) == 0,
gelf_update_symshndx);
}
} /* if symbol is _DYNAMIC else */
}
continue;
} /* handle special-section symbols */
/* The symbol must refer to a section which is not being
removed. */
if(shdr_info[scnidx].idx == 0)
{
FAILIF(GELF_ST_TYPE (sym->st_info) != STT_SECTION,
"Non-STT_SECTION symbol [%s] refers to section [%s],"
" which is being removed.\n",
symname,
shdr_info[scnidx].name);
INFO("STT_SECTION symbol [%s] refers to section [%s], "
"which is being removed. Skipping...\n",
symname,
shdr_info[scnidx].name);
continue;
}
INFO(" %8d %-17s ",
sym->st_shndx,
shdr_info[sym->st_shndx].name);
/* Has the section's offset (hence its virtual address,
because we set that to the same value as the offset) changed?
If so, calculate the delta and update the symbol entry.
*/
Elf64_Sxword delta;
delta =
shdr_info[scnidx].shdr.sh_offset -
shdr_info[scnidx].old_shdr.sh_offset;
Elf64_Sxword vaddr_delta;
vaddr_delta =
shdr_info[scnidx].shdr.sh_addr -
shdr_info[scnidx].old_shdr.sh_addr;
if (vaddr_delta || shdr_info[scnidx].idx != scnidx) {
if (sym->st_value)
INFO("0x%llx -> 0x%llx (delta %lld)",
sym->st_value,
sym->st_value + vaddr_delta,
vaddr_delta);
else {
INFO("(value is zero, not adjusting it)");
/* This might be a bit too paranoid, but symbols with values of
zero for which we are not adjusting the value must be in the
static-symbol section and refer to a section which is
not loaded at run time. If this assertion ever fails, figure
out why and also figure out whether the zero value should have
been adjusted, after all.
*/
ASSERT(!(shdr_info[sym->st_shndx].shdr.sh_flags & SHF_ALLOC));
ASSERT(shdr_info[i].shdr.sh_type == SHT_SYMTAB);
}
/* The section index of the symbol must coincide with
the shdr_info[] index of the section that the
symbol refers to. Since that section may have been
moved, its new setion index, which is stored in
the idx field, may have changed. However the index
of the original section must match.
*/
ASSERT(scnidx == elf_ndxscn(shdr_info[scnidx].scn));
if(unlikely(verbose_flag)) {
if (shdr_info[scnidx].idx != scnidx) {
INFO(" (updating sym->st_shndx = %lld --> %lld)\n",
sym->st_shndx,
shdr_info[scnidx].idx);
}
else INFO("(sym->st_shndx remains %lld)\n", sym->st_shndx);
}
sym->st_shndx = shdr_info[scnidx].idx;
if (sym->st_value)
sym->st_value += vaddr_delta;
FAILIF_LIBELF(gelf_update_symshndx(symdata,
NULL,
inner,
sym,
0) == 0,
gelf_update_symshndx);
}
else {
INFO(" (no change)\n");
}
} /* for each symbol */
} /* if it's a symbol table... */
} /* for each section... */
}
static void adjust_section_offset(Elf *newelf,
shdr_info_t *shdr_info,
Elf64_Sxword delta)
{
Elf_Scn *scn = elf_getscn (newelf, shdr_info->idx);
ASSERT(scn != NULL);
ASSERT(((Elf64_Sxword)shdr_info->shdr.sh_offset) + delta >= 0);
shdr_info->shdr.sh_offset += delta;
ASSERT(shdr_info->shdr.sh_addralign);
#ifdef DEBUG
/* The assumption is that the delta is calculated so that it will preserve
the alignment. Of course, we don't trust ourselves so we verify.
NOTE: The assertion below need not hold about NOBITS sections (such as
the .bss section), for which the offset in the file and the address at
which the section is to be loaded may differ.
*/
if (shdr_info->shdr.sh_type != SHT_NOBITS)
{
Elf64_Off new_offset = shdr_info->shdr.sh_offset;
new_offset += shdr_info->shdr.sh_addralign - 1;
new_offset &= ~((GElf_Off)(shdr_info->shdr.sh_addralign - 1));
ASSERT(shdr_info->shdr.sh_offset == new_offset);
}
#endif
INFO("\t\t\t\tsection offset %lld -> %lld%s\n",
shdr_info->old_shdr.sh_offset,
shdr_info->shdr.sh_offset,
(shdr_info->old_shdr.sh_offset ==
shdr_info->shdr.sh_offset ? " (SAME)" : ""));
/* If there is a delta for an ALLOC section, then the sections address must match the sections's offset in
the file, if that section is not marked SHT_NOBITS. For SHT_NOBITS sections, the two may differ.
Note that we compare against the old_shdr.sh_offset because we just modified shdr.sh_offset!
*/
ASSERT(!delta ||
!(shdr_info->shdr.sh_flags & SHF_ALLOC) ||
shdr_info->shdr.sh_type == SHT_NOBITS ||
shdr_info->shdr.sh_addr == shdr_info->old_shdr.sh_offset);
if ((shdr_info->shdr.sh_flags & SHF_ALLOC) == SHF_ALLOC)
{
ASSERT(shdr_info->shdr.sh_addr);
shdr_info->shdr.sh_addr += delta;
INFO("\t\t\t\tsection address %lld -> %lld%s\n",
shdr_info->old_shdr.sh_addr,
shdr_info->shdr.sh_addr,
(shdr_info->old_shdr.sh_addr ==
shdr_info->shdr.sh_addr ? " (SAME)" : ""));
}
/* Set the section header in the new file. There cannot be any
overflows. */
INFO("\t\t\t\tupdating section header (size %lld)\n",
shdr_info->shdr.sh_size);
FAILIF(!gelf_update_shdr (scn, &shdr_info->shdr),
"Could not update section header for section %s!\n",
shdr_info->name);
}
#ifdef MOVE_SECTIONS_IN_RANGES
static int get_end_of_range(shdr_info_t *shdr_info,
int num_shdr_info,
int start,
Elf64_Xword *alignment,
Elf32_Word *real_align)
{
int end = start;
ASSERT(start < num_shdr_info);
/* Note that in the loop below we do not check to see if a section is
being thrown away. If a section in the middle of a range is thrown
away, that will cause the section to be removed, but it will not cause
the relative offsets of the sections in the block to be modified.
*/
*alignment = real_align[start];
while (end < num_shdr_info &&
((shdr_info[end].shdr.sh_flags & SHF_ALLOC) == SHF_ALLOC) &&
((shdr_info[end].shdr.sh_type == SHT_PROGBITS) ||
(shdr_info[end].shdr.sh_type == SHT_INIT_ARRAY) ||
(shdr_info[end].shdr.sh_type == SHT_FINI_ARRAY) ||
(shdr_info[end].shdr.sh_type == SHT_PREINIT_ARRAY) ||
/* (shdr_info[end].shdr.sh_type == SHT_NOBITS) || */
#ifdef ARM_SPECIFIC_HACKS
/* SHF_ALLOC sections with with names starting with ".ARM." are
part of the ARM EABI extensions to ELF.
*/
!strncmp(shdr_info[end].name, ".ARM.", 5) ||
#endif
(shdr_info[end].shdr.sh_type == SHT_DYNAMIC)))
{
if (real_align[end] > *alignment) {
*alignment = real_align[end];
}
end++;
}
return end == start ? end + 1 : end;
}
#endif/*MOVE_SECTIONS_IN_RANGES*/
static GElf_Off update_last_offset(shdr_info_t *shdr_info,
range_list_t *section_ranges,
GElf_Off offset)
{
GElf_Off filesz = 0;
if (shdr_info->shdr.sh_type != SHT_NOBITS) {
/* This function is used as an assertion: if the range we are
adding conflicts with another range already in the list,
then add_unique_range() will call FAILIF().
*/
add_unique_range_nosort(section_ranges,
shdr_info->shdr.sh_offset,
shdr_info->shdr.sh_size,
shdr_info,
handle_range_error,
NULL);
filesz = shdr_info->shdr.sh_size;
}
/* Remember the last section written so far. */
if (offset < shdr_info->shdr.sh_offset + filesz) {
offset = shdr_info->shdr.sh_offset + filesz;
INFO("\t\t\t\tupdated lastoffset to %lld\n", offset);
}
return offset;
}
static GElf_Off move_sections(Elf *newelf,
shdr_info_t *shdr_info,
int num_shdr_info,
int start,
int end,
GElf_Off offset,
Elf64_Xword alignment,
range_list_t *section_ranges,
bool adjust_alloc_section_offsets)
{
/* The alignment parameter is expected to contain the largest alignment of
all sections in the block. Thus, when we iterate over all sections in
the block and apply the same offset to them, we are guaranteed to
preserve (a) the relative offsets between the sections in the block and
(b) the alignment requirements of each individual section.
*/
ASSERT(start < num_shdr_info);
ASSERT(end <= num_shdr_info);
Elf64_Sxword delta = offset - shdr_info[start].shdr.sh_offset;
delta += (alignment - 1);
delta &= ~(alignment - 1);
while (start < end) {
if (shdr_info[start].idx > 0) {
if (adjust_alloc_section_offsets || (shdr_info[start].shdr.sh_flags & SHF_ALLOC) != SHF_ALLOC) {
INFO("\t\t\t%03d:\tAdjusting offset of section %s "
"(index %d) from 0x%llx (%lld) to 0x%llx (%lld) (DELTA %lld)...\n",
start,
(shdr_info[start].name ?: "(no name)"),
shdr_info[start].idx,
shdr_info[start].old_shdr.sh_offset, shdr_info[start].old_shdr.sh_offset,
offset, offset,
delta);
/* Compute the new offset of the section. */
adjust_section_offset(newelf, shdr_info + start, delta);
}
else {
INFO("\t\t\t%03d: NOT adjusting offset of section %s (index %d)"
": (not moving SHF_ALLOC sections)...\n",
start,
(shdr_info[start].name ?: "(no name)"),
shdr_info[start].idx);
}
offset = update_last_offset(shdr_info + start,
section_ranges,
offset);
} /* if (shdr_info[start].idx > 0) */
else {
INFO("\t\t\t%03d: NOT adjusting offset of section %s (index %d)"
" (ignored)...\n",
start,
(shdr_info[start].name ?: "(no name)"),
shdr_info[start].idx);
}
start++;
}
sort_ranges(section_ranges);
return offset;
}
/* Compute the alignments of sections with consideration of segment
alignments. Returns an array of Elf32_Word containing the alignment
of sections. Callee is responsible to deallocate the array after use. */
Elf32_Word *
get_section_real_align (GElf_Ehdr *ehdr, GElf_Phdr *phdr_info,
struct shdr_info_t *shdr_info, int shdr_info_len)
{
size_t max_align_array_size;
Elf32_Word *max_align;
size_t first_section;
bool propagate_p;
int si, pi;
max_align_array_size = sizeof(Elf32_Word) * shdr_info_len;
max_align = (Elf32_Word*) malloc (max_align_array_size);
FAILIF(!max_align, "malloc(%zu) failed.\n", max_align_array_size);
/* Initialize alignment array. */
max_align[0] = 0;
for (si = 1; si < shdr_info_len; si++)
max_align[si] = shdr_info[si].shdr.sh_addralign;
/* Determine which sections need to be aligned with the alignment of
containing segments. Becasue the first section in a segment may
be deleted, we need to look at all sections and compare their offsets.
*/
for (pi = 0; pi < ehdr->e_phnum; ++pi) {
/* Skip null segment. */
if (phdr_info[pi].p_type == PT_NULL)
continue;
/* Look for the first non-deleted section of a segment in output.
We assume asections are sorted by offsets. Also check to see if
a segment starts with a section. We only want to propagate
alignment if the segment starts with a section. */
propagate_p = false;
first_section = 0;
for (si = 1; si < shdr_info_len && first_section == 0; si++) {
if (shdr_info[si].old_shdr.sh_offset == phdr_info[pi].p_offset)
propagate_p = true;
if (shdr_info[si].idx > 0
&& section_belongs_to_header(&shdr_info[si].old_shdr,
&phdr_info[pi]))
first_section = si;
}
if (!propagate_p || first_section == 0)
continue;
/* Adjust alignment of first section. Note that a section can appear
in multiple segments. We only need the extra alignment if the
section's alignment is smaller than that of the segment. */
if (first_section != 0 &&
max_align[first_section] < phdr_info[pi].p_align) {
max_align[first_section] = phdr_info[pi].p_align;
}
}
return max_align;
}
static range_list_t *
update_section_offsets(Elf *elf,
Elf *newelf,
GElf_Phdr *phdr_info,
shdr_info_t *shdr_info,
int num_shdr_info,
range_list_t *section_ranges,
bool adjust_alloc_section_offsets)
{
Elf32_Word *real_align;
ASSERT(section_ranges);
INFO("Updating section addresses and offsets...\n");
/* The initial value of lastoffset is set to the size of the ELF header
plus the size of the program-header table. libelf seems to always
place the program-header table for a new file immediately after the
ELF header itself... or I could not find any other way to change it
otherwise.
*/
GElf_Ehdr ehdr_mem, *ehdr;
ehdr = gelf_getehdr (elf, &ehdr_mem);
FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
const size_t ehdr_size = gelf_fsize (elf, ELF_T_EHDR, 1, EV_CURRENT);
FAILIF(ehdr->e_phoff != ehdr_size,
"Expecting the program-header table to follow the ELF header"
" immediately!\n");
GElf_Off lastoffset = 0;
lastoffset += ehdr_size;
lastoffset += ehdr->e_phnum * ehdr->e_phentsize;
INFO("Section offsets will start from %lld.\n", lastoffset);
int start = 1, end = 1;
ASSERT(num_shdr_info > 0);
real_align = get_section_real_align (ehdr, phdr_info, shdr_info,
num_shdr_info);
while (end < num_shdr_info) {
Elf64_Xword alignment;
/* end is the index one past the last section of the block. */
#ifdef MOVE_SECTIONS_IN_RANGES
end = get_end_of_range(shdr_info, num_shdr_info,
start, &alignment, real_align);
#else
end = start + 1;
alignment = real_align[start];
#endif
INFO("\tAdjusting sections [%d - %d) as a group (start offset %lld, alignment %lld)\n",
start, end, lastoffset, alignment);
lastoffset = move_sections(newelf,
shdr_info,
num_shdr_info,
start, end,
lastoffset,
alignment,
section_ranges,
adjust_alloc_section_offsets);
start = end;
}
ASSERT(lastoffset == get_last_address(section_ranges));
free (real_align);
return section_ranges;
}
void handle_range_error(range_error_t err, range_t *left, range_t *right)
{
shdr_info_t *info_l = (shdr_info_t *)left->user;
shdr_info_t *info_r = (shdr_info_t *)right->user;
ASSERT(info_l);
ASSERT(info_r);
switch (err) {
case ERROR_CONTAINS:
ERROR("ERROR: section [%s] (%lld, %lld bytes) contains "
"section [%s] (%lld, %lld bytes)\n",
info_l->name,
left->start, left->length,
info_r->name,
right->start, right->length);
break;
case ERROR_OVERLAPS:
ERROR("ERROR: Section [%s] (%lld, %lld bytes) intersects "
"section [%s] (%lld, %lld bytes)\n",
info_l->name,
left->start, left->length,
info_r->name,
right->start, right->length);
break;
default:
ASSERT(!"Unknown range error code!");
}
FAILIF(1, "Range error.\n");
}
#ifdef DEBUG
/* Functions to ELF file is still sane after adjustment. */
static bool
sections_overlap_p (GElf_Shdr *s1, GElf_Shdr *s2)
{
GElf_Addr a1, a2;
GElf_Off o1, o2;
if ((s1->sh_flags & s2->sh_flags & SHF_ALLOC) != 0) {
a1 = (s1->sh_addr > s2->sh_addr)? s1->sh_addr : s2->sh_addr;
a2 = ((s1->sh_addr + s1->sh_size < s2->sh_addr + s2->sh_size)?
(s1->sh_addr + s1->sh_size) : (s2->sh_addr + s2->sh_size));
if (a1 < a2)
return true;
}
if (s1->sh_type != SHT_NOBITS && s2->sh_type != SHT_NOBITS) {
o1 = (s1->sh_offset > s2->sh_offset)? s1->sh_offset : s2->sh_offset;
o2 = ((s1->sh_offset + s1->sh_size < s2->sh_offset + s2->sh_size)?
(s1->sh_offset + s1->sh_size) : (s2->sh_offset + s2->sh_size));
if (o1 < o2)
return true;
}
return false;
}
/* Return size of the overlapping portion of section S and segment P
in memory. */
static GElf_Word
mem_overlap_size (GElf_Shdr *s, GElf_Phdr *p)
{
GElf_Addr a1, a2;
if (s->sh_flags & SHF_ALLOC) {
a1 = p->p_vaddr > s->sh_addr ? p->p_vaddr : s->sh_addr;
a2 = ((p->p_vaddr + p->p_memsz < s->sh_addr + s->sh_size) ?
(p->p_vaddr + p->p_memsz) : (s->sh_addr + s->sh_size));
if (a1 < a2) {
return a2 - a1;
}
}
return 0;
}
/* Return size of the overlapping portion of section S and segment P
in file. */
static GElf_Word
file_overlap_size (GElf_Shdr *s, GElf_Phdr *p)
{
GElf_Off o1, o2;
if (s->sh_type != SHT_NOBITS) {
o1 = p->p_offset > s->sh_offset ? p->p_offset : s->sh_offset;
o2 = ((p->p_offset + p->p_filesz < s->sh_offset + s->sh_size) ?
(p->p_offset + p->p_filesz) : (s->sh_offset + s->sh_size));
if (o1 < o2) {
return o2 - o1;
}
}
return 0;
}
/* Verify the ELF file is sane. */
static void
verify_elf(GElf_Ehdr *ehdr, struct shdr_info_t *shdr_info, int shdr_info_len,
GElf_Phdr *phdr_info)
{
int si, sj, pi;
GElf_Word addralign;
GElf_Word m_size, f_size;
/* Check all sections */
for (si = 1; si < shdr_info_len; si++) {
if (shdr_info[si].idx <= 0)
continue;
/* Check alignment */
addralign = shdr_info[si].shdr.sh_addralign;
if (addralign != 0) {
if (shdr_info[si].shdr.sh_flags & SHF_ALLOC) {
FAILIF ((addralign - 1) & shdr_info[si].shdr.sh_addr,
"Load address %llx of section %s is not "
"aligned to multiples of %u\n",
(long long unsigned) shdr_info[si].shdr.sh_addr,
shdr_info[si].name,
addralign);
}
if (shdr_info[si].shdr.sh_type != SHT_NOBITS) {
FAILIF ((addralign - 1) & shdr_info[si].shdr.sh_offset,
"Offset %lx of section %s is not "
"aligned to multiples of %u\n",
shdr_info[si].shdr.sh_offset,
shdr_info[si].name,
addralign);
}
}
/* Verify that sections do not overlap. */
for (sj = si + 1; sj < shdr_info_len; sj++) {
if (shdr_info[sj].idx <= 0)
continue;
FAILIF (sections_overlap_p (&shdr_info[si].shdr,
&shdr_info[sj].shdr),
"sections %s and %s overlap.\n", shdr_info[si].name,
shdr_info[sj].name);
}
/* Verify that section is properly contained in segments. */
for (pi = 0; pi < ehdr->e_phnum; pi++) {
if (phdr_info[pi].p_type == PT_NULL)
continue;
f_size = file_overlap_size (&shdr_info[si].shdr, &phdr_info[pi]);
m_size = mem_overlap_size (&shdr_info[si].shdr, &phdr_info[pi]);
if (f_size) {
FAILIF (shdr_info[si].shdr.sh_size > phdr_info[pi].p_filesz,
"Section %s is larger than segment %d\n",
shdr_info[si].name, pi);
FAILIF (f_size != shdr_info[si].shdr.sh_size,
"Section %s partially overlaps segment %d in file.\n",
shdr_info[si].name, pi);
}
if (m_size) {
FAILIF (shdr_info[si].shdr.sh_size > phdr_info[pi].p_memsz,
"Section %s is larger than segment %d\n",
shdr_info[si].name, pi);
FAILIF (m_size != shdr_info[si].shdr.sh_size,
"Section %s partially overlaps segment %d in memory.\n",
shdr_info[si].name, pi);
}
}
}
}
#endif /* DEBUG */