/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* dldump(3c) creates a new file image from the specified input file.
*/
#include <fcntl.h>
#include <stdio.h>
#include <libelf.h>
#include <link.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "libld.h"
#include "msg.h"
#include "_librtld.h"
/*
* Generic clean up routine
*/
static void
{
if (icache) {
}
}
if (mcache)
if (ielf)
if (oelf)
if (melf)
if (fd)
if (opath)
}
/*
* The dldump(3x) interface directs control to the runtime linker. The runtime
* linker brings in librtld.so.1 to provide the underlying support for this
* is rather expensive to drag around with ld.so.1).
*
* rt_dldump(Rt_map * lmp, const char * opath, int flags, Addr addr)
*
* lmp provides the link-map of the ipath (the input file).
*
* opath specifies the output file.
*
* flags provides a variety of options that control how the new image will be
* relocated (if required).
*
* addr indicates the base address at which the associated input image is mapped
* within the process.
*
* The modes of operation and the various flags provide a number of combinations
* of images that can be created, some are useful, some maybe not. The
* following provide a couple of basic models for dldump(3x) use:
*
* new executable - dldump(0, outfile, RTLD_MEMORY)
*
* A dynamic executable may undergo some initialization
* and the results of this saved in a new file for later
* execution. The executable will presumable update
* parts of its data segment and heap (note that the heap
* should be acquired using malloc() so that it follows
* the end of the data segment for this technique to be
* useful). These updated memory elements are saved to the
* new file, including a new .SUNW_heap section if
* required.
*
* For greatest flexibility, no relocated information
* should be saved (by default any relocated information is
* returned to the value it had in its original file).
* This allows the new image to bind to new dynamic objects
* when executed on the same or newer upgrades of the OS.
*
* Fixing relocations by applying RTLD_REL_ALL will bind
* the image to the dependencies presently mapped as part
* of the process. Thus the new executable will only work
* correctly when these same dependencies map to exactly
* to the same locations. (note that RTLD_REL_RELATIVE will
* have no effect as dynamic executables commonly don't
* contain any relative relocations).
*
* new shared object - dldump(infile, outfile, RTLD_REL_RELATIVE)
*
* A shared object can be fixed to a known address so as
* to reduce its relocation overhead on startup. Because
* the new file is fixed to a new base address (which is
* the address at which the object was found mapped to the
* process) it is now a dynamic executable.
*
* Data changes that have occurred due to the object
* gaining control (at the least this would be .init
* processing) will not be carried over to the new image.
*
* By only performing relative relocations all global
* relocations are available for unique binding to each
* process - thus interposition etc. is still available.
*
* Using RTLD_REL_ALL will fix all relocations in the new
* file, which will certainly provide for faster startup
* of the new image, but at the loss of interposition
* flexibility.
*/
int
{
int pfd;
/*
* Get a /proc descriptor.
*/
(int)getpid());
return (1);
}
/*
* If we've been asked to process the dynamic executable we
* might not know its full path (this is prior to realpath()
* processing becoming default), and thus use /proc to obtain a
* file descriptor of the input file.
*/
return (1);
}
/*
* Obtain the process's status structure from which we can
* determine the size of the process's heap. Note, if the
* application is using mapmalloc then the heap size is going
* to be zero, and if we're dumping a data section that makes
* reference to the malloc'ed area we're not going to get a
* useful image.
*/
if (!(flags & RTLD_NOHEAP)) {
return (1);
}
}
} else {
/*
* Open the specified file.
*/
return (1);
}
}
/*
* Initialize with the ELF library and make sure this is a suitable
* ELF file we're dealing with.
*/
(void) elf_version(EV_CURRENT);
return (1);
}
return (1);
}
/*
* Make sure we can create the new output file.
*/
return (1);
}
return (1);
}
/*
* Obtain the input program headers. Remember the last data segments
* program header entry as this will be updated later to reflect any new
* heap section size.
*/
return (1);
}
/*
* Save the program header that contains the NOBITS section, or
* the last loadable program header if no NOBITS exists. A
* NOBITS section translates to a memory size requirement that
* is greater than the file data it is mapped from. Note that
* we inspect all headers just incase there only exist text
* segments.
*/
else if (data_phdr) {
} else
}
}
/*
* If there is no data segment, and a heap section is required,
* warn the user and disable the heap addition (Note that you can't
* simply append the heap to the last segment, as it might be a text
* segment, and would therefore have the wrong permissions).
*/
status = 0;
}
/*
* Obtain the input files section header string table.
*/
return (1);
}
return (1);
}
return (1);
}
/*
* Construct a cache to maintain the input files section information.
* Obtain an extra cache element if a heap addition is required. Also
* add an additional entry (marked FLG_C_END) to make the processing of
* this cache easier.
*/
return (1);
}
if (status)
num++;
return (1);
}
_icache++;
/*
* Traverse each section from the input file collecting the appropriate
* ELF information. Indicate how the section will be processed to
* generate the output image.
*/
return (1);
}
return (1);
}
/*
* Process any .SUNW_syminfo section. Symbols that are tagged
* as NO_DIRECT are collected, as they should not be bound to.
*/
if ((flags & ~RTLD_REL_RELATIVE) &&
return (1);
}
}
/*
* If the section has no address it is not part of the mapped
* image, and is unlikely to require any further processing.
* The section header string table will be rewritten (this isn't
* always necessary, it's only really required when relocation
* sections are renamed or sections are stripped, but we do
* things the same way regardless).
*/
MSG_ORIG(MSG_SCN_SHSTR))) == 0))
else if (flags & RTLD_STRIP) {
continue;
}
}
/*
* Skip relocation sections for the time being, they'll be
* analyzed after all sections have been processed.
*/
continue;
/*
* Sections at this point will simply be passed through to the
* output file. Keep track of the section header string table
* size.
*/
/*
* If a heap section is to be added to the output image,
* indicate that it will be added following the last data
* section.
*/
if (status) {
_icache++;
(char *)MSG_ORIG(MSG_SCN_HEAP);
}
}
}
/*
* Now that we've processed all input sections count the relocation
* entries (relocation sections need to reference their symbol tables).
*/
continue;
/*
* If any form of relocations are to be applied to the output
* image determine what relocation counts exist. These will be
* used to reorganize (localize) the relocation records.
*/
nodirect)) {
return (1);
}
}
}
/*
* If any form of relocations are to be applied to the output image
* then we will reorganize (localize) the relocation records. If this
* reorganization occurs, the relocation sections will no longer have a
* one-to-one relationship with the section they relocate, hence we
* rename them to a more generic name.
*/
continue;
if (rel_null_no) {
(char *)MSG_ORIG(MSG_SCN_RELOC);
}
}
}
/*
* If there is no data section, and a heap is required, warn the user
* and disable the heap addition.
*/
if (!data_cache) {
status = 0;
endx = 0;
}
/*
* Determine the value of _edata (which will also be _end) and its
* section index for updating the data segments phdr and symbol table
* information later. If a new heap section is being added, update
* the values appropriately.
*/
if (status)
if (endx) {
/* LINTED */
if (status)
endx++;
}
/*
* We're now ready to construct the new elf image.
*
* Obtain a new elf header and initialize it with any basic information
* that isn't calculated as part of elf_update().
*/
return (1);
}
if (addr)
/*
* Obtain a new set of program headers. Initialize these with the same
* information as the input program headers. Update the virtual address
* and the data segments size to reflect any new heap section.
*/
return (1);
}
if (status)
}
}
/*
* Establish a buffer for the new section header string table. This
* will be filled in as each new section is created.
*/
return (1);
}
*_shstr++ = '\0';
/*
* Use the input files cache information to generate new sections.
*/
/*
* Skip any excluded sections.
*/
continue;
/*
* Create a matching section header in the output file.
*/
return (1);
}
return (1);
}
/*
* If this is the heap section initialize the appropriate
* entries, otherwise simply use the original section header
* information.
*/
} else
/*
* Create a matching data buffer for this section.
*/
return (1);
}
/*
* Determine what data will be used for this section.
*/
/*
* Reassign the shstrtab to the new data buffer we're
* creating. Insure that the new elf header references
* this section header table.
*/
/* LINTED */
/*
* libelf deals with e_shnum for us, but we
* need to deal with e_shstrndx ourselves.
*/
return (1);
}
} else {
}
/*
* Assign the heap to the appropriate memory offset.
*/
/*
* If some relocations are to be saved in the new image
* then the relocation sections will be reorganized to
* localize their contents. These relocation sections
* will no longer have a one-to-one relationship with
* the section they relocate, hence we rename them and
* remove their sh_info info.
*/
} else {
/*
* By default simply pass the section through. If
* we've been asked to use the memory image of the
* input file reestablish the data buffer address.
*/
/*
* Update any NOBITS section to indicate that it now
* contains data. If this image is being created
* directly from the input file, zero out the .bss
* section (this saves ld.so.1 having to zero out memory
*/
if (!(flags & RTLD_MEMORY)) {
return (1);
}
}
}
}
/*
* Update the section header string table.
*/
/* LINTED */
/*
* For each section that has a virtual address update its
* address to the fixed location of the new image.
*/
/*
* If we've inserted a new section any later sections may need
* their sh_link fields updated (.stabs comes to mind).
*/
}
/*
* Generate the new image, and obtain a new elf descriptor that will
* allow us to write and update the new image.
*/
return (1);
}
return (1);
}
return (1);
}
return (1);
}
/*
* Construct a cache to maintain the memory files section information.
*/
return (1);
}
_mcache++;
return (1);
}
return (1);
}
}
/*
* Now that we have a complete description of the new image update any
* sections that are required.
*
* o reset any symbol table entries.
*
* o reset any relocation entries.
*
* o reset dynamic entries.
*/
continue;
_mcache++;
/*
* Update the symbol table entries. _end and _edata will be
* changed to reflect any heap addition. All global symbols
* will be updated to their new fixed address.
*/
continue;
}
/*
* Update any relocations. All relocation requirements will
* have been established in count_reloc().
*/
(rel_null_no * rel_entsize));
(rel_data_no * rel_entsize));
}
continue;
}
/*
* Perform any dynamic entry updates after all relocation
* processing has been carried out (as its possible the .dynamic
* section could occur before the .rel sections, delay this
* processing until last).
*/
}
if (dyn_cache) {
/*
* If we're dumping a fixed object (typically the dynamic
* executable) compensate for its real base address.
*/
if (!addr)
elf_checksum(melf))) {
return (1);
}
}
/*
* Having completed all section updates write the memory file out.
*/
return (1);
}
return (0);
}